diff --git a/docs/concepts/.pages b/docs/concepts/.pages index e44ad29e..61ce4250 100644 --- a/docs/concepts/.pages +++ b/docs/concepts/.pages @@ -5,6 +5,7 @@ nav: - About tags: tags.md - About access control: access-control.md - About vulnerability data sources: about-vulnerability-data-sources.md + - About vulnerability findings: vulnerability-findings.md - About notifications: notifications.md - About time series metrics: time-series-metrics.md - About component policies: component-policies.md diff --git a/docs/concepts/time-series-metrics.md b/docs/concepts/time-series-metrics.md index cfc3eb3f..fc4dafa6 100644 --- a/docs/concepts/time-series-metrics.md +++ b/docs/concepts/time-series-metrics.md @@ -10,7 +10,7 @@ drift across days, weeks, and quarters. ## Snapshots, not deltas A metrics record is a *snapshot*: a complete count of vulnerabilities, -findings, suppressions, audit progress, and policy violations at a single +[findings](vulnerability-findings.md), suppressions, audit progress, and policy violations at a single moment, scoped to one component, project, or the portfolio. Reconstructing trends from deltas is fragile (a missed event corrupts the rest of the series), so Dependency-Track stores the absolute numbers and lets readers diff --git a/docs/concepts/vulnerability-findings.md b/docs/concepts/vulnerability-findings.md new file mode 100644 index 00000000..b4db5eb3 --- /dev/null +++ b/docs/concepts/vulnerability-findings.md @@ -0,0 +1,126 @@ +# About vulnerability findings + +A finding is the association between a component in one of your projects and a vulnerability that +affects it. Findings are what you see on a project's **Audit Vulnerabilities** tab and what drives +the vulnerability counts in [time series metrics](time-series-metrics.md). Every finding carries an +audit trail and moves through a lifecycle that this page describes. + +## Where findings come from + +Each enabled [vulnerability analyzer](../reference/analyzers.md) matches your components +against its data source. When an analyzer concludes that a vulnerability affects a component, it +attributes the finding to itself. The same `(component, vulnerability)` pair is one finding even when +more than one analyzer identifies it. Dependency-Track records one attribution per analyzer, so a +finding remembers which sources confirmed it. + +For each attribution, the finding stores the analyzer identity, the date the analyzer attributed +the finding (refreshed when the same analyzer reactivates an inactive finding), and a +reference URL the analyzer supplied. + +Each finding ties to one specific vulnerability record, not to the underlying issue. When analyzers +report the same underlying vulnerability under different IDs, for example, one under a CVE and +another under its GHSA alias, the result is two separate findings on the same component. Each +finding surfaces the aliases Dependency-Track knows for its vulnerability, so the connection stays +visible during triage. [Time series metrics](time-series-metrics.md) deduplicate by alias group, so +a single underlying vulnerability counted under more than one ID still contributes once to the +project's findings total and severity counts. + +## Auditing a finding + +A finding carries an *analysis* that captures the team's triage decision. The analysis has four +fields: + +* **Analysis state** records the triage conclusion, from initial triage through to whether the + finding is exploitable, not applicable, or already resolved. +* **Justification** explains why the component is not affected. It aligns with the CycloneDX VEX + vocabulary and is most meaningful when the analysis state is *Not Affected*. +* **Vendor response** records the intended remediation action, such as updating, rolling back, or + applying a workaround. +* **Details** is free-form text that explains the decision in context. + +Suppression is part of the analysis as well, and the next section covers it on its own. + +Every change to any of these fields appends an entry to the finding's audit history. The history +records who made the change, when, and what changed, and it includes the free-form comments reviewers +add as they work. The audit history is permanent. A later analyzer report never overwrites it, and +it survives when a finding goes inactive. + +[Vulnerability policies](vulnerability-policies.md) apply the same audit fields automatically when a +finding matches a policy condition, and the policy's decision lands in the audit history alongside +manual entries. + +## Suppression + +Suppressing a finding hides it from the default views without losing any of its history. A suppressed +finding: + +* Does not appear in the project's findings table or in API responses unless the caller opts in by + toggling **Show Suppressed** in the frontend, or by passing the matching flag in the REST API. +* Does not contribute to the active vulnerability counts in + [time series metrics](time-series-metrics.md), and does not raise the project's risk score. +* Counts in the dedicated suppressed-findings metric, so the portfolio still has visibility into + how much triage work has resulted in suppression. + +Suppression survives across re-scans. If an analyzer reports the finding again later, the suppression +still applies. To stop suppressing, clear the flag on the finding's analysis. The audit history +records the change. + +## Active and inactive findings + +A finding is *active* while at least one analyzer still reports it. As long as one attribution is +live, the finding is active even if other analyzers stop reporting it. + +A finding becomes *inactive* when no analyzer reports it anymore. This typically happens after a data +source correction, for example, when the upstream advisory narrows its affected range and excludes +the component's version. An inactive finding: + +* Stays hidden from the project's findings table and from API responses, with no toggle to reveal it. +* Does not contribute to severity counts, the audited count, or the project's risk score in + [time series metrics](time-series-metrics.md). +* Keeps its full audit history. + +If an analyzer reports the finding again later, Dependency-Track reactivates it rather than creating +a new one. The earlier audit state, including analysis state, justification, vendor response, +details, and suppression, applies as before, and any new attributions extend the same audit history. + +This distinction matters during triage. A suppressed finding is a deliberate decision you can +reverse by clearing the suppression. An inactive finding is the analyzers' decision, reversed only +when an analyzer reports the finding again. + +## Notifications + +Lifecycle moments on a finding map to specific notification groups: + +* A newly created finding fires [`NEW_VULNERABILITY`](../reference/notifications/groups.md#new_vulnerability). + Reactivation of an inactive finding fires the same group, so subscribers see both first-time + discoveries and returns under one stream. +* A finding going inactive fires + [`VULNERABILITY_RETRACTED`](../reference/notifications/groups.md#vulnerability_retracted). +* When a component appears in a project for the first time and any analyzer attributes findings to + it during the same analysis run, the project fires one + [`NEW_VULNERABLE_DEPENDENCY`](../reference/notifications/groups.md#new_vulnerable_dependency) per + new component, summarizing every finding on it. +* An audit change, whether a reviewer made it or a [vulnerability policy](vulnerability-policies.md) + applied it, fires + [`PROJECT_AUDIT_CHANGE`](../reference/notifications/groups.md#project_audit_change). + +Suppressing a finding and clearing that suppression both flow through `PROJECT_AUDIT_CHANGE` rather +than a dedicated group. See [About notifications](notifications.md) for how groups, scopes, and +levels combine into alerts. + +## How findings drive metrics + +[Time series metrics](time-series-metrics.md) count only active, non-suppressed findings toward +severity totals and the project's risk score. The audited count is a subset of those: only findings +whose analysis state has moved past the initial triage values contribute to it. The dedicated +suppressed metric tracks suppression separately. Inactive findings do not contribute to any of +these counts. + +## Further reading + +* [About vulnerability data sources](about-vulnerability-data-sources.md): the analyzers and feeds + that produce findings. +* [About vulnerability policies](vulnerability-policies.md): how policies apply analyses to findings + automatically. +* [About time series metrics](time-series-metrics.md): how active, suppressed, and inactive findings + affect the project's posture trend. diff --git a/docs/concepts/vulnerability-policies.md b/docs/concepts/vulnerability-policies.md index 7e66c424..be4d6e0b 100644 --- a/docs/concepts/vulnerability-policies.md +++ b/docs/concepts/vulnerability-policies.md @@ -2,7 +2,7 @@ Vulnerability policies let organisations encode how specific vulnerabilities should be triaged across the portfolio. Where a [component policy](../reference/policies/component-policies.md) raises violations, a vulnerability policy acts -on the finding itself. It applies an analysis (state, justification, vendor response, details), +on the [finding](vulnerability-findings.md) itself. It applies an analysis (state, justification, vendor response, details), optionally overrides the vulnerability's ratings, and can suppress the finding altogether. Typical use cases include: diff --git a/docs/reference/notifications/groups.md b/docs/reference/notifications/groups.md index 2d161b44..0456d71d 100644 --- a/docs/reference/notifications/groups.md +++ b/docs/reference/notifications/groups.md @@ -138,13 +138,6 @@ Fires when an analysis or suppression state changes on a project finding. Fires on creation of a new project. -### `PROJECT_VULN_ANALYSIS_COMPLETE` - -- **Trigger:** Event -- **Level:** Informational - -Fires when vulnerability analysis for a project completes. - ### `VEX_CONSUMED` - **Trigger:** Event @@ -195,7 +188,6 @@ each subject schema. | `NEW_VULNERABLE_DEPENDENCY` | [NewVulnerableDependencySubject](../schemas/notification.md#newvulnerabledependencysubject) | | `POLICY_VIOLATION` | [PolicyViolationSubject](../schemas/notification.md#policyviolationsubject) | | `PROJECT_AUDIT_CHANGE` | [VulnerabilityAnalysisDecisionChangeSubject](../schemas/notification.md#vulnerabilityanalysisdecisionchangesubject) or [PolicyViolationAnalysisDecisionChangeSubject](../schemas/notification.md#policyviolationanalysisdecisionchangesubject) | -| `PROJECT_VULN_ANALYSIS_COMPLETE` | [ProjectVulnAnalysisCompleteSubject](../schemas/notification.md#projectvulnanalysiscompletesubject) | | `VEX_CONSUMED`, `VEX_PROCESSED` | [VexConsumedOrProcessedSubject](../schemas/notification.md#vexconsumedorprocessedsubject) | | `USER_CREATED`, `USER_DELETED` | [UserSubject](../schemas/notification.md#usersubject) | | `NEW_VULNERABILITIES_SUMMARY` | [NewVulnerabilitiesSummarySubject](../schemas/notification.md#newvulnerabilitiessummarysubject) |