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 b04d9ba8 Count a cached base type's whole derivation chain against the 
walk depth (#178)
b04d9ba8 is described below

commit b04d9ba8b2c192eeede7da9a91652bc14c17d581
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Thu Sep 24 17:01:40 2026 +0100

    Count a cached base type's whole derivation chain against the walk depth 
(#178)
---
 .../ws/commons/schema/walker/XmlSchemaScope.java   | 42 +++++++++++++++-------
 .../schema/walker/WalkerDepthLimitTest.java        | 35 ++++++++++++++++++
 2 files changed, 64 insertions(+), 13 deletions(-)

diff --git 
a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java
 
b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java
index fb835948..f7cb595f 100644
--- 
a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java
+++ 
b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java
@@ -80,6 +80,13 @@ final class XmlSchemaScope {
     private int maxDepth;
     private final Set<QName> attributeGroupsInProgress = new HashSet<QName>();
 
+    /*
+     * The number of types in this scope's derivation chain, counting its own: 
one more than the
+     * deepest scope it derives from. A cached scope is not walked again, so 
the recursion bound
+     * in walkWithCycleCheck() never sees the chain below it; this carries 
that chain's length.
+     */
+    private int derivationDepth = 1;
+
     /**
      * Initialization of members to be filled in during the walk.
      */
@@ -128,11 +135,7 @@ final class XmlSchemaScope {
         // Each level of derivation recurses; an acyclic chain can still
         // exhaust the thread stack.
         if (typesInProgress.size() >= maxDepth) {
-            throw new XmlSchemaException("The type " + getName(type, 
"{Anonymous}")
-                                         + " is derived through more than " + 
maxDepth
-                                         + " levels of base types; refusing to 
walk it. The limit"
-                                         + " may be changed with the "
-                                         + XmlSchemaWalker.MAX_DEPTH_PROPERTY 
+ " system property.");
+            throw derivedTooDeeply(type);
         }
         if (!typesInProgress.add(type)) {
             throw new XmlSchemaException("Cyclic type derivation detected 
involving type "
@@ -794,15 +797,28 @@ final class XmlSchemaScope {
     }
 
     private XmlSchemaScope getScope(XmlSchemaType type) {
-        if ((type.getQName() != null) && 
scopeCache.containsKey(type.getQName())) {
-            return scopeCache.get(type.getQName());
-        } else {
-            XmlSchemaScope scope = new XmlSchemaScope(this, type);
-            if (type.getQName() != null) {
-                scopeCache.put(type.getQName(), scope);
-            }
-            return scope;
+        final boolean cached = (type.getQName() != null) && 
scopeCache.containsKey(type.getQName());
+        final XmlSchemaScope scope = cached ? scopeCache.get(type.getQName()) 
: new XmlSchemaScope(this, type);
+
+        // The whole chain counts, including the part a cached scope already 
walked: the types in
+        // progress above this point, then the base scope's own chain.
+        if (typesInProgress.size() + scope.derivationDepth > maxDepth) {
+            throw derivedTooDeeply(type);
         }
+        derivationDepth = Math.max(derivationDepth, scope.derivationDepth + 1);
+
+        if (!cached && (type.getQName() != null)) {
+            scopeCache.put(type.getQName(), scope);
+        }
+        return scope;
+    }
+
+    private XmlSchemaException derivedTooDeeply(XmlSchemaType type) {
+        return new XmlSchemaException("The type " + getName(type, 
"{Anonymous}")
+                                      + " is derived through more than " + 
maxDepth
+                                      + " levels of base types; refusing to 
walk it. The limit"
+                                      + " may be changed with the "
+                                      + XmlSchemaWalker.MAX_DEPTH_PROPERTY + " 
system property.");
     }
 
     private QName getUserRecognizedType(QName simpleType, XmlSchemaTypeInfo 
parent) {
diff --git 
a/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/WalkerDepthLimitTest.java
 
b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/WalkerDepthLimitTest.java
index f4cd8570..b852ad37 100644
--- 
a/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/WalkerDepthLimitTest.java
+++ 
b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/WalkerDepthLimitTest.java
@@ -131,6 +131,41 @@ public class WalkerDepthLimitTest extends Assert {
         return body.toString();
     }
 
+    /**
+     * S1 restricts S0, S2 restricts S1, and so on, and root has an optional 
child of type S_k for
+     * every k that is a multiple of step, in increasing order. Each child's 
type is walked with the
+     * types before it already walked and cached.
+     */
+    private static String steppedRestrictionChain(int length, int step) {
+        StringBuilder body = new StringBuilder("<xs:element 
name=\"root\"><xs:complexType><xs:sequence>");
+        for (int k = step; k < length; k += step) {
+            body.append("<xs:element name=\"c").append(k).append("\" 
type=\"tns:S").append(k)
+                .append("\" minOccurs=\"0\"/>");
+        }
+        body.append("</xs:sequence></xs:complexType></xs:element>")
+            .append("<xs:simpleType name=\"S0\"><xs:restriction 
base=\"xs:string\"/></xs:simpleType>");
+        for (int i = 1; i < length; i++) {
+            body.append("<xs:simpleType 
name=\"S").append(i).append("\"><xs:restriction base=\"tns:S")
+                .append(i - 1).append("\"/></xs:simpleType>");
+        }
+        return body.toString();
+    }
+
+    /**
+     * A chain walked a step at a time used to pass: each step recursed only 
as far as the cached
+     * type before it, so the limit never saw the whole chain, and the type 
information it built
+     * was as deep as the chain.
+     */
+    @Test
+    public void testDerivationChainWalkedInStepsIsRejected() {
+        assertRejected(steppedRestrictionChain(1000, 100));
+    }
+
+    @Test
+    public void testShallowDerivationChainWalkedInStepsIsAccepted() {
+        walkRoot(steppedRestrictionChain(200, 50));
+    }
+
     /** S1 restricts S0, S2 restricts S1, and so on. */
     private static String simpleRestrictionChain(int length) {
         StringBuilder body = new StringBuilder("<xs:element name=\"root\" 
type=\"tns:S")

Reply via email to