Skip to content

Add rating source tracking for context-aware OWASP scores#1928

Open
fahedouch wants to merge 5 commits intoDependencyTrack:mainfrom
fahedouch:feature/rating-source-tracking
Open

Add rating source tracking for context-aware OWASP scores#1928
fahedouch wants to merge 5 commits intoDependencyTrack:mainfrom
fahedouch:feature/rating-source-tracking

Conversation

@fahedouch
Copy link
Copy Markdown

@fahedouch fahedouch commented Mar 24, 2026

Description

fix DependencyTrack/dependency-track#5796

This PR enables context-aware OWASP Risk Rating scores at the component level, so the same CVE can have different scores depending on the deployment context.

What changed

Added a precedence system to track where analysis ownership comes from:

  • POLICY (highest) - organizational standards
  • VEX - authoritative context-aware assessments
  • MANUAL - analyst notes
  • NVD (lowest) - default database scores

Higher precedence sources can overwrite lower ones, but not vice versa. This prevents VEX imports from accidentally overwriting your policy ratings or analyst assessments.

OWASP ratings store vector + score, no separate severity column — consistent with how CVSS is handled on Analysis.

Use case

Import contextual OWASP scores from VEX documents (e.g., generated by VENS) that reflect your actual deployment - internet-facing vs internal, production vs dev, sensitive data handling, etc.

Addressed Issue

fixes DependencyTrack/dependency-track#5796

Checklist

  • I have read and understand the contributing guidelines
  • This PR implements an enhancement, and I have provided tests to verify that it works as intended
  • This PR introduces changes to the database model, and I have updated the migration changelog accordingly
  • This PR introduces new or alters existing behavior, and I have updated the documentation accordingly

Copilot AI review requested due to automatic review settings March 24, 2026 17:34
@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch from faf1f2a to 1b90695 Compare March 24, 2026 17:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces tracking of the source of component-level OWASP Risk Rating overrides so that context-aware scores (e.g., from VEX) can be applied with a defined precedence model, aiming to prevent lower-precedence sources from overwriting higher-precedence ratings.

Changes:

  • Adds a new OWASPSOURCE column to ANALYSIS plus an index, and wires it into the Analysis model.
  • Introduces a RatingSource enum with precedence + overwrite logic, and adds unit tests for it.
  • Extends analysis update flows to carry OWASP vector/score/source via MakeAnalysisCommand, and imports OWASP ratings from CycloneDX VEX as RatingSource.VEX.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
migration/src/main/resources/migration/changelog-v5.8.0.xml Adds OWASPSOURCE column and index in Liquibase.
migration/src/main/resources/migration/changelog-main.xml Includes the v5.8.0 changelog in the master migration chain.
apiserver/src/test/java/org/dependencytrack/model/RatingSourceTest.java Adds tests validating precedence and overwrite behavior.
apiserver/src/main/java/org/dependencytrack/persistence/command/MakeAnalysisCommand.java Adds OWASP vector/score/source fields and a convenience withOwasp(...) method.
apiserver/src/main/java/org/dependencytrack/persistence/AnalysisQueryManager.java Applies precedence when updating OWASP vector/score and persists owaspSource.
apiserver/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDXVexImporter.java Imports OWASP ratings from CycloneDX VEX and sets source to VEX, with basic validation.
apiserver/src/main/java/org/dependencytrack/model/RatingSource.java Defines precedence order and overwrite logic.
apiserver/src/main/java/org/dependencytrack/model/Analysis.java Adds the persisted owaspSource field mapped to OWASPSOURCE.
RATING_SOURCE_TRACKING.md Documents the precedence model and examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.math.BigDecimal;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

java.math.BigDecimal is imported but never used in this class. This will fail compilation due to an unused import; please remove it (or use it if intended).

Suggested change
import java.math.BigDecimal;

Copilot uses AI. Check for mistakes.
Comment on lines +238 to +240
* <li>MANUAL - User-provided ratings</li>
* <li>VEX - Ratings from VEX documents</li>
* <li>POLICY - Ratings from organizational policies</li>
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precedence order documented here (MANUAL > VEX > POLICY > NVD) contradicts the precedence defined in RatingSource (POLICY > VEX > MANUAL > NVD) and the PR description. Please correct the Javadoc to reflect the actual precedence to avoid future logic being implemented based on the wrong ordering.

Suggested change
* <li>MANUAL - User-provided ratings</li>
* <li>VEX - Ratings from VEX documents</li>
* <li>POLICY - Ratings from organizational policies</li>
* <li>POLICY - Ratings from organizational policies</li>
* <li>VEX - Ratings from VEX documents</li>
* <li>MANUAL - User-provided ratings</li>

Copilot uses AI. Check for mistakes.
Comment on lines +201 to +205
command = command.withOwasp(
rating.getVector(),
rating.getScore(),
RatingSource.VEX);
break; // Only use the first OWASP rating
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For OWASP ratings, this logic allows a rating with severity != null but vector == null (and potentially score == null) to be treated as valid, then it calls withOwasp(null, null, VEX) and immediately breaks. That means a later OWASP rating with a usable vector+score in the same VEX document would be ignored, and no OWASP data will be applied. Consider requiring vector+score for OWASP imports (since those are the only fields persisted), or only break once at least one of vector/score is actually applied.

Suggested change
command = command.withOwasp(
rating.getVector(),
rating.getScore(),
RatingSource.VEX);
break; // Only use the first OWASP rating
if (rating.getVector() == null || rating.getScore() == null) {
LOGGER.warn("VEX OWASP rating is missing vector and/or score - skipping");
continue;
}
command = command.withOwasp(
rating.getVector(),
rating.getScore(),
RatingSource.VEX);
break; // Only use the first OWASP rating with usable vector and score

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +158
@Persistent(defaultFetchGroup = "true")
@Column(name = "OWASPSOURCE", jdbcType = "VARCHAR")
@JsonProperty(value = "owaspSource")
private RatingSource owaspSource;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding owaspSource to the JDO model is not sufficient to enforce precedence if OWASP scores/vectors are also written via the JDBI-based policy/analysis upserts: those SQL paths currently don't read/write OWASPSOURCE, so the column will remain NULL or stale while OWASPVECTOR/OWASPSCORE change. This breaks the intended precedence rules (e.g., policy updates won't mark the source as POLICY, and VEX may overwrite rows with NULL source). Please update the relevant JDBI mappers/queries/upserts to include OWASPSOURCE wherever OWASP ratings are set.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +17
<addColumn tableName="ANALYSIS">
<column name="OWASPSOURCE" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
</addColumn>
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new OWASPSOURCE column is nullable and there's no data migration/backfill. As a result, any existing analyses that already have OWASPVECTOR/OWASPSCORE populated will have a NULL source and will be treated as overwritable by any new source (since canOverwrite(null) returns true). If the goal is to prevent VEX imports from overwriting existing policy/manual ratings, consider backfilling OWASPSOURCE for existing rows (or introducing an explicit default/UNKNOWN source with appropriate precedence).

Copilot uses AI. Check for mistakes.

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static wildcard import is disallowed by this repo's Checkstyle rules (AvoidStarImport in support/checkstyle/config.xml). Please replace import static org.junit.jupiter.api.Assertions.*; with explicit static imports for the assertions used in this test.

Suggested change
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Copilot uses AI. Check for mistakes.
Comment thread apiserver/src/main/java/org/dependencytrack/model/Analysis.java Outdated
@fahedouch
Copy link
Copy Markdown
Author

Frontend PR

Capture d’écran 2026-03-30 à 23 57 09

@fahedouch fahedouch requested a review from nscuro March 31, 2026 09:24
@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch 3 times, most recently from e791dc0 to ad2c4a5 Compare April 8, 2026 15:12
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented Apr 8, 2026

Up to standards ✅

🟢 Issues 10 minor

Results:
10 new issues

Category Results
CodeStyle 10 minor

View in Codacy

🟢 Metrics 37 complexity

Metric Results
Complexity 37

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

Comment on lines +155 to +162
@Persistent(defaultFetchGroup = "true")
@Column(name = "OWASPSEVERITY")
@Extensions(value = {
@Extension(vendorName = "datanucleus", key = "insert-function", value = "CAST(? AS severity)"),
@Extension(vendorName = "datanucleus", key = "update-function", value = "CAST(? AS severity)")
})
@JsonProperty(value = "owaspSeverity")
private Severity owaspSeverity;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case for tracking the severity for OWASP ratings separately? Adding more severity fields adds complexity to all queries that operate on severities, including metrics calculation.

Analogue to a previous comment, if we do this for OWASP ratings, why not also for CVSS?

Copy link
Copy Markdown
Author

@fahedouch fahedouch Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, dropped owaspSeverity — no separate severity column for CVSS either, no reason to add one for OWASP.

@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch 4 times, most recently from 8f6546b to 88804a6 Compare April 17, 2026 13:37
@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch from 88804a6 to 425ff0a Compare April 17, 2026 13:41
@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch from 425ff0a to 964150c Compare April 17, 2026 13:44
@fahedouch fahedouch requested a review from nscuro April 17, 2026 14:22
@fahedouch
Copy link
Copy Markdown
Author

fahedouch commented Apr 17, 2026

@nscuro thank you for the review. I’ve addressed your latest comments.

Two questions before I go further:

1. OWASP rating validation in VEX imports
Right now the importer skips OWASP ratings if vector or score is missing. CVSS fields elsewhere in the codebase are independent though (you can have a score without a vector, or vice versa). Should I relax this to match, or keep both required for OWASP?

2. Scope of the precedence guard
The source-based precedence currently only protects OWASP vector/score — state, justification, response, suppression are still updatable regardless of source. I kept it scoped to ratings intentionally. Worth extending to the full analysis, or fine as-is for now?

@fahedouch fahedouch force-pushed the feature/rating-source-tracking branch from ae67a02 to abb31ba Compare April 17, 2026 16:49
@nscuro
Copy link
Copy Markdown
Member

nscuro commented Apr 23, 2026

@fahedouch

  1. OWASP rating validation in VEX imports
    Right now the importer skips OWASP ratings if vector or score is missing. CVSS fields elsewhere in the codebase are independent though (you can have a score without a vector, or vice versa). Should I relax this to match, or keep both required for OWASP?

I'd say relax it. If we enforce invariants it should be consistent.

  1. Scope of the precedence guard
    The source-based precedence currently only protects OWASP vector/score — state, justification, response, suppression are still updatable regardless of source. I kept it scoped to ratings intentionally. Worth extending to the full analysis, or fine as-is for now?

It should be scoped to the entire analysis, otherwise ownership becomes messy or even impossible to track.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OWASP scores should be per-component, not global, to support contextual VEX ratings

3 participants