From c2e4e93ba910e2fd388d13a6ce031e7a74e31237 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Thu, 13 Nov 2025 18:44:25 +0100 Subject: [PATCH] feat: report recommendations error but show vulnerabilities Signed-off-by: Ruben Romero Montes --- .../providers/ProviderResponseHandler.java | 16 ++- .../providers/osv/OsvResponseHandler.java | 11 +- .../trustify/RecommendationAggregation.java | 43 ++++++- .../trustify/TrustifyIntegration.java | 27 ++-- .../trustify/TrustifyResponseHandler.java | 11 +- .../integration/AbstractAnalysisTest.java | 37 ++++++ .../ProviderResponseHandlerTest.java | 20 ++- .../providers/osv/OsvResponseHandlerTest.java | 3 +- .../RecommendationAggregationTest.java | 115 ++++++++++++++++++ .../trustify/TrustifyResponseHandlerTest.java | 31 +++-- 10 files changed, 260 insertions(+), 54 deletions(-) diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandler.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandler.java index 660c4518..6747ddaa 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandler.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandler.java @@ -71,8 +71,7 @@ public abstract class ProviderResponseHandler { protected abstract String getProviderName(Exchange exchange); - public abstract ProviderResponse responseToIssues(byte[] response, DependencyTree tree) - throws IOException; + public abstract ProviderResponse responseToIssues(Exchange exchange) throws IOException; public ProviderResponse aggregateSplit(ProviderResponse oldExchange, ProviderResponse newExchange) throws IOException { @@ -111,7 +110,7 @@ public ProviderResponse aggregateSplit(ProviderResponse oldExchange, ProviderRes return exchange; } - protected ProviderStatus defaultOkStatus(String provider) { + public ProviderStatus defaultOkStatus(String provider) { return new ProviderStatus() .name(provider) .ok(Boolean.TRUE) @@ -204,7 +203,7 @@ public void processTokenFallBack(Exchange exchange) { private static String prettifyHttpError(HttpOperationFailedException httpException) { String text = httpException.getStatusText(); String defaultReason = - httpException.getResponseBody() != null + httpException.getResponseBody() != null && !httpException.getResponseBody().isBlank() ? httpException.getResponseBody() : httpException.getMessage(); return text @@ -216,9 +215,8 @@ private static String prettifyHttpError(HttpOperationFailedException httpExcepti }; } - public ProviderResponse emptyResponse( - @ExchangeProperty(Constants.DEPENDENCY_TREE_PROPERTY) DependencyTree tree) { - return new ProviderResponse(Collections.emptyMap(), null); + public ProviderResponse emptyResponse(Exchange exchange) { + return new ProviderResponse(Collections.emptyMap(), defaultOkStatus(getProviderName(exchange))); } /** @@ -295,7 +293,7 @@ public ProviderReport buildReport( @Body ProviderResponse response, @ExchangeProperty(Constants.DEPENDENCY_TREE_PROPERTY) DependencyTree tree) throws IOException { - if (response.status() != null) { + if (response.status() != null && response.pkgItems() == null) { return new ProviderReport().status(response.status()).sources(Collections.emptyMap()); } var providerName = getProviderName(exchange); @@ -306,7 +304,7 @@ public ProviderReport buildReport( .entrySet() .forEach( entry -> reports.put(entry.getKey(), buildReportForSource(entry.getValue(), tree))); - return new ProviderReport().status(defaultOkStatus(providerName)).sources(reports); + return new ProviderReport().status(response.status()).sources(reports); } private Source buildReportForSource(Map pkgItemsData, DependencyTree tree) { diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandler.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandler.java index 4e3f76c6..fd5da592 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandler.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandler.java @@ -25,9 +25,7 @@ import java.util.Map; import java.util.stream.Collectors; -import org.apache.camel.Body; import org.apache.camel.Exchange; -import org.apache.camel.ExchangeProperty; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,12 +60,11 @@ protected String getProviderName(Exchange exchange) { } @Override - public ProviderResponse responseToIssues( - @Body byte[] response, - @ExchangeProperty(Constants.DEPENDENCY_TREE_PROPERTY) DependencyTree tree) - throws IOException { + public ProviderResponse responseToIssues(Exchange exchange) throws IOException { + var response = exchange.getIn().getBody(byte[].class); + var tree = exchange.getProperty(Constants.DEPENDENCY_TREE_PROPERTY, DependencyTree.class); var json = (ObjectNode) mapper.readTree(response); - return new ProviderResponse(getIssues(json, tree), null); + return new ProviderResponse(getIssues(json, tree), defaultOkStatus(getProviderName(exchange))); } private Map getIssues(ObjectNode response, DependencyTree tree) { 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 f2eff3c8..4dbff241 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 @@ -24,9 +24,11 @@ import org.apache.camel.AggregationStrategy; import org.apache.camel.Exchange; +import org.jboss.logging.Logger; import io.github.guacsec.trustifyda.api.PackageRef; import io.github.guacsec.trustifyda.api.v5.Issue; +import io.github.guacsec.trustifyda.api.v5.ProviderStatus; import io.github.guacsec.trustifyda.api.v5.Remediation; import io.github.guacsec.trustifyda.api.v5.RemediationTrustedContent; import io.github.guacsec.trustifyda.model.PackageItem; @@ -36,25 +38,60 @@ public class RecommendationAggregation implements AggregationStrategy { + private static final Logger LOGGER = Logger.getLogger(RecommendationAggregation.class); + @Override public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { if (oldExchange == null) { return newExchange; } + var worstStatus = getWorstStatus(oldExchange, newExchange); + var providerResponse = getProviderResponse(oldExchange, newExchange); - var recommendations = getRecommendations(oldExchange, newExchange); + if (providerResponse == null) { + providerResponse = new ProviderResponse(new HashMap<>(), worstStatus); + } + Map recommendations = null; + try { + recommendations = getRecommendations(oldExchange, newExchange); + } catch (Exception e) { + LOGGER.warn("Error getting recommendations", e); + } if (providerResponse.pkgItems() == null) { - providerResponse = new ProviderResponse(new HashMap<>(), providerResponse.status()); + providerResponse = new ProviderResponse(new HashMap<>(), worstStatus); } if (recommendations != null && !recommendations.isEmpty()) { setTrustedContent(recommendations, providerResponse); } - oldExchange.getIn().setBody(providerResponse); + oldExchange.getIn().setBody(new ProviderResponse(providerResponse.pkgItems(), worstStatus)); return oldExchange; } + private ProviderStatus getWorstStatus(Exchange oldExchange, Exchange newExchange) { + ProviderStatus oldStatus = getStatus(oldExchange); + ProviderStatus newStatus = getStatus(newExchange); + + if (oldStatus == null && newStatus == null) { + return null; + } + if (oldStatus == null) { + return newStatus; + } + if (newStatus == null) { + return oldStatus; + } + return Boolean.FALSE.equals(oldStatus.getOk()) ? oldStatus : newStatus; + } + + private ProviderStatus getStatus(Exchange exchange) { + if (exchange.getIn().getBody() instanceof ProviderResponse response) { + return response.status(); + } + return null; + } + private ProviderResponse getProviderResponse(Exchange oldExchange, Exchange newExchange) { var body = oldExchange.getIn().getBody(); if (body != null && body instanceof ProviderResponse) { 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 794f1fef..71e4f969 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 @@ -106,24 +106,23 @@ public void configure() throws Exception { .parallelProcessing() .transform() .method(requestBuilder, "buildRequest") - .circuitBreaker() - .faultToleranceConfiguration() - .timeoutEnabled(true) - .timeoutDuration(TIMEOUT_DURATION) - .end() - - .to(direct("trustifyRequest")) - .onFallback() - .process(responseHandler::processResponseError); + .to(direct("trustifyRequest")); from(direct("recommendations")) .routeId("recommendations") .routePolicy(new ProviderRoutePolicy(registry)) .choice() .when(exchangeProperty(Constants.RECOMMEND_PARAM).isEqualTo(Boolean.TRUE)) + .circuitBreaker() + .faultToleranceConfiguration() + .timeoutEnabled(true) + .timeoutDuration(TIMEOUT_DURATION) + .end() .process(this::processRecommendationsRequest) .toD("${exchangeProperty.trustifyUrl}") .process(this::processRecommendations) + .onFallback() + .process(responseHandler::processResponseError) .endChoice() .end(); @@ -131,9 +130,16 @@ public void configure() throws Exception { from(direct("vulnerabilities")) .routeId("vulnerabilities") .routePolicy(new ProviderRoutePolicy(registry)) - .process(this::processVulnerabilitiesRequest) + .circuitBreaker() + .faultToleranceConfiguration() + .timeoutEnabled(true) + .timeoutDuration(TIMEOUT_DURATION) + .end() + .process(this::processVulnerabilitiesRequest) .toD("${exchangeProperty.trustifyUrl}") .transform(method(responseHandler, "responseToIssues")) + .onFallback() + .process(responseHandler::processResponseError) .end(); from(direct("trustifyRequest")) @@ -141,7 +147,6 @@ public void configure() throws Exception { .process(this::setProviderConfig) .process(this::addAuthentication) .multicast(new RecommendationAggregation()) - .stopOnException() .parallelProcessing() .to(direct("vulnerabilities")) .to(direct("recommendations")) diff --git a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandler.java b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandler.java index c2f24e3f..38a82d38 100644 --- a/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandler.java +++ b/src/main/java/io/github/guacsec/trustifyda/integration/providers/trustify/TrustifyResponseHandler.java @@ -26,9 +26,7 @@ import java.util.Map; import java.util.stream.Collectors; -import org.apache.camel.Body; import org.apache.camel.Exchange; -import org.apache.camel.ExchangeProperty; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -70,12 +68,11 @@ protected String getProviderName(Exchange exchange) { } @Override - public ProviderResponse responseToIssues( - @Body byte[] response, - @ExchangeProperty(Constants.DEPENDENCY_TREE_PROPERTY) DependencyTree tree) - throws IOException { + public ProviderResponse responseToIssues(Exchange exchange) throws IOException { + var response = exchange.getIn().getBody(byte[].class); + var tree = exchange.getProperty(Constants.DEPENDENCY_TREE_PROPERTY, DependencyTree.class); var json = (ObjectNode) mapper.readTree(response); - return new ProviderResponse(getIssues(json, tree), null); + return new ProviderResponse(getIssues(json, tree), defaultOkStatus(getProviderName(exchange))); } private Map getIssues(ObjectNode response, DependencyTree tree) { diff --git a/src/test/java/io/github/guacsec/trustifyda/integration/AbstractAnalysisTest.java b/src/test/java/io/github/guacsec/trustifyda/integration/AbstractAnalysisTest.java index 12943237..87f60acc 100644 --- a/src/test/java/io/github/guacsec/trustifyda/integration/AbstractAnalysisTest.java +++ b/src/test/java/io/github/guacsec/trustifyda/integration/AbstractAnalysisTest.java @@ -188,8 +188,39 @@ protected void verifyProviders(Collection providers, Map } protected void stubRecommendRequests() { + // Missing token + server.stubFor(post(Constants.TRUSTIFY_RECOMMEND_PATH).willReturn(aResponse().withStatus(401))); + + // Invalid token + server.stubFor( + post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader(Constants.AUTHORIZATION_HEADER, equalTo("Bearer " + INVALID_TOKEN)) + .willReturn( + aResponse() + .withStatus(401) + .withBody( + "{\"error\": \"Unauthorized\", \"message\": \"Verify the provided" + + " credentials are valid.\"}}"))); + // Internal Error + server.stubFor( + post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader(Constants.AUTHORIZATION_HEADER, equalTo("Bearer " + ERROR_TOKEN)) + .willReturn(aResponse().withStatus(500).withBody("Unexpected error"))); + // Forbidden server.stubFor( post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader(Constants.AUTHORIZATION_HEADER, equalTo("Bearer " + UNAUTH_TOKEN)) + .willReturn( + aResponse() + .withStatus(403) + .withBody( + "{\"error\": \"Forbidden\", \"message\": \"The provided credentials don't" + + " have the required permissions.\"}}"))); + server.stubFor( + post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader( + Constants.AUTHORIZATION_HEADER, + equalTo("Bearer " + TRUSTIFY_TOKEN).or(equalTo("Bearer " + OK_TOKEN))) .willReturn( aResponse() .withStatus(200) @@ -197,6 +228,9 @@ protected void stubRecommendRequests() { .withBodyFile("trustedcontent/empty_report.json"))); server.stubFor( post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader( + Constants.AUTHORIZATION_HEADER, + equalTo("Bearer " + TRUSTIFY_TOKEN).or(equalTo("Bearer " + OK_TOKEN))) .withRequestBody( equalToJson( loadFileAsString("__files/trustedcontent/maven_request.json"), true, false)) @@ -207,6 +241,9 @@ protected void stubRecommendRequests() { .withBodyFile("trustedcontent/maven_report.json"))); server.stubFor( post(Constants.TRUSTIFY_RECOMMEND_PATH) + .withHeader( + Constants.AUTHORIZATION_HEADER, + equalTo("Bearer " + TRUSTIFY_TOKEN).or(equalTo("Bearer " + OK_TOKEN))) .withRequestBody( equalToJson( loadFileAsString("__files/trustedcontent/batch_request.json"), true, false)) diff --git a/src/test/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandlerTest.java b/src/test/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandlerTest.java index 0fd9c7a1..3fc8b351 100644 --- a/src/test/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandlerTest.java +++ b/src/test/java/io/github/guacsec/trustifyda/integration/providers/ProviderResponseHandlerTest.java @@ -20,6 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.IOException; import java.util.Collections; @@ -31,6 +33,7 @@ import java.util.stream.Stream; import org.apache.camel.Exchange; +import org.apache.camel.Message; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -46,6 +49,7 @@ import io.github.guacsec.trustifyda.api.v5.SeverityUtils; import io.github.guacsec.trustifyda.api.v5.Source; import io.github.guacsec.trustifyda.api.v5.SourceSummary; +import io.github.guacsec.trustifyda.integration.Constants; import io.github.guacsec.trustifyda.model.DependencyTree; import io.github.guacsec.trustifyda.model.DirectDependency; import io.github.guacsec.trustifyda.model.PackageItem; @@ -70,7 +74,6 @@ public void testSummary( ProviderResponseHandler handler = new TestResponseHandler(); ProviderReport response = handler.buildReport(Mockito.mock(Exchange.class), new ProviderResponse(data, null), tree); - assertOkStatus(response); SourceSummary summary = getValidSource(response, sourceName).getSummary(); assertEquals(expected.getDirect(), summary.getDirect()); @@ -192,7 +195,6 @@ public void testSorted() throws IOException { handler.buildReport( Mockito.mock(Exchange.class), new ProviderResponse(data, null), buildTree()); - assertOkStatus(response); DependencyReport reportHighest = getValidSource(response, TEST_SOURCE).getDependencies().get(0); assertEquals("ab", reportHighest.getRef().name()); @@ -224,7 +226,6 @@ public void testHighestVulnerabilityInDirectDependency() throws IOException { handler.buildReport( Mockito.mock(Exchange.class), new ProviderResponse(data, null), buildTree()); - assertOkStatus(response); DependencyReport highest = getValidSource(response, TEST_SOURCE).getDependencies().get(0); assertEquals("ISSUE-002", highest.getHighestVulnerability().getId()); assertEquals(9f, highest.getHighestVulnerability().getCvssScore()); @@ -246,7 +247,6 @@ public void testHighestVulnerabilityInTransitiveDependency() throws IOException handler.buildReport( Mockito.mock(Exchange.class), new ProviderResponse(data, null), buildTree()); - assertOkStatus(response); DependencyReport highest = getValidSource(response, TEST_SOURCE).getDependencies().get(0); assertEquals("ISSUE-002", highest.getHighestVulnerability().getId()); assertEquals(9f, highest.getHighestVulnerability().getCvssScore()); @@ -426,9 +426,17 @@ protected String getProviderName(Exchange exchange) { } @Override - public ProviderResponse responseToIssues(byte[] response, DependencyTree tree) - throws IOException { + public ProviderResponse responseToIssues(Exchange exchange) throws IOException { throw new IllegalArgumentException("not implemented"); } } + + public static Exchange buildExchange(byte[] response, DependencyTree tree) { + var exchange = mock(Exchange.class); + when(exchange.getIn()).thenReturn(mock(Message.class)); + when(exchange.getIn().getBody(byte[].class)).thenReturn(response); + when(exchange.getProperty(Constants.DEPENDENCY_TREE_PROPERTY, DependencyTree.class)) + .thenReturn(tree); + return exchange; + } } diff --git a/src/test/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandlerTest.java b/src/test/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandlerTest.java index f6e67e91..9621a735 100644 --- a/src/test/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandlerTest.java +++ b/src/test/java/io/github/guacsec/trustifyda/integration/providers/osv/OsvResponseHandlerTest.java @@ -17,6 +17,7 @@ package io.github.guacsec.trustifyda.integration.providers.osv; +import static io.github.guacsec.trustifyda.integration.providers.ProviderResponseHandlerTest.buildExchange; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -57,7 +58,7 @@ void testVectors() throws IOException, URISyntaxException { deps.put(jacksonRef, new DirectDependency(jacksonRef, null)); var dependencyTree = new DependencyTree(deps); - var report = handler.responseToIssues(providerResponse, dependencyTree); + var report = handler.responseToIssues(buildExchange(providerResponse, dependencyTree)); assertFalse(report.pkgItems().isEmpty()); assertEquals(2, report.pkgItems().size()); diff --git a/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregationTest.java b/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregationTest.java index b70f09b9..dd3a32dd 100644 --- a/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregationTest.java +++ b/src/test/java/io/github/guacsec/trustifyda/integration/providers/trustify/RecommendationAggregationTest.java @@ -42,6 +42,7 @@ import io.github.guacsec.trustifyda.api.PackageRef; import io.github.guacsec.trustifyda.api.v5.Issue; +import io.github.guacsec.trustifyda.api.v5.ProviderStatus; import io.github.guacsec.trustifyda.api.v5.RemediationTrustedContent; import io.github.guacsec.trustifyda.api.v5.SeverityUtils; import io.github.guacsec.trustifyda.model.PackageItem; @@ -257,6 +258,112 @@ static Stream provideTestCases() { assertNotNull(resultItem); assertEquals(1, resultItem.issues().size()); assertNull(resultItem.recommendation()); + })), + + // Status aggregation tests for getWorstStatus + Arguments.of( + "StatusAggregation_BothNull_ReturnsNull", + new TestCase( + "StatusAggregation_BothNull_ReturnsNull", + new ProviderResponse(new HashMap<>(), null), + new ProviderResponse(new HashMap<>(), null), + result -> assertNull(result.status()))), + Arguments.of( + "StatusAggregation_OldNull_NewOk_ReturnsNew", + new TestCase( + "StatusAggregation_OldNull_NewOk_ReturnsNew", + new ProviderResponse(new HashMap<>(), null), + new ProviderResponse(new HashMap<>(), createOkStatus("provider")), + result -> { + assertNotNull(result.status()); + assertTrue(result.status().getOk()); + assertEquals("provider", result.status().getName()); + })), + Arguments.of( + "StatusAggregation_OldNull_NewNotOk_ReturnsNew", + new TestCase( + "StatusAggregation_OldNull_NewNotOk_ReturnsNew", + new ProviderResponse(new HashMap<>(), null), + new ProviderResponse(new HashMap<>(), createNotOkStatus("provider", 500, "Error")), + result -> { + assertNotNull(result.status()); + assertEquals(Boolean.FALSE, result.status().getOk()); + assertEquals("provider", result.status().getName()); + assertEquals(500, result.status().getCode()); + })), + Arguments.of( + "StatusAggregation_OldOk_NewNull_ReturnsOld", + new TestCase( + "StatusAggregation_OldOk_NewNull_ReturnsOld", + new ProviderResponse(new HashMap<>(), createOkStatus("oldProvider")), + new ProviderResponse(new HashMap<>(), null), + result -> { + assertNotNull(result.status()); + assertTrue(result.status().getOk()); + assertEquals("oldProvider", result.status().getName()); + })), + Arguments.of( + "StatusAggregation_OldNotOk_NewNull_ReturnsOld", + new TestCase( + "StatusAggregation_OldNotOk_NewNull_ReturnsOld", + new ProviderResponse( + new HashMap<>(), createNotOkStatus("oldProvider", 500, "Error")), + new ProviderResponse(new HashMap<>(), null), + result -> { + assertNotNull(result.status()); + assertEquals(Boolean.FALSE, result.status().getOk()); + assertEquals("oldProvider", result.status().getName()); + })), + Arguments.of( + "StatusAggregation_OldOk_NewOk_ReturnsNew", + new TestCase( + "StatusAggregation_OldOk_NewOk_ReturnsNew", + new ProviderResponse(new HashMap<>(), createOkStatus("oldProvider")), + new ProviderResponse(new HashMap<>(), createOkStatus("newProvider")), + result -> { + assertNotNull(result.status()); + assertTrue(result.status().getOk()); + assertEquals("newProvider", result.status().getName()); + })), + Arguments.of( + "StatusAggregation_OldOk_NewNotOk_ReturnsNew", + new TestCase( + "StatusAggregation_OldOk_NewNotOk_ReturnsNew", + new ProviderResponse(new HashMap<>(), createOkStatus("oldProvider")), + new ProviderResponse( + new HashMap<>(), createNotOkStatus("newProvider", 500, "Error")), + result -> { + assertNotNull(result.status()); + assertEquals(Boolean.FALSE, result.status().getOk()); + assertEquals("newProvider", result.status().getName()); + })), + Arguments.of( + "StatusAggregation_OldNotOk_NewOk_ReturnsOld", + new TestCase( + "StatusAggregation_OldNotOk_NewOk_ReturnsOld", + new ProviderResponse( + new HashMap<>(), createNotOkStatus("oldProvider", 500, "Error")), + new ProviderResponse(new HashMap<>(), createOkStatus("newProvider")), + result -> { + assertNotNull(result.status()); + assertEquals(Boolean.FALSE, result.status().getOk()); + assertEquals("oldProvider", result.status().getName()); + assertEquals(500, result.status().getCode()); + })), + Arguments.of( + "StatusAggregation_OldNotOk_NewNotOk_ReturnsOld", + new TestCase( + "StatusAggregation_OldNotOk_NewNotOk_ReturnsOld", + new ProviderResponse( + new HashMap<>(), createNotOkStatus("oldProvider", 500, "Old Error")), + new ProviderResponse( + new HashMap<>(), createNotOkStatus("newProvider", 404, "New Error")), + result -> { + assertNotNull(result.status()); + assertEquals(Boolean.FALSE, result.status().getOk()); + assertEquals("oldProvider", result.status().getName()); + assertEquals(500, result.status().getCode()); + assertEquals("Old Error", result.status().getMessage()); }))); } @@ -322,6 +429,14 @@ private static Issue createIssue(String id, String title, float cvssScore) { return issue; } + private static ProviderStatus createOkStatus(String providerName) { + return new ProviderStatus().name(providerName).ok(Boolean.TRUE).code(200).message("OK"); + } + + private static ProviderStatus createNotOkStatus(String providerName, int code, String message) { + return new ProviderStatus().name(providerName).ok(Boolean.FALSE).code(code).message(message); + } + @Test void testWithNullOldExchange() { Exchange newExchange = mock(Exchange.class); 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 0b5c2e32..1733b4a6 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 @@ -17,6 +17,7 @@ package io.github.guacsec.trustifyda.integration.providers.trustify; +import static io.github.guacsec.trustifyda.integration.providers.ProviderResponseHandlerTest.buildExchange; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -188,7 +189,8 @@ void testResponseToIssuesWithValidData() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); assertNotNull(result.pkgItems()); @@ -272,7 +274,8 @@ void testResponseToIssuesWithMultipleScoreTypes() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); assertNotNull(packageItem); @@ -289,7 +292,8 @@ void testResponseToIssuesWithEmptyResponse() throws IOException { String jsonResponse = "{}"; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); assertNotNull(result.pkgItems()); @@ -306,7 +310,8 @@ void testResponseToIssuesWithEmptyVulnerabilityArray() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); @@ -330,7 +335,8 @@ void testResponseToIssuesWithMissingStatusField() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); @@ -355,7 +361,8 @@ void testResponseToIssuesWithMissingAffectedField() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); @@ -395,7 +402,8 @@ void testResponseToIssuesWithNoScores() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); @@ -446,7 +454,8 @@ void testResponseToIssuesWithFallbackToDescription() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); assertNotNull(packageItem); @@ -509,7 +518,8 @@ void testResponseToIssuesWithMultipleFixedVersions() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); PackageItem packageItem = result.pkgItems().get("pkg:maven/org.postgresql/postgresql@42.5.0"); assertNotNull(packageItem); @@ -568,7 +578,8 @@ void testResponseToIssuesWithDependencyNotInTree() throws IOException { """; byte[] responseBytes = jsonResponse.getBytes(); - ProviderResponse result = handler.responseToIssues(responseBytes, dependencyTree); + ProviderResponse result = + handler.responseToIssues(buildExchange(responseBytes, dependencyTree)); assertNotNull(result); assertTrue(result.pkgItems().isEmpty());