This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git


The following commit(s) were added to refs/heads/master by this push:
     new e623a26a Bound the nesting depth of annotation markup (#167)
e623a26a is described below

commit e623a26af6169c73eafbc410cc4c117afa2dcb2a
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Thu Sep 24 10:59:36 2026 +0100

    Bound the nesting depth of annotation markup (#167)
---
 .../apache/ws/commons/schema/SchemaBuilder.java    | 46 ++++++++++++++++++++
 .../src/test/java/tests/NestingDepthLimitTest.java | 50 ++++++++++++++++++++++
 2 files changed, 96 insertions(+)

diff --git 
a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java 
b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java
index e7609d9f..d038cad2 100644
--- 
a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java
+++ 
b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java
@@ -151,6 +151,50 @@ public class SchemaBuilder {
         collection.builderNestingDepth--;
     }
 
+    /**
+     * Refuse appinfo or documentation markup nested deeper than the structural
+     * bound allows. The markup is not schema structure, so the handlers never
+     * count it, but it is copied with a recursive cloneNode on this same 
thread
+     * stack, so it counts toward the same bound. Measured iteratively, as the
+     * point is to avoid recursing over it.
+     */
+    private void checkMarkupDepth(Element content) {
+        int depth = 0;
+        int maxDepth = 0;
+        Node node = content.getFirstChild();
+        if (node != null) {
+            depth = 1;
+        }
+        while (node != null) {
+            if (depth > maxDepth) {
+                maxDepth = depth;
+                if (collection.builderNestingDepth + maxDepth > 
MAX_NESTING_DEPTH) {
+                    throw new XmlSchemaException("The markup of an annotation 
is nested more than "
+                                                 + MAX_NESTING_DEPTH + " 
levels deep, counting the"
+                                                 + " schema structure around 
it; refusing to build it."
+                                                 + " The limit may be changed 
with the"
+                                                 + " 
org.apache.ws.commons.schema.maxNestingDepth"
+                                                 + " system property.");
+                }
+            }
+            if (node.getFirstChild() != null) {
+                node = node.getFirstChild();
+                depth++;
+                continue;
+            }
+            while (node != null && node.getNextSibling() == null) {
+                node = node.getParentNode();
+                depth--;
+                if (node == content) {
+                    node = null;
+                }
+            }
+            if (node != null) {
+                node = node.getNextSibling();
+            }
+        }
+    }
+
     private static int getIntProperty(String name, int defaultValue) {
         try {
             Integer value = Integer.getInteger(name);
@@ -306,6 +350,7 @@ public class SchemaBuilder {
      */
     XmlSchemaAppInfo handleAppInfo(Element content) {
         XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
+        checkMarkupDepth(content);
         NodeList markup = new DocumentFragmentNodeList(content);
 
         if (!content.hasAttribute("source") && markup.getLength() == 0) {
@@ -418,6 +463,7 @@ public class SchemaBuilder {
     // to collection
     XmlSchemaDocumentation handleDocumentation(Element content) {
         XmlSchemaDocumentation documentation = new XmlSchemaDocumentation();
+        checkMarkupDepth(content);
         List<Node> markup = getChildren(content);
 
         if (!content.hasAttribute("source") && 
!content.hasAttribute("xml:lang") && markup == null) {
diff --git a/xmlschema-core/src/test/java/tests/NestingDepthLimitTest.java 
b/xmlschema-core/src/test/java/tests/NestingDepthLimitTest.java
index d9323542..e9c70811 100644
--- a/xmlschema-core/src/test/java/tests/NestingDepthLimitTest.java
+++ b/xmlschema-core/src/test/java/tests/NestingDepthLimitTest.java
@@ -129,6 +129,56 @@ public class NestingDepthLimitTest extends Assert {
         assertNotNull(schema);
     }
 
+    /**
+     * Annotation markup is copied with a recursive cloneNode, so deep markup
+     * inside xs:documentation or xs:appinfo must hit the same bound rather
+     * than overflow the stack.
+     */
+    @Test
+    public void testDeeplyNestedDocumentationMarkupIsRejected() throws 
Exception {
+        assertMarkupRejected("documentation");
+    }
+
+    @Test
+    public void testDeeplyNestedAppInfoMarkupIsRejected() throws Exception {
+        assertMarkupRejected("appinfo");
+    }
+
+    @Test
+    public void testReasonablyNestedMarkupStillParses() throws Exception {
+        XmlSchemaCollection collection = new XmlSchemaCollection();
+        XmlSchema schema = collection.read(new 
StringReader(buildAnnotatedSchema("documentation", 50)));
+        assertNotNull(schema);
+        schema = collection.read(new 
StringReader(buildAnnotatedSchema("appinfo", 50)));
+        assertNotNull(schema);
+    }
+
+    private void assertMarkupRejected(String kind) {
+        XmlSchemaCollection collection = new XmlSchemaCollection();
+        try {
+            collection.read(new StringReader(buildAnnotatedSchema(kind, 
5000)));
+            fail("xs:" + kind + " markup nested 5000 levels deep should be 
rejected.");
+        } catch (XmlSchemaException expected) {
+            assertTrue(expected.getMessage().contains("nested"));
+        }
+    }
+
+    private String buildAnnotatedSchema(String kind, int depth) {
+        StringBuilder schema = new StringBuilder();
+        schema.append("<xs:schema 
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"";)
+            .append(" 
targetNamespace=\"urn:markup-").append(kind).append("\">");
+        schema.append("<xs:annotation><xs:").append(kind).append(">");
+        for (int i = 0; i < depth; i++) {
+            schema.append("<a>");
+        }
+        schema.append("text");
+        for (int i = 0; i < depth; i++) {
+            schema.append("</a>");
+        }
+        
schema.append("</xs:").append(kind).append("></xs:annotation></xs:schema>");
+        return schema.toString();
+    }
+
     private String buildNestedSchema(int depth) {
         return buildNestedSchema(depth, "urn:nesting");
     }

Reply via email to