DependencyTrack / hyades

Incubating project for decoupling responsibilities from Dependency-Track's monolithic API server into separate, scalable services.
https://dependencytrack.github.io/hyades/latest
Apache License 2.0
61 stars 18 forks source link

Support global vulnerability analysis policies #930

Open nscuro opened 1 year ago

nscuro commented 1 year ago

Context

Since v4.x, Dependency-Track no longer supports global auditing of vulnerabilities.

This was done because analyses are not generally applicable to all projects depending on a given component. Context matters, and decisions like "not affected" can only ever be made on the project level.

It is thus also not possible to suppress false positives for all projects in a portfolio anymore. However, the community still has a need for it: https://github.com/DependencyTrack/dependency-track/issues/1495

Automating analysis works, utilizing both Webhook notifications and Dependency-Track's REST API. An example implementation of such automation is available with dtapac.

What's problematic about this, is that at the point at which Webhooks are sent, it's already too late:

Projects like OWASP Dependency-Check support suppression files. Typically based on regular expressions for component identifiers, and vulnerability IDs.

Dependency-Track supports more than just suppression though.

Organizations may wish to:

How does this differ from VEX / VDR?

While VEX and VDR can be used to communicate analysis of a given finding (which may lead to suppression), the analysis in both cases is static and can not be bound to additional constraints.

Why not use the existing policy feature?

Policy evaluation happens "too late" in the workflow. When the evaluation is executed, findings have already been created, and NEW_VULNERABILITY notification have already been sent. This is sub-optimal, as users are notified about findings that ultimately may be suppressed anyway. For CI use cases, build systems could fetch the list of findings before policy evaluation completed.

Existing policies are further scoped components. Their evaluation context includes:

Thus, if a policy matches, it is not possible to tell for which of the 0-N vulnerabilities it matched.

Does this cover false negatives?

No. The proposed functionality exclusively applies to vulnerabilities that are identified. Dependency-Track's internal vulnerability database can be used to address false negatives.

Proposed solution

Introduce a new type of policy, entirely dedicated to applying analyses in an automated manner. For lack of better terms, this new policy type will be called "Vulnerability Policy" for now.

There will be a new tab in the UI for them. Existing policies will (likely) be renamed to "Component Policies".

image

Policy structure

Vulnerability policies consist of the following:

Field Description Required
name Name of the policy
description Short description of the policy
author Name of the policy author
validFrom When the policy should take effect (in RFC3339 format)
validUntil Until when the policy should take effect (in RFC3339 format)
conditions 1..N conditions as CEL expressions
analysis The analysis to apply when all expressions match
ratings 0..3 ratings to apply to the finding when all expressions match
Conditions

One or more conditions MUST be provided for each policy. Conditions MUST be valid CEL expressions, and MAY access everything that is already available in component policies.

When multiple conditions are provided, all of them have to match (conjunctive).

Analysis

A policy MUST define an analysis.

The analysis encompasses everything that can currently be set manually via UI or REST API:

Field Description Required
state State of the analysis (e.g. EXPLOITABLE, NOT_AFFECTED)
justification Justification for the state (e.g. CODE_NOT_REACHABLE)
vendor response What to communicate to consumers for this finding (from a vendor POV)
details Additional details about why this analysis was made
suppress Whether to suppress the finding

This is also in line with fields available in CycloneDX VDR and VEX.

[!NOTE] Analyses applied via policy will populate the audit trail, just like a manual application would.

Ratings

A policy may define up to three ratings that should be applied to matching findings, effectively overriding the ratings provided by vulnerability databases. This may be done to enrich CVSS vectors with temporal and environmental metrics, which can lead to severity reduction.

Vulnerabilities in Dependency-Track can currently have the following severity / risk ratings:

Thus, policies can define overrides for all of them. However, providing multiple ratings with identical method (e.g. two CVSSv2 ratings) is ambiguous and will not be supported.

Field Description Required
method Methodology of the rating (e.g. CVSSv2, OWASP)
severity Severity (LOW, MEDIUM, etc.)
vector Vector of the method
score Numeric score for the method

This is in line with fields available in CycloneDX VDR and VEX.

Example

Example policy (represented as YAML for brevity):

name: Example
description: Foo bar
author: Jane Doe
validUntil: 2024-01-01T00:00:00Z
conditions:
- vuln.id == "CVE-123" || vuln.aliases.exists(alias, alias.id == "CVE-123")
- |-
 component.name == "foo" 
   && project.name == "bar"
   && "internal" in project.tags
   && !component.is_dependency_of(v1.Component{group: "org.springframework.boot"})
analysis:
  state: NOT_AFFECTED
  justification: CODE_NOT_REACHABLE
  details: Because foo bar baz
  suppress: true
ratings:
- method: CVSSv3
  vector: CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
  severity: medium
  score: 6.3

Evaluation context

The context of condition evaluation will contain the following variables:

Policy integration

The following describes a potential workflow. Key components:

This workflow is executed for both ad-hoc vulnerability analysis upon BOM upload, as well as the scheduled continuous analysis of existing projects.

sequenceDiagram
    Kafka->>+VulnScanResultProcessor: Vuln Scan Result
    VulnScanResultProcessor->>VulnScanResultProcessor: Parse
    loop for each vulnerability
        VulnScanResultProcessor->>Database: Sync vuln details
        VulnScanResultProcessor->>AnalysisPolicyEvaluator: Evaluate policies
        Note over VulnScanResultProcessor, AnalysisPolicyEvaluator: Context: Component, Project, Vulnerability
        AnalysisPolicyEvaluator->>AnalysisPolicyProvider: Get applicable policies
        Note over AnalysisPolicyEvaluator, AnalysisPolicyProvider: Provider could be SPI to plug in various<br/>backends (Git, S3, Database, ...)
        AnalysisPolicyProvider->>AnalysisPolicyEvaluator: Applicable policies
        AnalysisPolicyEvaluator->>AnalysisPolicyEvaluator: Evaluate policies
        AnalysisPolicyEvaluator->>VulnScanResultProcessor: state=NOT_AFFECTED<br/>justification=CODE_NOT_REACHABLE<br/>detail=Because foo bar baz<br/>suppress=true
        Note over AnalysisPolicyEvaluator, VulnScanResultProcessor: What if multiple policies match?<br/>Short-circuit, prioritization?
        critical atomically
            VulnScanResultProcessor->>Database: Create finding
            VulnScanResultProcessor->>Database: Apply analysis
        end 
        opt finding not suppressed
            VulnScanResultProcessor->>Kafka: Send NEW_VULNERABILITY<br/>notification 
        end
    end

[!NOTE]
"Finding" refers to the association of a component with a vulnerability.

Change control

As analysis policies not only notify, but modify, there is a need to optionally have a change control mechanism in place. There are multiple options to achieve this.

Generally, we do not believe that it's worth having approval workflows baked into Dependency-Track. It just adds a lot of complexity without much benefit, as other systems are available that already enforce such workflows perfectly.

[!NOTE] This will be entirely optional. Users who prefer to use the UI for policy management will be able to continue to do so.

Policy management in Git, synchronization via REST API
sequenceDiagram
    actor User A
    actor User B
    User A->>Git Server: Push policies
    User A->>Git Server: Create PR
    User B->>Git Server: Review / Approve
    Git Server->>Git Server: Perform merge to<br/>main branch
    Git Server->>CI Server: Webhook
    CI Server->>Git Server: Clone repository
    CI Server->>Dependency-Track: Reconcile policies<br/>(CUD)
Policy management in Git, synchronization directly
sequenceDiagram
    actor User A
    actor User B
    User A->>Git Server: Push policies
    User A->>Git Server: Create PR
    User B->>Git Server: Review / Approve
    Git Server->>Git Server: Perform merge to<br/>main branch
    loop regularly
        Dependency-Track->>Git Server: Clone / pull
        Dependency-Track->>Dependency-Track: Reconcile policies<br/>(CUD)
    end
Policy management in Git, synchronization via bundle

Similar to OPA's policy bundles, we can follow a similar approach to allow for atomic application of updates. Managing access to blob storage / HTTP file servers is a lot simpler than for Git.

sequenceDiagram
    actor User A
    actor User B
    User A->>Git Server: Push policies
    User A->>Git Server: Create PR
    User B->>Git Server: Review / Approve
    Git Server->>Git Server: Perform merge to<br/>main branch
    Git Server->>CI Server: Webhook
    CI Server->>Git Server: Clone repository
    CI Server->>CI Server: Create policy bundle<br/>(.tgz / .zip)
    CI Server->>Blob storage: Upload bundle
    loop regularly
        Dependency-Track->>Blob storage: Download bundle
        Dependency-Track->>Dependency-Track: Reconcile policies<br/>(CUD)
    end

[!NOTE] In addition, it should be possible to upload bundles directly via REST API.

nscuro commented 1 year ago
nscuro commented 1 year ago

High-level tasks for an initial MVP:

valentijnscholten commented 1 year ago

I suggest next to entering raw CEL expressions (which can be complicated and makes the user think), to also have a "simple" way to suppress vulnerabilities for:

May be this can be a button that puts a template into the expression field or something like that.

nscuro commented 1 year ago

@valentijnscholten Good suggestion. The way we did it for the existing policy functionality, is that users can continue to use the pre-defined conditions (e.g. Coordinates), but in the background we re-write them to CEL and execute them that way.

Offering a set of condition templates as you say also makes sense to me.

tomaszn commented 1 year ago

@nscuro This is fabulous.

One thing seems missing to me. Can the scope of the rule updates be limited per user? For example, I would like to limit users having access only to projects A and B to be only permitted to create policies that cover only these projects. This check would apply to changes both via UI, and also via API, so separate bot accounts could be used per team, to prevent one team from suppressing findings in other team's projects. This would be useful in the scenario of policy management in Git with synchronization via REST API, where each team or project could maintain their policies and update them e.g. in CI just before uploading the SBOM.

skhokhlov commented 11 months ago

I'm so excited about this feature. One thing I want to suggest is to have a policies archive push endpoint in api and ui. In this case policies can be properly controlled and DT won't need any integration with file storage systems. Also it allows to rollback policies to a previous version without any troubles. In case of polling an external endpoint for policies archive, the problem may be that artefact storage won't allow to override file with a new version. It means that for each new version the link will be different.

nscuro commented 11 months ago

Thanks @skhokhlov, your input is much appreciated!

One thing I want to suggest is to have a policies archive push endpoint in api and ui. In this case policies can be properly controlled and DT won't need any integration with file storage systems.

Good point. I think DT should support both push and pull models. Ideally, it shouldn't matter to DT how bundles are supplied.

Also it allows to rollback policies to a previous version without any troubles.

Yeah I guess having to wait a few minutes for polling to kick in is not something you want when a faulty policy was deployed.

In case of polling an external endpoint for policies archive, the problem may be that artefact storage won't allow to override file with a new version. It means that for each new version the link will be different.

Hmmm true. Hadn't thought about this limitation.

rkg-mm commented 11 months ago

This would address https://github.com/DependencyTrack/dependency-track/issues/2183, and I totally need this yesterday 😆

One idea: In the existing vulnerability analysis view, add a button like "Apply to all project versions", clicking it opens the UI to create a corresponding policy, fields pre-filled and customizable if the user wants to. This would make the workflow much easier (teams usually work in the vulnerability view, do an analysis starting from there and add the result in this view. Forcing them to then switch to another view entering details that already were there in the view before would be a usability nightmare for this use case)

Also I have questions about the permissions this requires. Creating policies today requires specific permissions the teams usually don't have in my org (and won't have in future). However, creating policies affecting a specific project (can it include children?) could be done if the vulnerability analysis permission is there for the user and this project.

nscuro commented 11 months ago

@rkg-mm One idea: In the existing vulnerability analysis view, add a button like "Apply to all project versions", clicking it opens the UI to create a corresponding policy, fields pre-filled and customizable if the user wants to.

I like this idea!

However, creating policies affecting a specific project (can it include children?) could be done if the vulnerability analysis permission is there for the user and this project.

I think this is an ACL check we should be able to do. At least off the top of my head I don't see anything that would prevent us from doing that. But, this and the idea you mentioned earlier necessitate some form of policy "ownership", which likely needs to be scoped to teams. So related to what @tomaszn mentioned.

When users create policies from the audit view, the policies are owned by the team(s) that user is a member of.