From 2f0659898760956cc510caaab16c42a4d1af0826 Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Mon, 15 Dec 2025 14:10:51 +0800 Subject: [PATCH] fix: java stack analysis doesn'tt ignore deps inherited version --- .../providers/JavaMavenProvider.java | 40 ++++++++++++++----- .../trustifyda/sbom/CycloneDXSbom.java | 30 ++++++++++++++ .../github/guacsec/trustifyda/sbom/Sbom.java | 2 + 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/guacsec/trustifyda/providers/JavaMavenProvider.java b/src/main/java/io/github/guacsec/trustifyda/providers/JavaMavenProvider.java index 424c2d0a..8402a6d7 100644 --- a/src/main/java/io/github/guacsec/trustifyda/providers/JavaMavenProvider.java +++ b/src/main/java/io/github/guacsec/trustifyda/providers/JavaMavenProvider.java @@ -75,24 +75,32 @@ public Content provideStack() throws IOException { // create a temp file for storing the dependency tree in var tmpFile = Files.createTempFile("TRUSTIFY_DA_dot_graph_", null); // the tree command will build the project and create the dependency tree in the temp file - var mvnTreeCmd = - buildMvnCommandArgs( - "org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree", - "-Dverbose", - "-DoutputType=text", - String.format("-DoutputFile=%s", tmpFile.toString()), - "-f", - manifest.toString(), - "--batch-mode", - "-q"); + var mvnTreeCmdArgs = + new ArrayList<>( + List.of( + "org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree", + "-Dscope=compile", + "-Dverbose", + "-DoutputType=text", + String.format("-DoutputFile=%s", tmpFile.toString()), + "-f", + manifest.toString(), + "--batch-mode", + "-q")); // if we have dependencies marked as ignored, exclude them from the tree command var ignored = getDependencies(manifest).stream() .filter(d -> d.ignored) - .map(DependencyAggregator::toPurl) + .map(DependencyAggregator::toPurlWithoutVersion) .map(PackageURL::getCoordinates) .collect(Collectors.toList()); + + if (!ignored.isEmpty()) { + mvnTreeCmdArgs.add("-Dexcludes=" + String.join(",", ignored)); + } + // execute the tree command + var mvnTreeCmd = buildMvnCommandArgs(mvnTreeCmdArgs.toArray(String[]::new)); Operations.runProcess(manifest.getParent(), mvnTreeCmd.toArray(String[]::new), mvnEnvs); if (debugLoggingIsNeeded()) { String stackAnalysisDependencyTree = Files.readString(tmpFile); @@ -110,6 +118,7 @@ public Content provideStack() throws IOException { private Sbom buildSbomFromTextFormat(Path textFormatFile) throws IOException { var sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive"); + sbom.setCoordinateBasedMatching(); List lines = Files.readAllLines(textFormatFile); var root = lines.get(0); var rootPurl = parseDep(root); @@ -408,6 +417,15 @@ public PackageURL toPurl() { } } + /** Creates a PackageURL without version for coordinate-based matching. */ + public PackageURL toPurlWithoutVersion() { + try { + return new PackageURL(Type.MAVEN.getType(), groupId, artifactId, null, null, null); + } catch (MalformedPackageURLException e) { + throw new IllegalArgumentException("Unable to parse PackageURL", e); + } + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/io/github/guacsec/trustifyda/sbom/CycloneDXSbom.java b/src/main/java/io/github/guacsec/trustifyda/sbom/CycloneDXSbom.java index accdf592..78f67775 100644 --- a/src/main/java/io/github/guacsec/trustifyda/sbom/CycloneDXSbom.java +++ b/src/main/java/io/github/guacsec/trustifyda/sbom/CycloneDXSbom.java @@ -95,6 +95,31 @@ private BiPredicate, Component> getBelongingConditionByPurl() { collection.contains(componentToPurl(component).getCoordinates()); } + /** + * Creates a coordinate-based belonging condition that matches by groupId:artifactId only, + * ignoring version. Used for handling dependencies with unresolved version properties. + */ + private BiPredicate, Component> getBelongingConditionByCoordinatesOnly() { + return (collection, component) -> { + PackageURL componentPurl = componentToPurl(component); + String componentCoords = componentPurl.getNamespace() + ":" + componentPurl.getName(); + + return collection.stream() + .filter(String.class::isInstance) + .map(String.class::cast) + .anyMatch( + ignoredPurlCoords -> { + try { + PackageURL ignoredPurl = new PackageURL(ignoredPurlCoords); + String ignoredCoords = ignoredPurl.getNamespace() + ":" + ignoredPurl.getName(); + return componentCoords.equals(ignoredCoords); + } catch (MalformedPackageURLException e) { + return false; + } + }); + }; + } + public Sbom addRoot(PackageURL rootRef) { this.root = rootRef; Component rootComponent = newRootComponent(rootRef); @@ -280,6 +305,11 @@ public void setBelongingCriteriaBinaryAlgorithm(BelongingCondition belongingCond } } + @Override + public void setCoordinateBasedMatching() { + belongingCriteriaBinaryAlgorithm = getBelongingConditionByCoordinatesOnly(); + } + @Override public boolean checkIfPackageInsideDependsOnList(PackageURL component, String name) { boolean result = false; diff --git a/src/main/java/io/github/guacsec/trustifyda/sbom/Sbom.java b/src/main/java/io/github/guacsec/trustifyda/sbom/Sbom.java index ed5bcf79..d15219b4 100644 --- a/src/main/java/io/github/guacsec/trustifyda/sbom/Sbom.java +++ b/src/main/java/io/github/guacsec/trustifyda/sbom/Sbom.java @@ -33,6 +33,8 @@ public interface Sbom { public void setBelongingCriteriaBinaryAlgorithm(BelongingCondition belongingCondition); + public void setCoordinateBasedMatching(); + public boolean checkIfPackageInsideDependsOnList(PackageURL component, String name); void removeRootComponent();