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")