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-neethi.git


The following commit(s) were added to refs/heads/master by this push:
     new 3099a3f  Adding test for last commit
3099a3f is described below

commit 3099a3f37885fc6fa5c69a7d41a097b78b6beb73
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Tue Apr 21 14:49:13 2026 +0100

    Adding test for last commit
---
 .../apache/neethi/PolicyNormalizationDoSTest.java  | 116 +++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/src/test/java/org/apache/neethi/PolicyNormalizationDoSTest.java 
b/src/test/java/org/apache/neethi/PolicyNormalizationDoSTest.java
new file mode 100644
index 0000000..195f54e
--- /dev/null
+++ b/src/test/java/org/apache/neethi/PolicyNormalizationDoSTest.java
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.neethi;
+
+import javax.xml.namespace.QName;
+
+import org.apache.neethi.builders.PrimitiveAssertion;
+
+import org.junit.Test;
+
+/**
+ * <p>Policy normalization computes a Cartesian cross-product of the 
alternatives inside
+ * nested {@code All} operators.  Each {@code All} that contains {@code k}
+ * {@code ExactlyOne} children, where each child carries 2 alternatives, 
multiplies the
+ * output set by 2.  A policy with {@code N} such levels therefore produces
+ * {@code 2^N} normalised alternatives — uncapped.
+ *
+ * <p>Structure of the crafted policy (one level shown, repeated {@code 
NESTING_DEPTH} times):
+ * <pre>
+ * Policy (= All)
+ *   ExactlyOne_1       ← 2 branches
+ *     All → PrimitiveAssertion(a1)
+ *     All → PrimitiveAssertion(b1)
+ *   ExactlyOne_2       ← 2 branches
+ *     All → PrimitiveAssertion(a2)
+ *     All → PrimitiveAssertion(b2)
+ *   …
+ *   ExactlyOne_N       ← 2 branches
+ *     All → PrimitiveAssertion(aN)
+ *     All → PrimitiveAssertion(bN)
+ * </pre>
+ *
+ * <p>Normalising this produces {@code 2^N} alternatives. Test that this is 
handled without
+ * throwing {@link OutOfMemoryError} or hanging indefinitely, and that a clear 
exception is thrown
+ * indicating an alternative-count limit was enforced.
+ */
+public class PolicyNormalizationDoSTest extends PolicyTestCase {
+
+    /**
+     * Number of {@code ExactlyOne} nodes placed directly inside the top-level 
policy.
+     * Each adds one level of binary branching, so the cross-product of the 
normalised
+     * policy has {@code 2^NESTING_DEPTH} alternatives.
+     *
+     * <p>25 levels → 33,554,432 alternatives.  This is deliberately chosen to 
be far
+     * beyond any reasonable cap so that the vulnerability is unambiguous.
+     */
+    private static final int NESTING_DEPTH = 75;
+
+    /**
+     * Builds a {@link Policy} whose normalization produces {@code 
2^NESTING_DEPTH}
+     * alternatives, then asserts that normalization throws a {@link 
RuntimeException}
+     * whose message indicates an alternative-count limit was enforced.
+     *
+     */
+    @Test(expected = RuntimeException.class)
+    public void 
testExponentialCrossProductIsRejectedWithAlternativeCountLimit()
+            throws InterruptedException {
+        Policy policy = buildExponentialPolicy(NESTING_DEPTH);
+
+        policy.normalize(registry, true);
+    }
+
+    // -----------------------------------------------------------------------
+    // Helpers
+    // -----------------------------------------------------------------------
+
+    /**
+     * Builds a {@link Policy} containing {@code depth} {@link ExactlyOne} 
nodes.
+     * Each {@link ExactlyOne} has exactly two branches ({@link All} nodes), 
making
+     * the normalised policy produce {@code 2^depth} alternatives.
+     *
+     * <pre>
+     * Policy
+     *   ExactlyOne_i   (i = 1 … depth)
+     *     All → PrimitiveAssertion("a{i}", ns="urn:test")
+     *     All → PrimitiveAssertion("b{i}", ns="urn:test")
+     * </pre>
+     */
+    private static Policy buildExponentialPolicy(int depth) {
+        Policy policy = new Policy();
+        for (int i = 0; i < depth; i++) {
+            ExactlyOne eo = new ExactlyOne();
+
+            All branchA = new All();
+            branchA.addPolicyComponent(
+                new PrimitiveAssertion(new QName("urn:test", "a" + i)));
+
+            All branchB = new All();
+            branchB.addPolicyComponent(
+                new PrimitiveAssertion(new QName("urn:test", "b" + i)));
+
+            eo.addPolicyComponent(branchA);
+            eo.addPolicyComponent(branchB);
+            policy.addPolicyComponent(eo);
+        }
+        return policy;
+    }
+
+}

Reply via email to