Skip to content

Commit f179fb6

Browse files
authored
feat: allow users to opt-out from trusted content (#433)
Signed-off-by: Ruben Romero Montes <[email protected]>
1 parent d6d5d42 commit f179fb6

File tree

9 files changed

+3658
-4
lines changed

9 files changed

+3658
-4
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ You can disable a given provider for the dependency graph analysis by using `api
3939

4040
Providers should be defined as a multi-valued list in the `providers` Query Parameter. e.g. `/analysis?providers=snyk&providers=oss-index`
4141

42+
## Recommendations
43+
44+
By default the service will look for Red Hat Trusted Content remmediations and recommendations. If you want to opt out from this service
45+
you can use the `recommend` query parameter and set it to `false`. Example `/analysis?recommend=false`
46+
4247
## Package URL Types
4348

4449
The supported Package URL types depends on each external provider.

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<deploy-plugin.version>3.1.1</deploy-plugin.version>
4949

5050
<!-- Dependencies -->
51-
<exhort-api.version>1.0.5</exhort-api.version>
51+
<exhort-api.version>1.0.15</exhort-api.version>
5252
<sentry.version>7.8.0</sentry.version>
5353
<spdx.version>2.0.2</spdx.version>
5454
<htmlunit.version>4.11.1</htmlunit.version>
@@ -222,6 +222,7 @@
222222
<dependency>
223223
<groupId>io.quarkus</groupId>
224224
<artifactId>quarkus-junit5-mockito</artifactId>
225+
<scope>test</scope>
225226
</dependency>
226227
<dependency>
227228
<groupId>io.rest-assured</groupId>

src/main/java/com/redhat/exhort/integration/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ private Constants() {}
3535

3636
public static final String EXCLUDE_FROM_READINESS_CHECK = "exclusionFromReadiness";
3737
public static final String PROVIDERS_PARAM = "providers";
38+
public static final String RECOMMEND_PARAM = "recommend";
3839

3940
public static final String HEALTH_CHECKS_LIST_HEADER_NAME = "healthChecksRoutesList";
4041
public static final String SBOM_TYPE_PARAM = "sbomType";

src/main/java/com/redhat/exhort/integration/backend/ExhortIntegration.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package com.redhat.exhort.integration.backend;
2020

2121
import static com.redhat.exhort.integration.Constants.PROVIDERS_PARAM;
22+
import static com.redhat.exhort.integration.Constants.RECOMMEND_PARAM;
2223
import static com.redhat.exhort.integration.Constants.REQUEST_CONTENT_PROPERTY;
2324
import static com.redhat.exhort.integration.Constants.VERBOSE_MODE_HEADER;
2425

@@ -41,6 +42,7 @@
4142
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
4243
import org.apache.camel.component.micrometer.MicrometerConstants;
4344
import org.apache.camel.component.micrometer.routepolicy.MicrometerRoutePolicyFactory;
45+
import org.apache.camel.model.rest.RestParamType;
4446
import org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy;
4547

4648
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -145,9 +147,21 @@ public void configure() {
145147
rest()
146148
.post("/v4/analysis")
147149
.routeId("restAnalysis")
150+
.param()
151+
.name(RECOMMEND_PARAM)
152+
.type(RestParamType.query)
153+
.dataType("boolean")
154+
.defaultValue(Boolean.TRUE.toString())
155+
.endParam()
148156
.to("direct:analysis")
149157
.post("/v4/batch-analysis")
150158
.routeId("restBatchAnalysis")
159+
.param()
160+
.name(RECOMMEND_PARAM)
161+
.type(RestParamType.query)
162+
.dataType("boolean")
163+
.defaultValue(Boolean.TRUE.toString())
164+
.endParam()
151165
.to("direct:batchAnalysis")
152166
.get("/v4/token")
153167
.routeId("restTokenValidation")
@@ -197,6 +211,7 @@ public void configure() {
197211
.setProperty(Constants.GZIP_RESPONSE_PROPERTY, constant(Boolean.TRUE))
198212
.end()
199213
.setProperty(PROVIDERS_PARAM, method(vulnerabilityProvider, "getProvidersFromQueryParam"))
214+
.setProperty(RECOMMEND_PARAM, header(RECOMMEND_PARAM))
200215
.setProperty(REQUEST_CONTENT_PROPERTY, method(BackendUtils.class, "getResponseMediaType"))
201216
.setProperty(VERBOSE_MODE_HEADER, header(VERBOSE_MODE_HEADER));
202217

src/main/java/com/redhat/exhort/integration/providers/snyk/SnykIntegration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package com.redhat.exhort.integration.providers.snyk;
2020

2121
import static com.redhat.exhort.integration.Constants.PROVIDERS_PARAM;
22+
import static com.redhat.exhort.integration.Constants.RECOMMEND_PARAM;
2223

2324
import org.apache.camel.Exchange;
2425
import org.apache.camel.Message;
@@ -174,6 +175,7 @@ private void processRequestHeaders(Message message) {
174175
message.removeHeader(Constants.SNYK_TOKEN_HEADER);
175176
message.removeHeader(Constants.ACCEPT_ENCODING_HEADER);
176177
message.removeHeader(PROVIDERS_PARAM);
178+
message.removeHeader(RECOMMEND_PARAM);
177179
message.setHeader(
178180
Constants.USER_AGENT_HEADER,
179181
String.format(Constants.SNYK_USER_AGENT_HEADER_FORMAT, projectName, projectVersion));

src/main/java/com/redhat/exhort/integration/trustedcontent/TcResponseAggregation.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public TrustedContentResponse aggregateCachedResponse(
5959
@ExchangeProperty(Constants.CACHED_RECOMMENDATIONS) TrustedContentCachedRequest cached,
6060
Exchange exchange)
6161
throws ExecutionException {
62+
6263
var externalResponse = exchange.getIn().getBody(TrustedContentResponse.class);
6364
if (externalResponse == null) {
6465
externalResponse =
@@ -73,8 +74,19 @@ public TrustedContentResponse aggregateCachedResponse(
7374
cacheService.cacheRecommendations(externalResponse, cached.miss());
7475
Map<PackageRef, IndexedRecommendation> recommendations =
7576
new HashMap<>(externalResponse.recommendations());
77+
cacheService.cacheRecommendations(externalResponse, cached.miss());
7678
recommendations.putAll(cached.cached());
7779
exchange.removeProperty(Constants.CACHED_RECOMMENDATIONS);
7880
return new TrustedContentResponse(recommendations, externalResponse.status());
7981
}
82+
83+
public TrustedContentResponse aggregateEmptyResponse(Exchange exchange) {
84+
return new TrustedContentResponse(
85+
new HashMap<>(),
86+
new ProviderStatus()
87+
.name(Constants.TRUSTED_CONTENT_PROVIDER)
88+
.code(Status.OK.getStatusCode())
89+
.message(Status.OK.getReasonPhrase())
90+
.ok(Boolean.TRUE));
91+
}
8092
}

src/main/java/com/redhat/exhort/integration/trustedcontent/TrustedContentIntegration.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,13 @@ public void configure() {
5050
// fmt:off
5151
from(direct("getTrustedContent"))
5252
.routeId("getTrustedContent")
53-
.setBody(method(requestBuilder, "filterCachedRecommendations"))
54-
.to(direct("getRemoteTrustedContent"))
55-
.setBody(method(aggregation, "aggregateCachedResponse"));
53+
.choice().when(exchangeProperty(Constants.RECOMMEND_PARAM).isEqualTo(Boolean.TRUE))
54+
.setBody(method(requestBuilder, "filterCachedRecommendations"))
55+
.to(direct("getRemoteTrustedContent"))
56+
.setBody(method(aggregation, "aggregateCachedResponse"))
57+
.otherwise()
58+
.setBody(method(aggregation, "aggregateEmptyResponse"))
59+
.endChoice();
5660

5761
from(direct("getRemoteTrustedContent"))
5862
.routeId("getRemoteTrustedContent")

src/test/java/com/redhat/exhort/integration/AnalysisTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,35 @@ public void testDefaultTokens() {
327327
assertJson("reports/report_default_token.json", body);
328328
verifySnykRequest(SNYK_TOKEN);
329329
verifyTpaRequest(TPA_TOKEN);
330+
verifyTrustedContentRequest();
331+
}
332+
333+
@Test
334+
public void testDefaultTokensOptOutRecommendations() {
335+
stubAllProviders();
336+
337+
var body =
338+
given()
339+
.header(CONTENT_TYPE, Constants.CYCLONEDX_MEDIATYPE_JSON)
340+
.header("Accept", MediaType.APPLICATION_JSON)
341+
.queryParam(Constants.RECOMMEND_PARAM, Boolean.FALSE)
342+
.body(loadSBOMFile(CYCLONEDX))
343+
.when()
344+
.post("/api/v4/analysis")
345+
.then()
346+
.assertThat()
347+
.statusCode(200)
348+
.header(
349+
Constants.EXHORT_REQUEST_ID_HEADER,
350+
MatchesPattern.matchesPattern(REGEX_MATCHER_REQUEST_ID))
351+
.contentType(MediaType.APPLICATION_JSON)
352+
.extract()
353+
.body()
354+
.asPrettyString();
355+
assertJson("reports/report_default_token_no_recommend.json", body);
356+
verifySnykRequest(SNYK_TOKEN);
357+
verifyTpaRequest(TPA_TOKEN);
358+
verifyNoInteractionsWithTrustedContent();
330359
}
331360

332361
@Test

0 commit comments

Comments
 (0)