fix(hardened): enable hardened recommendations for single analysis and fix cache leakage (TC-4987)#639
Conversation
…d fix cache leakage (TC-4987) Wire the single analysis endpoint (/api/v5/analysis) to return hardened image recommendations for OCI PURLs, matching the batch endpoint behavior. Also fix a cache contamination bug where recommend=false was bypassed by cached PackageItems containing recommendation data from previous requests. - Set SBOM_ID_PROPERTY from root PURL in processAnalysisRequest - Add addSbomIdToDependencyTree to single analysis route - Guard addSbomIdToDependencyTree against null/blank sbomId - Preserve all DependencyTree fields when rebuilding - Keep OCI PURLs in filterLocalPackages for hardened image matching - Strip cached recommendation data when recommend=false Implements TC-4987 Assisted-by: Claude Code
Reviewer's GuideEnables hardened image recommendations for single-analysis flows, ensures SBOM-root handling works correctly for OCI images and dependency monitoring, and prevents recommendation data from leaking via cached results when recommendations are disabled. Sequence diagram for hardened single-analysis flow and cache-safe recommendationssequenceDiagram
actor Client
participant ExhortIntegration
participant TrustifyIntegration
Client->>ExhortIntegration: processAnalysisRequest
ExhortIntegration->>ExhortIntegration: processAnalysisRequest
ExhortIntegration->>ExhortIntegration: setProperty SBOM_ID_PROPERTY
ExhortIntegration->>ExhortIntegration: addSbomIdToDependencyTree
ExhortIntegration->>TrustifyIntegration: analyzeSbom
TrustifyIntegration->>TrustifyIntegration: filterLocalPackages
alt [sbomId is OCI_PURL_TYPE]
TrustifyIntegration->>TrustifyIntegration: keep PackageRef sbomId
else [sbomId is not OCI_PURL_TYPE]
TrustifyIntegration->>TrustifyIntegration: packages.remove PackageRef sbomId
end
TrustifyIntegration->>TrustifyIntegration: aggregateCacheHits
TrustifyIntegration->>TrustifyIntegration: isRecommendEnabled
alt [recommend=false]
TrustifyIntegration->>TrustifyIntegration: cacheHits.replaceAll new PackageItem
end
TrustifyIntegration-->>Client: ProviderResponse
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
processAnalysisRequest, consider guarding against a nulltree.root().purl()before callingcanonicalize()to avoid a potential NPE for SBOMs with a root component but no PURL. - The new
isRecommendEnabledhelper defaults totruewhen the property is missing or not a boolean/string; double-check this aligns with the expected behavior for requests that omit therecommendparameter, especially with the new cache-stripping logic.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `processAnalysisRequest`, consider guarding against a null `tree.root().purl()` before calling `canonicalize()` to avoid a potential NPE for SBOMs with a root component but no PURL.
- The new `isRecommendEnabled` helper defaults to `true` when the property is missing or not a boolean/string; double-check this aligns with the expected behavior for requests that omit the `recommend` parameter, especially with the new cache-stripping logic.
## Individual Comments
### Comment 1
<location path="src/main/java/io/github/guacsec/trustifyda/integration/backend/ExhortIntegration.java" line_range="321-322" />
<code_context>
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);
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against a null `purl()` on `tree.root()` before calling `canonicalize()`.
`tree.root()` being non-null doesn’t guarantee `root().purl()` is non-null, so `canonicalize()` can still throw a `NullPointerException`. Please add a null check for `root().purl()` or explicitly handle the case where the SBOM root has no PURL before calling `canonicalize()`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
… licenseExpressions (TC-4987) Exclude the root PURL from DepsDevRequestBuilder.fromSbom() — it represents the project itself, not a dependency, so deps.dev has no data for it. This prevents uncacheable cache misses that broke testLicensesCaching. Pass null for licenseExpressions in addSbomIdToDependencyTree to preserve existing batch license behavior and avoid breaking the batch_report.json fixture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #639 +/- ##
============================================
- Coverage 56.86% 56.63% -0.23%
Complexity 823 823
============================================
Files 94 94
Lines 4815 4831 +16
Branches 624 630 +6
============================================
- Hits 2738 2736 -2
- Misses 1802 1818 +16
- Partials 275 277 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review —
|
Verification Report for TC-4987 (commit 609ff3f)
Overall: WARNTwo informational warnings:
This comment was AI-generated by sdlc-workflow/verify-pr v0.11.0. |
Summary
SBOM_ID_PROPERTYfrom root PURL in single-analysis (processAnalysisRequest), enabling hardened image lookup for non-batch requestsprocessOriginalRequestbeforeaddSbomIdToDependencyTreeso monitoring captures the original dependency count, and remove redundantsetPropertyso the report uses the original tree for scanned countaddSbomIdToDependencyTreeand preserve allDependencyTreefields (licenseExpressions,root,componentHashes)filterLocalPackages— only remove the sbomId root ref when it is NOT an OCI type, so container image entries reach the hardened recommendation lookupaggregateCacheHits()whenrecommend=false— prevents Redis cache contamination where a priorrecommend=truerequest's cachedPackageItemobjects (which include recommendation data) leak through on subsequentrecommend=falserequestsTest plan
dockerfile-recommendations-disabledintegration test — should pass (no recommendations whenrecommend=false)dockerfile-recommendations-enabledintegration test — should pass (hardened recommendations present)recommend=falseafter arecommend=truerequest — verify no recommendation leakage from cachemvn test🤖 Generated with Claude Code
Summary by Sourcery
Ensure SBOM root handling and recommendation behavior correctly support hardened image lookups and avoid leaking cached recommendations into non-recommendation analyses.
Bug Fixes:
Enhancements: