diff --git a/core/pom.xml b/core/pom.xml index eb3f6b21b64..fe5a4e7a4e7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -213,8 +213,8 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. test - commons-collections - commons-collections + org.apache.commons + commons-collections4 org.apache.commons diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzer.java index 27f90409b69..7065429a9ea 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzer.java @@ -21,6 +21,8 @@ import com.github.packageurl.PackageURL; import com.github.packageurl.PackageURL.StandardTypes; import com.github.packageurl.PackageURLBuilder; +import com.vdurmont.semver4j.Semver; +import com.vdurmont.semver4j.Semver.SemverType; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nodeaudit.Advisory; import org.owasp.dependencycheck.data.nodeaudit.NodeAuditSearch; @@ -38,6 +40,7 @@ import java.io.File; import java.io.IOException; import java.net.MalformedURLException; +import java.util.Collection; import java.util.List; import java.util.Map; import javax.annotation.concurrent.ThreadSafe; @@ -48,6 +51,7 @@ import javax.json.JsonString; import javax.json.JsonValue; import javax.json.JsonValue.ValueType; +import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.lang3.StringUtils; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException; @@ -440,7 +444,7 @@ protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationExcep * @throws CpeValidationException thrown when a CPE cannot be created */ protected void processResults(final List advisories, Engine engine, - Dependency dependency, Map dependencyMap) + Dependency dependency, MultiValuedMap dependencyMap) throws CpeValidationException { for (Advisory advisory : advisories) { //Create a new vulnerability out of the advisory returned by nsp. @@ -464,9 +468,8 @@ protected void processResults(final List advisories, Engine engine, String version = advisory.getVersion(); if (version == null && dependencyMap.containsKey(advisory.getModuleName())) { - version = dependencyMap.get(advisory.getModuleName()); + version = determineVersionFromMap(advisory.getVulnerableVersions(), dependencyMap.get(advisory.getModuleName())); } - final Dependency existing = findDependency(engine, advisory.getModuleName(), version); if (existing == null) { final Dependency nodeModule = createDependency(dependency, advisory.getModuleName(), version, "transitive"); @@ -509,4 +512,17 @@ protected void replaceOrAddVulnerability(Dependency dependency, Vulnerability vu protected NodeAuditSearch getSearcher() { return searcher; } + + public String determineVersionFromMap(String versionRange, Collection availableVersions) { + if (availableVersions.size()==1) { + return availableVersions.iterator().next(); + } + for (String v : availableVersions){ + Semver version = new Semver(v, SemverType.NPM); + if (version.satisfies(versionRange)) { + return v; + } + } + return availableVersions.iterator().next(); + } } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java index 90c7d139abe..ee33b8f3d64 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java @@ -30,15 +30,15 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.concurrent.ThreadSafe; import javax.json.Json; import javax.json.JsonException; import javax.json.JsonObject; import javax.json.JsonReader; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.owasp.dependencycheck.analyzer.exception.SearchException; import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException; import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem; @@ -140,7 +140,8 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An } final File packageJson = new File(packageLock.getParentFile(), "package.json"); final List advisories; - final Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); + //final Map dependencyMap = new HashMap<>(); if (packageJson.isFile()) { advisories = analyzePackage(packageLock, packageJson, dependency, dependencyMap); } else { @@ -170,7 +171,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An * submitting the npm audit API payload */ private List analyzePackage(final File lockFile, final File packageFile, - Dependency dependency, Map dependencyMap) + Dependency dependency, MultiValuedMap dependencyMap) throws AnalysisException { try { final JsonReader packageReader = Json.createReader(FileUtils.openInputStream(packageFile)); @@ -227,7 +228,7 @@ private List analyzePackage(final File lockFile, final File packageFil * @throws AnalysisException thrown when there is an error creating or * submitting the npm audit API payload */ - private List legacyAnalysis(final File file, Dependency dependency, Map dependencyMap) + private List legacyAnalysis(final File file, Dependency dependency, MultiValuedMap dependencyMap) throws AnalysisException { try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) { diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java index 27b652d0904..712f8df116a 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java @@ -43,9 +43,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; @ThreadSafe public class PnpmAuditAnalyzer extends AbstractNpmAnalyzer { @@ -89,7 +89,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An return; } final List advisories; - final Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); advisories = analyzePackage(packageLock, dependency); try { processResults(advisories, engine, dependency, dependencyMap); diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java index 85ddac2be76..1ebf482cec1 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java @@ -23,15 +23,15 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.concurrent.ThreadSafe; import javax.json.Json; import javax.json.JsonException; import javax.json.JsonObject; import javax.json.JsonReader; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -102,7 +102,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An } final File packageJson = new File(packageLock.getParentFile(), "package.json"); final List advisories; - final Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); advisories = analyzePackage(packageLock, packageJson, dependency, dependencyMap); try { processResults(advisories, engine, dependency, dependencyMap); @@ -276,7 +276,7 @@ private JsonObject fetchYarnAuditJson(Dependency dependency, boolean skipDevDepe * submitting the npm audit API payload */ private List analyzePackage(final File lockFile, final File packageFile, - Dependency dependency, Map dependencyMap) + Dependency dependency, MultiValuedMap dependencyMap) throws AnalysisException { try { final Boolean skipDevDependencies = getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_SKIPDEV, false); diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilder.java b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilder.java index 7bc7ca90dec..eb12e5502a9 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilder.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilder.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.data.nodeaudit; +import com.esotericsoftware.minlog.Log; import org.owasp.dependencycheck.analyzer.NodePackageAnalyzer; import java.util.Map; @@ -28,6 +29,7 @@ import javax.json.JsonString; import javax.json.JsonValue; import javax.annotation.concurrent.ThreadSafe; +import org.apache.commons.collections4.MultiValuedMap; /** * Class used to create the payload to submit to the NPM Audit API service. @@ -56,7 +58,7 @@ private NpmPayloadBuilder() { * @return the npm audit API payload */ public static JsonObject build(JsonObject lockJson, JsonObject packageJson, - Map dependencyMap, boolean skipDevDependencies) { + MultiValuedMap dependencyMap, boolean skipDevDependencies) { final JsonObjectBuilder payloadBuilder = Json.createObjectBuilder(); addProjectInfo(packageJson, payloadBuilder); @@ -132,7 +134,7 @@ public static JsonObject build(JsonObject lockJson, JsonObject packageJson, * populated while building the payload * @return the JSON payload for NPN Audit */ - public static JsonObject build(JsonObject packageJson, Map dependencyMap) { + public static JsonObject build(JsonObject packageJson, MultiValuedMap dependencyMap) { final JsonObjectBuilder payloadBuilder = Json.createObjectBuilder(); addProjectInfo(packageJson, payloadBuilder); @@ -215,7 +217,7 @@ private static void addConstantElements(final JsonObjectBuilder payloadBuilder) * @param dependencyMap the collection of child dependencies * @return the dependencies structure needed for the npm audit API payload */ - private static JsonObject buildDependencies(JsonObject dep, Map dependencyMap) { + private static JsonObject buildDependencies(JsonObject dep, MultiValuedMap dependencyMap) { final JsonObjectBuilder depBuilder = Json.createObjectBuilder(); depBuilder.add("version", dep.getString("version")); diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzerIT.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzerIT.java new file mode 100644 index 00000000000..93f42c26cca --- /dev/null +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractNpmAnalyzerIT.java @@ -0,0 +1,98 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2021 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.json.JsonArray; +import javax.json.JsonObject; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.data.nodeaudit.Advisory; +import org.owasp.dependencycheck.data.nodeaudit.NodeAuditSearch; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Vulnerability; + +/** + * + * @author jeremy long + */ +public class AbstractNpmAnalyzerIT { + + + /** + * Test of determineVersionFromMap method, of class AbstractNpmAnalyzer. + */ + @Test + public void testDetermineVersionFromMap() { + String versionRange = ">2.1.1 <5.0.1"; + Collection availableVersions = new ArrayList<>(); + availableVersions.add("2.0.2"); + availableVersions.add("5.0.2"); + availableVersions.add("10.1.0"); + availableVersions.add("8.1.0"); + availableVersions.add("5.1.0"); + availableVersions.add("7.1.0"); + availableVersions.add("3.0.0"); + availableVersions.add("2.0.0"); + AbstractNpmAnalyzer instance = new AbstractNpmAnalyzerImpl(); + String expResult = "3.0.0"; + String result = instance.determineVersionFromMap(versionRange, availableVersions); + assertEquals(expResult, result); + } + + public class AbstractNpmAnalyzerImpl extends AbstractNpmAnalyzer { + + @Override + protected FileFilter getFileFilter() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected String getAnalyzerEnabledSettingKey() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getName() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AnalysisPhase getAnalysisPhase() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } + +} diff --git a/core/src/test/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilderTest.java b/core/src/test/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilderTest.java index d8a7b166d55..75e159ab2b8 100644 --- a/core/src/test/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilderTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilderTest.java @@ -28,6 +28,8 @@ import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonReader; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.owasp.dependencycheck.BaseTest; @@ -53,7 +55,7 @@ public void testSanitizer() { ); JsonObject packageJson = builder.build(); - Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); JsonObject sanitized = NpmPayloadBuilder.build(packageJson, dependencyMap); Assert.assertTrue(sanitized.containsKey("name")); @@ -98,7 +100,7 @@ public void testSkippedDependencies() { ); JsonObject packageJson = builder.build(); - Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); JsonObject sanitized = NpmPayloadBuilder.build(packageJson, dependencyMap); Assert.assertTrue(sanitized.containsKey("name")); @@ -121,7 +123,7 @@ public void testSkippedDependencies() { @Test public void testSanitizePackage() { InputStream in = BaseTest.getResourceAsStream(this, "nodeaudit/package-lock.json"); - Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); try (JsonReader jsonReader = Json.createReader(in)) { JsonObject packageJson = jsonReader.readObject(); JsonObject sanitized = NpmPayloadBuilder.build(packageJson, dependencyMap); @@ -144,7 +146,7 @@ public void testSanitizePackage() { public void testPayloadWithLockAndPackage() { InputStream lock = BaseTest.getResourceAsStream(this, "nodeaudit/package-lock.json"); InputStream json = BaseTest.getResourceAsStream(this, "nodeaudit/package.json"); - Map dependencyMap = new HashMap<>(); + final MultiValuedMap dependencyMap = new HashSetValuedHashMap<>(); try (JsonReader jsonReader = Json.createReader(json); JsonReader lockReader = Json.createReader(lock)) { JsonObject packageJson = jsonReader.readObject(); JsonObject lockJson = lockReader.readObject(); @@ -162,8 +164,6 @@ public void testPayloadWithLockAndPackage() { Assert.assertFalse(sanitized.containsKey("lockfileVersion")); Assert.assertFalse(sanitized.containsKey("random")); - - Assert.assertTrue(sanitized.containsKey("name")); Assert.assertTrue(sanitized.containsKey("version")); Assert.assertTrue(sanitized.containsKey("dependencies")); diff --git a/pom.xml b/pom.xml index cf7bc450acc..47ee8f455e4 100644 --- a/pom.xml +++ b/pom.xml @@ -1167,9 +1167,9 @@ Copyright (c) 2012 - Jeremy Long ${maven-reporting-api.version} - commons-collections - commons-collections - ${commons-collections.version} + org.apache.commons + commons-collections4 + 4.4 org.apache.velocity