Skip to content

Honor --exclude-paths in JVM manifest generation and reachability analysis#1411

Merged
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 3 commits into
v1.xfrom
exclude-paths-jvm-reachability
Jul 10, 2026
Merged

Honor --exclude-paths in JVM manifest generation and reachability analysis#1411
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 3 commits into
v1.xfrom
exclude-paths-jvm-reachability

Conversation

@jfblaa

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a customer-reported issue where --exclude-paths was not respected for JVM, .NET, and Rust reachability analysis. This PR is the socket-cli part of a cross-repo fix (Coana CLI 15.8.7 carries the engine-side changes) — ENG-5198.

Manifest producers (gradle / maven / sbt)

  • A wholly excluded subproject is now skipped: its configurations are never resolved and it emits no project records, so its dependencies stay out of the generated SBOM. Exception (by design): if a kept subproject depends on an excluded one as a project dependency, the excluded module's dependencies still enter through the kept subproject's tree.
  • Patterns are anchored globs relative to the scan root. Leading **/ patterns are expanded into zero-depth variants so NIO glob matching agrees with the CLI's micromatch semantics (**/tests also matches a root-level tests).
  • The producers now emit all source sets/configurations (not just main/test); source-file-level exclusion is applied downstream by the reachability analysis (--exclude-dirs).

CLI

  • socket manifest {auto,gradle,kotlin,maven,scala} expose --exclude-paths.
  • Glob shapes the producers' NIO glob parser cannot handle (unbalanced [ / {, nested {} groups) are rejected up front with a clear InputError instead of crashing the build tool mid-run.
  • Coana CLI updated to 15.8.7, which honors --exclude-dirs in JVM/.NET/Rust reachability, filters framework resources (SPI registrations, spring.factories, Spring XML) in excluded dirs, and aligns glob semantics (* no longer crosses /).

Testing

  • pnpm test:unit src/commands/scan/exclude-paths.test.mts — 40/40 (new validation cases pinned against Java's actual glob parser behavior).
  • End-to-end on real two-module gradle, maven, and sbt builds with --exclude-paths '**/excluded-mod' (the previously broken root-level case): the excluded module and its dependencies are absent from the records; the sibling module is unaffected.
  • Producer glob logic verified against real Gradle (9.2.1), real sbt (1.x), and a compiled test harness for the maven extension; jar rebuilt via build-jar.sh.
  • tsgo and eslint clean.

Note

Medium Risk
Changes dependency graph and reachability inputs for multi-module JVM builds when exclusions are used; incorrect glob semantics could omit needed modules, but behavior matches an existing scan flag and adds upfront validation.

Overview
Release 1.1.141 wires --exclude-paths through JVM manifest generation and tightens validation; Coana CLI bumps to 15.8.7 for reachability-side exclusions (JVM, .NET, Rust).

socket manifest auto, gradle, kotlin, maven, and scala now accept --exclude-paths, validate patterns up front via assertValidExcludePaths, and pass them into runManifestFacts / auto-manifest (including socket scan create --auto-manifest). Gradle, Maven, and sbt producers skip wholly excluded reactor modules/subprojects (no project records, no resolution) while keeping module identity for cross-project deps on kept modules; glob handling adds zero-depth **/ variants so NIO glob matches CLI micromatch. Gradle/sbt also emit all source sets/configurations (not only main/test) for downstream reachability filtering.

Invalid globs that NIO cannot parse (unbalanced [/{, nested {}) fail early with InputError instead of crashing mid-build. Help snapshots and changelog document the new behavior.

Reviewed by Cursor Bugbot for commit 2bef25d. Configure here.

…lysis

The socket facts producers (gradle/maven/sbt) now skip wholly excluded
subprojects: their configurations are not resolved and they emit no
project records, so their dependencies stay out of the generated SBOM
unless a kept subproject depends on them as a project dependency.
Patterns are matched as anchored globs relative to the scan root, with
zero-depth variants for leading `**/` so NIO glob matching agrees with
the CLI's micromatch semantics. The producers also emit all source
sets/configurations (not just main/test); source-file-level exclusion
is applied by the reachability analysis.

The manifest commands (auto/gradle/kotlin/maven/scala) expose
--exclude-paths directly, and glob patterns the producers cannot parse
(unbalanced brackets) are rejected at the CLI instead of crashing the
build tool mid-run.

Upgrades @coana-tech/cli to 15.8.7, which applies --exclude-dirs to
JVM/.NET/Rust reachability analysis (ENG-5198).

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Trailing globstar skips module dirs
    • Updated Maven, Gradle, and sbt exclude matcher expansion so trailing /** patterns also match the module directory itself.

Create PR

Or push these changes by commenting:

@cursor push 073166575a
Preview (073166575a)
diff --git a/src/commands/manifest/scripts/maven-extension/src/main/java/tech/coana/socket/SocketSupport.java b/src/commands/manifest/scripts/maven-extension/src/main/java/tech/coana/socket/SocketSupport.java
--- a/src/commands/manifest/scripts/maven-extension/src/main/java/tech/coana/socket/SocketSupport.java
+++ b/src/commands/manifest/scripts/maven-extension/src/main/java/tech/coana/socket/SocketSupport.java
@@ -108,8 +108,9 @@
 
   /**
    * NIO glob requires a slash-adjacent {@code **} to consume at least one path segment, but the
-   * CLI's micromatch lets it match zero ({@code **}{@code /x} matches root-level {@code x}). Emit
-   * every variant with {@code **}{@code /} occurrences dropped so both semantics hold.
+   * CLI's micromatch lets it match zero ({@code **}{@code /x} matches root-level {@code x};
+   * {@code x/**} matches {@code x}). Emit every variant with those zero-depth {@code **}
+   * occurrences dropped so both semantics hold.
    */
   private static Set<String> zeroDepthVariants(String glob) {
     Set<String> out = new LinkedHashSet<>();
@@ -118,6 +119,10 @@
     while (!work.isEmpty()) {
       String cur = work.poll();
       if (!out.add(cur)) continue;
+      if (cur.endsWith("/**")) {
+        String collapsed = cur.substring(0, cur.length() - 3);
+        if (!collapsed.isEmpty()) work.add(collapsed);
+      }
       int idx = cur.indexOf("**/");
       while (idx >= 0) {
         if (idx == 0 || cur.charAt(idx - 1) == '/') {

diff --git a/src/commands/manifest/scripts/socket-facts.init.gradle b/src/commands/manifest/scripts/socket-facts.init.gradle
--- a/src/commands/manifest/scripts/socket-facts.init.gradle
+++ b/src/commands/manifest/scripts/socket-facts.init.gradle
@@ -17,8 +17,8 @@
 // Standard glob semantics (anchored to the scan root, matching the CLI flag): `x` is root-level, `**/x`
 // matches at any depth. Mirrors the sbt/maven producers.
 // NIO glob requires a slash-adjacent `**` to consume at least one path segment, but the CLI's
-// micromatch lets it match zero (`**/x` matches root-level `x`). Emit every variant with `**/`
-// occurrences dropped so both semantics hold.
+// micromatch lets it match zero (`**/x` matches root-level `x`; `x/**` matches `x`). Emit every
+// variant with those zero-depth `**` occurrences dropped so both semantics hold.
 gradle.ext.socketZeroDepthVariants = { String glob ->
   def out = new LinkedHashSet()
   def work = new ArrayDeque()
@@ -26,6 +26,10 @@
   while (!work.isEmpty()) {
     def cur = work.poll()
     if (!out.add(cur)) { continue }
+    if (cur.endsWith('/**')) {
+      def collapsed = cur.substring(0, cur.length() - 3)
+      if (!collapsed.isEmpty()) { work.add(collapsed) }
+    }
     int idx = cur.indexOf('**/')
     while (idx >= 0) {
       if (idx == 0 || cur[idx - 1] == '/') {

diff --git a/src/commands/manifest/scripts/socket-facts.plugin.scala b/src/commands/manifest/scripts/socket-facts.plugin.scala
--- a/src/commands/manifest/scripts/socket-facts.plugin.scala
+++ b/src/commands/manifest/scripts/socket-facts.plugin.scala
@@ -414,14 +414,18 @@
   }
 
   // NIO glob requires a slash-adjacent `**` to consume at least one path segment, but the CLI's
-  // micromatch lets it match zero (`**/x` matches root-level `x`). Emit every variant with `**/`
-  // occurrences dropped so both semantics hold.
+  // micromatch lets it match zero (`**/x` matches root-level `x`; `x/**` matches `x`). Emit every
+  // variant with those zero-depth `**` occurrences dropped so both semantics hold.
   private def zeroDepthVariants(glob: String): Seq[String] = {
     val out = mutable.LinkedHashSet[String]()
     val work = mutable.Queue(glob)
     while (work.nonEmpty) {
       val cur = work.dequeue()
       if (out.add(cur)) {
+        if (cur.endsWith("/**")) {
+          val collapsed = cur.substring(0, cur.length - 3)
+          if (collapsed.nonEmpty) work.enqueue(collapsed)
+        }
         var idx = cur.indexOf("**/")
         while (idx >= 0) {
           if (idx == 0 || cur.charAt(idx - 1) == '/') {

You can send follow-ups to the cloud agent here.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2bef25d. Configure here.

Comment thread src/commands/manifest/cmd-manifest-gradle.mts
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​coana-tech/​cli@​15.8.7961008098100

View full report

@socket-security-staging

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​coana-tech/​cli@​15.8.7971008098100

View full report

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) merged commit 0e05ba4 into v1.x Jul 10, 2026
12 checks passed
@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) deleted the exclude-paths-jvm-reachability branch July 10, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants