diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/backend/ExhortIntegration.java b/src/main/java/io/github/guacsec/trustifyda/integration/backend/ExhortIntegration.java index c0d95d75..435393a3 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/backend/ExhortIntegration.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/backend/ExhortIntegration.java @@ -218,6 +218,7 @@ public void configure() { .to(direct("preProcessAnalysisRequest")) .process(this::processAnalysisRequest) .process(monitoringProcessor::processOriginalRequest) + .bean(this, "addSbomIdToDependencyTree") .to(direct("analyzeSbom")) .to(direct("enrichTrustedLibraries")) .to(direct("report")) @@ -317,6 +318,9 @@ private void processAnalysisRequest(Exchange exchange) { var parser = getSbomParser(exchange); var tree = parser.parse(exchange.getIn().getBody(InputStream.class)); exchange.setProperty(Constants.DEPENDENCY_TREE_PROPERTY, tree); + if (tree.root() != null) { + exchange.setProperty(Constants.SBOM_ID_PROPERTY, tree.root().purl().canonicalize()); + } exchange.getIn().setBody(tree); } @@ -418,6 +422,9 @@ private void validateSbomsInput(Exchange exchange) { public DependencyTree addSbomIdToDependencyTree( @Body DependencyTree tree, @ExchangeProperty(Constants.SBOM_ID_PROPERTY) String sbomId) { + if (sbomId == null || sbomId.isBlank()) { + return tree; + } Map dependencies = new HashMap<>(); if (tree.dependencies() != null) { dependencies.putAll(tree.dependencies()); @@ -426,7 +433,7 @@ public DependencyTree addSbomIdToDependencyTree( if (!dependencies.containsKey(sbomRef)) { dependencies.put(sbomRef, new DirectDependency(sbomRef, Collections.emptySet())); } - return DependencyTree.builder().dependencies(dependencies).build(); + return new DependencyTree(dependencies, null, tree.root(), tree.componentHashes()); } public Map.Entry transformBatchAnalysisReport( diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/licenses/DepsDevRequestBuilder.java b/src/main/java/io/github/guacsec/trustifyda/integration/licenses/DepsDevRequestBuilder.java index 164be9e3..3a744a82 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/licenses/DepsDevRequestBuilder.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/licenses/DepsDevRequestBuilder.java @@ -68,6 +68,9 @@ public List fromEndpoint(LicensesRequest request) { public List fromSbom(DependencyTree tree) { var purls = tree.getAll(); + if (tree.root() != null) { + purls.remove(tree.root()); + } return new ArrayList(purls); } diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyIntegration.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyIntegration.java index 97dee126..b500e7c3 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyIntegration.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyIntegration.java @@ -462,10 +462,13 @@ private void filterLocalPackages(Exchange exchange) { .collect(Collectors.toSet()); var sbomId = exchange.getProperty(Constants.SBOM_ID_PROPERTY, String.class); if (sbomId != null && !sbomId.isBlank()) { - // Batch analysis injects the SBOM root component as a dependency entry to drive other - // processing; it is not necessarily returned by Trustify and would otherwise stay a cache - // miss on every request. - packages.remove(new PackageRef(sbomId)); + // The SBOM root component is injected as a dependency entry to drive recommendation + // processing; remove it from the vulnerability scan unless it is an OCI image PURL, + // which must stay so the hardened-image recommendation route can match it. + PackageRef sbomRef = new PackageRef(sbomId); + if (!OCI_PURL_TYPE.equals(sbomRef.purl().getType())) { + packages.remove(sbomRef); + } } exchange.getIn().setBody(packages); } @@ -501,6 +504,13 @@ private void aggregateCacheHits(Exchange exchange) { return; } + // Cached PackageItems may contain recommendation data from a previous request + // with recommend=true. Strip it when the current request has recommend=false. + if (!isRecommendEnabled(exchange)) { + cacheHits.replaceAll( + (ref, item) -> new PackageItem(item.packageRef(), null, item.issues(), item.warnings())); + } + ProviderResponse response = exchange.getIn().getBody(ProviderResponse.class); if (response == null) { String providerName = exchange.getProperty(Constants.PROVIDER_NAME_PROPERTY, String.class); @@ -520,4 +530,11 @@ private void aggregateCacheHits(Exchange exchange) { exchange.getIn().setBody(new ProviderResponse(mergedItems, response.status())); } + + private boolean isRecommendEnabled(Exchange exchange) { + Object param = exchange.getProperty(Constants.RECOMMEND_PARAM); + if (param instanceof Boolean b) return b; + if (param instanceof String s) return Boolean.parseBoolean(s); + return true; + } }