fix(maven): report the CVSS score that failed the build - #8727
Conversation
checkForFailure fails the build when any of the CVSS scores of a vulnerability reaches failBuildOnCVSS, but prints the score of the newest CVSS version available. When an older version is the one that reached the threshold the message contradicts itself, e.g. "score greater than or equal to '7.0': ... CVE-2021-42550(6.6)" for a CVE whose CVSSv2 score is 8.5. Pick the score that actually reached the threshold, falling back to the newest CVSS version when no threshold applies, so the reported score always explains the failure. Fixes dependency-check#5658 Signed-off-by: Eljees <3.14hell@gmail.com>
chadlwilson
left a comment
There was a problem hiding this comment.
My 2c is that notwithstanding whether the functional change is desirable or not - this doesn't address the linked issue so should be unlinked.
The linked issue is
- reported against Gradle initially, not Maven (so changing the Maven text output won't help, by definition - later comments are unrelated comments conflating different issues)
- is reporting a mismatch in the report contents "Highest Severity" column independent of what it prints on the command line, which is caused by inconsistenct between the way scores are chosen from each vulnerability for sorting (prefer V4 to V3 to V3 to unscored, regardless of score, like the PR printing logic) with the failure limits (if any of the CVSS scores for an individual vuln is > threshold --> fail)
The fundamental problem in the linked issue is not that it doesn't report the score in the Maven output, it's that the methodology is inconsistent between integration and report and there hasn't been a discussion on what the best/correct methodology actually is - in order to make the sorting for report consistent with the command line failure output.
While this PR does ensure the error-reported score is the one that triggered the failure for Maven (which has special logic here inconsistent with other integrations), it does so with some unnecessary complexity.
To actually address the linked issue, we probably need to do this holistically, and ideally have a reusable component/helper methods on the Vulnerability itself which
- are consistent with sort logic
- will minimise the chance of behavioural differences between integrations
- will minimise the chance of sort vs choice logic accidentally deviating
- is reused by the integrations to then decide how to display that logic rather than mixing logic and display/error-building semantics into a near-duplicated function
| } else if (cvssV2 >= 0.0) { | ||
| return cvssV2; | ||
| } | ||
| return unscoredCvss; |
There was a problem hiding this comment.
the logic could be much simpler as it is, essentially there are now two places that are applying the threshold checks rather than one, which increases the chance of more bugs.
There was a problem hiding this comment.
Agreed. I'll drop scoreToReport and choose the score inside the comparison that already applies the threshold, so it is decided in one place instead of two.
There was a problem hiding this comment.
Done in ddb9b48. The score is now chosen by the same comparison that decides the failure, and a vulnerability that reaches none of the thresholds is skipped, so scoreToReport is gone along with the second copy of the check.
| /** | ||
| * Determines the CVSS score to display for a vulnerability listed by | ||
| * {@link #checkForFailure(org.owasp.dependencycheck.dependency.Dependency[])}. | ||
| * <p> | ||
| * The build is failed when <em>any</em> of the CVSS scores of a vulnerability reaches the | ||
| * configured threshold, so the score that actually reached it is the one to display; showing | ||
| * the score of the newest CVSS version instead quotes a score below the threshold that the | ||
| * failure message itself states. When no threshold applies the newest CVSS version is used. | ||
| * | ||
| * @param cvssV2 the CVSS v2 base score, or -1 when the vulnerability has no v2 score | ||
| * @param cvssV3 the CVSS v3 base score, or -1 when the vulnerability has no v3 score | ||
| * @param cvssV4 the CVSS v4 base score, or -1 when the vulnerability has no v4 score | ||
| * @param unscoredCvss the score estimated from an unscored severity, or -1 when not applicable | ||
| * @param threshold the configured failBuildOnCVSS threshold | ||
| * @return the score to display, or -1 when the vulnerability carries no score at all | ||
| */ |
There was a problem hiding this comment.
The comment is more complex than the actual code. Seems completely unnecessary.
There was a problem hiding this comment.
Agreed - removing it.
There was a problem hiding this comment.
Removed in ddb9b48, together with the method it described.
There was a problem hiding this comment.
Pull request overview
Updates Maven failure messages to report the CVSS score that crossed the configured threshold.
Changes:
- Adds threshold-aware CVSS score selection.
- Adds unit tests for score selection and fallback behavior.
- Does not address the linked issue’s HTML report behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
BaseDependencyCheckMojo.java |
Selects the reported CVSS score. |
BaseDependencyCheckMojoTest.java |
Tests threshold and fallback selection. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| name += "(" + cvssV2 + ")"; | ||
| } else if (unscoredCvss >= 0.0) { | ||
| name += "(" + unscoredCvss + ")"; | ||
| final double reportedScore = scoreToReport(cvssV2, cvssV3, cvssV4, unscoredCvss, failBuildOnCVSS); |
There was a problem hiding this comment.
Unlinked. The description now refers to #5658 without a closing keyword, so merging this will not close it.
|
You're right about the linked issue, and I'm removing the That leaves a narrower Maven bug:
On the holistic fix, I'd rather not attach it to this PR without agreement on the methodology. The shape I'd propose is two methods on |
|
Posted twice by mistake - sorry about the noise. The original is above. Replying to your points one by one in the threads, as asked. |
Are you? You have still not done it.
IMHO its worth fixing on its own (but only since this extra inclusion of the score is only in the Maven plugin integration, so it doesn’t make the inconsistency between Maven and other integrations worse than it already is). And only if you actually properly respond to PR comments. One-by-one within context, please, not with a wall of slopped LLM text, spammed twice for no reason.
yes, don’t guess it. |
Review feedback: the threshold was applied twice - once in the condition that decides whether a vulnerability fails the build, and again in scoreToReport() when picking the score to display. Two copies of the same comparison can drift apart. Fold the score selection into the condition itself: the first CVSS version that reaches the threshold is both the reason the build fails and the score printed, and a vulnerability that reaches none of them is skipped. scoreToReport() and its javadoc are removed; the fallback branch it carried for "threshold set but nothing reached it" was unreachable from checkForFailure(). The three unit tests moved with the logic: they now drive checkForFailure() and assert on the failure message rather than on the removed helper. Signed-off-by: Eljees <3.14hell@gmail.com>
Description of Change
checkForFailurefails the build when any of the CVSS scores of a vulnerability reachesfailBuildOnCVSS, but the score it prints is the one of the newest CVSS version available. When an older version is the one that crossed the threshold, the message contradicts itself:for a CVE whose CVSSv2 score is 8.5, which is what actually failed the build.
The score to print is now chosen by the same comparison that decides whether the vulnerability fails the build: the first CVSS version to reach the threshold (v4, then v3, then v2, then the estimate for an unscored severity) is both the reason for the failure and the score shown, and a vulnerability that reaches none of them is not listed. With no threshold in play (
failBuildOnCVSS <= 0, i.e. report everything) the newest available version is still used, so nothing changes for vulnerabilities where the newest version is already above the threshold.I kept the reported score rather than switching to the maximum score (as suggested in the issue thread) because the maximum can come from a version that did not trigger the failure at all.
Related issues
Have test cases been added to cover the new functionality?
yes - three tests in
BaseDependencyCheckMojoTestdrivecheckForFailureand assert on the failure message it throws: the case from the issue (v2 8.5 / v3 6.6 at a 7.0 threshold), the unchanged no-threshold behaviour, and a vulnerability below the threshold not failing the build at all. Against the code before this PR the first of the three fails, quoting 6.6 in a message that states 7.0 was exceeded.