Fix an assert that happens on malformed XML (#21)

Add two tests that are hitting the assert.

Names starting with a colon are valid according to the spec, but the spec also recommends that they only be used for namespaces. To allow proper error recovery we simplify and do not accept colons in names.
This commit is contained in:
Kirill Osenkov 2020-03-30 17:50:25 -07:00 коммит произвёл GitHub
Родитель 5d483b5143
Коммит f112aae3b9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 38 добавлений и 7 удалений

Просмотреть файл

@ -59,6 +59,15 @@ namespace MonoDevelop.Xml.Parser
public static bool IsFirstNameChar (int ch)
{
// Names starting with a colon are valid according to the spec,
// but the spec also recommends that they only be used for namespaces.
// To allow proper error recovery we simplify and do not accept
// colons in names.
// https://www.w3.org/TR/xml/#NT-NameStartChar
if (ch == ':') {
return false;
}
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
return true;
} else if ((uint) ch <= 0xFFFF) {
@ -134,6 +143,15 @@ namespace MonoDevelop.Xml.Parser
public static bool IsNameChar (int ch)
{
// Names starting with a colon are valid according to the spec,
// but the spec also recommends that they only be used for namespaces.
// To allow proper error recovery we simplify and do not accept
// colons in names.
// https://www.w3.org/TR/xml/#NT-NameStartChar
if (ch == ':') {
return false;
}
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
return true;
} else if ((uint) ch <= 0xFFFF) {

Просмотреть файл

@ -34,22 +34,17 @@ namespace MonoDevelop.Xml.Parser
{
public class XmlNameState : XmlParserState
{
internal static bool IsValidNameStart (char c)
{
return char.IsLetter (c) || c == '_';
}
public override XmlParserState PushChar (char c, XmlParserContext context, ref string rollback)
{
var namedObject = context.Nodes.Peek () as INamedXObject;
if (namedObject == null || namedObject.Name.Prefix != null)
throw new InvalidOperationException ("Invalid state");
Debug.Assert (context.CurrentStateLength > 0 || IsValidNameStart (c),
Debug.Assert (context.CurrentStateLength > 0 || XmlChar.IsFirstNameChar (c),
"First character pushed to a XmlTagNameState must be a letter.");
Debug.Assert (context.CurrentStateLength > 0 || context.KeywordBuilder.Length == 0,
"Keyword builder must be empty when state begins.");
if (XmlChar.IsWhitespace (c) || c == '<' || c == '>' || c == '/' || c == '=') {
rollback = string.Empty;
if (context.KeywordBuilder.Length == 0) {

Просмотреть файл

@ -435,5 +435,23 @@ namespace MonoDevelop.Xml.Tests.Parser
Assert.AreEqual (0, processingInstruction.Span.Start);
Assert.AreEqual (5, processingInstruction.Span.Length);
}
[Test]
public void NameStartsWithWhitespaceAndColon ()
{
var docTxt = "< :";
var parser = new XmlTreeParser (CreateRootState ());
parser.Parse (docTxt);
}
[Test]
public void MismatchedElementNameWithWhitespaceInName2 ()
{
var docTxt = "<X><n\n:a></a><b></X>";
var parser = new XmlTreeParser (CreateRootState ());
parser.Parse (docTxt);
parser.AssertEmpty ();
parser.AssertErrorCount (4);
}
}
}