From ad6eae757db65b026be426d22b7079379d0e04f3 Mon Sep 17 00:00:00 2001 From: Adva Oren Date: Mon, 20 Jul 2026 18:23:19 +0300 Subject: [PATCH] refactor(hardened): remove UBI recommendation code and configuration The Hummingbird-powered Hardened Images provider now serves dynamic image recommendations, making the static UBI config-based mappings redundant. This removes the UBIRecommendation config interface, getUBIRecommendation() method, UBI properties from application.properties, and related test code. The imageMapping template parameter is set to "[]" to preserve UI compatibility. Implements TC-4971 Assisted-by: Claude Code --- CONVENTIONS.md | 3 +- .../trustify/RecommendationAggregation.java | 6 +-- .../trustify/TrustifyIntegration.java | 25 ----------- .../trustify/ubi/UBIRecommendation.java | 31 ------------- .../integration/report/ReportTemplate.java | 23 +--------- src/main/resources/application.properties | 11 ----- .../trustify/TrustifyResponseHandlerTest.java | 45 ------------------- .../__files/reports/batch_report.json | 14 +----- 8 files changed, 6 insertions(+), 152 deletions(-) delete mode 100644 src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/ubi/UBIRecommendation.java diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 65631f29..c114c46f 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -61,8 +61,7 @@ src/main/java/io/github/guacsec/trustifyda/ │ ├── lock/ # Distributed locking (LockService) │ ├── providers/ # Vulnerability providers │ │ └── trustify/ -│ │ ├── hardened/ # Hardened image recommendation provider -│ │ └── ubi/ # UBI image recommendation (config-based) +│ │ └── hardened/ # Hardened image recommendation provider │ ├── report/ # Report generation │ ├── registry/ # Ecosystem registry integrations (PEP 691, etc.) │ ├── sbom/ # SBOM parsing diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregation.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregation.java index 3795fd87..4bd2065c 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregation.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregation.java @@ -123,9 +123,9 @@ private Map> getRecommendations( /** * Normalizes recommendation maps to a consistent List-valued format. Handles both the legacy - * single-value format (Map<PackageRef, IndexedRecommendation>) from trusted-content/UBI - * providers and the new list format (Map<PackageRef, List<IndexedRecommendation>>) - * from the hardened image provider. + * single-value format (Map<PackageRef, IndexedRecommendation>) from the trusted-content + * provider and the list format (Map<PackageRef, List<IndexedRecommendation>>) from + * the hardened image provider. */ @SuppressWarnings("unchecked") private Map> normalizeRecommendations( 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 b500e7c3..47ddc5c1 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 @@ -42,7 +42,6 @@ import io.github.guacsec.trustifyda.integration.cache.CacheService; import io.github.guacsec.trustifyda.integration.providers.VulnerabilityProvider; import io.github.guacsec.trustifyda.integration.providers.trustify.hardened.HardenedImageProvider; -import io.github.guacsec.trustifyda.integration.providers.trustify.ubi.UBIRecommendation; import io.github.guacsec.trustifyda.model.DependencyTree; import io.github.guacsec.trustifyda.model.PackageItem; import io.github.guacsec.trustifyda.model.ProviderHealthCheckResult; @@ -84,7 +83,6 @@ public class TrustifyIntegration extends EndpointRouteBuilder { @Inject TrustifyResponseHandler responseHandler; @Inject TrustifyRequestBuilder requestBuilder; @Inject ObjectMapper mapper; - @Inject UBIRecommendation ubiRecommendation; @Inject HardenedImageProvider hardenedImageProvider; @Inject MeterRegistry registry; @Inject CacheService cacheService; @@ -94,7 +92,6 @@ public class TrustifyIntegration extends EndpointRouteBuilder { private static final List FIXED_STATUSES = List.of("NotAffected", "Fixed"); private static final String OCI_PURL_TYPE = "oci"; private static final String TRUSTED_CONTENT_SOURCE = "trusted-content"; - private static final String HARDENED_IMAGES_SOURCE = "hardened-images"; @Override public void configure() throws Exception { @@ -372,11 +369,9 @@ private String getTokenHeader(Exchange exchange) { */ public void processRecommendations(Exchange exchange) throws IOException { byte[] tcResponse = exchange.getIn().getBody(byte[].class); - String sbomId = exchange.getProperty(Constants.SBOM_ID_PROPERTY, String.class); var response = mapper.readValue(tcResponse, RecommendationsResponse.class); var mergedRecommendations = indexRecommendations(response); - mergedRecommendations.putAll(getUBIRecommendation(sbomId)); exchange.getMessage().setBody(mergedRecommendations); } @@ -411,26 +406,6 @@ private Map indexRecommendations( return result; } - private Map getUBIRecommendation(String sbomId) { - if (sbomId == null) { - return Collections.emptyMap(); - } - - var pkgRef = new PackageRef(sbomId); - if (!OCI_PURL_TYPE.equals(pkgRef.purl().getType())) { - return Collections.emptyMap(); - } - - var recommendedUBIPurl = ubiRecommendation.mapping().get(pkgRef.name()); - if (recommendedUBIPurl != null) { - var recommendation = - new IndexedRecommendation( - new PackageRef(recommendedUBIPurl), null, HARDENED_IMAGES_SOURCE); - return Collections.singletonMap(pkgRef, recommendation); - } - return Collections.emptyMap(); - } - private void processHardenedRecommendations(Exchange exchange) { String sbomId = exchange.getProperty(Constants.SBOM_ID_PROPERTY, String.class); exchange.getMessage().setBody(hardenedImageProvider.lookupBySbomId(sbomId)); diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/ubi/UBIRecommendation.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/ubi/UBIRecommendation.java deleted file mode 100644 index 2580e0d7..00000000 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/ubi/UBIRecommendation.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2023-2025 Trustify Dependency Analytics Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.github.guacsec.trustifyda.integration.providers.trustify.ubi; - -import java.util.Map; - -import io.smallrye.config.ConfigMapping; - -@ConfigMapping(prefix = "trustedcontent.recommendation.ubi") -public interface UBIRecommendation { - Map mapping(); - - Map purl(); - - Map catalogurl(); -} diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/report/ReportTemplate.java b/src/main/java/io/github/guacsec/trustifyda/integration/report/ReportTemplate.java index e95483da..17301511 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/report/ReportTemplate.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/report/ReportTemplate.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -33,10 +32,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; import io.github.guacsec.trustifyda.integration.Constants; -import io.github.guacsec.trustifyda.integration.providers.trustify.ubi.UBIRecommendation; import io.quarkus.runtime.annotations.RegisterForReflection; import jakarta.enterprise.context.ApplicationScoped; @@ -52,8 +49,6 @@ public class ReportTemplate { @ConfigProperty(name = "report.remediation.template") String remediationTemplate; - @Inject UBIRecommendation ubiRecommendation; - @ConfigProperty(name = Constants.TELEMETRY_WRITE_KEY) Optional writeKey; @@ -74,7 +69,7 @@ public Map setVariables( params.put("report", report); params.put("cveIssueTemplate", cveIssuePathRegex); params.put("remediationTemplate", remediationTemplate); - params.put("imageMapping", getImageMapping()); + params.put("imageMapping", "[]"); params.put("rhdaSource", rhdaSource); getBrandingConfig() .ifPresent(config -> params.put("brandingConfig", getBrandingConfigMap(config))); @@ -96,22 +91,6 @@ private String sanitize(String report) { return sanitizedReport; } - private String getImageMapping() throws JsonProcessingException { - List> urlMapping = - ubiRecommendation.purl().keySet().stream() - .map( - ubi -> { - Map urls = new HashMap<>(2); - urls.put("purl", ubiRecommendation.purl().get(ubi)); - urls.put("catalogUrl", ubiRecommendation.catalogurl().get(ubi)); - return urls; - }) - .toList(); - - ObjectWriter objectWriter = new ObjectMapper().writer(); - return objectWriter.writeValueAsString(urlMapping); - } - private Optional getBrandingConfig() { try { Config config = ConfigProvider.getConfig(); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bc678d14..770eb5ac 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -33,17 +33,6 @@ quarkus.management.enabled=true quarkus.micrometer.binder.http-server.enabled=false quarkus.http.limits.max-body-size=4G -trustedcontent.recommendation.ubi.purl.ubi9=pkg:oci/ubi@sha256:f5983f7c7878cc9b26a3962be7756e3c810e9831b0b9f9613e6f6b445f884e74?repository_url=registry.access.redhat.com/ubi9/ubi&tag=9.3-1552&arch=amd64 -trustedcontent.recommendation.ubi.catalogurl.ubi9=https://catalog.redhat.com/software/containers/ubi9/ubi/615bcf606feffc5384e8452e?architecture=amd64&image=65a82982a10f3e68777870b5f -trustedcontent.recommendation.ubi.purl.ubi9-minimal=pkg:oci/ubi-minimal@sha256:06d06f15f7b641a78f2512c8817cbecaa1bf549488e273f5ac27ff1654ed33f0?repository_url=registry.access.redhat.com/ubi9/ubi-minimal&tag=9.3-1552&arch=amd64 -trustedcontent.recommendation.ubi.catalogurl.ubi9-minimal=https://catalog.redhat.com/software/containers/ubi9/ubi-minimal/615bd9b4075b022acc111bf5?architecture=amd64&image=65a828e3cda4984705d45d26 -trustedcontent.recommendation.ubi.mapping.alpine=${trustedcontent.recommendation.ubi.purl.ubi9-minimal} -trustedcontent.recommendation.ubi.mapping.ubuntu=${trustedcontent.recommendation.ubi.purl.ubi9} -trustedcontent.recommendation.ubi.mapping.centos=${trustedcontent.recommendation.ubi.purl.ubi9} -trustedcontent.recommendation.ubi.mapping.debian=${trustedcontent.recommendation.ubi.purl.ubi9} -trustedcontent.recommendation.ubi.mapping.fedora=${trustedcontent.recommendation.ubi.purl.ubi9} -trustedcontent.recommendation.ubi.mapping.amazonlinux=${trustedcontent.recommendation.ubi.purl.ubi9} - # Hardened image recommendation provider (Hummingbird) trustedcontent.recommendation.hardened.url=${HUMMINGBIRD_URL:} trustedcontent.recommendation.hardened.refresh-interval=${HUMMINGBIRD_REFRESH_INTERVAL:1h} diff --git a/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandlerTest.java b/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandlerTest.java index 5fdaabbe..15e128b3 100644 --- a/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandlerTest.java +++ b/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandlerTest.java @@ -53,7 +53,6 @@ import io.github.guacsec.trustifyda.api.v5.RemediationInfo; import io.github.guacsec.trustifyda.api.v5.Severity; import io.github.guacsec.trustifyda.integration.Constants; -import io.github.guacsec.trustifyda.integration.providers.trustify.ubi.UBIRecommendation; import io.github.guacsec.trustifyda.model.DependencyTree; import io.github.guacsec.trustifyda.model.DirectDependency; import io.github.guacsec.trustifyda.model.PackageItem; @@ -66,7 +65,6 @@ public class TrustifyResponseHandlerTest { private DependencyTree dependencyTree; private TrustifyIntegration trustifyIntegration; - private UBIRecommendation ubiRecommendation; private static final PackageRef rootPackageRef = new PackageRef("pkg:maven/org.acme/app@1.0"); @@ -89,15 +87,6 @@ void setUp() { // Setup for TrustifyIntegration.processRecommendations() tests trustifyIntegration = new TrustifyIntegration(); trustifyIntegration.mapper = new ObjectMapper(); - - // Create a mock UBIRecommendation - ubiRecommendation = mock(UBIRecommendation.class); - Map mapping = new HashMap<>(); - mapping.put("alpine", "pkg:oci/ubi@0.0.2"); - when(ubiRecommendation.mapping()).thenReturn(mapping); - when(ubiRecommendation.purl()).thenReturn(Collections.emptyMap()); - when(ubiRecommendation.catalogurl()).thenReturn(Collections.emptyMap()); - trustifyIntegration.ubiRecommendation = ubiRecommendation; } private static Stream testResponseToIssuesWithValidData() { @@ -989,40 +978,6 @@ void testProcessRecommendationsEmpty() throws IOException { assertTrue(recommendations.isEmpty()); } - @Test - void testProcessRecommendationsWithSbomId() throws IOException { - String sbomId = "pkg:oci/alpine@0.0.1"; - Exchange exchange = mock(Exchange.class); - Message inMessage = mock(Message.class); - Message outMessage = mock(Message.class); - - when(exchange.getIn()).thenReturn(inMessage); - when(exchange.getMessage()).thenReturn(outMessage); - when(exchange.getProperty(Constants.SBOM_ID_PROPERTY, String.class)).thenReturn(sbomId); - - byte[] responseBytes = - getClass() - .getClassLoader() - .getResourceAsStream("__files/trustedcontent/empty_report.json") - .readAllBytes(); - when(inMessage.getBody(byte[].class)).thenReturn(responseBytes); - - trustifyIntegration.processRecommendations(exchange); - - @SuppressWarnings("rawtypes") - ArgumentCaptor bodyCaptor = forClass(Map.class); - verify(outMessage).setBody(bodyCaptor.capture()); - @SuppressWarnings("unchecked") - Map recommendations = bodyCaptor.getValue(); - assertNotNull(recommendations); - - PackageRef sbomRef = new PackageRef(sbomId); - IndexedRecommendation recommendation = - new IndexedRecommendation(new PackageRef("pkg:oci/ubi@0.0.2"), null, "hardened-images"); - assertEquals(1, recommendations.size()); - assertEquals(recommendation, recommendations.get(sbomRef)); - } - @Test void testResponseToIssuesWithUnscannedRefs() throws IOException { String jsonResponse = diff --git a/src/test/resources/__files/reports/batch_report.json b/src/test/resources/__files/reports/batch_report.json index 05701020..5c0eab66 100644 --- a/src/test/resources/__files/reports/batch_report.json +++ b/src/test/resources/__files/reports/batch_report.json @@ -1124,19 +1124,7 @@ "warnings": {} }, "sources": {}, - "recommendations": { - "hardened-images": { - "summary": { - "total": 1 - }, - "dependencies": [ - { - "ref": "pkg:oci/debian@sha256%3A7c288032ecf3319045d9fa538c3b0cc868a320d01d03bce15b99c2c336319994?tag=0.0.1", - "recommendation": "pkg:oci/ubi@sha256%3Af5983f7c7878cc9b26a3962be7756e3c810e9831b0b9f9613e6f6b445f884e74?arch=amd64&repository_url=registry.access.redhat.com%2Fubi9%2Fubi&tag=9.3-1552" - } - ] - } - } + "recommendations": {} } }, "licenses": [