Skip to content

fix(manifest): keep unresolved JVM dependencies from looking resolved - #1642

Closed
Jeppe Fredsgaard Blaabjerg (jfblaa) wants to merge 5 commits into
v1.xfrom
jfblaa/maven-facts-partial-graph-files
Closed

Jeppe Fredsgaard Blaabjerg (jfblaa) wants to merge 5 commits into
v1.xfrom
jfblaa/maven-facts-partial-graph-files

Conversation

@jfblaa

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

LLM Description written by Claude Code:claude-opus-5-5

Reachability reads a sidecar component with no targets as not installed, and targets: [] as resolved with no artifact. Several paths in the Maven, Gradle and sbt facts producers blurred the two, so a dependency that failed to resolve could reach Coana looking healthy or code-less, and its vulnerabilities could be reported unreachable.

What changes

  • Maven: one failed POM no longer strips the jar paths of every other dependency in the module. POMs Maven only warned about (missing, invalid, or cached as failed by an earlier module) are reported. system-scope jars get their files. A failure gets the scope Maven would have given it, so excludeConfigs and version-conflict losers no longer over-block. Maven 4 failures show their real cause.
  • Gradle: dependency-verification failures, failed project dependencies and artifacts that cannot be listed are reported instead of swallowed. Failures name the version Gradle actually attempted.
  • sbt: a jar coursier silently skips, or Ivy fails to download, is reported under its full coordinate. POM-only and non-jar (war, aar) modules keep their real shape. Covers coursier (sbt 1.3+), Ivy (sbt 0.13) and sbt 1.x with useCoursier := false.
  • Assembler and records: a new unfetched record marks an artifact that could not be fetched, and any such or missing file leaves a component with no targets. A module excluded with --exclude-paths keeps its paths (new excludedProject record), so the projects depending on it are analysed with its code. An sbt root that crashes before writing no longer reuses another root's records.
  • Reports: Gradle variant ambiguity blocks only when the module resolved in no configuration of its project. Reachability scans' remedies also name --reach-continue-on-install-errors.

Testing

  • New and extended compat suites fail on the old producers and pass on all supported versions: Gradle 1.12–9.2.1 (new 6.2.2 row for verification), Maven 3.2.5–4.0.0-rc-6, sbt 0.13.18–1.9.9 (new sbt 1.x Ivy row).
  • Unit tests, typecheck and lint pass.

Follow-ups (filed, not in this PR): REA-1005, REA-1006, REA-1007, REA-1008, REA-1009.

🤖 Generated with Claude Code


Note

Medium Risk
Changes core JVM SBOM/reachability fact assembly and failure handling across Maven, Gradle, and sbt; incorrect edge-case behavior could still affect scan completeness, but the intent is to fix false “resolved” states rather than broaden silent under-reporting.

Overview
Fixes JVM manifest/reachability paths where unresolved or unfetchable dependencies could look fully resolved, which made vulnerability reachability wrong or silent.

Facts producers (Maven, Gradle, sbt) now report more failure modes (missing/invalid POMs, verification failures, failed project deps, artifacts that never downloaded) and emit unfetched records so the assembler omits targets instead of implying a healthy jar. Maven adds repository-event observation and can still materialize jars for healthy nodes when collection partially fails. Gradle reports the version actually attempted and treats strict dependency verification as failure. sbt distinguishes coursier “expected but missing” jars from true POM-only modules and surfaces Ivy download failures.

Assembler & sidecar: partial or missing files drop targets; excludedProject keeps source/classpath paths for --exclude-paths modules so dependents still analyze their code. Resolution reports use resolvedModules so Gradle variant ambiguity only stays non-blocking when the module resolved elsewhere; reachability hints mention --reach-continue-on-install-errors with ignoreUnresolved. Auto-manifest aborts on facts generator null outcomes, not only process.exitCode. sbt clears shared records.tsv before each root so a crashed build cannot reuse another root’s output.

Extensive compat smoke tests and unit tests cover the new behavior across tool versions.

Reviewed by Cursor Bugbot for commit 0463ffd. Configure here.

Reachability reads a sidecar component without targets as not installed and
`targets: []` as resolved with no artifact. Several producer paths blurred the
two, so failed dependencies looked healthy or code-less:

- Maven: a collection failure no longer strips every jar of the module; missing
  or invalid POMs, cached descriptor failures in later modules and system-scope
  jars are reported; failures get the scope Maven would have given them.
- Gradle: verification failures, failed project dependencies and artifacts
  that cannot be listed are reported; failures name the attempted version.
- sbt: a jar coursier skips or Ivy fails to download is reported under its full
  coordinate, while pom-packaged and non-jar modules keep their real shape.
- Assembler: an artifact with any unfetched or missing file has no targets; an
  excluded module keeps its paths for the projects depending on it; a crashed
  sbt root no longer reuses another root's records.
- Reports: ambiguity blocks only when the module resolved nowhere, and
  reachability remedies name --reach-continue-on-install-errors.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
- sbt: an artifact failure that alone fails sbt's update is reported without
  files too; the Ivy report search stays inside the project's target.
- Gradle: verification is checked only for builds with verification metadata,
  a failure of another artifact of the same module no longer drops this
  configuration's jar, and an unrelated artifact-view error no longer discards
  a walked configuration; folding artifactless nodes is linear.
- Maven: a direct test-jar dependency matches its declared scope, and a POM a
  strict descriptor policy rejects is reported once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ring

Share the repeated failure reporting, XML parsing and descriptor matching in
the producers; build expensive state (lenient sbt state, scope maps, resolved
modules, the Gradle unresolved-dependency walk) only when something failed;
keep excluded projects out of the parsed project list; and key modules through
one helper.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Maven 4 derives and manages transitive scopes differently from Maven 3, so
  a transitive failure missing from its graph is no longer given a Maven 3
  scope; its position is unknown.
- Gradle verification uses the root build's metadata, as included builds do,
  and matches failed artifacts by the name Gradle reports them under.
- sbt treats an `ejb` module's artifact as a jar, as current coursier does, and
  no longer reads Ivy's "null" download details as a reason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@BarrensZeppelin

Copy link
Copy Markdown
Member

Can you split this into reviewable pieces?

@jfblaa

Copy link
Copy Markdown
Contributor Author

[agent] Split into smaller PRs with the same end result: #1643 stands alone, and #1644 → #1650 form a stacked series on v1.x. Together they apply exactly this PR's diff (apart from the changelog wording). The branch is left in place for reference.

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