Skip to content

Commit

Permalink
Merge pull request #787 from DependencyTrack/delay-bom-processed-noti…
Browse files Browse the repository at this point in the history
…fication-e2e

Add e2e test for delayed `BOM_PROCESSED` notification
  • Loading branch information
VithikaS authored Sep 6, 2023
2 parents 2cfce42 + bb719f5 commit d1c119a
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 1 deletion.
7 changes: 6 additions & 1 deletion e2e/src/test/java/org/hyades/e2e/AbstractE2ET.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private void initializeRedpanda() {

@SuppressWarnings("resource")
private GenericContainer<?> createApiServerContainer() {
return new GenericContainer<>(DockerImageName.parse(API_SERVER_IMAGE))
final var container = new GenericContainer<>(DockerImageName.parse(API_SERVER_IMAGE))
.withImagePullPolicy(PullPolicy.alwaysPull())
.withEnv("EXTRA_JAVA_OPTIONS", "-Xmx2g")
.withEnv("SYSTEM_REQUIREMENT_CHECK_ENABLED", "false")
Expand All @@ -119,6 +119,11 @@ private GenericContainer<?> createApiServerContainer() {
.withNetworkAliases("apiserver")
.withNetwork(internalNetwork)
.withExposedPorts(8080);
customizeApiServerContainer(container);
return container;
}

protected void customizeApiServerContainer(final GenericContainer<?> container) {
}

@SuppressWarnings("resource")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package org.hyades.e2e;

import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import org.hyades.apiserver.model.BomUploadRequest;
import org.hyades.apiserver.model.BomUploadResponse;
import org.hyades.apiserver.model.CreateNotificationRuleRequest;
import org.hyades.apiserver.model.CreateVulnerabilityRequest;
import org.hyades.apiserver.model.NotificationPublisher;
import org.hyades.apiserver.model.NotificationRule;
import org.hyades.apiserver.model.UpdateNotificationRuleRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.shaded.org.apache.commons.io.IOUtils;

import java.time.Duration;
import java.util.Base64;
import java.util.List;
import java.util.Set;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

public class BomProcessedNotificationDelayedE2ET extends AbstractE2ET {

@RegisterExtension
static WireMockExtension wireMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@Override
@BeforeEach
void beforeEach() throws Exception {
// host.docker.internal may not always be available, so use testcontainer's
// solution for host port exposure instead: https://www.testcontainers.org/features/networking/#exposing-host-ports-to-the-container
Testcontainers.exposeHostPorts(wireMock.getRuntimeInfo().getHttpPort());

super.beforeEach();
}

@Override
protected void customizeApiServerContainer(final GenericContainer<?> container) {
container.withEnv("TMP_DELAY_BOM_PROCESSED_NOTIFICATION", "true");
}

@Override
protected void customizeVulnAnalyzerContainer(final GenericContainer<?> container) {
// Disable all scanners except the internal one.
container
.withEnv("SCANNER_INTERNAL_ENABLED", "true")
.withEnv("SCANNER_OSSINDEX_ENABLED", "false")
.withEnv("SCANNER_SNYK_ENABLED", "false");
}

@Test
void test() throws Exception {
final List<NotificationPublisher> publishers = apiServerClient.getAllNotificationPublishers();

// Find the webhook notification publisher.
final NotificationPublisher webhookPublisher = publishers.stream()
.filter(publisher -> publisher.name().equals("Outbound Webhook"))
.findAny()
.orElseThrow(() -> new AssertionError("Unable to find webhook notification publisher"));

// Create a webhook alert for NEW_VULNERABILITY notifications and point it to WireMock.
final NotificationRule webhookRule = apiServerClient.createNotificationRule(new CreateNotificationRuleRequest(
"foo", "PORTFOLIO", "INFORMATIONAL", new CreateNotificationRuleRequest.Publisher(webhookPublisher.uuid())));
apiServerClient.updateNotificationRule(new UpdateNotificationRuleRequest(webhookRule.uuid(), webhookRule.name(), true,
"INFORMATIONAL", Set.of("BOM_PROCESSED", "PROJECT_VULN_ANALYSIS_COMPLETE"), """
{
"destination": "http://host.testcontainers.internal:%d/notification"
}
""".formatted(wireMock.getPort())));

wireMock.stubFor(post(urlPathEqualTo("/notification"))
.willReturn(aResponse()
.withStatus(201)));

// Create a new internal vulnerability for jackson-databind.
apiServerClient.createVulnerability(new CreateVulnerabilityRequest("INT-123", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", List.of(917, 502), List.of(
new CreateVulnerabilityRequest.AffectedComponent("PURL", "pkg:maven/com.fasterxml.jackson.core/[email protected]", "EXACT")
)));

// Parse and base64 encode a BOM.
final byte[] bomBytes = IOUtils.resourceToByteArray("/dtrack-apiserver-4.5.0.bom.json");
final String bomBase64 = Base64.getEncoder().encodeToString(bomBytes);

// Upload the BOM
final BomUploadResponse response = apiServerClient.uploadBom(new BomUploadRequest("foo", "bar", true, bomBase64));
assertThat(response.token()).isNotEmpty();

// Wait up to 15sec for the BOM processing to complete.
await("BOM_PROCESSED webhook notification")
.atMost(Duration.ofSeconds(15))
.untilAsserted(this::verifyBomProcessedWebhookNotification);

await("PROJECT_VULN_ANALYSIS_COMPLETE webhook notification")
.atMost(Duration.ofSeconds(15))
.untilAsserted(this::verifyProjectVulnAnalysisCompleteNotification);
}

private void verifyBomProcessedWebhookNotification() {
wireMock.verify(1, postRequestedFor(urlPathEqualTo("/notification"))
.withRequestBody(equalToJson("""
{
"notification" : {
"level" : "LEVEL_INFORMATIONAL",
"scope" : "SCOPE_PORTFOLIO",
"group" : "GROUP_BOM_PROCESSED",
"timestamp" : "${json-unit.regex}(^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$)",
"title" : "Bill of Materials Processed",
"content" : "A CycloneDX BOM was processed",
"subject" : {
"project" : {
"uuid" : "${json-unit.any-string}",
"name" : "foo",
"version" : "bar",
"purl" : "pkg:maven/org.dependencytrack/[email protected]?type=war"
},
"bom" : {
"content" : "(Omitted)",
"format" : "CycloneDX",
"specVersion" : "Unknown"
}
}
}
}
""")));
}

private void verifyProjectVulnAnalysisCompleteNotification() {
wireMock.verify(1, postRequestedFor(urlPathEqualTo("/notification"))
.withRequestBody(equalToJson("""
{
"notification" : {
"level" : "LEVEL_INFORMATIONAL",
"scope" : "SCOPE_PORTFOLIO",
"group" : "GROUP_PROJECT_VULN_ANALYSIS_COMPLETE",
"timestamp" : "${json-unit.any-string}",
"title" : "Project vulnerability analysis complete",
"content" : "${json-unit.any-string}",
"subject" : {
"project" : {
"uuid": "${json-unit.any-string}",
"name" : "foo",
"version" : "bar",
"purl": "pkg:maven/org.dependencytrack/[email protected]?type=war"
},
"findings" : [ {
"component" : {
"uuid": "${json-unit.any-string}",
"group" : "com.fasterxml.jackson.core",
"name" : "jackson-databind",
"version" : "2.13.2.2",
"purl" : "pkg:maven/com.fasterxml.jackson.core/[email protected]?type=jar",
"md5" : "055c97cb488b0956801e13abcc2a0cfe",
"sha1" : "ffeb635597d093509f33e1e94274d14be610f933",
"sha256" : "efb86b148712a838b94b3cfc95769785a116b3461f709b4cc510055a58b804b2",
"sha512" : "0e9398591d86f80f16fc2d6ff0dda3e7821033e2c59472981eaab61443be3d77198655682905b85260fb2186a2cf0f33988aff689a49bb54e56c07e02f607e8a"
},
"vulnerabilities" : [ {
"uuid": "${json-unit.any-string}",
"vulnId" : "INT-123",
"source" : "INTERNAL",
"severity" : "CRITICAL",
"cvssv3": 10.0
} ]
} ],
"status" : "PROJECT_VULN_ANALYSIS_STATUS_COMPLETED"
}
}
}
""")));
}

}

0 comments on commit d1c119a

Please sign in to comment.