Skip to content
This repository was archived by the owner on Oct 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions Amazon.IonDotnet.Tests/Internals/TextScannerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using System;
using System.IO;
using System.Text;
using Amazon.IonDotnet.Internals.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Amazon.IonDotnet.Tests.Internals
{
[TestClass]
public class TextScannerTest
{
private TextScanner CreateScanner(string input)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(input);
var stream = new MemoryStream(bytes);
var textStream = new UnicodeStream(stream);
return new TextScanner(textStream);
}

[TestMethod]
public void TestMalformedBlobHandling()
{
// Test simple malformed blob
var scanner = CreateScanner("{{");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test the specific malformed input that caused the infinite loop
var hexString = "282f5959595959595959593a3a282b2727357b7b7b7b7b7b7b7b27272728fb2b272829";
var bytes = new byte[hexString.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}

var stream = new MemoryStream(bytes);
var textStream = new UnicodeStream(stream);
scanner = new TextScanner(textStream);

Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});
}

[TestMethod]
public void TestSingleQuotedStringEofHandling()
{
// Test EOF in single-quoted string
var scanner = CreateScanner("'unterminated");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test EOF after escape in single-quoted string
scanner = CreateScanner("'escaped\\");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});
}

[TestMethod]
public void TestTripleQuotedStringEofHandling()
{
// Test EOF in triple-quoted string
var scanner = CreateScanner("'''unterminated");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test EOF after partial triple quote
scanner = CreateScanner("'''content''");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test EOF after escape in triple-quoted string
scanner = CreateScanner("'''escaped\\");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});
}

[TestMethod]
public void TestDoubleQuotedStringEofHandling()
{
// Test EOF in double-quoted string
var scanner = CreateScanner("\"unterminated");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test EOF after escape in double-quoted string
scanner = CreateScanner("\"escaped\\");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});
}

[TestMethod]
public void TestMalformedClobHandling()
{
// Test malformed clob with missing closing braces
var scanner = CreateScanner("{{\"clob content\"");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});

// Test malformed clob with triple quotes
scanner = CreateScanner("{{{'''clob content");
Assert.ThrowsException<UnexpectedEofException>(() =>
{
scanner.NextToken();
while (scanner.Token != TextConstants.TokenEof)
{
scanner.NextToken();
}
});
}
}
}
60 changes: 48 additions & 12 deletions Amazon.IonDotnet/Internals/Text/TextScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public void LoadBlob(StringBuilder sb)
var c = this.SkipOverWhiteSpace(CommentStrategy.Break);
while (true)
{
if (c == TextConstants.TokenEof)
if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}
Expand All @@ -217,7 +217,7 @@ public void LoadBlob(StringBuilder sb)
}

c = this.ReadChar();
if (c == TextConstants.TokenEof)
if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}
Expand Down Expand Up @@ -1006,7 +1006,7 @@ private void SkipOverBlob()
var c = this.SkipOverWhiteSpace(CommentStrategy.Break);
while (true)
{
if (c == TextConstants.TokenEof)
if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}
Expand All @@ -1020,7 +1020,8 @@ private void SkipOverBlob()
}

c = this.ReadChar();
if (c == TextConstants.TokenEof)

if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}
Expand All @@ -1038,21 +1039,40 @@ private void SkipTripleQuotedString(CommentStrategy commentStrategy)
var c = this.ReadChar();
switch (c)
{
case -1:
case CharacterSequence.CharSeqEof:
throw new UnexpectedEofException();
case '\\':
this.ReadChar();
var escaped = this.ReadChar();
if (escaped == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

break;
case '\'':
c = this.ReadChar();
if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

// the 2nd '
if (c == '\'')
{
c = this.ReadChar();
if (c == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

// the 3rd
if (c == this.ReadChar())
var next = this.ReadChar();
if (next == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

if (c == next)
{
c = this.SkipOverWhiteSpace(commentStrategy);
if (c == '\'' && this.Is2SingleQuotes())
Expand Down Expand Up @@ -1515,12 +1535,23 @@ private int SkipSingleQuotedString()
var c = this.ReadStringChar(Characters.ProhibitionContext.None);
switch (c)
{
case -1:
case CharacterSequence.CharSeqEof:
throw new UnexpectedEofException();
case '\'':
return this.ReadChar();
var next = this.ReadChar();
if (next == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

return next;
case '\\':
this.ReadChar();
var escaped = this.ReadChar();
if (escaped == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

break;
}
}
Expand All @@ -1537,7 +1568,7 @@ private void SkipDoubleQuotedString()
var c = this.ReadStringChar(Characters.ProhibitionContext.None);
switch (c)
{
case -1:
case CharacterSequence.CharSeqEof:
throw new UnexpectedEofException();
case CharacterSequence.CharSeqEscapedNewlineSequence1:
case CharacterSequence.CharSeqEscapedNewlineSequence2:
Expand All @@ -1547,7 +1578,12 @@ private void SkipDoubleQuotedString()
case '"':
return;
case '\\':
this.ReadChar();
var escaped = this.ReadChar();
if (escaped == CharacterSequence.CharSeqEof)
{
throw new UnexpectedEofException();
}

break;
}
}
Expand Down