From 4119d46a116eaa3f38cf1cc20a4568702b2a5166 Mon Sep 17 00:00:00 2001
From: nscuro <nscuro@protonmail.com>
Date: Sat, 1 Jun 2024 16:46:09 +0200
Subject: [PATCH 1/2] Fix Slack notifications failing when no base URL is
 configured

See https://github.com/DependencyTrack/dependency-track/issues/3742#issuecomment-2143465872 for details.

If no base URL is configured, the *View Component*, *View Project*, and *View Vulnerability* buttons will be omitted from the notification. Because they are intended to link back to Dependency-Track, it doesn't make sense to include them without valid URL.

Fixes #3742

Signed-off-by: nscuro <nscuro@protonmail.com>
---
 .../notification/publisher/slack.peb          |  12 +-
 .../publisher/AbstractPublisherTest.java      |  22 ++
 .../publisher/SlackPublisherTest.java         | 342 ++++++++++++++++++
 3 files changed, 372 insertions(+), 4 deletions(-)

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/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<SlackPublisher> {
 
@@ -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"
+                        		}
+                        	  ]
+                        	}
+                          ]
+                        }
+                        """)));
+    }
+
 }

From a17d9ea3f10a8a9054251d4af4ba16a08abe9909 Mon Sep 17 00:00:00 2001
From: nscuro <nscuro@protonmail.com>
Date: Sat, 1 Jun 2024 17:01:06 +0200
Subject: [PATCH 2/2] Add publisher tests for `NEW_VULNERABLE_DEPENDENCY`

Signed-off-by: nscuro <nscuro@protonmail.com>
---
 .../publisher/JiraPublisherTest.java          | 23 +++++
 .../publisher/MattermostPublisherTest.java    | 15 ++++
 .../publisher/MsTeamsPublisherTest.java       | 34 ++++++++
 .../publisher/SendMailPublisherTest.java      | 84 ++++++++++++++-----
 .../publisher/WebhookPublisherTest.java       | 72 ++++++++++++++++
 5 files changed, 207 insertions(+), 21 deletions(-)

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/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();