Skip to content

Commit 5dcdd7f

Browse files
committed
normalize VULNERABLESOFTWARE table, make CPE matching case-insensitive
Signed-off-by: Steffen Ohrendorf <[email protected]>
1 parent 5aa6230 commit 5dcdd7f

File tree

9 files changed

+72
-16
lines changed

9 files changed

+72
-16
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,23 +262,23 @@ public String getPart() {
262262
}
263263

264264
public void setPart(String part) {
265-
this.part = part;
265+
this.part = part == null ? null : part.toLowerCase();
266266
}
267267

268268
public String getVendor() {
269269
return vendor;
270270
}
271271

272272
public void setVendor(String vendor) {
273-
this.vendor = vendor;
273+
this.vendor = vendor == null ? null : vendor.toLowerCase();
274274
}
275275

276276
public String getProduct() {
277277
return product;
278278
}
279279

280280
public void setProduct(String product) {
281-
this.product = product;
281+
this.product = product == null ? null : product.toLowerCase();
282282
}
283283

284284
public String getVersion() {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ public List<VulnerableSoftware> getAllVulnerableSoftware(
276276
// SELECT "ID" FROM "VULNERABLESOFTWARE" WHERE "PART" = 'foo' ...
277277

278278
if (cpePart != null && cpeVendor != null && cpeProduct != null) {
279-
final List<CpeFilterCondition> partConditions = buildCpeFilterConditions("\"PART\"", cpePart);
280-
final List<CpeFilterCondition> vendorConditions = buildCpeFilterConditions("\"VENDOR\"", cpeVendor);
281-
final List<CpeFilterCondition> productConditions = buildCpeFilterConditions("\"PRODUCT\"", cpeProduct);
279+
final List<CpeFilterCondition> partConditions = buildCpeFilterConditions("\"PART\"", cpePart.toLowerCase());
280+
final List<CpeFilterCondition> vendorConditions = buildCpeFilterConditions("\"VENDOR\"", cpeVendor.toLowerCase());
281+
final List<CpeFilterCondition> productConditions = buildCpeFilterConditions("\"PRODUCT\"", cpeProduct.toLowerCase());
282282

283283
for (final CpeFilterCondition partCondition : partConditions) {
284284
for (final CpeFilterCondition vendorCondition : vendorConditions) {

src/main/java/org/dependencytrack/search/FuzzyVulnerableSoftwareSearchManager.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import us.springett.parsers.cpe.values.Part;
4646

4747
import java.io.IOException;
48-
4948
import java.util.ArrayList;
5049
import java.util.Collections;
5150
import java.util.HashMap;
@@ -262,7 +261,7 @@ public static String getLuceneCpeRegexp(String cpeString) {
262261

263262
private static String getComponentRegex(String component) {
264263
if (component != null) {
265-
return component.replace("*", ".*");
264+
return component.replace("*", ".*").toLowerCase();
266265
} else {
267266
return ".*";
268267
}
@@ -274,7 +273,7 @@ private static String escapeLuceneQuery(final String input) {
274273
} else if (input.equals(".*")) {
275274
return input;
276275
}
277-
return QueryParser.escape(input);
276+
return QueryParser.escape(input.toLowerCase());
278277
}
279278

280279
}

src/main/java/org/dependencytrack/search/VulnerableSoftwareIndexer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ private static List<VulnerableSoftwareDocument> fetchNext(final QueryManager qm,
126126
private Document convertToDocument(final VulnerableSoftwareDocument vs) {
127127
final var doc = new Document();
128128
addField(doc, IndexConstants.VULNERABLESOFTWARE_UUID, vs.uuid().toString(), Field.Store.YES, false);
129-
addField(doc, IndexConstants.VULNERABLESOFTWARE_CPE_22, vs.cpe22(), Field.Store.YES, false);
130-
addField(doc, IndexConstants.VULNERABLESOFTWARE_CPE_23, vs.cpe23(), Field.Store.YES, false);
129+
final var cpe22 = vs.cpe22() != null ? vs.cpe22().toLowerCase() : null;
130+
addField(doc, IndexConstants.VULNERABLESOFTWARE_CPE_22, cpe22, Field.Store.YES, false);
131+
final var cpe23 = vs.cpe23() != null ? vs.cpe23().toLowerCase() : null;
132+
addField(doc, IndexConstants.VULNERABLESOFTWARE_CPE_23, cpe23, Field.Store.YES, false);
131133
addField(doc, IndexConstants.VULNERABLESOFTWARE_VENDOR, vs.vendor(), Field.Store.YES, true);
132134
addField(doc, IndexConstants.VULNERABLESOFTWARE_PRODUCT, vs.product(), Field.Store.YES, true);
133135
addField(doc, IndexConstants.VULNERABLESOFTWARE_VERSION, vs.version(), Field.Store.YES, true);

src/main/java/org/dependencytrack/tasks/scanners/AbstractVulnerableSoftwareAnalysisTask.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,20 @@ protected void analyzeVersionRange(final QueryManager qm, final List<VulnerableS
6565
}
6666
}
6767
}
68+
69+
private static String toLowerCaseNullable(final String string) {
70+
return string == null ? null : string.toLowerCase();
71+
}
6872

6973
private Boolean maybeMatchCpe(final VulnerableSoftware vs, final Cpe targetCpe, final String targetVersion) {
7074
if (targetCpe == null || vs.getCpe23() == null) {
7175
return null;
7276
}
7377

7478
final List<Relation> relations = List.of(
75-
Cpe.compareAttribute(vs.getPart(), targetCpe.getPart().getAbbreviation()),
76-
Cpe.compareAttribute(vs.getVendor(), targetCpe.getVendor()),
77-
Cpe.compareAttribute(vs.getProduct(), targetCpe.getProduct()),
79+
Cpe.compareAttribute(vs.getPart(), toLowerCaseNullable(targetCpe.getPart().getAbbreviation())),
80+
Cpe.compareAttribute(vs.getVendor(), toLowerCaseNullable(targetCpe.getVendor())),
81+
Cpe.compareAttribute(vs.getProduct(), toLowerCaseNullable(targetCpe.getProduct())),
7882
Cpe.compareAttribute(vs.getVersion(), targetVersion),
7983
Cpe.compareAttribute(vs.getUpdate(), targetCpe.getUpdate()),
8084
Cpe.compareAttribute(vs.getEdition(), targetCpe.getEdition()),

src/main/java/org/dependencytrack/upgrade/UpgradeItems.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class UpgradeItems {
4545
UPGRADE_ITEMS.add(org.dependencytrack.upgrade.v4130.v4130Updater.class);
4646
UPGRADE_ITEMS.add(org.dependencytrack.upgrade.v4130.v4130_1Updater.class);
4747
UPGRADE_ITEMS.add(org.dependencytrack.upgrade.v4131.v4131Updater.class);
48+
UPGRADE_ITEMS.add(org.dependencytrack.upgrade.v4135.v4135Updater.class);
4849
}
4950

5051
static List<Class<? extends UpgradeItem>> getUpgradeItems() {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.upgrade.v4135;
20+
21+
import alpine.common.logging.Logger;
22+
import alpine.persistence.AlpineQueryManager;
23+
import alpine.server.upgrade.AbstractUpgradeItem;
24+
25+
import java.sql.Connection;
26+
import java.sql.SQLException;
27+
import java.sql.Statement;
28+
29+
public class v4135Updater extends AbstractUpgradeItem {
30+
private static final Logger LOGGER = Logger.getLogger(v4135Updater.class);
31+
32+
@Override
33+
public String getSchemaVersion() {
34+
return "4.13.5";
35+
}
36+
37+
@Override
38+
public void executeUpgrade(final AlpineQueryManager qm, final Connection connection) throws Exception {
39+
normalizeCpeData(connection);
40+
}
41+
42+
private void normalizeCpeData(final Connection connection) throws SQLException {
43+
try (final Statement statement = connection.createStatement()) {
44+
LOGGER.info("Normalizing \"VULNERABLESOFTWARE\" CPE columns");
45+
statement.execute(/* language=SQL */ """
46+
UPDATE TABLE "VULNERABLESOFTWARE" SET "PART" = LOWER("PART"), "VENDOR" = LOWER("VENDOR"), "PRODUCT" = LOWER("PRODUCT")
47+
""");
48+
}
49+
}
50+
}

src/test/java/org/dependencytrack/search/FuzzyVulnerableSoftwareSearchManagerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void getLuceneCpeRegexp() throws CpeValidationException, CpeEncodingException {
101101
us.springett.parsers.cpe.Cpe os = new us.springett.parsers.cpe.Cpe( Part.OPERATING_SYSTEM, "vendor", "product", "1\\.0", "2", "33","en", "inside", "Vista", "x86", "other");
102102

103103
Assertions.assertEquals("cpe23:/cpe\\:2\\.3\\:a\\:.*\\:.*\\:.*\\:.*\\:.*\\:.*\\:.*\\:.*\\:.*\\:.*/", FuzzyVulnerableSoftwareSearchManager.getLuceneCpeRegexp("cpe:2.3:a:*:*:*:*:*:*:*:*:*:*"));
104-
Assertions.assertEquals("cpe23:/cpe\\:2\\.3\\:o\\:vendor\\:product\\:1.0\\:2\\:33\\:en\\:inside\\:Vista\\:x86\\:other/", FuzzyVulnerableSoftwareSearchManager.getLuceneCpeRegexp(os.toCpe23FS()));
104+
Assertions.assertEquals("cpe23:/cpe\\:2\\.3\\:o\\:vendor\\:product\\:1.0\\:2\\:33\\:en\\:inside\\:vista\\:x86\\:other/", FuzzyVulnerableSoftwareSearchManager.getLuceneCpeRegexp(os.toCpe23FS()));
105105
Assertions.assertEquals("cpe22:/cpe\\:\\/o\\:vendor\\:product\\:1.0\\:2\\:33\\:en/", FuzzyVulnerableSoftwareSearchManager.getLuceneCpeRegexp(os.toCpe22Uri()));
106106
}
107107

src/test/java/org/dependencytrack/tasks/scanners/InternalAnalysisTaskCpeMatchingTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ public static Collection<Arguments> parameters() {
353353
// Note: CPEs with uppercase "part" are considered invalid by the cpe-parser library.
354354
// TODO: This should match, but can't currently support this as it would require an function index on UPPER("PART"),
355355
// UPPER("VENDOR"), and UPPER("PRODUCT"), which we cannot add through JDO annotations.
356-
Arguments.of("cpe:2.3:o:lInUx:lInUx_KeRnEl:5.15.37:*:*:*:*:*:*:*", WITHOUT_RANGE, DOES_NOT_MATCH, "cpe:2.3:o:LiNuX:LiNuX_kErNeL:5.15.37:*:*:*:*:*:*:*"),
356+
Arguments.of("cpe:2.3:o:lInUx:lInUx_KeRnEl:5.15.37:*:*:*:*:*:*:*", WITHOUT_RANGE, MATCHES, "cpe:2.3:o:LiNuX:LiNuX_kErNeL:5.15.37:*:*:*:*:*:*:*"),
357357
// ---
358358
// Issue: https://github.com/DependencyTrack/dependency-track/issues/2988
359359
// Scenario: "other" attribute of source is NA, "other" attribute of target is ANY -> SUBSET.

0 commit comments

Comments
 (0)