Sophie

Sophie

distrib > Mageia > 7 > armv7hl > by-pkgid > 48237abfbaa2087c763e0401d24e053d > files > 1

dom4j-2.0.0-4.1.mga7.src.rpm

From e598eb43d418744c4dbf62f647dd2381c9ce9387 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Filip=20Jirs=C3=A1k?= <filip@jirsak.org>
Date: Sun, 1 Jul 2018 11:27:15 +0200
Subject: [PATCH] #48 Validate QName inputs - throw IllegalArgumentException
 when qualified name contains disallowed character.

---
 src/main/java/org/dom4j/Namespace.java        |   4 +
 src/main/java/org/dom4j/QName.java            | 101 ++++++++++++++++++
 src/main/java/org/dom4j/tree/QNameCache.java  |   2 +
 src/test/java/org/dom4j/AllowedCharsTest.java |  78 ++++++++++++++
 4 files changed, 185 insertions(+)
 create mode 100644 src/test/java/org/dom4j/AllowedCharsTest.java

diff --git a/src/main/java/org/dom4j/Namespace.java b/src/main/java/org/dom4j/Namespace.java
index fd123b9..8f948ad 100644
--- a/src/main/java/org/dom4j/Namespace.java
+++ b/src/main/java/org/dom4j/Namespace.java
@@ -49,6 +49,10 @@
     public Namespace(String prefix, String uri) {
         this.prefix = (prefix != null) ? prefix : "";
         this.uri = (uri != null) ? uri : "";
+
+        if (!this.prefix.isEmpty()) {
+            QName.validateNCName(this.prefix);
+        }
     }
 
     /**
diff --git a/src/main/java/org/dom4j/QName.java b/src/main/java/org/dom4j/QName.java
index 9ac0d4d..e9b2170 100644
--- a/src/main/java/org/dom4j/QName.java
+++ b/src/main/java/org/dom4j/QName.java
@@ -11,6 +11,7 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.regex.Pattern;
 
 import org.dom4j.tree.QNameCache;
 import org.dom4j.util.SingletonStrategy;
@@ -21,11 +22,86 @@
  * object is immutable.
  *
  * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
+ * @author Filip Jirsák
  */
 public class QName implements Serializable {
     /** The Singleton instance */
     private static SingletonStrategy<QNameCache> singleton = null;
 
+    /**
+     * {@code NameStartChar} without colon.
+     *
+     * <pre>NameStartChar	::=	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final String NAME_START_CHAR = "_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
+
+    /**
+     * {@code NameChar} without colon.
+     *
+     * <pre>NameChar	::=	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final String NAME_CHAR = NAME_START_CHAR + "-.0-9\u00B7\u0300-\u036F\u203F-\u2040";
+
+    /**
+     * {@code NCName}
+     *
+     * <pre>
+     * NCName		::=	NCNameStartChar NCNameChar*	(An XML Name, minus the ":")
+     * NCNameChar	::=	NameChar -':'
+     * NCNameStartChar	::=	NameStartChar -':'
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final String NCNAME = "["+NAME_START_CHAR+"]["+NAME_CHAR+"]*";
+
+    /**
+     * Regular expression for {@code Name} (with colon).
+     *
+     * <pre>Name	::=	NameStartChar (NameChar)*</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final Pattern RE_NAME = Pattern.compile("[:"+NAME_START_CHAR+"][:"+NAME_CHAR+"]*");
+
+    /**
+     * Regular expression for {@code NCName}.
+     *
+     * <pre>
+     * NCName		::=	NCNameStartChar NCNameChar*	(An XML Name, minus the ":")
+     * NCNameChar	::=	NameChar -':'
+     * NCNameStartChar	::=	NameStartChar -':'
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final Pattern RE_NCNAME = Pattern.compile(NCNAME);
+
+    /**
+     * Regular expression for {@code QName}.
+     *
+     * <pre>
+     * QName		::=	PrefixedName | UnprefixedName
+     * PrefixedName	::=	Prefix ':' LocalPart
+     * UnprefixedName	::=	LocalPart
+     * Prefix		::=	NCName
+     * LocalPart	::=	NCName
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final Pattern RE_QNAME = Pattern.compile("(?:"+NCNAME+":)?"+NCNAME);
+
     static {
         try {
             String defaultSingletonClass = "org.dom4j.util.SimpleSingleton";
@@ -71,6 +147,11 @@ public QName(String name, Namespace namespace) {
         this.name = (name == null) ? "" : name;
         this.namespace = (namespace == null) ? Namespace.NO_NAMESPACE
                 : namespace;
+        if (this.namespace.equals(Namespace.NO_NAMESPACE)) {
+            validateName(this.name);
+        } else {
+            validateNCName(this.name);
+        }
     }
 
     public QName(String name, Namespace namespace, String qualifiedName) {
@@ -78,6 +159,8 @@ public QName(String name, Namespace namespace, String qualifiedName) {
         this.qualifiedName = qualifiedName;
         this.namespace = (namespace == null) ? Namespace.NO_NAMESPACE
                 : namespace;
+        validateNCName(this.name);
+        validateQName(this.qualifiedName);
     }
 
     public static QName get(String name) {
@@ -251,6 +334,24 @@ private static QNameCache getCache() {
         QNameCache cache = singleton.instance();
         return cache;
     }
+
+    private static void validateName(String name) {
+        if (!RE_NAME.matcher(name).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in name: '%s'.", name));
+        }
+    }
+
+    protected static void validateNCName(String ncname) {
+        if (!RE_NCNAME.matcher(ncname).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in local name: '%s'.", ncname));
+        }
+    }
+
+    private static void validateQName(String qname) {
+        if (!RE_QNAME.matcher(qname).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in qualified name: '%s'.", qname));
+        }
+    }
 }
 
 
diff --git a/src/main/java/org/dom4j/tree/QNameCache.java b/src/main/java/org/dom4j/tree/QNameCache.java
index 330f379..d37e8aa 100644
--- a/src/main/java/org/dom4j/tree/QNameCache.java
+++ b/src/main/java/org/dom4j/tree/QNameCache.java
@@ -152,6 +152,8 @@ public QName get(String qualifiedName, String uri) {
 
         if (index < 0) {
             return get(qualifiedName, Namespace.get(uri));
+        } else if (index == 0){
+            throw new IllegalArgumentException("Qualified name cannot start with ':'.");
         } else {
             String name = qualifiedName.substring(index + 1);
             String prefix = qualifiedName.substring(0, index);
diff --git a/src/test/java/org/dom4j/AllowedCharsTest.java b/src/test/java/org/dom4j/AllowedCharsTest.java
new file mode 100644
index 0000000..20c1de0
--- /dev/null
+++ b/src/test/java/org/dom4j/AllowedCharsTest.java
@@ -0,0 +1,78 @@
+package org.dom4j;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Filip Jirsák
+ */
+public class AllowedCharsTest {
+    @Test
+    public void localName() {
+        QName.get("element");
+        QName.get(":element");
+        QName.get("elem:ent");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void localNameFail() {
+        QName.get("!element");
+    }
+
+    @Test
+    public void qname() {
+        QName.get("element", "http://example.com/namespace");
+        QName.get("ns:element", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void qnameFail1() {
+        QName.get("ns:elem:ent", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void qnameFail2() {
+        QName.get(":nselement", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementLT() {
+        DocumentHelper.createElement("element<name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementGT() {
+        DocumentHelper.createElement("element>name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementAmpersand() {
+        DocumentHelper.createElement("element&name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElement() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("element>name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementQualified() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("element>name", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementQualifiedPrefix() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("ns:element>name", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementPrefix() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("ns>:element", "http://example.com/namespace");
+    }
+
+    //TODO It is illegal to create element or attribute with namespace prefix and empty namespace IRI.
+    //See https://www.w3.org/TR/2006/REC-xml-names11-20060816/#scoping
+}