Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guides/user/.pages
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ nav:
- exchanging-vex-documents.md
- managing-private-vulnerabilities.md
- configuring-notifications.md
- receiving-policy-violation-updates.md
- managing-secrets.md
3 changes: 3 additions & 0 deletions docs/guides/user/configuring-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ with the alert's scope and trigger type. Refer to
[Notification groups](../../reference/notifications/groups.md) for the
full list and what each group represents.

To report changes to an existing component policy violation, see
[Receive policy violation updates](receiving-policy-violation-updates.md).

## Setting the destination

The destination format depends on the publisher. Common cases:
Expand Down
57 changes: 57 additions & 0 deletions docs/guides/user/receiving-policy-violation-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Receive policy violation updates

Configure an alert to report changes to the matched vulnerabilities of an existing component policy violation.

!!! note "Unreleased feature"

This guide requires the API server change proposed in
[PR #7908](https://github.com/DependencyTrack/dependency-track/pull/7908)
and a frontend that exposes the new notification groups.
No released version currently includes these notification groups.

## Prerequisites

- Permission to [edit component policies](managing-component-policies.md).
- The `SYSTEM_CONFIGURATION` permission to configure alerts.
- An enabled [KEV data source](../administration/configuring-kev-sources.md).
- A configured [notification publisher](../../reference/notifications/publishers.md) and a destination you control.

## Return the matched identifiers

1. Open **Policy Management → Policies**, and select the component policy to edit.
2. Add or edit a condition with the **Expression** subject and **Security** violation type.
3. Use this expression to record the matched KEV identifiers:

```js
vulns.filter(vuln, vuln.is_kev).map(vuln, vuln.id).join(",")
```

4. Save the condition.

Include every identifier that matters to your alert, and avoid time-dependent values in the message.
For the message length limit and result semantics, see [Condition expression results](../../reference/policies/condition-expressions.md#result).

## Subscribe to changes

1. Open **Administration → Notifications → Alerts**.
2. Create an alert with the **Portfolio** scope, **Event** trigger, **Informational** level, and your chosen publisher. Select **Create**.
3. Open the alert for editing. You can also use an existing alert with these scope, trigger, and level settings.
4. Select **POLICY_VIOLATION_UPDATED** for changes to violations that are neither approved nor suppressed.
5. If you also want changes while a violation remains approved or suppressed, select **POLICY_VIOLATION_UPDATED_REVIEWED**.
6. Keep **POLICY_VIOLATION** selected if you also want notifications when violations first appear.
7. Configure the destination, project or tag restrictions, and optional filter expression. Turn on **Enabled**, and select **Submit**.

The [update group reference](../../reference/notifications/groups.md#policy-violation-updates) describes review decisions, field presence, and events that do not trigger a notification.

## Verify delivery

1. Select **Perform Test** on the alert to check the destination.
2. Trigger project analysis for a project that matches the policy.
3. When the matching KEV identifiers change, check the delivered notification's previous and current messages and violation UUID.
4. Run analysis again without changing the matches, and check that no new update event appears.

For approved or suppressed violations, check delivery to an alert that explicitly includes **POLICY_VIOLATION_UPDATED_REVIEWED**.
Adding that subscription does not send past message changes. See [Message change behavior](../../reference/notifications/groups.md#message-change-behavior).

If an expected notification does not arrive, follow [Debugging missing notifications](../administration/debugging-notifications.md).
For a custom template, add an [update notification branch](../../reference/notifications/templating.md#policy-violation-updates).
21 changes: 21 additions & 0 deletions docs/reference/notifications/filter-expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,27 @@ in the portfolio. The following expression narrows an alert to violations of one
subject.policy_violation.condition.policy.name == "Known exploited vulnerabilities"
```

### Policy violation message changes

The unreleased [policy update groups](groups.md#policy-violation-updates) expose optional previous and current messages.
This expression matches update events whose current message contains a specific identifier:

```js linenums="1"
has(subject.current_message)
&& subject.current_message.contains("CVE-2021-45046")
```

This expression matches reviewed updates with an approved analysis state:

```js linenums="1"
has(subject.analysis_state) && subject.analysis_state == "APPROVED"
```

An alert still needs an explicit subscription to `POLICY_VIOLATION_UPDATED_REVIEWED` to receive reviewed updates.
A filter does not add subscriptions or classify whether a text change increased risk.
Optional-field checks make the required captured values explicit.
The existing [fail-open behavior](../../concepts/notifications.md#how-alert-filtering-works) still applies to invalid expressions.

### Combining conditions

The following expression matches `NEW_VULNERABILITY` notifications for `CRITICAL` vulnerabilities
Expand Down
80 changes: 79 additions & 1 deletion docs/reference/notifications/groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ cron schedule.
The [notification schema reference](schema.md) describes the
subject schema for each group.

!!! note "Unreleased policy update groups"

`POLICY_VIOLATION_UPDATED` and `POLICY_VIOLATION_UPDATED_REVIEWED` are proposed in
[API server PR #7908](https://github.com/DependencyTrack/dependency-track/pull/7908).
No released version currently includes these groups.

## System scope

### `ANALYZER`
Expand Down Expand Up @@ -122,7 +128,25 @@ Fires when a vulnerable component becomes a dependency of a project.
- **Trigger:** Event
- **Level:** Informational

Fires when the platform records a policy violation.
Fires when the platform first records a policy violation. Message changes to an existing violation do not emit this group.

### `POLICY_VIOLATION_UPDATED`

- **Trigger:** Event
- **Level:** Informational

Fires when the stored message of an existing component policy violation changes.
This includes changes between a text message and no message. This group excludes approved or suppressed violations.
The violation keeps its UUID.

### `POLICY_VIOLATION_UPDATED_REVIEWED`

- **Trigger:** Event
- **Level:** Informational

Fires when the stored message of an approved or suppressed component policy violation changes.
An alert receives this group only through an explicit subscription. The review decision and violation UUID stay unchanged.
A rejected, unsuppressed violation belongs to `POLICY_VIOLATION_UPDATED` instead.

### `PROJECT_AUDIT_CHANGE`

Expand Down Expand Up @@ -187,8 +211,62 @@ each subject schema.
| `NEW_VULNERABILITY` | [NewVulnerabilitySubject](schema.md#newvulnerabilitysubject) |
| `NEW_VULNERABLE_DEPENDENCY` | [NewVulnerableDependencySubject](schema.md#newvulnerabledependencysubject) |
| `POLICY_VIOLATION` | [PolicyViolationSubject](schema.md#policyviolationsubject) |
| `POLICY_VIOLATION_UPDATED`, `POLICY_VIOLATION_UPDATED_REVIEWED` | [PolicyViolationUpdatedSubject](schema.md#policyviolationupdatedsubject) |
| `PROJECT_AUDIT_CHANGE` | [VulnerabilityAnalysisDecisionChangeSubject](schema.md#vulnerabilityanalysisdecisionchangesubject) or [PolicyViolationAnalysisDecisionChangeSubject](schema.md#policyviolationanalysisdecisionchangesubject) |
| `VEX_CONSUMED`, `VEX_PROCESSED` | [VexConsumedOrProcessedSubject](schema.md#vexconsumedorprocessedsubject) |
| `USER_CREATED`, `USER_DELETED` | [UserSubject](schema.md#usersubject) |
| `NEW_VULNERABILITIES_SUMMARY` | [NewVulnerabilitiesSummarySubject](schema.md#newvulnerabilitiessummarysubject) |
| `NEW_POLICY_VIOLATIONS_SUMMARY` | [NewPolicyViolationsSummarySubject](schema.md#newpolicyviolationssummarysubject) |

## Policy violation updates

Both update groups have portfolio scope, use event triggers, and have informational level.
Existing alerts gain neither subscription automatically. Scheduled summaries keep their existing behavior.
Project, tag, level, and [filter expression](filter-expressions.md) restrictions still apply.
One message change belongs to exactly one update group, even when an alert subscribes to both.

### Message change behavior

| Change | Result |
|:-------|:-------|
| A condition first matches | A new violation and a `POLICY_VIOLATION` event |
| Stored message stays the same | No update event |
| Stored message changes, with no approval or suppression | `POLICY_VIOLATION_UPDATED` |
| Stored message changes while approved or suppressed | `POLICY_VIOLATION_UPDATED_REVIEWED`, with the decision retained |
| Approval, suppression, or subscriptions change without a message change | No update event or replay of past changes |
| A condition stops matching | The existing violation disappears, without an update event |
| A violation disappears and later returns | A new UUID and a creation event |

Update events describe text changes, including removals, replacements, and wording changes while the violation still matches. They do not classify increases in risk or identify new KEV matches from arbitrary text.
A Boolean condition has no message that can change when another vulnerability starts matching. Different matches that produce equal stored messages are indistinguishable, including text truncated by the existing 1024-character limit.

### Subject fields

Both groups use [PolicyViolationUpdatedSubject](schema.md#policyviolationupdatedsubject).
The Protobuf and CEL field names differ from the lower camel case names in Protobuf JSON and Pebble templates.

| Protobuf / CEL field | JSON / Pebble field | Content |
|:---------------------|:--------------------|:--------|
| `project` | `project` | Project details |
| `component` | `component` | Component details |
| `policy_violation` | `policyViolation` | Original UUID, policy, condition, and current message when present |
| `previous_message` | `previousMessage` | Optional previous stored message |
| `current_message` | `currentMessage` | Optional current stored message |
| `changed_at` | `changedAt` | Time of the reconciliation statement on the database server |
| `analysis_state` | `analysisState` | Optional analysis state captured for a reviewed update |
| `suppressed` | `suppressed` | Optional suppression value captured for a reviewed update |

An absent message field means no message. A present empty string means empty text.
Ordinary updates omit review metadata. Reviewed updates capture both review values while assembling the event.
An absent review field means no captured value. A present `suppressed: false` means explicitly unsuppressed.
Later review decisions do not change the captured event.

### Delivery and compatibility

The message change and notification creation commit together. Delivery uses the existing notification workflow after that commit.
Retries can deliver the same notification ID more than once. Recipients can deduplicate by `notification.id`.
Permanent delivery failures keep [notification diagnostics](../../guides/administration/debugging-notifications.md). Repeating analysis with unchanged text does not create a replacement event for a failed delivery.

The new groups use Protobuf values 25 and 26 without changing existing values or subscriptions.
Consumers that subscribe to either group need support for [PolicyViolationUpdatedSubject](schema.md#policyviolationupdatedsubject).
Custom templates that only handle `GROUP_POLICY_VIOLATION` do not match update groups. See [Update notification templates](templating.md#policy-violation-updates).
23 changes: 23 additions & 0 deletions docs/reference/notifications/notification.proto
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ enum Group {
// Scheduled summary of new policy violations across projects.
GROUP_NEW_POLICY_VIOLATIONS_SUMMARY = 24;

// The message of an existing policy violation changed.
GROUP_POLICY_VIOLATION_UPDATED = 25;

// The message of an approved or suppressed violation changed.
// Subscribing explicitly opts in to updates excluded from the ordinary group.
GROUP_POLICY_VIOLATION_UPDATED_REVIEWED = 26;

// Indexing service has been removed as of
// https://github.com/DependencyTrack/hyades/issues/661
reserved 5;
Expand Down Expand Up @@ -141,6 +148,22 @@ message PolicyViolationSubject {
PolicyViolation policy_violation = 3;
}

// Subject for ordinary and explicitly opted-in reviewed violation updates.
message PolicyViolationUpdatedSubject {
Component component = 1;
Project project = 2;
PolicyViolation policy_violation = 3;
// Absent means the previous message was null.
optional string previous_message = 4;
// Absent means the current message is null.
optional string current_message = 5;
google.protobuf.Timestamp changed_at = 6;
// Review metadata observed in the subject assembly statement snapshot.
// Absent means no state or suppression value was recorded, not false.
optional string analysis_state = 7;
optional bool suppressed = 8;
}

message VexConsumedOrProcessedSubject {
Project project = 1;
bytes vex = 2;
Expand Down
31 changes: 31 additions & 0 deletions docs/reference/notifications/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Dependency-Track represents notifications internally as Protobuf messages. Some








Expand Down Expand Up @@ -346,6 +348,30 @@ Subject for GROUP_NEW_VULNERABILITIES_SUMMARY notifications.



<a name="org-dependencytrack-notification-v1-PolicyViolationUpdatedSubject"></a>

### PolicyViolationUpdatedSubject

Subject for ordinary and explicitly opted-in reviewed violation updates.


| Field | Type | Description |
| :---- | :--- | :---------- |
| `component` | [`Component`](#org-dependencytrack-notification-v1-Component) | | - |
| `project` | [`Project`](#org-dependencytrack-notification-v1-Project) | | - |
| `policy_violation` | [`PolicyViolation`](#org-dependencytrack-notification-v1-PolicyViolation) | | - |
| `previous_message` | `string` | Absent means the previous message was null. | - |
| `current_message` | `string` | Absent means the current message is null. | - |
| `changed_at` | `google.protobuf.Timestamp` | | - |
| `analysis_state` | `string` | Review metadata observed in the subject assembly statement snapshot. Absent means no state or suppression value was recorded, not false. | - |
| `suppressed` | `bool` | | - |









<a name="org-dependencytrack-notification-v1-UserSubject"></a>
Expand Down Expand Up @@ -914,6 +940,8 @@ Subject for GROUP_NEW_VULNERABILITIES_SUMMARY notifications.





<a name="org-dependencytrack-notification-v1-Project"></a>

### Project
Expand Down Expand Up @@ -1093,6 +1121,9 @@ Subject for GROUP_NEW_VULNERABILITIES_SUMMARY notifications.
e.g. due to upstream sources correcting their data. |
| `GROUP_NEW_VULNERABILITIES_SUMMARY` | Scheduled summary of new vulnerabilities across projects. |
| `GROUP_NEW_POLICY_VIOLATIONS_SUMMARY` | Scheduled summary of new policy violations across projects. |
| `GROUP_POLICY_VIOLATION_UPDATED` | The message of an existing policy violation changed. |
| `GROUP_POLICY_VIOLATION_UPDATED_REVIEWED` | The message of an approved or suppressed violation changed.
Subscribing explicitly opts in to updates excluded from the ordinary group. |



Expand Down
26 changes: 26 additions & 0 deletions docs/reference/notifications/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ message type emitted for each group.
over from Dependency-Track v4 referenced `timestampEpochSecond`
(singular) and require an update.

## Policy violation updates

The unreleased [policy update groups](groups.md#policy-violation-updates) use the same update subject.
Default templates display previous and current messages, the change time, and reviewed-event context.
Custom templates with a branch for `GROUP_POLICY_VIOLATION` do not match either update group.

This Pebble excerpt shows an update branch with explicit optional-field checks:

```pebble
{% if notification.group == "GROUP_POLICY_VIOLATION_UPDATED" or notification.group == "GROUP_POLICY_VIOLATION_UPDATED_REVIEWED" %}
Violation: {{ subject.policyViolation.uuid }}
Previous message: {% if subject.hasPreviousMessage() %}{{ subject.previousMessage }}{% else %}not recorded{% endif %}
Current message: {% if subject.hasCurrentMessage() %}{{ subject.currentMessage }}{% else %}not recorded{% endif %}
Changed at: {{ subject.changedAt | formatTimestamp }}
{% if notification.group == "GROUP_POLICY_VIOLATION_UPDATED_REVIEWED" %}
Analysis state: {% if subject.hasAnalysisState() %}{{ subject.analysisState }}{% else %}not recorded{% endif %}
Suppressed: {% if subject.hasSuppressed() %}{{ subject.suppressed }}{% else %}not recorded{% endif %}
Review decision retained
{% endif %}
{% endif %}
```

The excerpt illustrates the available fields, rather than a complete publisher payload.
The publisher's output format determines escaping and surrounding structure.
The [`subjectJson` context variable](#template-context) contains the structured subject as JSON, including optional-field presence.

## Custom filters

Beyond the [built-in Pebble filters], Dependency-Track registers
Expand Down
10 changes: 10 additions & 0 deletions docs/reference/policies/condition-expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,16 @@ cel.bind(kev, vulns.filter(vuln, vuln.is_kev),

Returning `""` when nothing matches records no violation.

An expression that lists every matching identifier can expose changes to the matching set:

```js linenums="1"
vulns.filter(vuln, vuln.is_kev).map(vuln, vuln.id).join(",")
```

The input order is stable by database identity. Expressions that include the current time or other changing values can produce different messages for the same matches.
The unreleased [policy update notification groups](../notifications/groups.md#policy-violation-updates) report changes to the stored message without changing the violation UUID.
A Boolean condition, or a message that only names the first match, cannot expose every change to the matching set.

### Suppressing a specific CVE in a vulnerability policy

In a [vulnerability policy](vulnerability-policies.md), the subject is a single
Expand Down