fix(go): distinguish direct from indirect dependencies#449
Merged
ruromero merged 3 commits intoguacsec:mainfrom Apr 30, 2026
Merged
fix(go): distinguish direct from indirect dependencies#449ruromero merged 3 commits intoguacsec:mainfrom
ruromero merged 3 commits intoguacsec:mainfrom
Conversation
Contributor
Reviewer's GuideAdjusts Go SBOM generation to treat only non-indirect go.mod entries as direct dependencies, aligns CycloneDX JSON structure with the JS client, tightens Dependabot configuration, and adds project coding conventions plus updated Go SBOM fixtures. Sequence diagram for GoModulesProvider SBOM generation with direct dependency detectionsequenceDiagram
actor Caller
participant GoModulesProvider
participant Operations
Caller->>GoModulesProvider: getDependenciesSbom(manifestPath, buildTree)
GoModulesProvider->>GoModulesProvider: determineMainModuleVersion(manifestPath.parent)
GoModulesProvider->>GoModulesProvider: getIgnoredDeps(manifestPath)
GoModulesProvider->>Operations: runProcessGetOutput(manifestPath.parent, "go mod edit -json")
Operations-->>GoModulesProvider: goModEditOutput
GoModulesProvider->>GoModulesProvider: getDirectDependencyPaths(manifestPath)
GoModulesProvider-->>GoModulesProvider: directDepPaths
alt matchManifestVersions
GoModulesProvider->>Operations: runProcessGetOutput(manifestPath.parent, "go mod graph")
Operations-->>GoModulesProvider: goModulesResult
GoModulesProvider->>GoModulesProvider: performManifestVersionsCheck(goModulesResult, manifestPath)
else no_match_manifest_versions
GoModulesProvider->>Operations: runProcessGetOutput(manifestPath.parent, "go list -m all")
Operations-->>GoModulesProvider: goModulesResult
end
alt buildTree == false
GoModulesProvider->>GoModulesProvider: buildSbomFromList(goModulesResult, ignoredDeps, directDepPaths)
else buildTree == true
GoModulesProvider->>GoModulesProvider: buildSbomFromGraph(goModulesResult, ignoredDeps, manifestPath, directDepPaths)
end
GoModulesProvider-->>Caller: Sbom
Updated class diagram for GoModulesProvider and CycloneDXSbomclassDiagram
class GoModulesProvider {
+Sbom getDependenciesSbom(Path manifestPath, boolean buildTree)
-Sbom buildSbomFromGraph(String goModulesResult, List ignoredDeps, Path manifestPath, Set directDepPaths)
-Sbom buildSbomFromList(String golangDeps, List ignoredDeps, Set directDepPaths)
-Set getDirectDependencyPaths(Path manifestPath)
-static String getModulePath(String graphEntry)
-void determineMainModuleVersion(Path directory)
-List getIgnoredDeps(Path manifestPath)
}
class CycloneDXSbom {
-Bom bom
+Sbom removeIgnoredDepsFromSbom(List refsToIgnore)
+Sbom addDependency(PackageURL sourceRef, PackageURL targetRef, String scope)
+String getAsJsonString()
-String ensureComponentsField(String json)
}
class Sbom {
+void addRoot(PackageURL root, String license)
+void addDependency(PackageURL sourceRef, PackageURL targetRef, String scope)
+String getAsJsonString()
}
GoModulesProvider --> Sbom : builds
CycloneDXSbom ..|> Sbom
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
getDirectDependencyPaths, consider hardening the JSON parsing by checking for missing/nullPathor non-booleanIndirectfields and logging/ignoring malformed entries instead of assuming they exist, to avoidNullPointerException/unexpected failures on slightly differentgo mod edit -jsonoutputs. - The
ensureComponentsFieldmethod re-parses and re-serializes the full JSON string; if possible, it would be cleaner and more efficient to enforce an emptycomponentslist directly on theBommodel (or via CycloneDX generator configuration) rather than doing a post-processing pass on the serialized JSON. - In
ensureComponentsField, errors are silently swallowed by returning the original JSON; consider at least logging the exception at debug level so that failures in this post-processing step can be diagnosed if thecomponentsfield is unexpectedly missing.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `getDirectDependencyPaths`, consider hardening the JSON parsing by checking for missing/null `Path` or non-boolean `Indirect` fields and logging/ignoring malformed entries instead of assuming they exist, to avoid `NullPointerException`/unexpected failures on slightly different `go mod edit -json` outputs.
- The `ensureComponentsField` method re-parses and re-serializes the full JSON string; if possible, it would be cleaner and more efficient to enforce an empty `components` list directly on the `Bom` model (or via CycloneDX generator configuration) rather than doing a post-processing pass on the serialized JSON.
- In `ensureComponentsField`, errors are silently swallowed by returning the original JSON; consider at least logging the exception at debug level so that failures in this post-processing step can be diagnosed if the `components` field is unexpectedly missing.
## Individual Comments
### Comment 1
<location path="CONVENTIONS.md" line_range="22" />
<code_context>
+- **Charset**: UTF-8
+- **License header**: Apache 2.0, automatically injected by Spotless
+- **EditorConfig**: `.editorconfig` enforces formatting rules
+- **Code coverage**: 81% unit test threshold (JaCoCo), 50 mutation threshold (PIT)
+
+## Naming Conventions
</code_context>
<issue_to_address>
**suggestion (typo):** Clarify "50 mutation threshold" (likely intended as "50% mutation threshold").
The phrase "50 mutation threshold (PIT)" is unclear. If you mean mutation coverage, consider "50% mutation threshold (PIT)" or "50% mutation coverage threshold (PIT)" instead.
```suggestion
- **Code coverage**: 81% unit test threshold (JaCoCo), 50% mutation coverage threshold (PIT)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
e1853f2 to
075da61
Compare
Add go mod edit -json parsing to GoModulesProvider to identify which dependencies in go.mod are direct vs indirect. Since Go 1.17, go mod tidy records all transitively-imported modules as require entries with an // indirect marker, causing go mod graph to emit root-level edges for all of them. Previously the Java client treated all root-level edges as direct dependencies, inflating component analysis SBOMs. The fix adds getDirectDependencyPaths() which runs go mod edit -json and builds a Set of module paths where Indirect is false/absent. Both buildSbomFromGraph (stack analysis) and buildSbomFromList (component analysis) now filter root-level edges to only include direct deps. Also adds getModulePath() helper to strip version suffixes from go mod graph entries for comparison with go mod edit -json output. Implements TC-4300 Assisted-by: Claude Code
When all dependencies are filtered out, the Java client now produces the same SBOM structure as the JS client: "components": [] is always present and "dependencies": [] is empty (no orphan root dep entry). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Strum355
reviewed
Apr 30, 2026
Strum355
approved these changes
Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
go mod edit -jsonparsing, matching the JS client behaviorgo mod tidyrecords all transitively-imported modules ingo.modasrequireentries with// indirect, andgo mod graphemits root-level edges for all of them — the Java client was treating all root-level edges as direct dependencies"components": []is always present and"dependencies": []is empty when all components are filtered outChanges
GoModulesProvider.javagetDirectDependencyPaths()— runsgo mod edit -json, parses theRequirearray, returns the set of non-indirect module pathsbuildSbomFromGraph()andbuildSbomFromList()to only include direct dependenciesCycloneDXSbom.java"components"key is always present (CycloneDX library omits empty collections)Test plan
go_mod_light_no_ignore,go_mod_no_ignore,go_mod_with_ignore,go_mod_with_all_ignore, andmsc/golang/mvs_logicNoClassDefFoundErrorincyclonedx-core-java12.1.0 JPMS module setup — exists onmain)Closes TC-4300
Related to TC-3818
🤖 Generated with Claude Code
Summary by Sourcery
Correct Go module SBOM generation to only include direct dependencies and align CycloneDX JSON output structure with other clients, while updating automation config and documentation.
Bug Fixes:
Enhancements:
go mod edit -jsonoutput to derive direct dependency module paths for Go projects.Build:
Documentation:
Tests: