mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-03-22 17:19:30 +01:00
overhaul node package and nsp analyzer
This commit is contained in:
@@ -164,6 +164,10 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
|||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.vdurmont</groupId>
|
||||||
|
<artifactId>semver4j</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- Note, to stay compatible with Jenkins installations only JARs compiled to 1.6 can be used -->
|
<!-- Note, to stay compatible with Jenkins installations only JARs compiled to 1.6 can be used -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
|
|||||||
@@ -85,6 +85,14 @@ public abstract class AbstractAnalyzer implements Analyzer {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(Settings settings) {
|
public void initialize(Settings settings) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
final String key = getAnalyzerEnabledSettingKey();
|
||||||
|
try {
|
||||||
|
this.setEnabled(settings.getBoolean(key, true));
|
||||||
|
} catch (InvalidSettingException ex) {
|
||||||
|
final String msg = String.format("Invalid setting for property '%s'", key);
|
||||||
|
LOGGER.warn(msg);
|
||||||
|
LOGGER.debug(msg, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,15 +103,6 @@ public abstract class AbstractAnalyzer implements Analyzer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void prepare(Engine engine) throws InitializationException {
|
public final void prepare(Engine engine) throws InitializationException {
|
||||||
final String key = getAnalyzerEnabledSettingKey();
|
|
||||||
try {
|
|
||||||
this.setEnabled(settings.getBoolean(key, true));
|
|
||||||
} catch (InvalidSettingException ex) {
|
|
||||||
final String msg = String.format("Invalid setting for property '%s'", key);
|
|
||||||
LOGGER.warn(msg);
|
|
||||||
LOGGER.debug(msg, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
prepareAnalyzer(engine);
|
prepareAnalyzer(engine);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* 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) 2017 Steve Springett. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
|
import org.owasp.dependencycheck.Engine;
|
||||||
|
import org.owasp.dependencycheck.dependency.Confidence;
|
||||||
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonArray;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import javax.json.JsonObjectBuilder;
|
||||||
|
import javax.json.JsonString;
|
||||||
|
import javax.json.JsonValue;
|
||||||
|
import org.owasp.dependencycheck.dependency.EvidenceType;
|
||||||
|
import org.owasp.dependencycheck.utils.Checksum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract NPM analyzer that contains common methods for concrete
|
||||||
|
* implementations.
|
||||||
|
*
|
||||||
|
* @author Steve Springett
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
public abstract class AbstractNpmAnalyzer extends AbstractFileTypeAnalyzer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logger.
|
||||||
|
*/
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNpmAnalyzer.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A descriptor for the type of dependencies processed or added by this
|
||||||
|
* analyzer.
|
||||||
|
*/
|
||||||
|
public static final String NPM_DEPENDENCY_ECOSYSTEM = "npm";
|
||||||
|
/**
|
||||||
|
* The file name to scan.
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_JSON = "package.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the file can be analyzed by the analyzer.
|
||||||
|
*
|
||||||
|
* @param pathname the path to the file
|
||||||
|
* @return true if the file can be analyzed by the given analyzer; otherwise
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname) {
|
||||||
|
boolean accept = super.accept(pathname);
|
||||||
|
if (accept) {
|
||||||
|
try {
|
||||||
|
// Do not scan the node_modules directory
|
||||||
|
if (pathname.getCanonicalPath().contains(File.separator + "node_modules" + File.separator)) {
|
||||||
|
LOGGER.debug("Skipping analysis of node module: " + pathname.getCanonicalPath());
|
||||||
|
accept = false;
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a dependency object.
|
||||||
|
*
|
||||||
|
* @param dependency the parent dependency
|
||||||
|
* @param name the name of the dependency to create
|
||||||
|
* @param version the version of the dependency to create
|
||||||
|
* @param scope the scope of the dependency being created
|
||||||
|
* @return the generated dependency
|
||||||
|
*/
|
||||||
|
protected Dependency createDependency(Dependency dependency, String name, String version, String scope) {
|
||||||
|
final Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "?" + name), true);
|
||||||
|
nodeModule.setEcosystem(NPM_DEPENDENCY_ECOSYSTEM);
|
||||||
|
//this is virtual - the sha1 is purely for the hyperlink in the final html report
|
||||||
|
nodeModule.setSha1sum(Checksum.getSHA1Checksum(String.format("%s:%s", name, version)));
|
||||||
|
nodeModule.setMd5sum(Checksum.getMD5Checksum(String.format("%s:%s", name, version)));
|
||||||
|
nodeModule.addEvidence(EvidenceType.PRODUCT, "package.json", "name", name, Confidence.HIGHEST);
|
||||||
|
nodeModule.addEvidence(EvidenceType.VENDOR, "package.json", "name", name, Confidence.HIGH);
|
||||||
|
nodeModule.addEvidence(EvidenceType.VERSION, "package.json", "version", version, Confidence.HIGHEST);
|
||||||
|
nodeModule.addProjectReference(dependency.getName() + ": " + scope);
|
||||||
|
nodeModule.setName(name);
|
||||||
|
nodeModule.setVersion(version);
|
||||||
|
nodeModule.addIdentifier("npm", String.format("%s:%s", name, version), null, Confidence.HIGHEST);
|
||||||
|
return nodeModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a part of package.json (as defined by JsonArray) and update the
|
||||||
|
* specified dependency with relevant info.
|
||||||
|
*
|
||||||
|
* @param engine the dependency-check engine
|
||||||
|
* @param dependency the Dependency to update
|
||||||
|
* @param jsonArray the jsonArray to parse
|
||||||
|
* @param depType the dependency type
|
||||||
|
*/
|
||||||
|
protected void processPackage(Engine engine, Dependency dependency, JsonArray jsonArray, String depType) {
|
||||||
|
final JsonObjectBuilder builder = Json.createObjectBuilder();
|
||||||
|
for (JsonString str : jsonArray.getValuesAs(JsonString.class)) {
|
||||||
|
builder.add(str.toString(), "");
|
||||||
|
}
|
||||||
|
final JsonObject jsonObject = builder.build();
|
||||||
|
processPackage(engine, dependency, jsonObject, depType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a part of package.json (as defined by JsonObject) and update
|
||||||
|
* the specified dependency with relevant info.
|
||||||
|
*
|
||||||
|
* @param engine the dependency-check engine
|
||||||
|
* @param dependency the Dependency to update
|
||||||
|
* @param jsonObject the jsonObject to parse
|
||||||
|
* @param depType the dependency type
|
||||||
|
*/
|
||||||
|
protected void processPackage(Engine engine, Dependency dependency, JsonObject jsonObject, String depType) {
|
||||||
|
for (int i = 0; i < jsonObject.size(); i++) {
|
||||||
|
for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
|
||||||
|
|
||||||
|
final String name = entry.getKey();
|
||||||
|
String version = "";
|
||||||
|
if (entry.getValue() != null && entry.getValue().getValueType() == JsonValue.ValueType.STRING) {
|
||||||
|
version = ((JsonString) entry.getValue()).getString();
|
||||||
|
}
|
||||||
|
final Dependency existing = findDependency(engine, name, version);
|
||||||
|
if (existing == null) {
|
||||||
|
final Dependency nodeModule = createDependency(dependency, name, version, depType);
|
||||||
|
engine.addDependency(nodeModule);
|
||||||
|
} else {
|
||||||
|
existing.addProjectReference(dependency.getName() + ": " + depType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds information to an evidence collection from the node json
|
||||||
|
* configuration.
|
||||||
|
*
|
||||||
|
* @param dep the dependency to add the evidence
|
||||||
|
* @param t the type of evidence to add
|
||||||
|
* @param json information from node.js
|
||||||
|
* @return the actual string set into evidence
|
||||||
|
* @param key the key to obtain the data from the json information
|
||||||
|
*/
|
||||||
|
private static String addToEvidence(Dependency dep, EvidenceType t, JsonObject json, String key) {
|
||||||
|
String evidenceStr = null;
|
||||||
|
if (json.containsKey(key)) {
|
||||||
|
final JsonValue value = json.get(key);
|
||||||
|
if (value instanceof JsonString) {
|
||||||
|
evidenceStr = ((JsonString) value).getString();
|
||||||
|
dep.addEvidence(t, PACKAGE_JSON, key, evidenceStr, Confidence.HIGHEST);
|
||||||
|
} else if (value instanceof JsonObject) {
|
||||||
|
final JsonObject jsonObject = (JsonObject) value;
|
||||||
|
for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
|
||||||
|
final String property = entry.getKey();
|
||||||
|
final JsonValue subValue = entry.getValue();
|
||||||
|
if (subValue instanceof JsonString) {
|
||||||
|
evidenceStr = ((JsonString) subValue).getString();
|
||||||
|
dep.addEvidence(t, PACKAGE_JSON,
|
||||||
|
String.format("%s.%s", key, property),
|
||||||
|
evidenceStr,
|
||||||
|
Confidence.HIGHEST);
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return evidenceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the dependency from the list of dependencies that have been
|
||||||
|
* scanned by the engine.
|
||||||
|
*
|
||||||
|
* @param engine the dependency-check engine
|
||||||
|
* @param name the name of the dependency to find
|
||||||
|
* @param version the version of the dependency to find
|
||||||
|
* @return the identified dependency; otherwise null
|
||||||
|
*/
|
||||||
|
protected Dependency findDependency(Engine engine, String name, String version) {
|
||||||
|
for (Dependency d : engine.getDependencies()) {
|
||||||
|
if (NPM_DEPENDENCY_ECOSYSTEM.equals(d.getEcosystem()) && name.equals(d.getName()) && version != null && d.getVersion() != null) {
|
||||||
|
String dependencyVersion = d.getVersion();
|
||||||
|
if (DependencyBundlingAnalyzer.npmVersionsMatch(version, dependencyVersion)) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
// if (dependencyVersion.startsWith("^") || dependencyVersion.startsWith("~")) {
|
||||||
|
// dependencyVersion = dependencyVersion.substring(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (version.equals(dependencyVersion)) {
|
||||||
|
// return d;
|
||||||
|
// }
|
||||||
|
// if (version.startsWith("^") || version.startsWith("~") || version.contains("*")) {
|
||||||
|
// String type;
|
||||||
|
// String tmp;
|
||||||
|
// if (version.startsWith("^") || version.startsWith("~")) {
|
||||||
|
// type = version.substring(0, 1);
|
||||||
|
// tmp = version.substring(1);
|
||||||
|
// } else {
|
||||||
|
// type = "*";
|
||||||
|
// tmp = version;
|
||||||
|
// }
|
||||||
|
// final String[] v = tmp.split(" ")[0].split("\\.");
|
||||||
|
// final String[] depVersion = dependencyVersion.split("\\.");
|
||||||
|
//
|
||||||
|
// if ("^".equals(type) && v[0].equals(depVersion[0])) {
|
||||||
|
// return d;
|
||||||
|
// } else if ("~".equals(type) && v.length >= 2 && depVersion.length >= 2
|
||||||
|
// && v[0].equals(depVersion[0]) && v[1].equals(depVersion[1])) {
|
||||||
|
// return d;
|
||||||
|
// } else if (v[0].equals("*")
|
||||||
|
// || (v.length >= 2 && v[0].equals(depVersion[0]) && v[1].equals("*"))
|
||||||
|
// || (v.length >= 3 && depVersion.length >= 2 && v[0].equals(depVersion[0])
|
||||||
|
// && v[1].equals(depVersion[1]) && v[2].equals("*"))) {
|
||||||
|
// return d;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects evidence from the given JSON for the associated dependency.
|
||||||
|
*
|
||||||
|
* @param json the JSON that contains the evidence to collect
|
||||||
|
* @param dependency the dependency to add the evidence too
|
||||||
|
*/
|
||||||
|
public void gatherEvidence(final JsonObject json, Dependency dependency) {
|
||||||
|
if (json.containsKey("name")) {
|
||||||
|
final Object value = json.get("name");
|
||||||
|
if (value instanceof JsonString) {
|
||||||
|
final String valueString = ((JsonString) value).getString();
|
||||||
|
dependency.setName(valueString);
|
||||||
|
dependency.setPackagePath(valueString);
|
||||||
|
dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
|
||||||
|
dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name", valueString, Confidence.HIGH);
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("JSON value not string as expected: {}", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String desc = addToEvidence(dependency, EvidenceType.PRODUCT, json, "description");
|
||||||
|
dependency.setDescription(desc);
|
||||||
|
addToEvidence(dependency, EvidenceType.VENDOR, json, "author");
|
||||||
|
final String version = addToEvidence(dependency, EvidenceType.VERSION, json, "version");
|
||||||
|
if (version != null) {
|
||||||
|
dependency.setVersion(version);
|
||||||
|
dependency.addIdentifier("npm", String.format("%s:%s", dependency.getName(), version), null, Confidence.HIGHEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the license if defined in package.json
|
||||||
|
if (json.containsKey("license")) {
|
||||||
|
final Object value = json.get("license");
|
||||||
|
if (value instanceof JsonString) {
|
||||||
|
dependency.setLicense(json.getString("license"));
|
||||||
|
} else {
|
||||||
|
dependency.setLicense(json.getJsonObject("license").getString("type"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.owasp.dependencycheck.analyzer;
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
|
import com.vdurmont.semver4j.Semver;
|
||||||
|
import com.vdurmont.semver4j.Semver.SemverType;
|
||||||
|
import com.vdurmont.semver4j.SemverException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -135,10 +138,11 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
|
|||||||
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||||
return true; //since we merged into the next dependency - skip forward to the next in mainIterator
|
return true; //since we merged into the next dependency - skip forward to the next in mainIterator
|
||||||
}
|
}
|
||||||
} else if (ecoSystemIs(NspAnalyzer.DEPENDENCY_ECOSYSTEM, dependency, nextDependency)
|
} else if (ecoSystemIs(AbstractNpmAnalyzer.NPM_DEPENDENCY_ECOSYSTEM, dependency, nextDependency)
|
||||||
&& namesAreEqual(dependency, nextDependency)
|
&& namesAreEqual(dependency, nextDependency)
|
||||||
&& versionsAreEqual(dependency, nextDependency)) {
|
&& npmVersionsMatch(dependency.getVersion(), nextDependency.getVersion())) {
|
||||||
if (dependency.isVirtual()) {
|
|
||||||
|
if (!dependency.isVirtual()) {
|
||||||
DependencyMergingAnalyzer.mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
DependencyMergingAnalyzer.mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
||||||
} else {
|
} else {
|
||||||
DependencyMergingAnalyzer.mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
DependencyMergingAnalyzer.mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||||
@@ -158,7 +162,7 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
|
|||||||
* removed from the main analysis loop, this function adds to this
|
* removed from the main analysis loop, this function adds to this
|
||||||
* collection
|
* collection
|
||||||
*/
|
*/
|
||||||
private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) {
|
public static void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) {
|
||||||
dependency.addRelatedDependency(relatedDependency);
|
dependency.addRelatedDependency(relatedDependency);
|
||||||
for (Dependency d : relatedDependency.getRelatedDependencies()) {
|
for (Dependency d : relatedDependency.getRelatedDependencies()) {
|
||||||
dependency.addRelatedDependency(d);
|
dependency.addRelatedDependency(d);
|
||||||
@@ -167,7 +171,9 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
|
|||||||
if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) {
|
if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) {
|
||||||
dependency.addAllProjectReferences(relatedDependency.getProjectReferences());
|
dependency.addAllProjectReferences(relatedDependency.getProjectReferences());
|
||||||
}
|
}
|
||||||
dependenciesToRemove.add(relatedDependency);
|
if (dependenciesToRemove != null) {
|
||||||
|
dependenciesToRemove.add(relatedDependency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -487,14 +493,72 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the dependency version is equal in the given dependencies.
|
* Determine if the dependency version is equal in the given dependencies.
|
||||||
|
* This method attempts to evaluate version range checks.
|
||||||
*
|
*
|
||||||
* @param dependency a dependency to compare
|
* @param current a dependency version to compare
|
||||||
* @param nextDependency a dependency to compare
|
* @param nextDependency a dependency version to compare
|
||||||
* @return true if the version is equal in both dependencies; otherwise
|
* @return true if the version is equal in both dependencies; otherwise
|
||||||
* false
|
* false
|
||||||
*/
|
*/
|
||||||
private boolean versionsAreEqual(Dependency dependency, Dependency nextDependency) {
|
public static boolean npmVersionsMatch(String current, String next) {
|
||||||
return dependency.getVersion() != null && dependency.getVersion().equals(nextDependency.getVersion());
|
String left = current;
|
||||||
|
String right = next;
|
||||||
|
if (left == null || right == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (left.equals(right) || "*".equals(left) || "*".equals(right)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (left.contains(" ")) { // we have a version string from package.json
|
||||||
|
if (right.contains(" ")) { // we can't evaluate this ">=1.5.4 <2.0.0" vs "2 || 3"
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!right.matches("^\\d.*$")) {
|
||||||
|
right = stripLeadingNonNumeric(right);
|
||||||
|
if (right == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Semver v = new Semver(right, SemverType.NPM);
|
||||||
|
return v.satisfies(left);
|
||||||
|
} catch (SemverException ex) {
|
||||||
|
LOGGER.trace("ignore", ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!left.matches("^\\d.*$")) {
|
||||||
|
left = stripLeadingNonNumeric(left);
|
||||||
|
if (left == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Semver v = new Semver(left, SemverType.NPM);
|
||||||
|
if (v.satisfies(right)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!right.contains((" "))) {
|
||||||
|
left = current;
|
||||||
|
right = stripLeadingNonNumeric(right);
|
||||||
|
if (right != null) {
|
||||||
|
v = new Semver(right, SemverType.NPM);
|
||||||
|
return v.satisfies(left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SemverException ex) {
|
||||||
|
LOGGER.trace("ignore", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripLeadingNonNumeric(String str) {
|
||||||
|
for (int x = 0; x < str.length(); x++) {
|
||||||
|
if (Character.isDigit(str.codePointAt(x))) {
|
||||||
|
return str.substring(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,4 +128,4 @@ updater.nvdcve.enabled=true
|
|||||||
updater.versioncheck.enabled=true
|
updater.versioncheck.enabled=true
|
||||||
analyzer.versionfilter.enabled=true
|
analyzer.versionfilter.enabled=true
|
||||||
|
|
||||||
ecosystem.skip.nvdcve=npm
|
ecosystem.skip.cpeanalyzer=npm
|
||||||
@@ -43,6 +43,9 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
* The analyzer to test.
|
* The analyzer to test.
|
||||||
*/
|
*/
|
||||||
private NodePackageAnalyzer analyzer;
|
private NodePackageAnalyzer analyzer;
|
||||||
|
/**
|
||||||
|
* A reference to the engine.
|
||||||
|
*/
|
||||||
private Engine engine;
|
private Engine engine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +90,8 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSupportsFiles() {
|
public void testSupportsFiles() {
|
||||||
assertThat(analyzer.accept(new File("package.json")), is(true));
|
assertThat(analyzer.accept(new File("package-lock.json")), is(true));
|
||||||
|
assertThat(analyzer.accept(new File("shrinkwrap.json")), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,10 +100,12 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
* @throws AnalysisException is thrown when an exception occurs.
|
* @throws AnalysisException is thrown when an exception occurs.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzePackageJson() throws AnalysisException {
|
public void testAnalyzeShrinkwrapJson() throws AnalysisException {
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this,
|
||||||
"nodejs/node_modules/dns-sync/package.json"));
|
"nodejs/shrinkwrap.json"));
|
||||||
analyzer.analyze(result, null);
|
analyzer.analyze(toScan, engine);
|
||||||
|
assertEquals("Expected 1 dependency", engine.getDependencies().length, 1);
|
||||||
|
final Dependency result = engine.getDependencies()[0];
|
||||||
final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString();
|
final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString();
|
||||||
assertThat(vendorString, containsString("Sanjeev Koranga"));
|
assertThat(vendorString, containsString("Sanjeev Koranga"));
|
||||||
assertThat(vendorString, containsString("dns-sync"));
|
assertThat(vendorString, containsString("dns-sync"));
|
||||||
@@ -109,4 +115,24 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
assertEquals("dns-sync", result.getName());
|
assertEquals("dns-sync", result.getName());
|
||||||
assertEquals("0.1.0", result.getVersion());
|
assertEquals("0.1.0", result.getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of inspect method, of class PythonDistributionAnalyzer.
|
||||||
|
*
|
||||||
|
* @throws AnalysisException is thrown when an exception occurs.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAnalyzePackageJsonWithShrinkwrap() throws AnalysisException {
|
||||||
|
final Dependency packageLock = new Dependency(BaseTest.getResourceAsFile(this,
|
||||||
|
"nodejs/package-lock.json"));
|
||||||
|
final Dependency shrinkwrap = new Dependency(BaseTest.getResourceAsFile(this,
|
||||||
|
"nodejs/shrinkwrap.json"));
|
||||||
|
engine.addDependency(packageLock);
|
||||||
|
engine.addDependency(shrinkwrap);
|
||||||
|
assertEquals(2, engine.getDependencies().length);
|
||||||
|
analyzer.analyze(packageLock, engine);
|
||||||
|
assertEquals(1, engine.getDependencies().length); //package-lock was removed without analysis
|
||||||
|
analyzer.analyze(shrinkwrap, engine);
|
||||||
|
assertEquals(1, engine.getDependencies().length); //shrinkwrap was removed with analysis adding 1 dependency
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.owasp.dependencycheck.analyzer;
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.owasp.dependencycheck.BaseTest;
|
import org.owasp.dependencycheck.BaseTest;
|
||||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||||
@@ -36,12 +34,20 @@ public class NspAnalyzerTest extends BaseTest {
|
|||||||
analyzer.setFilesMatched(true);
|
analyzer.setFilesMatched(true);
|
||||||
analyzer.initialize(getSettings());
|
analyzer.initialize(getSettings());
|
||||||
analyzer.prepare(engine);
|
analyzer.prepare(engine);
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json"));
|
final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json"));
|
||||||
analyzer.analyze(result, engine);
|
analyzer.analyze(toScan, engine);
|
||||||
|
boolean found = false;
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("owasp-nodejs-goat"));
|
assertEquals("4 dependencies should be identified", 4, engine.getDependencies().length);
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("A tool to learn OWASP Top 10 for node.js developers"));
|
for (Dependency result : engine.getDependencies()) {
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.3.0"));
|
if ("package.json?uglify-js".equals(result.getFileName())) {
|
||||||
|
found = true;
|
||||||
|
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("uglify-js"));
|
||||||
|
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("uglify-js"));
|
||||||
|
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("2.4.24"));
|
||||||
|
assertTrue(result.isVirtual());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("Uglify was not found", found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,38 +67,6 @@ public class NspAnalyzerTest extends BaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnalyzePackageJsonWithBundledDeps() throws AnalysisException, InitializationException {
|
|
||||||
try (Engine engine = new Engine(getSettings())) {
|
|
||||||
NspAnalyzer analyzer = new NspAnalyzer();
|
|
||||||
analyzer.setFilesMatched(true);
|
|
||||||
analyzer.initialize(getSettings());
|
|
||||||
analyzer.prepare(engine);
|
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/bundled.deps.package.json"));
|
|
||||||
analyzer.analyze(result, engine);
|
|
||||||
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Philipp Dunkel <pip@pipobscure.com>"));
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("Native Access to Mac OS-X FSEvents"));
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.1.1"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnalyzePackageJsonWithLicenseObject() throws AnalysisException, InitializationException {
|
|
||||||
try (Engine engine = new Engine(getSettings())) {
|
|
||||||
NspAnalyzer analyzer = new NspAnalyzer();
|
|
||||||
analyzer.setFilesMatched(true);
|
|
||||||
analyzer.initialize(getSettings());
|
|
||||||
analyzer.prepare(engine);
|
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/license.obj.package.json"));
|
|
||||||
analyzer.analyze(result, engine);
|
|
||||||
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Twitter, Inc."));
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("The most popular front-end framework for developing responsive, mobile first projects on the web"));
|
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("3.2.0"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException, InitializationException {
|
public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException, InitializationException {
|
||||||
try (Engine engine = new Engine(getSettings())) {
|
try (Engine engine = new Engine(getSettings())) {
|
||||||
@@ -100,12 +74,10 @@ public class NspAnalyzerTest extends BaseTest {
|
|||||||
analyzer.setFilesMatched(true);
|
analyzer.setFilesMatched(true);
|
||||||
analyzer.initialize(getSettings());
|
analyzer.initialize(getSettings());
|
||||||
analyzer.prepare(engine);
|
analyzer.prepare(engine);
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json"));
|
final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json"));
|
||||||
analyzer.analyze(result, engine);
|
engine.addDependency(toScan);
|
||||||
// package.json adds 5 bits of evidence
|
analyzer.analyze(toScan, engine);
|
||||||
assertTrue(result.size() == 5);
|
assertEquals("No dependencies should exist", 0, engine.getDependencies().length);
|
||||||
// but no vulnerabilities were cited
|
|
||||||
assertTrue(result.getVulnerabilities().isEmpty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,4 +124,4 @@ analyzer.vulnerabilitysuppression.enabled=true
|
|||||||
updater.nvdcve.enabled=true
|
updater.nvdcve.enabled=true
|
||||||
updater.versioncheck.enabled=true
|
updater.versioncheck.enabled=true
|
||||||
|
|
||||||
ecosystem.skip.nvdcve=npm
|
ecosystem.skip.cpeanalyzer=npm
|
||||||
@@ -443,9 +443,9 @@ public final class Settings {
|
|||||||
*/
|
*/
|
||||||
public static final String UPDATE_VERSION_CHECK_ENABLED = "updater.versioncheck.enabled";
|
public static final String UPDATE_VERSION_CHECK_ENABLED = "updater.versioncheck.enabled";
|
||||||
/**
|
/**
|
||||||
* The key to determine which ecosystems should skip the NVD CVE analysis.
|
* The key to determine which ecosystems should skip the CPE analysis.
|
||||||
*/
|
*/
|
||||||
public static final String ECOSYSTEM_SKIP_NVDCVE = "ecosystem.skip.nvdcve";
|
public static final String ECOSYSTEM_SKIP_CPEANALYZER = "ecosystem.skip.cpeanalyzer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private constructor because this is a "utility" class containing
|
* private constructor because this is a "utility" class containing
|
||||||
|
|||||||
5
pom.xml
5
pom.xml
@@ -625,6 +625,11 @@ Copyright (c) 2012 - Jeremy Long
|
|||||||
</reporting>
|
</reporting>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.vdurmont</groupId>
|
||||||
|
<artifactId>semver4j</artifactId>
|
||||||
|
<version>2.1.0</version>
|
||||||
|
</dependency>
|
||||||
<!-- analysis core (used by Jenkins) uses 1.6-->
|
<!-- analysis core (used by Jenkins) uses 1.6-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
|
|||||||
Reference in New Issue
Block a user