Add rating source tracking for context-aware OWASP scores#1928
Add rating source tracking for context-aware OWASP scores#1928fahedouch wants to merge 1 commit into
Conversation
faf1f2a to
1b90695
Compare
There was a problem hiding this comment.
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
OWASPSOURCEcolumn toANALYSISplus an index, and wires it into theAnalysismodel. - Introduces a
RatingSourceenum 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 asRatingSource.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; |
There was a problem hiding this comment.
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).
| import java.math.BigDecimal; |
| * <li>MANUAL - User-provided ratings</li> | ||
| * <li>VEX - Ratings from VEX documents</li> | ||
| * <li>POLICY - Ratings from organizational policies</li> |
There was a problem hiding this comment.
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.
| * <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> |
| command = command.withOwasp( | ||
| rating.getVector(), | ||
| rating.getScore(), | ||
| RatingSource.VEX); | ||
| break; // Only use the first OWASP rating |
There was a problem hiding this comment.
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.
| 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 |
| @Persistent(defaultFetchGroup = "true") | ||
| @Column(name = "OWASPSOURCE", jdbcType = "VARCHAR") | ||
| @JsonProperty(value = "owaspSource") | ||
| private RatingSource owaspSource; |
There was a problem hiding this comment.
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.
| <addColumn tableName="ANALYSIS"> | ||
| <column name="OWASPSOURCE" type="VARCHAR(50)"> | ||
| <constraints nullable="true"/> | ||
| </column> | ||
| </addColumn> |
There was a problem hiding this comment.
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).
|
|
||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; |
There was a problem hiding this comment.
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.
| import static org.junit.jupiter.api.Assertions.*; | |
| import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
aabffe2 to
e62e0c7
Compare
|
Frontend PR
|
e791dc0 to
ad2c4a5
Compare
Up to standards ✅🟢 Issues
|
| Category | Results |
|---|---|
| CodeStyle | 10 minor |
🟢 Metrics 35 complexity
Metric Results Complexity 35
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
| @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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Makes sense, dropped owaspSeverity — no separate severity column for CVSS either, no reason to add one for OWASP.
425ff0a to
964150c
Compare
|
@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 2. Scope of the precedence guard |
ae67a02 to
abb31ba
Compare
I'd say relax it. If we enforce invariants it should be consistent.
It should be scoped to the entire analysis, otherwise ownership becomes messy or even impossible to track. |
|
Thanks @nscuro, both addressed:
|
f1a9da8 to
15bcba3
Compare
15bcba3 to
1eb3c24
Compare
|
@nscuro squashed into one commit, should be easier to review now. Quick reading order if you're short on time:
JDBI is wired too — AnalysisDao writes SOURCE = POLICY on upsert, VulnerabilityPolicyDao clears it on unassign, AnalysisRowMapper reads it. Migration is one Flyway file: column + index + backfill. Your previous feedback is all in — per-analysis ownership, no owaspSeverity, precedence on full analysis, relaxed VEX validation. |
1eb3c24 to
70e187d
Compare
Signed-off-by: Fahed Dorgaa <fahed.dorgaa@gmail.com>
70e187d to
67db5ce
Compare
|
Hi @fahedouch, in preparation of the v5 GA release we have migrated the code to the canonical |
|
Thanks @nscuro. Re-opened it on dependency-track: DependencyTrack/dependency-track Just to make sure I understand: hyades-apiserver will be archived and everything v5 lives under dependency-track from now on, so "hyades" as a name is basically going away? |

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:
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