Skip to content

Commit 3bff5af

Browse files
authored
fix: Update NVD CPE search URLs to match new search interface (#7970)
Signed-off-by: Chad Wilson <[email protected]>
1 parent eaa76f9 commit 3bff5af

File tree

7 files changed

+125
-47
lines changed

7 files changed

+125
-47
lines changed

core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import java.io.BufferedReader;
2121
import java.io.IOException;
2222
import java.io.InputStreamReader;
23-
import java.io.UnsupportedEncodingException;
24-
import java.net.URLEncoder;
2523
import java.nio.charset.StandardCharsets;
2624
import java.util.ArrayList;
2725
import java.util.Arrays;
@@ -111,24 +109,7 @@ public class CPEAnalyzer extends AbstractAnalyzer {
111109
* alpha characters.
112110
*/
113111
private static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*";
114-
/**
115-
* UTF-8 character set name.
116-
*/
117-
private static final String UTF8 = StandardCharsets.UTF_8.name();
118-
/**
119-
* The URL to search the NVD CVE data at NIST. This is used by calling:
120-
* <pre>String.format(NVD_SEARCH_URL, vendor, product, version);</pre>
121-
*/
122-
public static final String NVD_SEARCH_URL = "https://nvd.nist.gov/vuln/search/results?form_type=Advanced&"
123-
+ "results_type=overview&search_type=all&cpe_vendor=cpe%%3A%%2F%%3A%1$s&cpe_product=cpe%%3A%%2F%%3A%1$s%%3A%2$s&"
124-
+ "cpe_version=cpe%%3A%%2F%%3A%1$s%%3A%2$s%%3A%3$s";
125112

126-
/**
127-
* The URL to search the NVD CVE data at NIST. This is used by calling:
128-
* <pre>String.format(NVD_SEARCH_URL, vendor, product);</pre>
129-
*/
130-
public static final String NVD_SEARCH_BROAD_URL = "https://nvd.nist.gov/vuln/search/results?form_type=Advanced&"
131-
+ "results_type=overview&search_type=all&cpe_vendor=cpe%%3A%%2F%%3A%1$s&cpe_product=cpe%%3A%%2F%%3A%1$s%%3A%2$s";
132113
/**
133114
* The CPE in memory index.
134115
*/
@@ -806,12 +787,11 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An
806787
* analysis
807788
* @return <code>true</code> if an identifier was added to the dependency;
808789
* otherwise <code>false</code>
809-
* @throws UnsupportedEncodingException is thrown if UTF-8 is not supported
810790
* @throws AnalysisException thrown if the suppression rules failed
811791
*/
812792
@SuppressWarnings("StringSplitter")
813793
protected boolean determineIdentifiers(Dependency dependency, String vendor, String product,
814-
Confidence currentConfidence) throws UnsupportedEncodingException, AnalysisException {
794+
Confidence currentConfidence) throws AnalysisException {
815795

816796
final CpeBuilder cpeBuilder = new CpeBuilder();
817797

@@ -864,8 +844,7 @@ protected boolean determineIdentifiers(Dependency dependency, String vendor, Str
864844
dbVerUpdate = DependencyVersionUtil.parseVersion(vs.getVersion() + '.' + vs.getUpdate(), true);
865845
}
866846
if (dbVer == null) { //special case, no version specified - everything is vulnerable
867-
final String url = String.format(NVD_SEARCH_BROAD_URL, URLEncoder.encode(vs.getVendor(), UTF8),
868-
URLEncoder.encode(vs.getProduct(), UTF8));
847+
final String url = CpeIdentifier.nvdProductSearchUrlFor(vs);
869848
final IdentifierMatch match = new IdentifierMatch(vs, url, IdentifierConfidence.BROAD_MATCH, conf);
870849
collected.add(match);
871850
} else if (evVer.equals(dbVer)) {
@@ -875,8 +854,7 @@ protected boolean determineIdentifiers(Dependency dependency, String vendor, Str
875854
bestGuessConf = conf;
876855
bestGuess = dbVer;
877856
bestGuessUpdate = evBaseVerUpdate;
878-
bestGuessURL = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getVendor(), UTF8),
879-
URLEncoder.encode(vs.getProduct(), UTF8), URLEncoder.encode(vs.getVersion(), UTF8));
857+
bestGuessURL = CpeIdentifier.nvdSearchUrlFor(vs);
880858
} else if (dbVerUpdate != null && evVer.getVersionParts().size() <= dbVerUpdate.getVersionParts().size()
881859
&& evVer.matchesAtLeastThreeLevels(dbVerUpdate)) {
882860
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
@@ -983,14 +961,12 @@ protected boolean determineIdentifiers(Dependency dependency, String vendor, Str
983961
* @param updateVersion the update version
984962
* @param conf the current confidence
985963
* @param collected a reference to the collected identifiers
986-
* @throws UnsupportedEncodingException thrown if UTF-8 is not supported
987964
*/
988965
private void addExactMatch(Cpe vs, String updateVersion, Confidence conf,
989-
final Set<IdentifierMatch> collected) throws UnsupportedEncodingException {
966+
final Set<IdentifierMatch> collected) {
990967

991968
final CpeBuilder cpeBuilder = new CpeBuilder();
992-
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getVendor(), UTF8),
993-
URLEncoder.encode(vs.getProduct(), UTF8), URLEncoder.encode(vs.getVersion(), UTF8));
969+
final String url = CpeIdentifier.nvdSearchUrlFor(vs);
994970
Cpe useCpe;
995971
if (updateVersion != null && "*".equals(vs.getUpdate())) {
996972
try {
@@ -1022,13 +998,11 @@ private void addExactMatch(Cpe vs, String updateVersion, Confidence conf,
1022998
* @param collected a reference to the identifiers matched
1023999
* @throws AnalysisException thrown if aliens attacked and valid input could
10241000
* not be used to construct a CPE
1025-
* @throws UnsupportedEncodingException thrown if run on a system that
1026-
* doesn't support UTF-8
10271001
*/
10281002
private void considerDependencyVersion(Dependency dependency,
10291003
String vendor, String product, Confidence confidence,
10301004
final Set<IdentifierMatch> collected)
1031-
throws AnalysisException, UnsupportedEncodingException {
1005+
throws AnalysisException {
10321006

10331007
if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) {
10341008
final CpeBuilder cpeBuilder = new CpeBuilder();
@@ -1051,8 +1025,7 @@ private void considerDependencyVersion(Dependency dependency,
10511025
addVersionAndUpdate(depVersion, cpeBuilder);
10521026
try {
10531027
final Cpe depCpe = cpeBuilder.build();
1054-
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vendor, UTF8),
1055-
URLEncoder.encode(product, UTF8), URLEncoder.encode(depCpe.getVersion(), UTF8));
1028+
final String url = CpeIdentifier.nvdSearchUrlFor(vendor, product, depCpe.getVersion());
10561029
final IdentifierMatch match = new IdentifierMatch(depCpe, url, IdentifierConfidence.EXACT_MATCH, confidence);
10571030
collected.add(match);
10581031
} catch (CpeValidationException ex) {

core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.commons.lang3.builder.HashCodeBuilder;
2929
import org.jetbrains.annotations.NotNull;
3030
import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException;
31+
import org.owasp.dependencycheck.dependency.naming.CpeIdentifier;
3132
import org.owasp.dependencycheck.utils.DependencyVersion;
3233
import us.springett.parsers.cpe.Cpe;
3334
import us.springett.parsers.cpe.ICpe;
@@ -517,4 +518,8 @@ public String toString() {
517518
}
518519
return sb.toString();
519520
}
521+
522+
public String toNvdSearchUrl() {
523+
return CpeIdentifier.nvdSearchUrlFor(this);
524+
}
520525
}

core/src/main/java/org/owasp/dependencycheck/dependency/naming/CpeIdentifier.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
import org.apache.commons.lang3.builder.CompareToBuilder;
2121
import org.apache.commons.lang3.builder.EqualsBuilder;
2222
import org.apache.commons.lang3.builder.HashCodeBuilder;
23+
import org.apache.hc.core5.net.PercentCodec;
2324
import org.jetbrains.annotations.NotNull;
2425
import org.owasp.dependencycheck.dependency.Confidence;
2526
import us.springett.parsers.cpe.Cpe;
2627
import us.springett.parsers.cpe.CpeBuilder;
2728
import us.springett.parsers.cpe.exceptions.CpeValidationException;
2829
import us.springett.parsers.cpe.values.Part;
2930

31+
import static java.nio.charset.StandardCharsets.UTF_8;
32+
3033
/**
3134
* A CPE Identifier for a dependency object.
3235
*
@@ -203,4 +206,41 @@ public int compareTo(@NotNull Identifier o) {
203206
.append(this.confidence, o.getConfidence())
204207
.toComparison();
205208
}
209+
210+
/**
211+
* Produces an NVD search URL for a given CPE to find all applicable vulnerabilities, including all populated parts
212+
* of the given CPE.
213+
* <p/>
214+
* The opened link should be sorted in descending order (sortDirection=2) by publish date (sortOrder=3).
215+
*/
216+
public static String nvdSearchUrlFor(Cpe cpe) {
217+
// Use PercentCodec to force `*` to be encoded for CPE strings inside the URL. Technically '*' is not a reserved
218+
// character in the fragment of URLs, but not all browsers handle this consistently, so better to encode aggressively.
219+
// URlEncoder does not distinguish between parts of URLs appropriately, as well as not forcing encoding of these.
220+
return String.format("https://nvd.nist.gov/vuln/search#/nvd/home?sortOrder=3&sortDirection=2&cpeFilterMode=applicability&resultType=records&cpeName=%s",
221+
PercentCodec.encode(cpe.toCpe23FS(), UTF_8));
222+
}
223+
224+
/**
225+
* Produces an NVD search URL for a given application vendor/product/version combination to find all applicable vulnerabilities.
226+
* <p/>
227+
* The opened link should be sorted in descending order (sortDirection=2) by publish date (sortOrder=3).
228+
*/
229+
public static String nvdSearchUrlFor(String vendor, String product, String version) throws CpeValidationException {
230+
return nvdSearchUrlFor(new CpeBuilder().part(Part.APPLICATION).vendor(vendor).product(product).version(version).build());
231+
}
232+
233+
/**
234+
* Produces an NVD search URL for a given CPE to find all applicable vulnerabilities, including only the part, vendor,
235+
* and product of the given CPE (if populated). Discards all other parts/discriminators of the CPE in the generated search.
236+
* <p/>
237+
* The opened link should be sorted in descending order (sortDirection=2) by publish date (sortOrder=3).
238+
*/
239+
public static String nvdProductSearchUrlFor(Cpe cpe) {
240+
try {
241+
return nvdSearchUrlFor(new CpeBuilder().part(cpe.getPart()).vendor(cpe.getVendor()).product(cpe.getProduct()).build());
242+
} catch (CpeValidationException e) {
243+
throw new RuntimeException(e);
244+
}
245+
}
206246
}

core/src/main/resources/templates/htmlReport.vsl

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,6 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
945945
#end
946946
#set($cnt=$cnt+1)
947947
<h4 id="header$cnt" class="subsectionheader white">Identifiers</h4>
948-
##:&nbsp;<a href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($cpevalue)" target="_blank">$enc.html($cpevalue)</a></h4>
949948
<div id="content$cnt" class="subsectioncontent standardsubsection">
950949
#set($supressPkgUrl='')
951950
#if ($dependency.getSoftwareIdentifiers().size()==0 && $dependency.getVulnerableSoftwareIdentifiers().size()==0)
@@ -1060,14 +1059,14 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
10601059
#if ($vuln.getSource().name().equals("NVD") && $vuln.matchedVulnerableSoftware)
10611060
#if ($vuln.getVulnerableSoftware().size()<2)
10621061
<p>Vulnerable Software &amp; Versions:<ul>
1063-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
1062+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
10641063
</ul></p>
10651064
#else
10661065
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" class="versionToggle" data-toggle=".vs$vsctr">show all</a>)<ul>
1067-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
1066+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
10681067
<li class="vs$vsctr">...</li>
10691068
#foreach($vs in $vuln.getVulnerableSoftware(true))
1070-
<li class="vs$vsctr hidden"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vs.toCpe22Uri())">$enc.html($vs.toString())</a></li>
1069+
<li class="vs$vsctr hidden"><a target="_blank" href="$enc.html($vs.toNvdSearchUrl())">$enc.html($vs.toString())</a></li>
10711070
#end
10721071
</ul></p>
10731072
#end
@@ -1174,7 +1173,6 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
11741173
#end
11751174
#set($cnt=$cnt+1)
11761175
<h4 id="header$cnt" class="subsectionheader white">Suppressed Identifiers</h4>
1177-
##:&nbsp;<a href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($cpevalue)" target="_blank">$enc.html($cpevalue)</a></h4>
11781176
<div id="content$cnt" class="subsectioncontent standardsubsection">
11791177
#if ($dependency.getSuppressedIdentifiers().size()==0)
11801178
<ul><li><b>None</b></li></ul>
@@ -1267,14 +1265,14 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
12671265
#if ($vuln.getSource().name().equals("NVD") && $vuln.matchedVulnerableSoftware)
12681266
#if ($vuln.getVulnerableSoftware().size()<2)
12691267
<p>Vulnerable Software &amp; Versions:<ul>
1270-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
1268+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
12711269
</ul></p>
12721270
#else
12731271
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" class="versionToggle" data-toggle=".vs$vsctr">show all</a>)<ul>
1274-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
1272+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
12751273
<li class="vs$vsctr">...</li>
12761274
#foreach($vs in $vuln.getVulnerableSoftware(true))
1277-
<li class="vs$vsctr hidden"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vs.toCpe22Uri())">$enc.html($vs.toString())</a></li>
1275+
<li class="vs$vsctr hidden"><a target="_blank" href="$enc.html($vs.toNvdSearchUrl())">$enc.html($vs.toString())</a></li>
12781276
#end
12791277
</ul></p>
12801278
#end

core/src/main/resources/templates/jenkinsReport.vsl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,6 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
670670
#end
671671
#set($cnt=$cnt+1)
672672
<h4 id="header$cnt" class="subsectionheader white">Identifiers</h4>
673-
##:&nbsp;<a href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($cpevalue)" target="_blank">$enc.html($cpevalue)</a></h4>
674673
<div id="content$cnt" class="subsectioncontent standardsubsection">
675674
#if ($dependency.getSoftwareIdentifiers().size()==0 && $dependency.getVulnerableSoftwareIdentifiers().size()==0)
676675
<ul><li><b>None</b></li></ul>
@@ -764,13 +763,13 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
764763
#if ($vuln.getSource().name().equals("NVD") && $vuln.matchedVulnerableSoftware)
765764
#if ($vuln.getVulnerableSoftware().size()<2)
766765
<p>Vulnerable Software &amp; Versions:<ul>
767-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
766+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
768767
</ul></p>
769768
#else
770769
<p>Vulnerable Software &amp; Versions:<ul>
771-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedVulnerableSoftware.toCpe22Uri())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
770+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vuln.matchedVulnerableSoftware.toString())</a></li>
772771
#foreach($vs in $vuln.getVulnerableSoftware(true))
773-
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vs.toCpe22Uri())">$enc.html($vs.toString())</a></li>
772+
<li class="vs$vsctr"><a target="_blank" href="$enc.html($vuln.matchedVulnerableSoftware.toNvdSearchUrl())">$enc.html($vs.toString())</a></li>
774773
#end
775774
</ul></p>
776775
#end

0 commit comments

Comments
 (0)