You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2025/11/28 14:44:53 UTC

(maven-resolver) branch master updated: GH-1668: Add catch-all for group filter (#1694)

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

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new beb63f6f6 GH-1668: Add catch-all for group filter (#1694)
beb63f6f6 is described below

commit beb63f6f62a0a739ea225d4206caafc2bbca3368
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Fri Nov 28 15:44:47 2025 +0100

    GH-1668: Add catch-all for group filter (#1694)
    
    Introduced `*` "root" entry, that may define the "default acceptance".
    
    Fixes #1668
---
 .../internal/impl/filter/ruletree/GroupTree.java   |  15 ++-
 .../aether/internal/impl/filter/ruletree/Node.java |   6 +-
 .../impl/filter/ruletree/GroupTreeTest.java        | 118 +++++++++++++++++++++
 src/site/markdown/remote-repository-filtering.md   |  10 ++
 4 files changed, 145 insertions(+), 4 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
index e2d0295d3..b4e29dfbd 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
@@ -36,7 +36,10 @@ import static java.util.stream.Collectors.toList;
  *     <li>a valid Maven groupID ie "org.apache.maven".</li>
  * </ul>
  * By default, a G entry ie {@code org.apache.maven} means "allow {@code org.apache.maven} G and all Gs below
- * (so {@code org.apache.maven.plugins} etc. are all allowed).
+ * (so {@code org.apache.maven.plugins} etc. are all allowed). There is one special entry {@code "*"} (asterisk)
+ * that means "root" and defines the default acceptance: {@code "*"} means "by default accept" and {@code "!*"}
+ * means "by default deny" (same effect as when this character is not present in file). Use of limiter modifier
+ * on "root" like {@code "=*"} has no effect, is simply ignored.
  *
  * Examples:
  * <pre>
@@ -61,6 +64,7 @@ import static java.util.stream.Collectors.toList;
 public class GroupTree extends Node {
     public static final GroupTree SENTINEL = new GroupTree("sentinel");
 
+    private static final String ROOT = "*";
     private static final String MOD_EXCLUSION = "!";
     private static final String MOD_STOP = "=";
 
@@ -69,7 +73,7 @@ public class GroupTree extends Node {
     }
 
     public GroupTree(String name) {
-        super(name, false, null);
+        super(name, false, false);
     }
 
     public int loadNodes(Stream<String> linesStream) {
@@ -95,6 +99,10 @@ public class GroupTree extends Node {
                 stop = true;
                 line = line.substring(MOD_STOP.length());
             }
+            if (ROOT.equals(line)) {
+                this.setAllow(allow);
+                return true;
+            }
             List<String> groupElements = elementsOfGroup(line);
             for (String groupElement : groupElements.subList(0, groupElements.size() - 1)) {
                 currentNode = currentNode.addSibling(groupElement, false, null);
@@ -122,6 +130,7 @@ public class GroupTree extends Node {
                 accepted = currentNode.isAllow();
             }
         }
-        return accepted != null && accepted;
+        // use `accepted`, if defined; otherwise fallback to root (it always has `allow` set)
+        return accepted != null ? accepted : this.isAllow();
     }
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
index 453a08704..beccf29c2 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
 class Node {
     private final String name;
     private final boolean stop;
-    private final Boolean allow;
+    private Boolean allow;
     private final HashMap<String, Node> siblings;
 
     protected Node(String name, boolean stop, Boolean allow) {
@@ -52,6 +52,10 @@ class Node {
         return allow;
     }
 
+    public void setAllow(Boolean allow) {
+        this.allow = allow;
+    }
+
     protected Node addSibling(String name, boolean stop, Boolean allow) {
         return siblings.computeIfAbsent(name, n -> new Node(n, stop, allow));
     }
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
index ffe8cc170..1cdecd136 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
@@ -18,10 +18,13 @@
  */
 package org.eclipse.aether.internal.impl.filter.ruletree;
 
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.stream.Stream;
 
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -50,5 +53,120 @@ public class GroupTreeTest {
 
         assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
         assertFalse(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+    }
+
+    @Test
+    void smokeWithPositiveDefault() {
+        GroupTree groupTree = new GroupTree("root");
+        groupTree.loadNodes(Stream.of(
+                "# comment",
+                "",
+                "org.apache.maven",
+                "!=org.apache.maven.foo",
+                "!org.apache.maven.bar",
+                "=org.apache.baz",
+                "*"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+        assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+        assertTrue(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+    }
+
+    @Test
+    void smokeWithPositiveDefaultExclusionsOnly() {
+        GroupTree groupTree = new GroupTree("root");
+        groupTree.loadNodes(Stream.of("# comment", "*", "!org.apache.maven.foo", "!=org.apache.maven.bar"));
+        // no applicable rule; root=ALLOW
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+        // exclusion rule present (this and below)
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+        // exclusion+stop rule present (only this)
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+        // no applicable rule; root=ALLOW
+        assertTrue(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+    }
+
+    @Test
+    void smokeWithNegativeDefault() {
+        GroupTree groupTree = new GroupTree("root");
+        groupTree.loadNodes(Stream.of(
+                "# comment",
+                "",
+                "org.apache.maven",
+                "!=org.apache.maven.foo",
+                "!org.apache.maven.bar",
+                "=org.apache.baz",
+                "!*"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+        assertTrue(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+        assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+        assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
+        assertFalse(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+        assertFalse(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+    }
+
+    @Test
+    void implicitAndExplicitDefaultIsSame() {
+        // w/o asterisk: uses coded defaults
+        GroupTree implicitTree = new GroupTree("root");
+        implicitTree.loadNodes(Stream.of(
+                "# comment",
+                "",
+                "org.apache.maven",
+                "!=org.apache.maven.foo",
+                "!org.apache.maven.bar",
+                "=org.apache.baz"));
+        HashMap<String, Boolean> implicitResults = new HashMap<>();
+        // w/ asterisk: set to same value as default (should cause no change)
+        GroupTree explicitTree = new GroupTree("root");
+        explicitTree.loadNodes(Stream.of(
+                "# comment",
+                "",
+                "org.apache.maven",
+                "!=org.apache.maven.foo",
+                "!org.apache.maven.bar",
+                "=org.apache.baz",
+                "!*"));
+        HashMap<String, Boolean> explicitResults = new HashMap<>();
+
+        for (String key : Arrays.asList(
+                "org.apache.maven",
+                "org.apache.maven.aaa",
+                "org.apache.maven.foo",
+                "org.apache.maven.foo.aaa",
+                "org.apache.maven.bar",
+                "org.apache.maven.bar.aaa",
+                "org.apache.baz",
+                "org.apache.baz.aaa",
+                "not.in.list.but.uses.default")) {
+            implicitResults.put(key, implicitTree.acceptedGroupId(key));
+            explicitResults.put(key, explicitTree.acceptedGroupId(key));
+        }
+
+        assertEquals(implicitResults, explicitResults);
     }
 }
diff --git a/src/site/markdown/remote-repository-filtering.md b/src/site/markdown/remote-repository-filtering.md
index c8420d514..8012895fd 100644
--- a/src/site/markdown/remote-repository-filtering.md
+++ b/src/site/markdown/remote-repository-filtering.md
@@ -181,6 +181,16 @@ Lines 1 and 2 are ignored. Line 3 means "allow `org.apache.maven` G and below".
 only" (so `org.apache.maven.foo.bar` is allowed due first line). Line 5 means "disallow `org.apache.maven.indexer` and below"
 and finally line 6 means "allow `org.apache.bar` ONLY" (so `org.apache.bar.foo` is NOT enabled).
 
+One can use one special entry "root" `*` (asterisk) to define the "default acceptance" (that without it 
+defaults to REJECTED). Similarly, adding `!*` to file defines "default acceptance" of FALSE/REJECTED as well, 
+and adding it to file changes nothing, as this is the default acceptance (but may serve some documentation purposes). 
+Be aware: In case a line with single asterisk `*` is present, the whole logic of Group filter is getting inverted, 
+hence there is no need to add "allowed entries" (they are allowed by default), but one can add "disallowed entries" by 
+adding `!com.foo` and alike.
+
+Conflicting rules: rule parser is intentionally trivial, so in case of conflicting rules the "first wins" strategy is 
+applied. Ideally, user should keep files sorted or handle them in a way one can detect conflicts in it.
+
 ## Operation
 
 To make RRF filters operate, as they are by default enabled, you have to make sure that: