Skip to content

Commit

Permalink
Feat: Add notification when severity of a vulnerability changes
Browse files Browse the repository at this point in the history
Signed-off-by: Enora Germond <[email protected]>
Signed-off-by: 4naesthetic <[email protected]>
  • Loading branch information
Enora Germond authored and 4naesthetic committed Aug 12, 2024
1 parent 2812b3a commit f8402c9
Show file tree
Hide file tree
Showing 23 changed files with 584 additions and 22 deletions.
85 changes: 67 additions & 18 deletions docs/_docs/integrations/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,25 @@ Notification levels behave identical to logging levels:
Each scope contains a set of notification groups that can be subscribed to. Some groups contain notifications of
multiple levels, while others can only ever have a single level.

| Scope | Group | Level(s) | Description |
|-----------|---------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------|
| SYSTEM | ANALYZER | (Any) | Notifications generated as a result of interacting with an external source of vulnerability intelligence |
| SYSTEM | DATASOURCE_MIRRORING | (Any) | Notifications generated when performing mirroring of one of the supported datasources such as the NVD |
| SYSTEM | INDEXING_SERVICE | (Any) | Notifications generated as a result of performing maintenance on Dependency-Tracks internal index used for global searching |
| SYSTEM | FILE_SYSTEM | (Any) | Notifications generated as a result of a file system operation. These are typically only generated on error conditions |
| SYSTEM | REPOSITORY | (Any) | Notifications generated as a result of interacting with one of the supported repositories such as Maven Central, RubyGems, or NPM |
| SYSTEM | USER_CREATED | INFORMATIONAL | Notifications generated as a result of a user creation |
| SYSTEM | USER_DELETED | INFORMATIONAL | Notifications generated as a result of a user deletion |
| PORTFOLIO | NEW_VULNERABILITY | INFORMATIONAL | Notifications generated whenever a new vulnerability is identified |
| PORTFOLIO | NEW_VULNERABLE_DEPENDENCY | INFORMATIONAL | Notifications generated as a result of a vulnerable component becoming a dependency of a project |
| PORTFOLIO | GLOBAL_AUDIT_CHANGE | INFORMATIONAL | Notifications generated whenever an analysis or suppression state has changed on a finding from a component (global) |
| PORTFOLIO | PROJECT_AUDIT_CHANGE | INFORMATIONAL | Notifications generated whenever an analysis or suppression state has changed on a finding from a project |
| PORTFOLIO | BOM_CONSUMED | INFORMATIONAL | Notifications generated whenever a supported BOM is ingested and identified |
| PORTFOLIO | BOM_PROCESSED | INFORMATIONAL | Notifications generated after a supported BOM is ingested, identified, and successfully processed |
| PORTFOLIO | BOM_PROCESSING_FAILED | ERROR | Notifications generated whenever a BOM upload process fails |
| PORTFOLIO | BOM_VALIDATION_FAILED | ERROR | Notifications generated whenever an invalid BOM is uploaded |
| PORTFOLIO | POLICY_VIOLATION | INFORMATIONAL | Notifications generated whenever a policy violation is identified |
| Scope | Group | Level(s) | Description |
|-----------|---------------------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------|
| SYSTEM | ANALYZER | (Any) | Notifications generated as a result of interacting with an external source of vulnerability intelligence |
| SYSTEM | DATASOURCE_MIRRORING | (Any) | Notifications generated when performing mirroring of one of the supported datasources such as the NVD |
| SYSTEM | INDEXING_SERVICE | (Any) | Notifications generated as a result of performing maintenance on Dependency-Tracks internal index used for global searching |
| SYSTEM | FILE_SYSTEM | (Any) | Notifications generated as a result of a file system operation. These are typically only generated on error conditions |
| SYSTEM | REPOSITORY | (Any) | Notifications generated as a result of interacting with one of the supported repositories such as Maven Central, RubyGems, or NPM |
| SYSTEM | USER_CREATED | INFORMATIONAL | Notifications generated as a result of a user creation |
| SYSTEM | USER_DELETED | INFORMATIONAL | Notifications generated as a result of a user deletion |
| PORTFOLIO | NEW_VULNERABILITY | INFORMATIONAL | Notifications generated whenever a new vulnerability is identified |
| PORTFOLIO | VULNERABILITY_UPDATED | INFORMATIONAL | Notifications generated if the severity of a vulnerability changes after it has been created |
| PORTFOLIO | NEW_VULNERABLE_DEPENDENCY | INFORMATIONAL | Notifications generated as a result of a vulnerable component becoming a dependency of a project |
| PORTFOLIO | GLOBAL_AUDIT_CHANGE | INFORMATIONAL | Notifications generated whenever an analysis or suppression state has changed on a finding from a component (global) |
| PORTFOLIO | PROJECT_AUDIT_CHANGE | INFORMATIONAL | Notifications generated whenever an analysis or suppression state has changed on a finding from a project |
| PORTFOLIO | BOM_CONSUMED | INFORMATIONAL | Notifications generated whenever a supported BOM is ingested and identified |
| PORTFOLIO | BOM_PROCESSED | INFORMATIONAL | Notifications generated after a supported BOM is ingested, identified, and successfully processed |
| PORTFOLIO | BOM_PROCESSING_FAILED | ERROR | Notifications generated whenever a BOM upload process fails |
| PORTFOLIO | BOM_VALIDATION_FAILED | ERROR | Notifications generated whenever an invalid BOM is uploaded |
| PORTFOLIO | POLICY_VIOLATION | INFORMATIONAL | Notifications generated whenever a policy violation is identified |

## Configuring Publishers

Expand Down Expand Up @@ -160,6 +161,54 @@ This type of notification will always contain:

> The `cwe` field is deprecated and will be removed in a later version. Please use `cwes` instead.

#### VULNERABILITY_UPDATED
This type of notification will always contain:
* 1 component
* 1 vulnerability
* 1 or more affected projects

```json
{
"notification": {
"level": "INFORMATIONAL",
"scope": "PORTFOLIO",
"group": "VULNERABILITY_UPDATED",
"timestamp": "2018-08-27T23:26:22.961",
"title": "Change in Severity of a Vulnerability on Project: [Acme Example]",
"content": "The vulnerability CVE-2012-5784 on component axis has changed severity from LOW to MEDIUM",
"subject": {
"vulnerability": {
"uuid": "941a93f5-e06b-4304-84de-4d788eeb4969",
"old": {
"severity": "MEDIUM"
},
"new": {
"severity": "HIGH"
}
},
"component": {
"uuid": "4d5cd8df-cff7-4212-a038-91ae4ab79396",
"group": "apache",
"name": "axis",
"version": "1.4",
"md5": "03dcfdd88502505cc5a805a128bfdd8d",
"sha1": "94a9ce681a42d0352b3ad22659f67835e560d107",
"sha256": "05aebb421d0615875b4bf03497e041fe861bf0556c3045d8dda47e29241ffdd3",
"purl": "pkg:maven/apache/[email protected]"
},
"affectedProjects": [
{
"uuid": "6fb1820f-5280-4577-ac51-40124aabe307",
"name": "Acme Example",
"version": "1.0.0"
}
]
}
}
}
```

#### NEW_VULNERABLE_DEPENDENCY
This type of notification will always contain:
* 1 project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.dependencytrack.tasks.VexUploadProcessingTask;
import org.dependencytrack.tasks.VulnDbSyncTask;
import org.dependencytrack.tasks.VulnerabilityAnalysisTask;
import org.dependencytrack.tasks.VulnerabilityUpdateTask;
import org.dependencytrack.tasks.metrics.ComponentMetricsUpdateTask;
import org.dependencytrack.tasks.metrics.PortfolioMetricsUpdateTask;
import org.dependencytrack.tasks.metrics.ProjectMetricsUpdateTask;
Expand Down Expand Up @@ -118,6 +119,7 @@ public void contextInitialized(final ServletContextEvent event) {
EVENT_SERVICE.subscribe(ClearComponentAnalysisCacheEvent.class, ClearComponentAnalysisCacheTask.class);
EVENT_SERVICE.subscribe(CallbackEvent.class, CallbackTask.class);
EVENT_SERVICE.subscribe(NewVulnerableDependencyAnalysisEvent.class, NewVulnerableDependencyAnalysisTask.class);
EVENT_SERVICE.subscribe(VulnerabilityUpdateEvent.class, VulnerabilityUpdateTask.class);
EVENT_SERVICE.subscribe(NistMirrorEvent.class, NistMirrorTask.class);
EVENT_SERVICE.subscribe(NistApiMirrorEvent.class, NistApiMirrorTask.class);
EVENT_SERVICE.subscribe(EpssMirrorEvent.class, EpssMirrorTask.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.event;

import alpine.event.framework.SingletonCapableEvent;
import org.dependencytrack.model.Vulnerability;
import org.dependencytrack.model.VulnerabilityUpdateDiff;


public class VulnerabilityUpdateEvent extends SingletonCapableEvent {
private final VulnerabilityUpdateDiff vulnerabilityUpdateDiff;
private final Vulnerability vulnerability;

public VulnerabilityUpdateEvent(Vulnerability vulnerability, VulnerabilityUpdateDiff vulnerabilityUpdateDiff) {
this.vulnerability = vulnerability;
this.vulnerabilityUpdateDiff = vulnerabilityUpdateDiff;
}

public Vulnerability getVulnerability() {
return vulnerability;
}

public VulnerabilityUpdateDiff getVulnerabilityUpdateDiff() {
return vulnerabilityUpdateDiff;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.model;

public class VulnerabilityUpdateDiff {
private Severity oldSeverity;
private Severity newSeverity;

public VulnerabilityUpdateDiff(Severity oldSeverity, Severity newSeverity) {
this.oldSeverity = oldSeverity;
this.newSeverity = newSeverity;
}

public Severity getOldSeverity() { return oldSeverity; }

public Severity getNewSeverity() { return newSeverity; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static class Title {
public static final String ANALYZER_ERROR = "Analyzer Error";
public static final String INTEGRATION_ERROR = "Integration Error";
public static final String NEW_VULNERABILITY = "New Vulnerability Identified";
public static final String VULNERABILITY_UPDATED = "Change in Severity of a Vulnerability";
public static final String NEW_VULNERABLE_DEPENDENCY = "Vulnerable Dependency Introduced";
public static final String ANALYSIS_DECISION_EXPLOITABLE = "Analysis Decision: Exploitable";
public static final String ANALYSIS_DECISION_IN_TRIAGE = "Analysis Decision: In Triage";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum NotificationGroup {
// Portfolio Groups
NEW_VULNERABILITY,
NEW_VULNERABLE_DEPENDENCY,
VULNERABILITY_UPDATED,
//NEW_OUTDATED_COMPONENT,
//FIXED_VULNERABILITY,
//FIXED_OUTDATED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.dependencytrack.notification.vo.PolicyViolationIdentified;
import org.dependencytrack.notification.vo.VexConsumedOrProcessed;
import org.dependencytrack.notification.vo.ViolationAnalysisDecisionChange;
import org.dependencytrack.notification.vo.VulnerabilityUpdate;
import org.dependencytrack.persistence.QueryManager;

import jakarta.json.Json;
Expand Down Expand Up @@ -178,6 +179,24 @@ List<NotificationRule> resolveRules(final PublishContext ctx, final Notification
}
}
}
} else if (NotificationScope.PORTFOLIO.name().equals(notification.getScope())
&& notification.getSubject() instanceof final VulnerabilityUpdate subject) {
for (final NotificationRule rule: result) {
if (rule.getNotifyOn().contains(NotificationGroup.valueOf(notification.getGroup()))) {
if (rule.getProjects() != null && rule.getProjects().size() > 0
&& subject.getComponent() != null) {
if (subject.getComponent().getProject() != null) {
for (final Project project : rule.getProjects()) {
if (subject.getComponent().getProject().getUuid().equals(project.getUuid()) || (Boolean.TRUE.equals(rule.isNotifyChildren() && checkIfChildrenAreAffected(project, subject.getComponent().getProject().getUuid())))) {
rules.add(rule);
}
}
}
} else {
rules.add(rule);
}
}
}
} else if (NotificationScope.PORTFOLIO.name().equals(notification.getScope())
&& notification.getSubject() instanceof final NewVulnerableDependency subject) {
limitToProject(ctx, rules, result, notification, subject.getComponent().getProject());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.dependencytrack.notification.vo.PolicyViolationIdentified;
import org.dependencytrack.notification.vo.VexConsumedOrProcessed;
import org.dependencytrack.notification.vo.ViolationAnalysisDecisionChange;
import org.dependencytrack.notification.vo.VulnerabilityUpdate;
import org.dependencytrack.persistence.QueryManager;
import org.dependencytrack.util.NotificationUtil;

Expand Down Expand Up @@ -106,6 +107,9 @@ default String prepareTemplate(final Notification notification, final PebbleTemp
if (notification.getSubject() instanceof final NewVulnerabilityIdentified subject) {
context.put("subject", subject);
context.put("subjectJson", NotificationUtil.toJson(subject));
} else if (notification.getSubject() instanceof final VulnerabilityUpdate subject) {
context.put("subject", subject);
context.put("subjectJson", NotificationUtil.toJson(subject));
} else if (notification.getSubject() instanceof final NewVulnerableDependency subject) {
context.put("subject", subject);
context.put("subjectJson", NotificationUtil.toJson(subject));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.notification.vo;

import org.dependencytrack.model.Component;
import org.dependencytrack.model.Project;
import org.dependencytrack.model.Vulnerability;
import org.dependencytrack.model.VulnerabilityUpdateDiff;

import java.util.Set;

public class VulnerabilityUpdate {
private final Vulnerability vulnerability;
private final VulnerabilityUpdateDiff vulnerabilityUpdateDiff;
private final Component component;
private final Set<Project> affectedProjects;

public VulnerabilityUpdate(final Vulnerability vulnerability, final VulnerabilityUpdateDiff vulnerabilityUpdateDiff, final Component component, final Set<Project> affectedProjects) {
this.vulnerability = vulnerability;
this.vulnerabilityUpdateDiff = vulnerabilityUpdateDiff;
this.component = component;
this.affectedProjects = affectedProjects;
}

public Vulnerability getVulnerability() {
return vulnerability;
}

public VulnerabilityUpdateDiff getVulnerabilityUpdateDiff(){ return vulnerabilityUpdateDiff; }

public Component getComponent() { return component; }

public Set<Project> getAffectedProjects() { return affectedProjects; }
}
Loading

0 comments on commit f8402c9

Please sign in to comment.