From decb887b0a35fa8fd758cc1e3a08341e99441d12 Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Wed, 26 Aug 2020 21:15:52 +0200 Subject: [PATCH] Throw if markup contains unescaped close tag --- src/Spectre.Console.Tests/Unit/MarkupTests.cs | 40 +++++++++++++++++++ .../Internal/Text/Markup/MarkupTokenizer.cs | 26 ++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/Spectre.Console.Tests/Unit/MarkupTests.cs diff --git a/src/Spectre.Console.Tests/Unit/MarkupTests.cs b/src/Spectre.Console.Tests/Unit/MarkupTests.cs new file mode 100644 index 0000000..4909570 --- /dev/null +++ b/src/Spectre.Console.Tests/Unit/MarkupTests.cs @@ -0,0 +1,40 @@ +using System; +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests.Unit +{ + public sealed class MarkupTests + { + [Theory] + [InlineData("Hello [[ World ]")] + [InlineData("Hello [[ World ] !")] + public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input) + { + // Given + var fixture = new PlainConsole(); + + // When + var result = Record.Exception(() => new Markup(input)); + + // Then + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + result.Message.ShouldBe("Encountered unescaped ']' token at position 16"); + } + + [Fact] + public void Should_Escape_Markup_Blocks_As_Expected() + { + // Given + var fixture = new PlainConsole(); + var markup = new Markup("Hello [[ World ]] !"); + + // When + fixture.Render(markup); + + // Then + fixture.Output.ShouldBe("Hello [ World ] !"); + } + } +} diff --git a/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs b/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs index 5737a8a..228ce85 100644 --- a/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs +++ b/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs @@ -91,6 +91,8 @@ namespace Spectre.Console.Internal { var position = _reader.Position; var builder = new StringBuilder(); + + var encounteredClosing = false; while (!_reader.Eof) { current = _reader.Peek(); @@ -98,10 +100,34 @@ namespace Spectre.Console.Internal { break; } + else if (current == ']') + { + if (encounteredClosing) + { + _reader.Read(); + encounteredClosing = false; + continue; + } + + encounteredClosing = true; + } + else + { + if (encounteredClosing) + { + throw new InvalidOperationException( + $"Encountered unescaped ']' token at position {_reader.Position}"); + } + } builder.Append(_reader.Read()); } + if (encounteredClosing) + { + throw new InvalidOperationException($"Encountered unescaped ']' token at position {_reader.Position}"); + } + Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position); return true; }