Skip to content

Commit 80b43d1

Browse files
committed
feat: Include all CVSS and OWASP RR vectors in Finding model
Expand the Finding and GroupedFinding objects to include all CVSS vectors (v2, v3, and v4) as well as the OWASP RR vector. Additionally, external references and vulnerability publication dates are included. These fields are now available via the API and in the Finding Packaging Format (FPF) export. Currently, findings only include numerical scores, which prevents downstream systems like DefectDojo from performing deep vector-based risk assessments. Including advisory links and publication dates further improves vulnerability context and SLA tracking in external management platforms. The addition of all available vectors enhances this capability significantly. SQL queries, internal mappings (Finding, GroupedFinding), and the persistence layer (FindingsSearchQueryManager) were adjusted to support these new fields. The test suite was updated to ensure compatibility with the modified data structure and shifted result set indices after resolving merge conflicts from the integration of CVSSv4 support. Signed-off-by: Andre Schlegel-Tylla <andre.schlegel-tylla@virtimo.de>
1 parent 9b3d85c commit 80b43d1

File tree

5 files changed

+166
-54
lines changed

5 files changed

+166
-54
lines changed

src/main/java/org/dependencytrack/model/Finding.java

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,20 @@ public class Finding implements Serializable {
7676
, "VULNERABILITY"."RECOMMENDATION"
7777
, "VULNERABILITY"."SEVERITY"
7878
, "VULNERABILITY"."CVSSV2BASESCORE"
79+
, "VULNERABILITY"."CVSSV2VECTOR"
7980
, "VULNERABILITY"."CVSSV3BASESCORE"
81+
, "VULNERABILITY"."CVSSV3VECTOR"
8082
, "VULNERABILITY"."CVSSV4SCORE"
83+
, "VULNERABILITY"."CVSSV4VECTOR"
8184
, "VULNERABILITY"."OWASPRRLIKELIHOODSCORE"
8285
, "VULNERABILITY"."OWASPRRTECHNICALIMPACTSCORE"
8386
, "VULNERABILITY"."OWASPRRBUSINESSIMPACTSCORE"
87+
, "VULNERABILITY"."OWASPRRVECTOR"
8488
, "VULNERABILITY"."EPSSSCORE"
8589
, "VULNERABILITY"."EPSSPERCENTILE"
8690
, "VULNERABILITY"."CWES"
91+
, "VULNERABILITY"."REFERENCES"
92+
, "VULNERABILITY"."PUBLISHED"
8793
, "FINDINGATTRIBUTION"."ANALYZERIDENTITY"
8894
, "FINDINGATTRIBUTION"."ATTRIBUTED_ON"
8995
, "FINDINGATTRIBUTION"."ALT_ID"
@@ -125,21 +131,26 @@ public class Finding implements Serializable {
125131
, "VULNERABILITY"."RECOMMENDATION"
126132
, "VULNERABILITY"."SEVERITY"
127133
, "VULNERABILITY"."CVSSV2BASESCORE"
134+
, "VULNERABILITY"."CVSSV2VECTOR"
128135
, "VULNERABILITY"."CVSSV3BASESCORE"
136+
, "VULNERABILITY"."CVSSV3VECTOR"
129137
, "VULNERABILITY"."CVSSV4SCORE"
138+
, "VULNERABILITY"."CVSSV4VECTOR"
130139
, "VULNERABILITY"."OWASPRRLIKELIHOODSCORE"
131140
, "VULNERABILITY"."OWASPRRTECHNICALIMPACTSCORE"
132141
, "VULNERABILITY"."OWASPRRBUSINESSIMPACTSCORE"
142+
, "VULNERABILITY"."OWASPRRVECTOR"
133143
, "VULNERABILITY"."EPSSSCORE"
134144
, "VULNERABILITY"."EPSSPERCENTILE"
135145
, "VULNERABILITY"."CWES"
146+
, "VULNERABILITY"."REFERENCES"
147+
, "VULNERABILITY"."PUBLISHED"
136148
, "FINDINGATTRIBUTION"."ANALYZERIDENTITY"
137149
, "FINDINGATTRIBUTION"."ATTRIBUTED_ON"
138150
, "FINDINGATTRIBUTION"."ALT_ID"
139151
, "FINDINGATTRIBUTION"."REFERENCE_URL"
140152
, "ANALYSIS"."STATE"
141153
, "ANALYSIS"."SUPPRESSED"
142-
, "VULNERABILITY"."PUBLISHED"
143154
, "PROJECT"."UUID"
144155
, "PROJECT"."NAME"
145156
, "PROJECT"."VERSION"
@@ -197,35 +208,47 @@ public Finding(UUID project, Object... o) {
197208
} else {
198209
optValue(vulnerability, "recommendation", o[13]);
199210
}
200-
final Severity severity = VulnerabilityUtil.getSeverity(o[14], (BigDecimal) o[15], (BigDecimal) o[16], (BigDecimal) o[17], (BigDecimal) o[18], (BigDecimal) o[19], (BigDecimal) o[20]);
211+
final Severity severity = VulnerabilityUtil.getSeverity(o[14], (BigDecimal) o[15], (BigDecimal) o[17], (BigDecimal) o[19], (BigDecimal) o[21], (BigDecimal) o[22], (BigDecimal) o[23]);
201212
optValue(vulnerability, "cvssV2BaseScore", o[15]);
202-
optValue(vulnerability, "cvssV3BaseScore", o[16]);
203-
optValue(vulnerability, "cvssV4Score", o[17]);
204-
optValue(vulnerability, "owaspLikelihoodScore", o[18]);
205-
optValue(vulnerability, "owaspTechnicalImpactScore", o[19]);
206-
optValue(vulnerability, "owaspBusinessImpactScore", o[20]);
213+
optValue(vulnerability, "cvssV2Vector", o[16]);
214+
optValue(vulnerability, "cvssV3BaseScore", o[17]);
215+
optValue(vulnerability, "cvssV3Vector", o[18]);
216+
optValue(vulnerability, "cvssV4Score", o[19]);
217+
optValue(vulnerability, "cvssV4Vector", o[20]);
218+
optValue(vulnerability, "owaspLikelihoodScore", o[21]);
219+
optValue(vulnerability, "owaspTechnicalImpactScore", o[22]);
220+
optValue(vulnerability, "owaspBusinessImpactScore", o[23]);
221+
optValue(vulnerability, "owaspRRVector", o[24]);
207222
optValue(vulnerability, "severity", severity.name());
208223
optValue(vulnerability, "severityRank", severity.ordinal());
209-
optValue(vulnerability, "epssScore", o[21]);
210-
optValue(vulnerability, "epssPercentile", o[22]);
211-
final List<Cwe> cwes = getCwes(o[23]);
224+
optValue(vulnerability, "epssScore", o[25]);
225+
optValue(vulnerability, "epssPercentile", o[26]);
226+
final List<Cwe> cwes = getCwes(o[27]);
212227
if (cwes != null && !cwes.isEmpty()) {
213228
// Ensure backwards-compatibility with DT < 4.5.0. Remove this in v5!
214229
optValue(vulnerability, "cweId", cwes.get(0).getCweId());
215230
optValue(vulnerability, "cweName", cwes.get(0).getName());
216231
}
217232
optValue(vulnerability, "cwes", cwes);
218-
optValue(attribution, "analyzerIdentity", o[24]);
219-
optValue(attribution, "attributedOn", o[25]);
220-
optValue(attribution, "alternateIdentifier", o[26]);
221-
optValue(attribution, "referenceUrl", o[27]);
222233

223-
optValue(analysis, "state", o[28]);
224-
optValue(analysis, "isSuppressed", o[29], false);
225-
if (o.length > 31) {
226-
optValue(vulnerability, "published", o[30]);
227-
optValue(component, "projectName", o[32]);
228-
optValue(component, "projectVersion", o[33]);
234+
if (o[28] instanceof final Clob clob) {
235+
optValue(vulnerability, "references", toString(clob));
236+
} else {
237+
optValue(vulnerability, "references", o[28]);
238+
}
239+
optValue(vulnerability, "published", o[29]);
240+
241+
optValue(attribution, "analyzerIdentity", o[30]);
242+
optValue(attribution, "attributedOn", o[31]);
243+
optValue(attribution, "alternateIdentifier", o[32]);
244+
optValue(attribution, "referenceUrl", o[33]);
245+
246+
optValue(analysis, "state", o[34]);
247+
optValue(analysis, "isSuppressed", o[35], false);
248+
249+
if (o.length > 36) {
250+
optValue(component, "projectName", o[37]);
251+
optValue(component, "projectVersion", o[38]);
229252
}
230253
}
231254

src/main/java/org/dependencytrack/persistence/FindingsSearchQueryManager.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public PaginatedResult getAllFindings(final Map<String, String> filters, final b
137137
final List<Object[]> list = totalList.subList(this.pagination.getOffset(), Math.min(this.pagination.getOffset() + this.pagination.getLimit(), totalList.size()));
138138
final List<Finding> findings = new ArrayList<>();
139139
for (final Object[] o : list) {
140-
final Finding finding = new Finding(UUID.fromString((String) o[31]), o);
140+
final Finding finding = new Finding(UUID.fromString((String) o[36]), o);
141141
final Component component = getObjectByUuid(Component.class, (String) finding.getComponent().get("uuid"));
142142
final Vulnerability vulnerability = getObjectByUuid(Vulnerability.class, (String) finding.getVulnerability().get("uuid"));
143143
final Analysis analysis = getAnalysis(component, vulnerability);
@@ -147,6 +147,12 @@ public PaginatedResult getAllFindings(final Map<String, String> filters, final b
147147
// These are CLOB fields. Handle these here so that database-specific deserialization doesn't need to be performed (in Finding)
148148
finding.getVulnerability().put("description", vulnerability.getDescription());
149149
finding.getVulnerability().put("recommendation", vulnerability.getRecommendation());
150+
finding.getVulnerability().put("references", vulnerability.getReferences());
151+
finding.getVulnerability().put("cvssV2Vector", vulnerability.getCvssV2Vector());
152+
finding.getVulnerability().put("cvssV3Vector", vulnerability.getCvssV3Vector());
153+
finding.getVulnerability().put("cvssV4Vector", vulnerability.getCvssV4Vector());
154+
finding.getVulnerability().put("owaspRRVector", vulnerability.getOwaspRRVector());
155+
150156
final PackageURL purl = component.getPurl();
151157
if (purl != null) {
152158
final RepositoryType type = RepositoryType.resolve(purl);
@@ -255,16 +261,21 @@ private void processFilters(Map<String, String> filters, StringBuilder queryFilt
255261
, "VULNERABILITY"."TITLE"
256262
, "VULNERABILITY"."SEVERITY"
257263
, "VULNERABILITY"."CVSSV2BASESCORE"
264+
, "VULNERABILITY"."CVSSV2VECTOR"
258265
, "VULNERABILITY"."CVSSV3BASESCORE"
266+
, "VULNERABILITY"."CVSSV3VECTOR"
259267
, "VULNERABILITY"."CVSSV4SCORE"
268+
, "VULNERABILITY"."CVSSV4VECTOR"
260269
, "VULNERABILITY"."EPSSSCORE"
261270
, "VULNERABILITY"."EPSSPERCENTILE"
262271
, "VULNERABILITY"."OWASPRRLIKELIHOODSCORE"
263272
, "VULNERABILITY"."OWASPRRTECHNICALIMPACTSCORE"
264273
, "VULNERABILITY"."OWASPRRBUSINESSIMPACTSCORE"
274+
, "VULNERABILITY"."OWASPRRVECTOR"
265275
, "FINDINGATTRIBUTION"."ANALYZERIDENTITY"
266276
, "VULNERABILITY"."PUBLISHED"
267277
, "VULNERABILITY"."CWES"
278+
, "VULNERABILITY"."REFERENCES"
268279
""");
269280
StringBuilder aggregateFilter = new StringBuilder();
270281
processAggregateFilters(filters, aggregateFilter, params);

src/test/java/org/dependencytrack/integrations/FindingPackagingFormatTest.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,57 @@ void testFindingsVulnerabilityAndAliases() {
7070

7171
Finding findingWithoutAlias = new Finding(project.getUuid(), "component-uuid-1", "component-name-1", "component-group",
7272
"component-version", "Optional","component-purl", "component-cpe", "vuln-uuid", Vulnerability.Source.GITHUB, "vuln-vulnId-1", "vuln-title",
73-
"vuln-subtitle", "vuln-description", "vuln-recommendation", Severity.CRITICAL, BigDecimal.valueOf(7.2), BigDecimal.valueOf(8.4),
74-
null, BigDecimal.valueOf(1.25), BigDecimal.valueOf(1.75), BigDecimal.valueOf(1.3),
75-
"0.5", "0.9", null, AnalyzerIdentity.OSSINDEX_ANALYZER, new Date(), null, null, AnalysisState.NOT_AFFECTED, true);
73+
"vuln-subtitle", "vuln-description", "vuln-recommendation",
74+
Severity.CRITICAL, // 14
75+
BigDecimal.valueOf(7.2), // 15
76+
"vector2", // 16
77+
BigDecimal.valueOf(8.4), // 17
78+
"vector3", // 18
79+
null, // 19
80+
null, // 20
81+
BigDecimal.valueOf(1.25), // 21
82+
BigDecimal.valueOf(1.75), // 22
83+
BigDecimal.valueOf(1.3), // 23
84+
null, // 24
85+
BigDecimal.valueOf(0.5), // 25
86+
BigDecimal.valueOf(0.9), // 26
87+
"787", // 27
88+
"references", // 28
89+
new Date(), // 29
90+
AnalyzerIdentity.OSSINDEX_ANALYZER, // 30
91+
new Date(), // 31
92+
null, // 32
93+
null, // 33
94+
AnalysisState.NOT_AFFECTED, // 34
95+
true // 35
96+
);
7697

7798
Finding findingWithAlias = new Finding(project.getUuid(), "component-uuid-2", "component-name-2", "component-group",
7899
"component-version", "Required","component-purl", "component-cpe", "vuln-uuid", Vulnerability.Source.NVD, "vuln-vulnId-2", "vuln-title",
79-
"vuln-subtitle", "vuln-description", "vuln-recommendation", Severity.HIGH, BigDecimal.valueOf(7.2), BigDecimal.valueOf(8.4),
80-
null, BigDecimal.valueOf(1.25), BigDecimal.valueOf(1.75), BigDecimal.valueOf(1.3),
81-
"0.5", "0.9", null, AnalyzerIdentity.INTERNAL_ANALYZER, new Date(), null, null, AnalysisState.NOT_AFFECTED, true);
100+
"vuln-subtitle", "vuln-description", "vuln-recommendation",
101+
Severity.HIGH, // 14
102+
BigDecimal.valueOf(7.2), // 15
103+
"vector2", // 16
104+
BigDecimal.valueOf(8.4), // 17
105+
"vector3", // 18
106+
null, // 19
107+
null, // 20
108+
BigDecimal.valueOf(1.25), // 21
109+
BigDecimal.valueOf(1.75), // 22
110+
BigDecimal.valueOf(1.3), // 23
111+
null, // 24
112+
BigDecimal.valueOf(0.5), // 25
113+
BigDecimal.valueOf(0.9), // 26
114+
"787", // 27
115+
"references", // 28
116+
new Date(), // 29
117+
AnalyzerIdentity.INTERNAL_ANALYZER, // 30
118+
new Date(), // 31
119+
null, // 32
120+
null, // 33
121+
AnalysisState.NOT_AFFECTED, // 34
122+
true // 35
123+
);
82124

83125
var alias = new VulnerabilityAlias();
84126
alias.setCveId("someCveId");

src/test/java/org/dependencytrack/model/FindingTest.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,30 @@ class FindingTest extends PersistenceCapableTest {
3636
private final Date attributedOn = new Date();
3737
private final Finding finding = new Finding(projectUuid, "component-uuid", "component-name", "component-group",
3838
"component-version", "Required","component-purl", "component-cpe", "vuln-uuid", "vuln-source", "vuln-vulnId", "vuln-title",
39-
"vuln-subtitle", "vuln-description", "vuln-recommendation", Severity.HIGH, BigDecimal.valueOf(7.2), BigDecimal.valueOf(8.4),
40-
BigDecimal.valueOf(9.2), BigDecimal.valueOf(1.25), BigDecimal.valueOf(1.75), BigDecimal.valueOf(1.3),
41-
"0.5", "0.9", null, AnalyzerIdentity.INTERNAL_ANALYZER, attributedOn, null, null, AnalysisState.NOT_AFFECTED, true);
39+
"vuln-subtitle", "vuln-description", "vuln-recommendation",
40+
Severity.HIGH, // 14
41+
BigDecimal.valueOf(7.2), // 15
42+
"CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P", // 16
43+
BigDecimal.valueOf(8.4), // 17
44+
"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", // 18
45+
BigDecimal.valueOf(9.2), // 19
46+
"CVSS:4.0/...", // 20
47+
BigDecimal.valueOf(1.25), // 21
48+
BigDecimal.valueOf(1.75), // 22
49+
BigDecimal.valueOf(1.3), // 23
50+
"OWASP_VECTOR", // 24
51+
BigDecimal.valueOf(0.5), // 25
52+
BigDecimal.valueOf(0.9), // 26
53+
"787,79", // 27
54+
"vuln-references", // 28
55+
attributedOn, // 29
56+
AnalyzerIdentity.INTERNAL_ANALYZER, // 30
57+
attributedOn, // 31
58+
null, // 32
59+
null, // 33
60+
AnalysisState.NOT_AFFECTED, // 34
61+
true // 35
62+
);
4263

4364
@Test
4465
void testComponent() {
@@ -61,13 +82,19 @@ void testVulnerability() {
6182
//Assertions.assertEquals("vuln-description", map.get("description"));
6283
//Assertions.assertEquals("vuln-recommendation", map.get("recommendation"));
6384
Assertions.assertEquals(BigDecimal.valueOf(7.2), map.get("cvssV2BaseScore"));
85+
Assertions.assertEquals("CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P", map.get("cvssV2Vector"));
6486
Assertions.assertEquals(BigDecimal.valueOf(8.4), map.get("cvssV3BaseScore"));
87+
Assertions.assertEquals("CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", map.get("cvssV3Vector"));
6588
Assertions.assertEquals(BigDecimal.valueOf(9.2), map.get("cvssV4Score"));
89+
Assertions.assertEquals("CVSS:4.0/...", map.get("cvssV4Vector"));
6690
Assertions.assertEquals(BigDecimal.valueOf(1.25), map.get("owaspLikelihoodScore"));
6791
Assertions.assertEquals(BigDecimal.valueOf(1.75), map.get("owaspTechnicalImpactScore"));
6892
Assertions.assertEquals(BigDecimal.valueOf(1.3), map.get("owaspBusinessImpactScore"));
93+
Assertions.assertEquals("OWASP_VECTOR", map.get("owaspRRVector"));
6994
Assertions.assertEquals(Severity.HIGH.name(), map.get("severity"));
7095
Assertions.assertEquals(1, map.get("severityRank"));
96+
Assertions.assertEquals("vuln-references", map.get("references"));
97+
Assertions.assertEquals(attributedOn, map.get("published"));
7198
}
7299

73100
@Test
@@ -103,5 +130,4 @@ void testGetCwesWhenInputIsEmpty() {
103130
void testGetCwesWhenInputIsNull() {
104131
assertThat(Finding.getCwes(null)).isNull();
105132
}
106-
107133
}

0 commit comments

Comments
 (0)