diff --git a/src/main/resources/templates/notification/publisher/slack.peb b/src/main/resources/templates/notification/publisher/slack.peb index c3947b057b..98d30c3d23 100644 --- a/src/main/resources/templates/notification/publisher/slack.peb +++ b/src/main/resources/templates/notification/publisher/slack.peb @@ -60,7 +60,7 @@ "text": "{{ subject.component.toString | escape(strategy="json") }}" } ] - }, + }{% if baseUrl is not empty %}, { "type": "actions", "elements": [ @@ -84,6 +84,7 @@ } ] } + {% endif %} ] } {% elseif notification.group == "NEW_VULNERABLE_DEPENDENCY" %} @@ -132,7 +133,7 @@ "text": "{{ subject.component.project.toString | escape(strategy="json") }}" } ] - }, + }{% if baseUrl is not empty %}, { "type": "actions", "elements": [ @@ -156,6 +157,7 @@ } ] } + {% endif %} ] } {% elseif notification.group == "PROJECT_AUDIT_CHANGE" %} @@ -250,7 +252,7 @@ "text": "{{ subject.project.toString | escape(strategy="json") }}" } ] - }, + }{% if baseUrl is not empty %}, { "type": "actions", "elements": [ @@ -283,6 +285,7 @@ } ] } + {% endif %} ] } {% elseif notification.group == "POLICY_VIOLATION" %} @@ -357,7 +360,7 @@ "text": "{{ subject.project.toString | escape(strategy="json") }}" } ] - }, + }{% if baseUrl is not empty %}, { "type": "actions", "elements": [ @@ -381,6 +384,7 @@ } ] } + {% endif %} ] } {% else %} diff --git a/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java index 5df72bea0d..9a0044c019 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java @@ -38,6 +38,7 @@ import org.dependencytrack.notification.vo.BomConsumedOrProcessed; import org.dependencytrack.notification.vo.BomProcessingFailed; import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; +import org.dependencytrack.notification.vo.NewVulnerableDependency; import org.junit.Test; import javax.json.Json; @@ -150,6 +151,27 @@ public void testInformWithNewVulnerabilityNotification() { .isThrownBy(() -> publisherInstance.inform(PublishContext.from(notification), notification, createConfig())); } + @Test + public void testInformWithNewVulnerableDependencyNotification() { + final var project = createProject(); + final var component = createComponent(project); + final var vuln = createVulnerability(); + + final var subject = new NewVulnerableDependency(component, List.of(vuln)); + + final var notification = new Notification() + .scope(NotificationScope.PORTFOLIO) + .group(NotificationGroup.NEW_VULNERABLE_DEPENDENCY) + .level(NotificationLevel.INFORMATIONAL) + .title(NotificationConstants.Title.NEW_VULNERABLE_DEPENDENCY) + .content("") + .timestamp(LocalDateTime.ofEpochSecond(66666, 666, ZoneOffset.UTC)) + .subject(subject); + + assertThatNoException() + .isThrownBy(() -> publisherInstance.inform(PublishContext.from(notification), notification, createConfig())); + } + @Test public void testInformWithProjectAuditChangeNotification() { final var project = createProject(); diff --git a/src/test/java/org/dependencytrack/notification/publisher/JiraPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/JiraPublisherTest.java index 6c3670faf2..1474fbb2f2 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/JiraPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/JiraPublisherTest.java @@ -182,6 +182,29 @@ public void testInformWithNewVulnerabilityNotification() { """))); } + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(urlPathEqualTo("/rest/api/2/issue")) + .withHeader("Authorization", equalTo("Basic amlyYVVzZXI6amlyYVBhc3N3b3Jk")) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "fields": { + "project": { + "key": "PROJECT" + }, + "issuetype": { + "name": "Task" + }, + "summary": "[Dependency-Track] [NEW_VULNERABLE_DEPENDENCY] Vulnerable dependency introduced on project projectName", + "description": "A component which contains one or more vulnerabilities has been added to your project.\\n\\\\\\\\\\n\\\\\\\\\\n*Project*\\n[pkg:maven/org.acme/projectName@projectVersion|https://example.com/projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95]\\n\\n*Component*\\n[componentName : componentVersion|https://example.com/components/94f87321-a5d1-4c2f-b2fe-95165debebc6]\\n\\n*Vulnerabilities*\\n- INT-001 (Medium)\\n" + } + } + """))); + } + @Override public void testInformWithProjectAuditChangeNotification() { super.testInformWithProjectAuditChangeNotification(); diff --git a/src/test/java/org/dependencytrack/notification/publisher/MattermostPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/MattermostPublisherTest.java index f5902b7f3b..927d406b02 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/MattermostPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/MattermostPublisherTest.java @@ -105,6 +105,21 @@ public void testInformWithNewVulnerabilityNotification() { """))); } + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "username" : "Dependency Track", + "icon_url" : "https://raw.githubusercontent.com/DependencyTrack/branding/master/dt-logo-symbol-blue-background.png", + "text" : "#### Vulnerable Dependency Introduced\\n\\n**Project**: \\n**Component**: componentName : componentVersion\\n[View Project](https://example.com/projects/) - [View Component](https://example.com/components/94f87321-a5d1-4c2f-b2fe-95165debebc6)" + } + """))); + } + @Override public void testInformWithProjectAuditChangeNotification() { super.testInformWithProjectAuditChangeNotification(); diff --git a/src/test/java/org/dependencytrack/notification/publisher/MsTeamsPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/MsTeamsPublisherTest.java index a7f22ed354..7aaf2290d9 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/MsTeamsPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/MsTeamsPublisherTest.java @@ -240,6 +240,40 @@ public void testInformWithNewVulnerabilityNotification() { """))); } + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "@type": "MessageCard", + "@context": "http://schema.org/extensions", + "summary": "Vulnerable Dependency Introduced", + "title": "Vulnerable Dependency Introduced", + "sections": [ + { + "activityTitle": "Dependency-Track", + "activitySubtitle": "1970-01-01T18:31:06.000000666", + "activityImage": "https://raw.githubusercontent.com/DependencyTrack/branding/master/dt-logo-symbol-blue-background.png", + "facts": [ + { + "name": "Project", + "value": "pkg:maven/org.acme/projectName@projectVersion" + }, + { + "name": "Component", + "value": "componentName : componentVersion" + } + ], + "text": "" + } + ] + } + """))); + } + @Override public void testInformWithProjectAuditChangeNotification() { super.testInformWithProjectAuditChangeNotification(); diff --git a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java index 9739227bfc..c91fcdf040 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java @@ -227,17 +227,17 @@ public void testInformWithDataSourceMirroringNotification() { GitHub Advisory Mirroring -------------------------------------------------------------------------------- - + Level: ERROR Scope: SYSTEM Group: DATASOURCE_MIRRORING - + -------------------------------------------------------------------------------- - + An error occurred mirroring the contents of GitHub Advisories. Check log for details. - + -------------------------------------------------------------------------------- - + 1970-01-01T18:31:06.000000666 """); }); @@ -255,9 +255,9 @@ public void testInformWithNewVulnerabilityNotification() { assertThat(content.getBodyPart(0)).isInstanceOf(MimeBodyPart.class); assertThat((String) content.getBodyPart(0).getContent()).isEqualToIgnoringNewLines(""" New Vulnerability Identified - + -------------------------------------------------------------------------------- - + Vulnerability ID: INT-001 Vulnerability URL: /vulnerability/?source=INTERNAL&vulnId=INT-001 Severity: MEDIUM @@ -268,13 +268,55 @@ public void testInformWithNewVulnerabilityNotification() { Version: projectVersion Description: projectDescription Project URL: /projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95 - + -------------------------------------------------------------------------------- - - - + + + -------------------------------------------------------------------------------- - + + 1970-01-01T18:31:06.000000666 + """); + }); + } + + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + assertThat(greenMail.getReceivedMessages()).satisfiesExactly(message -> { + assertThat(message.getSubject()).isEqualTo("[Dependency-Track] Vulnerable Dependency Introduced"); + assertThat(message.getContent()).isInstanceOf(MimeMultipart.class); + final MimeMultipart content = (MimeMultipart) message.getContent(); + assertThat(content.getCount()).isEqualTo(1); + assertThat(content.getBodyPart(0)).isInstanceOf(MimeBodyPart.class); + assertThat((String) content.getBodyPart(0).getContent()).isEqualToIgnoringNewLines(""" + Vulnerable Dependency Introduced + + -------------------------------------------------------------------------------- + + Project: pkg:maven/org.acme/projectName@projectVersion + Project URL: /projects/?uuid=c9c9539a-e381-4b36-ac52-6a7ab83b2c95 + Component: componentName : componentVersion + Component URL: /component/?uuid=94f87321-a5d1-4c2f-b2fe-95165debebc6 + + Vulnerabilities + + Vulnerability ID: INT-001 + Vulnerability URL: /vulnerability/?source=INTERNAL&vulnId=INT-001 + Severity: MEDIUM + Source: INTERNAL + Description: + vulnerabilityDescription + + + + -------------------------------------------------------------------------------- + + + + -------------------------------------------------------------------------------- + 1970-01-01T18:31:06.000000666 """); }); @@ -292,30 +334,30 @@ public void testInformWithProjectAuditChangeNotification() { assertThat(content.getBodyPart(0)).isInstanceOf(MimeBodyPart.class); assertThat((String) content.getBodyPart(0).getContent()).isEqualToIgnoringNewLines(""" Analysis Decision: Finding Suppressed - + -------------------------------------------------------------------------------- - + Analysis Type: Project Analysis - + Analysis State: FALSE_POSITIVE Suppressed: true Vulnerability ID: INT-001 Vulnerability URL: /vulnerability/?source=INTERNAL&vulnId=INT-001 Severity: MEDIUM Source: INTERNAL - + Component: componentName : componentVersion Component URL: /component/?uuid=94f87321-a5d1-4c2f-b2fe-95165debebc6 Project: pkg:maven/org.acme/projectName@projectVersion Description: projectDescription Project URL: /projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95 - + -------------------------------------------------------------------------------- - - - + + + -------------------------------------------------------------------------------- - + 1970-01-01T18:31:06.000000666 """); }); diff --git a/src/test/java/org/dependencytrack/notification/publisher/SlackPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SlackPublisherTest.java index 1ce6b5540c..1e47eba589 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/SlackPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/SlackPublisherTest.java @@ -18,11 +18,15 @@ */ package org.dependencytrack.notification.publisher; +import alpine.model.ConfigProperty; +import org.junit.Test; + import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static org.dependencytrack.model.ConfigPropertyConstants.GENERAL_BASE_URL; public class SlackPublisherTest extends AbstractWebhookPublisherTest { @@ -315,6 +319,87 @@ public void testInformWithNewVulnerabilityNotification() { """))); } + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "New Vulnerable Dependency" + } + }, + { + "type": "context", + "elements": [ + { + "text": "*INFORMATIONAL* | *PORTFOLIO*", + "type": "mrkdwn" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "text": "Vulnerable Dependency Introduced", + "type": "mrkdwn" + }, + "fields": [ + { + "type": "mrkdwn", + "text": "*Component*" + }, + { + "type": "plain_text", + "text": "componentName : componentVersion" + }, + { + "type": "mrkdwn", + "text": "*Project*" + }, + { + "type": "plain_text", + "text": "pkg:maven/org.acme/projectName@projectVersion" + } + ] + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Project" + }, + "action_id": "actionId-1", + "url": "https://example.com/projects/" + }, + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Component" + }, + "action_id": "actionId-2", + "url": "https://example.com/components/94f87321-a5d1-4c2f-b2fe-95165debebc6" + } + ] + } + ] + } + """))); + } + @Override public void testInformWithProjectAuditChangeNotification() { super.testInformWithProjectAuditChangeNotification(); @@ -451,4 +536,261 @@ public void testInformWithProjectAuditChangeNotification() { """))); } + @Test + public void testInformWithNewVulnerabilityNotificationWithoutBaseUrl() { + final ConfigProperty baseUrlProperty = qm.getConfigProperty( + GENERAL_BASE_URL.getGroupName(), + GENERAL_BASE_URL.getPropertyName() + ); + baseUrlProperty.setPropertyValue(null); + qm.persist(baseUrlProperty); + + super.testInformWithNewVulnerabilityNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "New Vulnerability" + } + }, + { + "type": "context", + "elements": [ + { + "text": "*INFORMATIONAL* | *PORTFOLIO*", + "type": "mrkdwn" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "text": "New Vulnerability Identified", + "type": "mrkdwn" + }, + "fields": [ + { + "type": "mrkdwn", + "text": "*VulnID*" + }, + { + "type": "plain_text", + "text": "INT-001" + }, + { + "type": "mrkdwn", + "text": "*Severity*" + }, + { + "type": "plain_text", + "text": "MEDIUM" + }, + { + "type": "mrkdwn", + "text": "*Source*" + }, + { + "type": "plain_text", + "text": "INTERNAL" + }, + { + "type": "mrkdwn", + "text": "*Component*" + }, + { + "type": "plain_text", + "text": "componentName : componentVersion" + } + ] + } + ] + } + """))); + } + + @Test + public void testInformWithNewVulnerableDependencyNotificationWithoutBaseUrl() { + final ConfigProperty baseUrlProperty = qm.getConfigProperty( + GENERAL_BASE_URL.getGroupName(), + GENERAL_BASE_URL.getPropertyName() + ); + baseUrlProperty.setPropertyValue(null); + qm.persist(baseUrlProperty); + + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "New Vulnerable Dependency" + } + }, + { + "type": "context", + "elements": [ + { + "text": "*INFORMATIONAL* | *PORTFOLIO*", + "type": "mrkdwn" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "text": "Vulnerable Dependency Introduced", + "type": "mrkdwn" + }, + "fields": [ + { + "type": "mrkdwn", + "text": "*Component*" + }, + { + "type": "plain_text", + "text": "componentName : componentVersion" + }, + { + "type": "mrkdwn", + "text": "*Project*" + }, + { + "type": "plain_text", + "text": "pkg:maven/org.acme/projectName@projectVersion" + } + ] + } + ] + } + """))); + } + + @Test + public void testInformWithProjectAuditChangeNotificationWithoutBaseUrl() { + final ConfigProperty baseUrlProperty = qm.getConfigProperty( + GENERAL_BASE_URL.getGroupName(), + GENERAL_BASE_URL.getPropertyName() + ); + baseUrlProperty.setPropertyValue(null); + qm.persist(baseUrlProperty); + + super.testInformWithProjectAuditChangeNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Project Audit Change" + } + }, + { + "type": "context", + "elements": [ + { + "text": "*INFORMATIONAL* | *PORTFOLIO*", + "type": "mrkdwn" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "text": "Analysis Decision: Finding Suppressed", + "type": "plain_text" + }, + "fields": [ + { + "type": "mrkdwn", + "text": "*Analysis State*" + }, + { + "type": "plain_text", + "emoji": true, + "text": "FALSE_POSITIVE" + }, + { + "type": "mrkdwn", + "text": "*Suppressed*" + }, + { + "type": "plain_text", + "text": "true" + }, + { + "type": "mrkdwn", + "text": "*VulnID*" + }, + { + "type": "plain_text", + "text": "INT-001" + }, + { + "type": "mrkdwn", + "text": "*Severity*" + }, + { + "type": "plain_text", + "text": "MEDIUM" + }, + { + "type": "mrkdwn", + "text": "*Source*" + }, + { + "type": "plain_text", + "text": "INTERNAL" + } + ] + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Component*" + }, + { + "type": "plain_text", + "text": "componentName : componentVersion" + }, + { + "type": "mrkdwn", + "text": "*Project*" + }, + { + "type": "plain_text", + "text": "pkg:maven/org.acme/projectName@projectVersion" + } + ] + } + ] + } + """))); + } + } diff --git a/src/test/java/org/dependencytrack/notification/publisher/WebhookPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/WebhookPublisherTest.java index cf97ceec6d..7dc26f53bc 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/WebhookPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/WebhookPublisherTest.java @@ -231,6 +231,78 @@ public void testInformWithNewVulnerabilityNotification() { """))); } + @Override + public void testInformWithNewVulnerableDependencyNotification() { + super.testInformWithNewVulnerableDependencyNotification(); + + verify(postRequestedFor(anyUrl()) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(""" + { + "notification": { + "level": "INFORMATIONAL", + "scope": "PORTFOLIO", + "group": "NEW_VULNERABLE_DEPENDENCY", + "timestamp": "1970-01-01T18:31:06.000000666", + "title": "Vulnerable Dependency Introduced", + "content": "", + "subject": { + "project": { + "uuid": "c9c9539a-e381-4b36-ac52-6a7ab83b2c95", + "name": "projectName", + "version": "projectVersion", + "description": "projectDescription", + "purl": "pkg:maven/org.acme/projectName@projectVersion", + "tags": "tag1,tag2" + }, + "component": { + "uuid": "94f87321-a5d1-4c2f-b2fe-95165debebc6", + "name": "componentName", + "version": "componentVersion" + }, + "vulnerabilities": [ + { + "uuid": "bccec5d5-ec21-4958-b3e8-22a7a866a05a", + "vulnId": "INT-001", + "source": "INTERNAL", + "aliases": [ + { + "source": "OSV", + "vulnId": "OSV-001" + } + ], + "title": "vulnerabilityTitle", + "subtitle": "vulnerabilitySubTitle", + "description": "vulnerabilityDescription", + "recommendation": "vulnerabilityRecommendation", + "cvssv2": 5.5, + "cvssv3": 6.6, + "owaspRRLikelihood": 1.1, + "owaspRRTechnicalImpact": 2.2, + "owaspRRBusinessImpact": 3.3, + "severity": "MEDIUM", + "cwe": { + "cweId": 666, + "name": "Operation on Resource in Wrong Phase of Lifetime" + }, + "cwes": [ + { + "cweId": 666, + "name": "Operation on Resource in Wrong Phase of Lifetime" + }, + { + "cweId": 777, + "name": "Regular Expression without Anchors" + } + ] + } + ] + } + } + } + """))); + } + @Override public void testInformWithProjectAuditChangeNotification() { super.testInformWithProjectAuditChangeNotification();