diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index 907fc199f..52cbf303a 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -17,6 +17,7 @@ import org.apache.xmlbeans.*; import org.apache.xmlbeans.impl.common.InvalidLexicalValueException; +import org.apache.xmlbeans.impl.common.XMLChar; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; @@ -482,6 +483,13 @@ public static QName lexQName(CharSequence charSeq, NamespaceContext nscontext) { localname = charSeq.toString(); } + if (!prefix.isEmpty() && !XMLChar.isValidNCName(prefix)) { + throw new InvalidLexicalValueException("invalid xsd:QName '" + charSeq + "'"); + } + if (!XMLChar.isValidNCName(localname)) { + throw new InvalidLexicalValueException("invalid xsd:QName '" + charSeq + "'"); + } + String uri = nscontext.getNamespaceURI(prefix); if (uri == null) { diff --git a/src/test/java/misc/checkin/RichParserTests.java b/src/test/java/misc/checkin/RichParserTests.java index f0c1049e5..1c74de7b1 100755 --- a/src/test/java/misc/checkin/RichParserTests.java +++ b/src/test/java/misc/checkin/RichParserTests.java @@ -83,6 +83,30 @@ void testInvalidBase64ThrowsInvalidLexicalValue() throws Exception { assertThrows(InvalidLexicalValueException.class, () -> attByName.getAttributeBase64Value("", "b")); } + @Test + void testInvalidQNameThrowsInvalidLexicalValue() throws Exception { + // The localname of an xsd:QName must be an NCName, so a value whose + // local part still contains a ':' (or any other non-NCName char) is + // outside the lexical space. lexQName resolved the prefix but never + // checked the parts, so "p:b:c" came back as QName{uri}b:c instead of + // being rejected like the holder validate path does. + XMLStreamReaderExt colonInLocal = atFirstStartElement("p:b:c"); + assertThrows(InvalidLexicalValueException.class, colonInLocal::getQNameValue); + + XMLStreamReaderExt spaceInLocal = atFirstStartElement("b c"); + assertThrows(InvalidLexicalValueException.class, spaceInLocal::getQNameValue); + + XMLStreamReaderExt emptyLocal = atFirstStartElement("p:"); + assertThrows(InvalidLexicalValueException.class, emptyLocal::getQNameValue); + + XMLStreamReaderExt attColon = atFirstStartElement(""); + assertThrows(InvalidLexicalValueException.class, () -> attColon.getAttributeQNameValue(0)); + + // a well-formed prefixed QName still resolves + XMLStreamReaderExt good = atFirstStartElement("p:good"); + assertEquals(new QName("urn:x", "good"), good.getQNameValue()); + } + private static XMLStreamReaderExt atFirstStartElement(String xml) throws Exception { XMLStreamReader xsr = XmlObject.Factory.parse(xml).newXMLStreamReader(); XMLStreamReaderExt ext = new XMLStreamReaderExtImpl(xsr);