Merge pull request #5574 from snieguu/5561

This commit is contained in:
Niklas 2025-11-27 11:11:14 +01:00 committed by GitHub
commit 01847b79fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 64 additions and 11 deletions

View file

@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory;
import jakarta.json.JsonObject;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public abstract class AbstractWebhookPublisher implements Publisher {
@ -48,13 +49,13 @@ public abstract class AbstractWebhookPublisher implements Publisher {
final Logger logger = LoggerFactory.getLogger(getClass());
if (config == null) {
logger.warn("No publisher configuration found; Skipping notification (%s)".formatted(ctx));
logger.warn("No publisher configuration found; Skipping notification ({})", ctx);
return;
}
final String destination = getDestinationUrl(config);
if (destination == null) {
logger.warn("No destination configured; Skipping notification (%s)".formatted(ctx));
logger.warn("No destination configured; Skipping notification ({})", ctx);
return;
}
@ -64,7 +65,7 @@ public abstract class AbstractWebhookPublisher implements Publisher {
} catch (RuntimeException e) {
logger.warn("""
An error occurred during the retrieval of credentials needed for notification \
publication; Skipping notification (%s)""".formatted(ctx), e);
publication; Skipping notification ({})""", ctx, e);
return;
}
@ -91,19 +92,19 @@ public abstract class AbstractWebhookPublisher implements Publisher {
}
try {
request.setEntity(new StringEntity(content));
request.setEntity(new StringEntity(content, StandardCharsets.UTF_8));
try (final CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) {
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode < 200 || statusCode >= 300) {
logger.warn("Destination responded with with status code %d, likely indicating a processing failure (%s)"
.formatted(statusCode, ctx));
logger.warn("Destination responded with with status code {}, likely indicating a processing failure ({})",
statusCode, ctx);
if (logger.isDebugEnabled()) {
logger.debug("Response headers: %s".formatted((Object[]) response.getAllHeaders()));
logger.debug("Response body: %s".formatted(EntityUtils.toString(response.getEntity())));
logger.debug("Response headers: {}", (Object[]) response.getAllHeaders());
logger.debug("Response body: {}", EntityUtils.toString(response.getEntity()));
}
} else if (ctx.shouldLogSuccess()) {
logger.info("Destination acknowledged reception of notification with status code %d (%s)"
.formatted(statusCode, ctx));
logger.info("Destination acknowledged reception of notification with status code {} ({})",
statusCode, ctx);
}
}
} catch (IOException ex) {
@ -136,7 +137,7 @@ public abstract class AbstractWebhookPublisher implements Publisher {
}
protected void handleRequestException(final PublishContext ctx, final Logger logger, final Exception e) {
logger.error("Failed to send notification request (%s)".formatted(ctx), e);
logger.error("Failed to send notification request ({})", ctx, e);
}
}

View file

@ -267,6 +267,35 @@ abstract class AbstractPublisherTest<T extends Publisher> extends PersistenceCap
.withMessage("Unexpected tag name \"include\" ({% include '/some/path' %}:1)");
}
public final void baseTestInformWithNewVulnerabilityCustomUTF8TemplateNotification() throws Exception {
final var project = createProject();
final var component = createComponent(project);
final var vuln = createVulnerability();
final var subject = new NewVulnerabilityIdentified(vuln, component, Set.of(project),
VulnerabilityAnalysisLevel.BOM_UPLOAD_ANALYSIS);
final var notification = new Notification()
.scope(NotificationScope.SYSTEM)
.group(NotificationGroup.NEW_VULNERABILITY)
.title(NotificationConstants.Title.NOTIFICATION_TEST)
.level(NotificationLevel.INFORMATIONAL)
.timestamp(LocalDateTime.ofEpochSecond(66666, 666, ZoneOffset.UTC))
.subject(subject);
final JsonObject defaultConfig = createConfig();
final String defaultTemplate = defaultConfig.getString(Publisher.CONFIG_TEMPLATE_KEY);
final String template = defaultTemplate.replaceAll("Vulnerability", "Vulnérabilité");
final JsonObject config = Json.createObjectBuilder(createConfig())
.add(Publisher.CONFIG_TEMPLATE_KEY, template)
.build();
assertThatNoException()
.isThrownBy(() -> publisherInstance.inform(PublishContext.from(notification), notification, config));
}
public final void baseTestPublishWithScheduledNewVulnerabilitiesNotification() {
final var project = createProject();
final var component = createComponent(project);

View file

@ -178,6 +178,29 @@ class JiraPublisherTest extends AbstractWebhookPublisherTest<JiraPublisher> {
""")));
}
@Test
public void testInformWithNewVulnerabilityCustomUTF8TemplateNotification() throws Exception {
super.baseTestInformWithNewVulnerabilityCustomUTF8TemplateNotification();
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_VULNERABILITY] [] New vulnerability identified: ",
"description" : "A new vulnerability has been identified on your project(s).\\n\\\\\\\\\\n\\\\\\\\\\n*Vulnérabilité description*\\n{code:none|bgColor=white|borderStyle=none}{code}\\n\\n*VulnID*\\n\\n\\n*Severity*\\n\\n\\n*Component*\\n[|https://example.com/components/]\\n\\n*Affected project(s)*\\n"
}
}
""")));
}
@Test
public void testInformWithNewVulnerabilityNotification() {
super.baseTestInformWithNewVulnerabilityNotification();