mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-03-24 18:11:47 +01:00
Merge branch 'master' into versions
This commit is contained in:
@@ -20,7 +20,7 @@ Copyright (c) 2017 - Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Dependency-Check Build-Reporting</name>
|
<name>Dependency-Check Build-Reporting</name>
|
||||||
<artifactId>build-reporting</artifactId>
|
<artifactId>build-reporting</artifactId>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>dependency-check-ant</artifactId>
|
<artifactId>dependency-check-ant</artifactId>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>dependency-check-cli</artifactId>
|
<artifactId>dependency-check-cli</artifactId>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>dependency-check-core</artifactId>
|
<artifactId>dependency-check-core</artifactId>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1038,6 +1038,15 @@ public class Engine implements FileFilter, AutoCloseable {
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the mode of the engine.
|
||||||
|
*
|
||||||
|
* @return the mode of the engine
|
||||||
|
*/
|
||||||
|
public Mode getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a file type analyzer. This has been added solely to assist in unit
|
* Adds a file type analyzer. This has been added solely to assist in unit
|
||||||
* testing the Engine.
|
* testing the Engine.
|
||||||
|
|||||||
@@ -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,291 @@
|
|||||||
|
/*
|
||||||
|
* 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.analyzer.exception.AnalysisException;
|
||||||
|
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 {
|
||||||
|
accept |= shouldProcess(pathname);
|
||||||
|
} catch (AnalysisException ex) {
|
||||||
|
throw new RuntimeException(ex.getMessage(), ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the path contains "/node_modules/" (i.e. it is a child
|
||||||
|
* module. This analyzer does not scan child modules.
|
||||||
|
*
|
||||||
|
* @param pathname the path to test
|
||||||
|
* @return <code>true</code> if the path does not contain "/node_modules/"
|
||||||
|
* @throws AnalysisException thrown if the canonical path cannot be obtained
|
||||||
|
* from the given file
|
||||||
|
*/
|
||||||
|
protected boolean shouldProcess(File pathname) throws AnalysisException {
|
||||||
|
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());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AnalysisException("Unable to process dependency", ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
final String dependencyVersion = d.getVersion();
|
||||||
|
if (DependencyBundlingAnalyzer.npmVersionsMatch(version, dependencyVersion)) {
|
||||||
|
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 if (value instanceof JsonArray) {
|
||||||
|
final JsonArray array = (JsonArray) value;
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
boolean addComma = false;
|
||||||
|
for (int x = 0; x < array.size(); x++) {
|
||||||
|
if (!array.isNull(x)) {
|
||||||
|
if (addComma) {
|
||||||
|
sb.append(", ");
|
||||||
|
} else {
|
||||||
|
addComma = true;
|
||||||
|
}
|
||||||
|
sb.append(array.getString(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependency.setLicense(sb.toString());
|
||||||
|
} else {
|
||||||
|
dependency.setLicense(json.getJsonObject("license").getString("type"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,13 +52,13 @@ import org.xml.sax.SAXException;
|
|||||||
public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Logger for use throughout the class
|
* The Logger for use throughout the class.
|
||||||
*/
|
*/
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSuppressionAnalyzer.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSuppressionAnalyzer.class);
|
||||||
/**
|
/**
|
||||||
* The list of suppression rules
|
* The list of suppression rules.
|
||||||
*/
|
*/
|
||||||
private SuppressionRule[] rules = null;
|
private List<SuppressionRule> rules = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of suppression rules.
|
* Get the number of suppression rules.
|
||||||
@@ -66,7 +66,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
* @return the number of suppression rules
|
* @return the number of suppression rules
|
||||||
*/
|
*/
|
||||||
protected int getRuleCount() {
|
protected int getRuleCount() {
|
||||||
return rules.length;
|
return rules.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,18 +86,24 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void prepareAnalyzer(Engine engine) throws InitializationException {
|
public synchronized void prepareAnalyzer(Engine engine) throws InitializationException {
|
||||||
if (rules == null) {
|
if (rules == null || rules.isEmpty()) {
|
||||||
|
try {
|
||||||
|
loadSuppressionBaseData();
|
||||||
|
} catch (SuppressionParseException ex) {
|
||||||
|
throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex, true);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loadSuppressionData();
|
loadSuppressionData();
|
||||||
} catch (SuppressionParseException ex) {
|
} catch (SuppressionParseException ex) {
|
||||||
throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex, true);
|
throw new InitializationException("Warn initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
||||||
if (rules == null || rules.length <= 0) {
|
if (rules == null || rules.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (final SuppressionRule rule : rules) {
|
for (final SuppressionRule rule : rules) {
|
||||||
@@ -111,18 +117,11 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
* @throws SuppressionParseException thrown if the XML cannot be parsed.
|
* @throws SuppressionParseException thrown if the XML cannot be parsed.
|
||||||
*/
|
*/
|
||||||
private void loadSuppressionData() throws SuppressionParseException {
|
private void loadSuppressionData() throws SuppressionParseException {
|
||||||
List<SuppressionRule> ruleList;
|
final List<SuppressionRule> ruleList = new ArrayList<>();
|
||||||
final SuppressionParser parser = new SuppressionParser();
|
final SuppressionParser parser = new SuppressionParser();
|
||||||
try {
|
|
||||||
final InputStream in = FileUtils.getResourceAsStream("dependencycheck-base-suppression.xml");
|
|
||||||
ruleList = parser.parseSuppressionRules(in);
|
|
||||||
} catch (SAXException ex) {
|
|
||||||
throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
|
|
||||||
}
|
|
||||||
final String[] suppressionFilePaths = getSettings().getArray(Settings.KEYS.SUPPRESSION_FILE);
|
final String[] suppressionFilePaths = getSettings().getArray(Settings.KEYS.SUPPRESSION_FILE);
|
||||||
final List<String> failedLoadingFiles = new ArrayList<>();
|
final List<String> failedLoadingFiles = new ArrayList<>();
|
||||||
if (suppressionFilePaths != null && suppressionFilePaths.length > 0) {
|
if (suppressionFilePaths != null && suppressionFilePaths.length > 0) {
|
||||||
|
|
||||||
// Load all the suppression file paths
|
// Load all the suppression file paths
|
||||||
for (final String suppressionFilePath : suppressionFilePaths) {
|
for (final String suppressionFilePath : suppressionFilePaths) {
|
||||||
try {
|
try {
|
||||||
@@ -133,8 +132,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rules = ruleList.toArray(new SuppressionRule[ruleList.size()]);
|
|
||||||
LOGGER.debug("{} suppression rules were loaded.", ruleList.size());
|
LOGGER.debug("{} suppression rules were loaded.", ruleList.size());
|
||||||
|
rules.addAll(ruleList);
|
||||||
if (!failedLoadingFiles.isEmpty()) {
|
if (!failedLoadingFiles.isEmpty()) {
|
||||||
LOGGER.debug("{} suppression files failed to load.", failedLoadingFiles.size());
|
LOGGER.debug("{} suppression files failed to load.", failedLoadingFiles.size());
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
@@ -145,6 +144,23 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all the base suppression rules files.
|
||||||
|
*
|
||||||
|
* @throws SuppressionParseException thrown if the XML cannot be parsed.
|
||||||
|
*/
|
||||||
|
private void loadSuppressionBaseData() throws SuppressionParseException {
|
||||||
|
final SuppressionParser parser = new SuppressionParser();
|
||||||
|
List<SuppressionRule> ruleList;
|
||||||
|
try {
|
||||||
|
final InputStream in = FileUtils.getResourceAsStream("dependencycheck-base-suppression.xml");
|
||||||
|
ruleList = parser.parseSuppressionRules(in);
|
||||||
|
} catch (SAXException ex) {
|
||||||
|
throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
|
||||||
|
}
|
||||||
|
rules.addAll(ruleList);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a single suppression rules file from the path provided using the
|
* Load a single suppression rules file from the path provided using the
|
||||||
* parser provided.
|
* parser provided.
|
||||||
@@ -185,7 +201,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
try {
|
try {
|
||||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
|
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex, suppressionFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,13 +222,13 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (DownloadFailedException ex) {
|
} catch (DownloadFailedException ex) {
|
||||||
throwSuppressionParseException("Unable to fetch the configured suppression file", ex);
|
throwSuppressionParseException("Unable to fetch the configured suppression file", ex, suppressionFilePath);
|
||||||
} catch (MalformedURLException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
throwSuppressionParseException("Configured suppression file has an invalid URL", ex);
|
throwSuppressionParseException("Configured suppression file has an invalid URL", ex, suppressionFilePath);
|
||||||
} catch (SuppressionParseException ex) {
|
} catch (SuppressionParseException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throwSuppressionParseException("Unable to create temp file for suppressions", ex);
|
throwSuppressionParseException("Unable to create temp file for suppressions", ex, suppressionFilePath);
|
||||||
} finally {
|
} finally {
|
||||||
if (deleteTempFile && file != null) {
|
if (deleteTempFile && file != null) {
|
||||||
FileUtils.delete(file);
|
FileUtils.delete(file);
|
||||||
@@ -226,11 +242,12 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
|||||||
*
|
*
|
||||||
* @param message the exception message
|
* @param message the exception message
|
||||||
* @param exception the cause of the exception
|
* @param exception the cause of the exception
|
||||||
|
* @param suppressionFilePath the path file
|
||||||
* @throws SuppressionParseException throws the generated
|
* @throws SuppressionParseException throws the generated
|
||||||
* SuppressionParseException
|
* SuppressionParseException
|
||||||
*/
|
*/
|
||||||
private void throwSuppressionParseException(String message, Exception exception) throws SuppressionParseException {
|
private void throwSuppressionParseException(String message, Exception exception, String suppressionFilePath) throws SuppressionParseException {
|
||||||
LOGGER.warn(message);
|
LOGGER.warn(String.format(message + "'%s'", suppressionFilePath));
|
||||||
LOGGER.debug("", exception);
|
LOGGER.debug("", exception);
|
||||||
throw new SuppressionParseException(message, exception);
|
throw new SuppressionParseException(message, exception);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.io.IOException;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -106,6 +107,12 @@ public class CPEAnalyzer extends AbstractAnalyzer {
|
|||||||
* The CVE Database.
|
* The CVE Database.
|
||||||
*/
|
*/
|
||||||
private CveDB cve;
|
private CveDB cve;
|
||||||
|
/**
|
||||||
|
* The list of ecosystems to skip during analysis. These are skipped because
|
||||||
|
* there is generally a more accurate vulnerability analyzer in the
|
||||||
|
* pipeline.
|
||||||
|
*/
|
||||||
|
private List<String> skipEcosystems;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of this analyzer.
|
* Returns the name of this analyzer.
|
||||||
@@ -136,6 +143,7 @@ public class CPEAnalyzer extends AbstractAnalyzer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void prepareAnalyzer(Engine engine) throws InitializationException {
|
public void prepareAnalyzer(Engine engine) throws InitializationException {
|
||||||
|
super.prepareAnalyzer(engine);
|
||||||
try {
|
try {
|
||||||
this.open(engine.getDatabase());
|
this.open(engine.getDatabase());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@@ -145,6 +153,13 @@ public class CPEAnalyzer extends AbstractAnalyzer {
|
|||||||
LOGGER.debug("Exception accessing the database", ex);
|
LOGGER.debug("Exception accessing the database", ex);
|
||||||
throw new InitializationException("An exception occurred accessing the database", ex);
|
throw new InitializationException("An exception occurred accessing the database", ex);
|
||||||
}
|
}
|
||||||
|
final String[] tmp = engine.getSettings().getArray(Settings.KEYS.ECOSYSTEM_SKIP_CPEANALYZER);
|
||||||
|
if (tmp == null) {
|
||||||
|
skipEcosystems = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
LOGGER.info("Skipping CPE Analysis for {}", tmp);
|
||||||
|
skipEcosystems = Arrays.asList(tmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,6 +540,9 @@ public class CPEAnalyzer extends AbstractAnalyzer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
||||||
|
if (skipEcosystems.contains(dependency.getEcosystem())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
determineCPE(dependency);
|
determineCPE(dependency);
|
||||||
} catch (CorruptIndexException ex) {
|
} catch (CorruptIndexException ex) {
|
||||||
|
|||||||
@@ -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,6 +138,16 @@ 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(AbstractNpmAnalyzer.NPM_DEPENDENCY_ECOSYSTEM, dependency, nextDependency)
|
||||||
|
&& namesAreEqual(dependency, nextDependency)
|
||||||
|
&& npmVersionsMatch(dependency.getVersion(), nextDependency.getVersion())) {
|
||||||
|
|
||||||
|
if (!dependency.isVirtual()) {
|
||||||
|
DependencyMergingAnalyzer.mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
||||||
|
} else {
|
||||||
|
DependencyMergingAnalyzer.mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -149,7 +162,8 @@ 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);
|
||||||
@@ -158,7 +172,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -452,4 +468,105 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
|
|||||||
return filePath != null && filePath.matches(".*\\.(ear|war)[\\\\/].*");
|
return filePath != null && filePath.matches(".*\\.(ear|war)[\\\\/].*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the dependency ecosystem is equal in the given dependencies.
|
||||||
|
*
|
||||||
|
* @param ecoSystem the ecosystem to validate against
|
||||||
|
* @param dependency a dependency to compare
|
||||||
|
* @param nextDependency a dependency to compare
|
||||||
|
* @return true if the ecosystem is equal in both dependencies; otherwise
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
private boolean ecoSystemIs(String ecoSystem, Dependency dependency, Dependency nextDependency) {
|
||||||
|
return ecoSystem.equals(dependency.getEcosystem()) && ecoSystem.equals(nextDependency.getEcosystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the dependency name is equal in the given dependencies.
|
||||||
|
*
|
||||||
|
* @param dependency a dependency to compare
|
||||||
|
* @param nextDependency a dependency to compare
|
||||||
|
* @return true if the name is equal in both dependencies; otherwise false
|
||||||
|
*/
|
||||||
|
private boolean namesAreEqual(Dependency dependency, Dependency nextDependency) {
|
||||||
|
return dependency.getName() != null && dependency.getName().equals(nextDependency.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the dependency version is equal in the given dependencies.
|
||||||
|
* This method attempts to evaluate version range checks.
|
||||||
|
*
|
||||||
|
* @param current a dependency version to compare
|
||||||
|
* @param next a dependency version to compare
|
||||||
|
* @return true if the version is equal in both dependencies; otherwise
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
public static boolean npmVersionsMatch(String current, String next) {
|
||||||
|
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 {
|
||||||
|
final 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips leading non-numeric values from the start of the string. If no
|
||||||
|
* numbers are present this will return null.
|
||||||
|
*
|
||||||
|
* @param str the string to modify
|
||||||
|
* @return the string without leading non-numeric characters
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,8 @@ public class DependencyMergingAnalyzer extends AbstractDependencyComparingAnalyz
|
|||||||
* 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) {
|
||||||
LOGGER.debug("Merging '{}' into '{}'", relatedDependency.getFilePath(), dependency.getFilePath());
|
LOGGER.debug("Merging '{}' into '{}'", relatedDependency.getFilePath(), dependency.getFilePath());
|
||||||
dependency.addRelatedDependency(relatedDependency);
|
dependency.addRelatedDependency(relatedDependency);
|
||||||
for (Evidence e : relatedDependency.getEvidence(EvidenceType.VENDOR)) {
|
for (Evidence e : relatedDependency.getEvidence(EvidenceType.VENDOR)) {
|
||||||
@@ -137,10 +138,10 @@ public class DependencyMergingAnalyzer extends AbstractDependencyComparingAnalyz
|
|||||||
dependency.addRelatedDependency(d);
|
dependency.addRelatedDependency(d);
|
||||||
relatedDependency.removeRelatedDependencies(d);
|
relatedDependency.removeRelatedDependencies(d);
|
||||||
}
|
}
|
||||||
if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) {
|
dependency.addAllProjectReferences(relatedDependency.getProjectReferences());
|
||||||
dependency.addAllProjectReferences(relatedDependency.getProjectReferences());
|
if (dependenciesToRemove != null) {
|
||||||
|
dependenciesToRemove.add(relatedDependency);
|
||||||
}
|
}
|
||||||
dependenciesToRemove.add(relatedDependency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package org.owasp.dependencycheck.analyzer;
|
|||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.owasp.dependencycheck.Engine;
|
import org.owasp.dependencycheck.Engine;
|
||||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||||
import org.owasp.dependencycheck.dependency.Confidence;
|
|
||||||
import org.owasp.dependencycheck.dependency.Dependency;
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||||
import org.owasp.dependencycheck.utils.Settings;
|
import org.owasp.dependencycheck.utils.Settings;
|
||||||
@@ -30,16 +29,22 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
import javax.json.JsonException;
|
import javax.json.JsonException;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
import javax.json.JsonReader;
|
import javax.json.JsonReader;
|
||||||
import javax.json.JsonString;
|
|
||||||
import javax.json.JsonValue;
|
import javax.json.JsonValue;
|
||||||
import org.owasp.dependencycheck.exception.InitializationException;
|
import org.owasp.dependencycheck.Engine.Mode;
|
||||||
|
import org.owasp.dependencycheck.dependency.Confidence;
|
||||||
import org.owasp.dependencycheck.dependency.EvidenceType;
|
import org.owasp.dependencycheck.dependency.EvidenceType;
|
||||||
|
import org.owasp.dependencycheck.exception.InitializationException;
|
||||||
|
import org.owasp.dependencycheck.utils.Checksum;
|
||||||
|
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to analyze Node Package Manager (npm) package.json files, and collect
|
* Used to analyze Node Package Manager (npm) package.json files, and collect
|
||||||
@@ -48,8 +53,7 @@ import org.owasp.dependencycheck.dependency.EvidenceType;
|
|||||||
* @author Dale Visser
|
* @author Dale Visser
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@Retired
|
public class NodePackageAnalyzer extends AbstractNpmAnalyzer {
|
||||||
public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logger.
|
* The logger.
|
||||||
@@ -59,7 +63,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
* A descriptor for the type of dependencies processed or added by this
|
* A descriptor for the type of dependencies processed or added by this
|
||||||
* analyzer.
|
* analyzer.
|
||||||
*/
|
*/
|
||||||
public static final String DEPENDENCY_ECOSYSTEM = "npm";
|
public static final String DEPENDENCY_ECOSYSTEM = NPM_DEPENDENCY_ECOSYSTEM;
|
||||||
/**
|
/**
|
||||||
* The name of the analyzer.
|
* The name of the analyzer.
|
||||||
*/
|
*/
|
||||||
@@ -73,10 +77,19 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
*/
|
*/
|
||||||
public static final String PACKAGE_JSON = "package.json";
|
public static final String PACKAGE_JSON = "package.json";
|
||||||
/**
|
/**
|
||||||
* Filter that detects files named "package.json".
|
* The file name to scan.
|
||||||
|
*/
|
||||||
|
public static final String PACKAGE_LOCK_JSON = "package-lock.json";
|
||||||
|
/**
|
||||||
|
* The file name to scan.
|
||||||
|
*/
|
||||||
|
public static final String SHRINKWRAP_JSON = "npm-shrinkwrap.json";
|
||||||
|
/**
|
||||||
|
* Filter that detects files named "package-lock.json" or
|
||||||
|
* "npm-shrinkwrap.json".
|
||||||
*/
|
*/
|
||||||
private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance()
|
private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance()
|
||||||
.addFilenames(PACKAGE_JSON).build();
|
.addFilenames(PACKAGE_LOCK_JSON, SHRINKWRAP_JSON).build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the FileFilter
|
* Returns the FileFilter
|
||||||
@@ -88,9 +101,35 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
return PACKAGE_JSON_FILTER;
|
return PACKAGE_JSON_FILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs validation on the configuration to ensure that the correct
|
||||||
|
* analyzers are in place.
|
||||||
|
*
|
||||||
|
* @param engine the dependency-check engine
|
||||||
|
* @throws InitializationException thrown if there is a configuration error
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException {
|
protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException {
|
||||||
// NO-OP
|
if (engine.getMode() != Mode.EVIDENCE_COLLECTION) {
|
||||||
|
try {
|
||||||
|
final Settings settings = engine.getSettings();
|
||||||
|
final String[] tmp = settings.getArray(Settings.KEYS.ECOSYSTEM_SKIP_CPEANALYZER);
|
||||||
|
if (tmp != null) {
|
||||||
|
final List<String> skipEcosystems = Arrays.asList(tmp);
|
||||||
|
if (skipEcosystems.contains(DEPENDENCY_ECOSYSTEM)
|
||||||
|
&& !settings.getBoolean(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED)) {
|
||||||
|
LOGGER.debug("NodePackageAnalyzer enabled without a corresponding vulnerability analyzer");
|
||||||
|
final String msg = "Invalid Configuration: enabling the Node Package Analyzer without "
|
||||||
|
+ "using the NSP Analyzer is not supported.";
|
||||||
|
throw new InitializationException(msg);
|
||||||
|
} else if (!skipEcosystems.contains(DEPENDENCY_ECOSYSTEM)) {
|
||||||
|
LOGGER.warn("Using the CPE Analyzer with Node.js can result in many false positives.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InvalidSettingException ex) {
|
||||||
|
throw new InitializationException("Unable to read configuration settings", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,10 +153,10 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the key used in the properties file to reference the analyzer's
|
* Returns the key used in the properties file to reference the enabled
|
||||||
* enabled property.
|
* property for the analyzer.
|
||||||
*
|
*
|
||||||
* @return the analyzer's enabled property setting key
|
* @return the enabled property setting key for the analyzer
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getAnalyzerEnabledSettingKey() {
|
protected String getAnalyzerEnabledSettingKey() {
|
||||||
@@ -126,29 +165,31 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
||||||
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
|
engine.removeDependency(dependency);
|
||||||
final File file = dependency.getActualFile();
|
final File dependencyFile = dependency.getActualFile();
|
||||||
if (!file.isFile() || file.length() == 0) {
|
if (!dependencyFile.isFile() || dependencyFile.length() == 0 || !shouldProcess(dependencyFile)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) {
|
final File baseDir = dependencyFile.getParentFile();
|
||||||
final JsonObject json = jsonReader.readObject();
|
if (PACKAGE_LOCK_JSON.equals(dependency.getFileName())) {
|
||||||
if (json.containsKey("name")) {
|
final File shrinkwrap = new File(baseDir, SHRINKWRAP_JSON);
|
||||||
final Object value = json.get("name");
|
if (shrinkwrap.exists()) {
|
||||||
if (value instanceof JsonString) {
|
return;
|
||||||
final String valueString = ((JsonString) value).getString();
|
|
||||||
dependency.setName(valueString);
|
|
||||||
dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
|
|
||||||
dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name_project",
|
|
||||||
String.format("%s_project", valueString), Confidence.LOW);
|
|
||||||
} else {
|
|
||||||
LOGGER.warn("JSON value not string as expected: {}", value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
addToEvidence(dependency, EvidenceType.PRODUCT, json, "description");
|
}
|
||||||
addToEvidence(dependency, EvidenceType.VENDOR, json, "author");
|
final File nodeModules = new File(baseDir, "node_modules");
|
||||||
final String version = addToEvidence(dependency, EvidenceType.VERSION, json, "version");
|
if (!nodeModules.isDirectory()) {
|
||||||
dependency.setVersion(version);
|
LOGGER.warn("Analyzing `{}` - however, the node_modules directory does not exist. "
|
||||||
|
+ "Please run `npm install` prior to running dependency-check", dependencyFile.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(dependencyFile))) {
|
||||||
|
final JsonObject json = jsonReader.readObject();
|
||||||
|
final String parentName = json.getString("name");
|
||||||
|
final String parentVersion = json.getString("version");
|
||||||
|
final String parentPackage = String.format("%s:%s", parentName, parentVersion);
|
||||||
|
processDependencies(json, baseDir, dependencyFile, parentPackage, engine);
|
||||||
} catch (JsonException e) {
|
} catch (JsonException e) {
|
||||||
LOGGER.warn("Failed to parse package.json file.", e);
|
LOGGER.warn("Failed to parse package.json file.", e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -157,41 +198,74 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds information to an evidence collection from the node json
|
* Process the dependencies in the lock file by first parsing its
|
||||||
* configuration.
|
* dependencies and then finding the package.json for the module and adding
|
||||||
|
* it as a dependency.
|
||||||
*
|
*
|
||||||
* @param dep the dependency to add the evidence
|
* @param json the data to process
|
||||||
* @param t the type of evidence to add
|
* @param baseDir the base directory being scanned
|
||||||
* @param json information from node.js
|
* @param rootFile the root package-lock/npm-shrinkwrap being analyzed
|
||||||
* @return the actual string set into evidence
|
* @param parentPackage the parent package name of the current node
|
||||||
* @param key the key to obtain the data from the json information
|
* @param engine a reference to the dependency-check engine
|
||||||
|
* @throws AnalysisException thrown if there is an exception
|
||||||
*/
|
*/
|
||||||
private String addToEvidence(Dependency dep, EvidenceType t, JsonObject json, String key) {
|
private void processDependencies(JsonObject json, File baseDir, File rootFile,
|
||||||
String evidenceStr = null;
|
String parentPackage, Engine engine) throws AnalysisException {
|
||||||
if (json.containsKey(key)) {
|
if (json.containsKey("dependencies")) {
|
||||||
final JsonValue value = json.get(key);
|
final JsonObject deps = json.getJsonObject("dependencies");
|
||||||
if (value instanceof JsonString) {
|
for (Map.Entry<String, JsonValue> entry : deps.entrySet()) {
|
||||||
evidenceStr = ((JsonString) value).getString();
|
final JsonObject jo = (JsonObject) entry.getValue();
|
||||||
dep.addEvidence(t, PACKAGE_JSON, key, evidenceStr, Confidence.HIGHEST);
|
final String name = entry.getKey();
|
||||||
} else if (value instanceof JsonObject) {
|
final String version = jo.getString("version");
|
||||||
final JsonObject jsonObject = (JsonObject) value;
|
final File base = Paths.get(baseDir.getPath(), "node_modules", name).toFile();
|
||||||
for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
|
final File f = new File(base, PACKAGE_JSON);
|
||||||
final String property = entry.getKey();
|
|
||||||
final JsonValue subValue = entry.getValue();
|
if (jo.containsKey("dependencies")) {
|
||||||
if (subValue instanceof JsonString) {
|
final String subPackageName = String.format("%s/%s:%s", parentPackage, name, version);
|
||||||
evidenceStr = ((JsonString) subValue).getString();
|
processDependencies(jo, base, rootFile, subPackageName, engine);
|
||||||
dep.addEvidence(t, PACKAGE_JSON,
|
}
|
||||||
String.format("%s.%s", key, property),
|
|
||||||
evidenceStr,
|
Dependency child;
|
||||||
Confidence.HIGHEST);
|
if (f.exists()) {
|
||||||
} else {
|
//TOOD - we should use the integrity value instead of calculating the SHA1/MD5
|
||||||
LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
|
child = new Dependency(f);
|
||||||
}
|
try (JsonReader jr = Json.createReader(FileUtils.openInputStream(f))) {
|
||||||
|
final JsonObject childJson = jr.readObject();
|
||||||
|
gatherEvidence(childJson, child);
|
||||||
|
|
||||||
|
} catch (JsonException e) {
|
||||||
|
LOGGER.warn("Failed to parse package.json file from dependency.", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AnalysisException("Problem occurred while reading dependency file.", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Unable to find node module: {}", f.toString());
|
||||||
|
child = new Dependency(rootFile, true);
|
||||||
|
//TOOD - we should use the integrity value instead of calculating the SHA1/MD5
|
||||||
|
child.setSha1sum(Checksum.getSHA1Checksum(String.format("%s:%s", name, version)));
|
||||||
|
child.setMd5sum(Checksum.getMD5Checksum(String.format("%s:%s", name, version)));
|
||||||
|
child.addEvidence(EvidenceType.VENDOR, rootFile.getName(), "name", name, Confidence.HIGHEST);
|
||||||
|
child.addEvidence(EvidenceType.PRODUCT, rootFile.getName(), "name", name, Confidence.HIGHEST);
|
||||||
|
child.addEvidence(EvidenceType.VERSION, rootFile.getName(), "version", version, Confidence.HIGHEST);
|
||||||
|
}
|
||||||
|
child.setName(name);
|
||||||
|
child.setVersion(version);
|
||||||
|
child.addProjectReference(parentPackage);
|
||||||
|
child.setEcosystem(DEPENDENCY_ECOSYSTEM);
|
||||||
|
|
||||||
|
final Dependency existing = findDependency(engine, name, version);
|
||||||
|
if (existing != null) {
|
||||||
|
if (existing.isVirtual()) {
|
||||||
|
DependencyMergingAnalyzer.mergeDependencies(child, existing, null);
|
||||||
|
engine.removeDependency(existing);
|
||||||
|
engine.addDependency(child);
|
||||||
|
} else {
|
||||||
|
DependencyBundlingAnalyzer.mergeDependencies(existing, child, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
engine.addDependency(child);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return evidenceStr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
|||||||
import org.owasp.dependencycheck.data.nsp.Advisory;
|
import org.owasp.dependencycheck.data.nsp.Advisory;
|
||||||
import org.owasp.dependencycheck.data.nsp.NspSearch;
|
import org.owasp.dependencycheck.data.nsp.NspSearch;
|
||||||
import org.owasp.dependencycheck.data.nsp.SanitizePackage;
|
import org.owasp.dependencycheck.data.nsp.SanitizePackage;
|
||||||
import org.owasp.dependencycheck.dependency.Confidence;
|
|
||||||
import org.owasp.dependencycheck.dependency.Dependency;
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
import org.owasp.dependencycheck.dependency.Identifier;
|
|
||||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||||
@@ -39,18 +37,14 @@ import java.net.MalformedURLException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
import javax.json.JsonArray;
|
|
||||||
import javax.json.JsonException;
|
import javax.json.JsonException;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
import javax.json.JsonObjectBuilder;
|
import javax.json.JsonObjectBuilder;
|
||||||
import javax.json.JsonReader;
|
import javax.json.JsonReader;
|
||||||
import javax.json.JsonString;
|
|
||||||
import javax.json.JsonValue;
|
|
||||||
import org.owasp.dependencycheck.dependency.EvidenceType;
|
|
||||||
import org.owasp.dependencycheck.exception.InitializationException;
|
import org.owasp.dependencycheck.exception.InitializationException;
|
||||||
|
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||||
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
|
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,7 +54,7 @@ import org.owasp.dependencycheck.utils.URLConnectionFailureException;
|
|||||||
* @author Steve Springett
|
* @author Steve Springett
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
public class NspAnalyzer extends AbstractNpmAnalyzer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logger.
|
* The logger.
|
||||||
@@ -71,7 +65,11 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
* The default URL to the NSP check API.
|
* The default URL to the NSP check API.
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_URL = "https://api.nodesecurity.io/check";
|
public static final String DEFAULT_URL = "https://api.nodesecurity.io/check";
|
||||||
|
/**
|
||||||
|
* A descriptor for the type of dependencies processed or added by this
|
||||||
|
* analyzer.
|
||||||
|
*/
|
||||||
|
public static final String DEPENDENCY_ECOSYSTEM = NPM_DEPENDENCY_ECOSYSTEM;
|
||||||
/**
|
/**
|
||||||
* The file name to scan.
|
* The file name to scan.
|
||||||
*/
|
*/
|
||||||
@@ -113,6 +111,16 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
throw new InitializationException("The configured URL to Node Security Platform is malformed", ex);
|
throw new InitializationException("The configured URL to Node Security Platform is malformed", ex);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
final Settings settings = engine.getSettings();
|
||||||
|
final boolean nodeEnabled = settings.getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED);
|
||||||
|
if (!nodeEnabled) {
|
||||||
|
LOGGER.warn("The Node Package Analyzer has been disabled; the resulting report will only "
|
||||||
|
+ " contain the known vulnerable dependency - not a bill of materials for the node project.");
|
||||||
|
}
|
||||||
|
} catch (InvalidSettingException ex) {
|
||||||
|
throw new InitializationException("Unable to read configuration settings", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,10 +144,10 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the key used in the properties file to reference the analyzer's
|
* Returns the key used in the properties file to determine if the analyzer
|
||||||
* enabled property.x
|
* is enabled.
|
||||||
*
|
*
|
||||||
* @return the analyzer's enabled property setting key
|
* @return the enabled property setting key for the analyzer
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getAnalyzerEnabledSettingKey() {
|
protected String getAnalyzerEnabledSettingKey() {
|
||||||
@@ -148,17 +156,13 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
|
||||||
|
engine.removeDependency(dependency);
|
||||||
final File file = dependency.getActualFile();
|
final File file = dependency.getActualFile();
|
||||||
if (!file.isFile() || file.length() == 0) {
|
if (!file.isFile() || file.length() == 0 || !shouldProcess(file)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) {
|
|
||||||
|
|
||||||
// Do not scan the node_modules directory
|
try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) {
|
||||||
if (file.getCanonicalPath().contains(File.separator + "node_modules" + File.separator)) {
|
|
||||||
LOGGER.debug("Skipping analysis of node module: " + file.getCanonicalPath());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves the contents of package.json from the Dependency
|
// Retrieves the contents of package.json from the Dependency
|
||||||
final JsonObject packageJson = jsonReader.readObject();
|
final JsonObject packageJson = jsonReader.readObject();
|
||||||
@@ -192,77 +196,22 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
* Create a single vulnerable software object - these do not use CPEs unlike the NVD.
|
* Create a single vulnerable software object - these do not use CPEs unlike the NVD.
|
||||||
*/
|
*/
|
||||||
final VulnerableSoftware vs = new VulnerableSoftware();
|
final VulnerableSoftware vs = new VulnerableSoftware();
|
||||||
//vs.setVersion(advisory.getVulnerableVersions());
|
//TODO consider changing this to available versions on the dependency
|
||||||
vs.setUpdate(advisory.getPatchedVersions());
|
// - the update is a part of the version, not versions to update to
|
||||||
|
//vs.setUpdate(advisory.getPatchedVersions());
|
||||||
|
|
||||||
vs.setName(advisory.getModule() + ":" + advisory.getVulnerableVersions());
|
vs.setName(advisory.getModule() + ":" + advisory.getVulnerableVersions());
|
||||||
vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs)));
|
vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs)));
|
||||||
|
|
||||||
// Add the vulnerability to package.json
|
final Dependency existing = findDependency(engine, advisory.getModule(), advisory.getVersion());
|
||||||
dependency.addVulnerability(vuln);
|
if (existing == null) {
|
||||||
}
|
final Dependency nodeModule = createDependency(dependency, advisory.getModule(), advisory.getVersion(), "transitive");
|
||||||
|
nodeModule.addVulnerability(vuln);
|
||||||
/*
|
engine.addDependency(nodeModule);
|
||||||
* Adds evidence about the node package itself, not any of the modules.
|
|
||||||
*/
|
|
||||||
if (packageJson.containsKey("name")) {
|
|
||||||
final Object value = packageJson.get("name");
|
|
||||||
if (value instanceof JsonString) {
|
|
||||||
final String valueString = ((JsonString) value).getString();
|
|
||||||
dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
|
|
||||||
dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name_project",
|
|
||||||
String.format("%s_project", valueString), Confidence.LOW);
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("JSON value not string as expected: {}", value);
|
existing.addVulnerability(vuln);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Processes the dependencies objects in package.json and adds all the modules as related dependencies
|
|
||||||
*/
|
|
||||||
if (packageJson.containsKey("dependencies")) {
|
|
||||||
final JsonObject dependencies = packageJson.getJsonObject("dependencies");
|
|
||||||
processPackage(dependency, dependencies, "dependencies");
|
|
||||||
}
|
|
||||||
if (packageJson.containsKey("devDependencies")) {
|
|
||||||
final JsonObject dependencies = packageJson.getJsonObject("devDependencies");
|
|
||||||
processPackage(dependency, dependencies, "devDependencies");
|
|
||||||
}
|
|
||||||
if (packageJson.containsKey("optionalDependencies")) {
|
|
||||||
final JsonObject dependencies = packageJson.getJsonObject("optionalDependencies");
|
|
||||||
processPackage(dependency, dependencies, "optionalDependencies");
|
|
||||||
}
|
|
||||||
if (packageJson.containsKey("peerDependencies")) {
|
|
||||||
final JsonObject dependencies = packageJson.getJsonObject("peerDependencies");
|
|
||||||
processPackage(dependency, dependencies, "peerDependencies");
|
|
||||||
}
|
|
||||||
if (packageJson.containsKey("bundleDependencies")) {
|
|
||||||
final JsonArray dependencies = packageJson.getJsonArray("bundleDependencies");
|
|
||||||
processPackage(dependency, dependencies, "bundleDependencies");
|
|
||||||
}
|
|
||||||
if (packageJson.containsKey("bundledDependencies")) {
|
|
||||||
final JsonArray dependencies = packageJson.getJsonArray("bundledDependencies");
|
|
||||||
processPackage(dependency, dependencies, "bundledDependencies");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adds the license if defined in package.json
|
|
||||||
*/
|
|
||||||
if (packageJson.containsKey("license")) {
|
|
||||||
final Object value = packageJson.get("license");
|
|
||||||
if (value instanceof JsonString) {
|
|
||||||
dependency.setLicense(packageJson.getString("license"));
|
|
||||||
} else {
|
|
||||||
dependency.setLicense(packageJson.getJsonObject("license").getString("type"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adds general evidence to about the package.
|
|
||||||
*/
|
|
||||||
addToEvidence(dependency, EvidenceType.PRODUCT, packageJson, "description");
|
|
||||||
addToEvidence(dependency, EvidenceType.VENDOR, packageJson, "author");
|
|
||||||
addToEvidence(dependency, EvidenceType.VERSION, packageJson, "version");
|
|
||||||
dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName()));
|
|
||||||
} catch (URLConnectionFailureException e) {
|
} catch (URLConnectionFailureException e) {
|
||||||
this.setEnabled(false);
|
this.setEnabled(false);
|
||||||
throw new AnalysisException(e.getMessage(), e);
|
throw new AnalysisException(e.getMessage(), e);
|
||||||
@@ -274,98 +223,4 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
throw new AnalysisException(String.format("Failed to parse %s file.", file.getPath()), e);
|
throw new AnalysisException(String.format("Failed to parse %s file.", file.getPath()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a part of package.json (as defined by JsonArray) and update the
|
|
||||||
* specified dependency with relevant info.
|
|
||||||
*
|
|
||||||
* @param dependency the Dependency to update
|
|
||||||
* @param jsonArray the jsonArray to parse
|
|
||||||
* @param depType the dependency type
|
|
||||||
*/
|
|
||||||
private void processPackage(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(dependency, jsonObject, depType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a part of package.json (as defined by JsonObject) and update
|
|
||||||
* the specified dependency with relevant info.
|
|
||||||
*
|
|
||||||
* @param dependency the Dependency to update
|
|
||||||
* @param jsonObject the jsonObject to parse
|
|
||||||
* @param depType the dependency type
|
|
||||||
*/
|
|
||||||
private void processPackage(Dependency dependency, JsonObject jsonObject, String depType) {
|
|
||||||
for (int i = 0; i < jsonObject.size(); i++) {
|
|
||||||
for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
|
|
||||||
/*
|
|
||||||
* Create identifies that include the npm module and version. Since these are defined,
|
|
||||||
* assign the highest confidence.
|
|
||||||
*/
|
|
||||||
final Identifier moduleName = new Identifier("npm", "Module", null, entry.getKey());
|
|
||||||
moduleName.setConfidence(Confidence.HIGHEST);
|
|
||||||
String version = "";
|
|
||||||
if (entry.getValue() != null && entry.getValue().getValueType() == JsonValue.ValueType.STRING) {
|
|
||||||
version = ((JsonString) entry.getValue()).getString();
|
|
||||||
}
|
|
||||||
final Identifier moduleVersion = new Identifier("npm", "Version", null, version);
|
|
||||||
moduleVersion.setConfidence(Confidence.HIGHEST);
|
|
||||||
|
|
||||||
final Identifier moduleDepType = new Identifier("npm", "Scope", null, depType);
|
|
||||||
moduleVersion.setConfidence(Confidence.HIGHEST);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create related dependencies for each module defined in package.json. The path to the related
|
|
||||||
* dependency will not actually exist but needs to be unique (due to the use of Set in Dependency).
|
|
||||||
* The use of related dependencies is a way to specify the actual software BOM in package.json.
|
|
||||||
*/
|
|
||||||
//TODO is this actually correct? or should these be transitive dependencies?
|
|
||||||
final Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "#" + entry.getKey()), true);
|
|
||||||
nodeModule.setDisplayFileName(entry.getKey());
|
|
||||||
nodeModule.addIdentifier(moduleName);
|
|
||||||
nodeModule.addIdentifier(moduleVersion);
|
|
||||||
nodeModule.addIdentifier(moduleDepType);
|
|
||||||
dependency.addRelatedDependency(nodeModule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds information to an evidence collection from the node json
|
|
||||||
* configuration.
|
|
||||||
*
|
|
||||||
* @param dep the dependency to which the evidence will be added
|
|
||||||
* @param type the type of evidence to be added
|
|
||||||
* @param json information from node.js
|
|
||||||
* @param key the key to obtain the data from the json information
|
|
||||||
*/
|
|
||||||
private void addToEvidence(Dependency dep, EvidenceType type, JsonObject json, String key) {
|
|
||||||
if (json.containsKey(key)) {
|
|
||||||
final JsonValue value = json.get(key);
|
|
||||||
if (value instanceof JsonString) {
|
|
||||||
dep.addEvidence(type, PACKAGE_JSON, key, ((JsonString) value).getString(), 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) {
|
|
||||||
dep.addEvidence(type, PACKAGE_JSON,
|
|
||||||
String.format("%s.%s", key, property),
|
|
||||||
((JsonString) subValue).getString(),
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
public static final String SPM_FILE_NAME = "Package.swift";
|
public static final String SPM_FILE_NAME = "Package.swift";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter that detects files named "package.json".
|
* Filter that detects files named "Package.swift".
|
||||||
*/
|
*/
|
||||||
private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
|
private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ public class NspSearch {
|
|||||||
try (InputStream in = new BufferedInputStream(conn.getInputStream());
|
try (InputStream in = new BufferedInputStream(conn.getInputStream());
|
||||||
JsonReader jsonReader = Json.createReader(in)) {
|
JsonReader jsonReader = Json.createReader(in)) {
|
||||||
final JsonArray array = jsonReader.readArray();
|
final JsonArray array = jsonReader.readArray();
|
||||||
|
|
||||||
if (array != null) {
|
if (array != null) {
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
final JsonObject object = array.getJsonObject(i);
|
final JsonObject object = array.getJsonObject(i);
|
||||||
|
|||||||
@@ -123,9 +123,13 @@ public class EscapeTool {
|
|||||||
*/
|
*/
|
||||||
public String csv(String text) {
|
public String csv(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
return text;
|
return "\"\"";
|
||||||
}
|
}
|
||||||
return StringEscapeUtils.escapeCsv(text.trim().replace("\n", " "));
|
final String str = text.trim().replace("\n", " ");
|
||||||
|
if (str.length() == 0) {
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
|
return StringEscapeUtils.escapeCsv(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +141,7 @@ public class EscapeTool {
|
|||||||
*/
|
*/
|
||||||
public String csvIdentifiers(Set<Identifier> ids) {
|
public String csvIdentifiers(Set<Identifier> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return "";
|
return "\"\"";
|
||||||
}
|
}
|
||||||
boolean addComma = false;
|
boolean addComma = false;
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
@@ -151,6 +155,9 @@ public class EscapeTool {
|
|||||||
sb.append(id.getValue());
|
sb.append(id.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +170,7 @@ public class EscapeTool {
|
|||||||
*/
|
*/
|
||||||
public String csvCpe(Set<Identifier> ids) {
|
public String csvCpe(Set<Identifier> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return "";
|
return "\"\"";
|
||||||
}
|
}
|
||||||
boolean addComma = false;
|
boolean addComma = false;
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
@@ -177,6 +184,9 @@ public class EscapeTool {
|
|||||||
sb.append(id.getValue());
|
sb.append(id.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +199,7 @@ public class EscapeTool {
|
|||||||
*/
|
*/
|
||||||
public String csvCpeConfidence(Set<Identifier> ids) {
|
public String csvCpeConfidence(Set<Identifier> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return "";
|
return "\"\"";
|
||||||
}
|
}
|
||||||
boolean addComma = false;
|
boolean addComma = false;
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
@@ -203,6 +213,9 @@ public class EscapeTool {
|
|||||||
sb.append(id.getConfidence());
|
sb.append(id.getConfidence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,12 +228,12 @@ public class EscapeTool {
|
|||||||
*/
|
*/
|
||||||
public String csvGav(Set<Identifier> ids) {
|
public String csvGav(Set<Identifier> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return "";
|
return "\"\"";
|
||||||
}
|
}
|
||||||
boolean addComma = false;
|
boolean addComma = false;
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for (Identifier id : ids) {
|
for (Identifier id : ids) {
|
||||||
if ("maven".equals(id.getType())) {
|
if ("maven".equals(id.getType()) || "npm".equals(id.getType())) {
|
||||||
if (addComma) {
|
if (addComma) {
|
||||||
sb.append(", ");
|
sb.append(", ");
|
||||||
} else {
|
} else {
|
||||||
@@ -229,7 +242,9 @@ public class EscapeTool {
|
|||||||
sb.append(id.getValue());
|
sb.append(id.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,8 +145,8 @@ public class H2DBLock {
|
|||||||
if (lock == null || !lock.isValid()) {
|
if (lock == null || !lock.isValid()) {
|
||||||
try {
|
try {
|
||||||
final Timestamp timestamp = new Timestamp(System.currentTimeMillis());
|
final Timestamp timestamp = new Timestamp(System.currentTimeMillis());
|
||||||
LOGGER.debug("Sleeping thread {} ({}) for 10 seconds because an exclusive lock on the database could not be obtained ({})",
|
LOGGER.debug("Sleeping thread {} ({}) for {} seconds because an exclusive lock on the database could not be obtained ({})",
|
||||||
Thread.currentThread().getName(), magic, timestamp.toString());
|
Thread.currentThread().getName(), magic, SLEEP_DURATION / 1000, timestamp.toString());
|
||||||
Thread.sleep(SLEEP_DURATION);
|
Thread.sleep(SLEEP_DURATION);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
LOGGER.debug("sleep was interrupted.", ex);
|
LOGGER.debug("sleep was interrupted.", ex);
|
||||||
|
|||||||
@@ -127,3 +127,5 @@ analyzer.vulnerabilitysuppression.enabled=true
|
|||||||
updater.nvdcve.enabled=true
|
updater.nvdcve.enabled=true
|
||||||
updater.versioncheck.enabled=true
|
updater.versioncheck.enabled=true
|
||||||
analyzer.versionfilter.enabled=true
|
analyzer.versionfilter.enabled=true
|
||||||
|
|
||||||
|
ecosystem.skip.cpeanalyzer=npm
|
||||||
@@ -17,7 +17,7 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved.
|
|||||||
|
|
||||||
@author Jeremy Long <jeremy.long@owasp.org>
|
@author Jeremy Long <jeremy.long@owasp.org>
|
||||||
@version 1 *###
|
@version 1 *###
|
||||||
"Project","ScanDate","DependencyName","DependencyPath","Description","License","Md5","Sha1","Identifiers","CPE","CVE","CWE","Vulnerability","Source","Severity","CVSSv2","GAV","CPE Confidence","Evidence Count"
|
"Project","ScanDate","DependencyName","DependencyPath","Description","License","Md5","Sha1","Identifiers","CPE","CVE","CWE","Vulnerability","Source","Severity","CVSSv2","Build Coordinates","CPE Confidence","Evidence Count"
|
||||||
#macro(writeSev $score)#if($score<4.0)"Low"#elseif($score>=7.0)"High"#else"Medium"#end#end
|
#macro(writeSev $score)#if($score<4.0)"Low"#elseif($score>=7.0)"High"#else"Medium"#end#end
|
||||||
#foreach($dependency in $dependencies)#if($dependency.getVulnerabilities().size()>0)
|
#foreach($dependency in $dependencies)#if($dependency.getVulnerabilities().size()>0)
|
||||||
#foreach($vuln in $dependency.getVulnerabilities(true))
|
#foreach($vuln in $dependency.getVulnerabilities(true))
|
||||||
|
|||||||
@@ -623,7 +623,7 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
|
|||||||
<thead><tr style="text-align:left">
|
<thead><tr style="text-align:left">
|
||||||
<th class="sortable" data-sort="string" title="The name of the dependency">Dependency</th>
|
<th class="sortable" data-sort="string" title="The name of the dependency">Dependency</th>
|
||||||
<th class="sortable" data-sort="string" title="The Common Platform Enumeration">CPE</th>
|
<th class="sortable" data-sort="string" title="The Common Platform Enumeration">CPE</th>
|
||||||
<th class="sortable" data-sort="string" title="The Maven GAV Coordinates">GAV</th>
|
<th class="sortable" data-sort="string" title="The Build Coordinates">Coordinates</th>
|
||||||
<th class="sortable" data-sort="int" title="The highest CVE Severity">Highest Severity</th>
|
<th class="sortable" data-sort="int" title="The highest CVE Severity">Highest Severity</th>
|
||||||
<th class="sortable" data-sort="int" title="The number of Common Vulnerability and Exposure (CVE) entries">CVE Count</th>
|
<th class="sortable" data-sort="int" title="The number of Common Vulnerability and Exposure (CVE) entries">CVE Count</th>
|
||||||
<th class="sortable" data-sort="string" title="The confidence rating dependency-check has for the identified CPE">CPE Confidence</th>
|
<th class="sortable" data-sort="string" title="The confidence rating dependency-check has for the identified CPE">CPE Confidence</th>
|
||||||
@@ -638,7 +638,7 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
|
|||||||
#set($cpeIdConf="")
|
#set($cpeIdConf="")
|
||||||
#set($sortValue="")
|
#set($sortValue="")
|
||||||
#foreach($id in $dependency.getIdentifiers())
|
#foreach($id in $dependency.getIdentifiers())
|
||||||
#if ($id.type!="maven")
|
#if ($id.type!="maven" && $id.type!="npm")
|
||||||
#set($sortValue=$sortValue+$id.value)
|
#set($sortValue=$sortValue+$id.value)
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
@@ -646,7 +646,7 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
|
|||||||
#set($sortValue="")
|
#set($sortValue="")
|
||||||
#set($cpeSort=0)
|
#set($cpeSort=0)
|
||||||
#foreach($id in $dependency.getIdentifiers())
|
#foreach($id in $dependency.getIdentifiers())
|
||||||
#if ($id.type=="maven")
|
#if ($id.type=="maven" || $id.type=="npm")
|
||||||
#if ($mavenlink=="" || !$mavenlink.url)
|
#if ($mavenlink=="" || !$mavenlink.url)
|
||||||
#set($mavenlink=$id)
|
#set($mavenlink=$id)
|
||||||
#end
|
#end
|
||||||
@@ -778,7 +778,7 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
|
|||||||
</li>
|
</li>
|
||||||
#end
|
#end
|
||||||
#if ($id.type=="npm")
|
#if ($id.type=="npm")
|
||||||
<li>$enc.html($id.value): $enc.html($id.description)</li>
|
<li>$enc.html($id.value)</li>
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -41,22 +41,15 @@
|
|||||||
"identifiers": [
|
"identifiers": [
|
||||||
#set($loopCount=0)
|
#set($loopCount=0)
|
||||||
#foreach($id in $related.getIdentifiers())
|
#foreach($id in $related.getIdentifiers())
|
||||||
#if ($id.type=="maven")
|
#if ($id.type=="maven" || $id.type=="npm")
|
||||||
#set($loopCount=$loopCount+1)
|
#set($loopCount=$loopCount+1)
|
||||||
#if($loopCount>1),#end
|
#if($loopCount>1),#end
|
||||||
{
|
{
|
||||||
"type": "$enc.json($id.type)",
|
"type": "$enc.json($id.type)",
|
||||||
"name": "$id.value"
|
"id": "$id.value"
|
||||||
#if ($id.url),"url": "$enc.json($id.url)"#end
|
#if ($id.url),"url": "$enc.json($id.url)"#end
|
||||||
#if ($id.notes),"notes": "$enc.json($id.notes)"#end
|
#if ($id.notes),"notes": "$enc.json($id.notes)"#end
|
||||||
}
|
#if ($id.description),"description":"$enc.json($id.description)"#end
|
||||||
#end
|
|
||||||
#if ($id.type=="npm")
|
|
||||||
#set($loopCount=$loopCount+1)
|
|
||||||
#if($loopCount>1),#end
|
|
||||||
{
|
|
||||||
"id":"$enc.json($id.value)"
|
|
||||||
,"description":"$enc.json($id.description)"
|
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
|
|||||||
@@ -76,16 +76,12 @@ public class AnalyzerServiceTest extends BaseDBTestCase {
|
|||||||
AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), getSettings());
|
AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), getSettings());
|
||||||
List<Analyzer> result = instance.getAnalyzers();
|
List<Analyzer> result = instance.getAnalyzers();
|
||||||
String experimental = "CMake Analyzer";
|
String experimental = "CMake Analyzer";
|
||||||
String retired = "Node.js Package Analyzer";
|
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
boolean retiredFound = false;
|
boolean retiredFound = false;
|
||||||
for (Analyzer a : result) {
|
for (Analyzer a : result) {
|
||||||
if (experimental.equals(a.getName())) {
|
if (experimental.equals(a.getName())) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (retired.equals(a.getName())) {
|
|
||||||
retiredFound = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assertFalse("Experimental analyzer loaded when set to false", found);
|
assertFalse("Experimental analyzer loaded when set to false", found);
|
||||||
assertFalse("Retired analyzer loaded when set to false", retiredFound);
|
assertFalse("Retired analyzer loaded when set to false", retiredFound);
|
||||||
@@ -99,9 +95,6 @@ public class AnalyzerServiceTest extends BaseDBTestCase {
|
|||||||
if (experimental.equals(a.getName())) {
|
if (experimental.equals(a.getName())) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (retired.equals(a.getName())) {
|
|
||||||
retiredFound = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assertTrue("Experimental analyzer not loaded when set to true", found);
|
assertTrue("Experimental analyzer not loaded when set to true", found);
|
||||||
assertFalse("Retired analyzer loaded when set to false", retiredFound);
|
assertFalse("Retired analyzer loaded when set to false", retiredFound);
|
||||||
@@ -116,11 +109,8 @@ public class AnalyzerServiceTest extends BaseDBTestCase {
|
|||||||
if (experimental.equals(a.getName())) {
|
if (experimental.equals(a.getName())) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (retired.equals(a.getName())) {
|
|
||||||
retiredFound = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assertFalse("Experimental analyzer loaded when set to false", found);
|
assertFalse("Experimental analyzer loaded when set to false", found);
|
||||||
assertTrue("Retired analyzer not loaded when set to true", retiredFound);
|
//assertTrue("Retired analyzer not loaded when set to true", retiredFound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.io.File;
|
|||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import org.owasp.dependencycheck.Engine;
|
||||||
import org.owasp.dependencycheck.dependency.EvidenceType;
|
import org.owasp.dependencycheck.dependency.EvidenceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +43,10 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Correctly setup the analyzer for testing.
|
* Correctly setup the analyzer for testing.
|
||||||
@@ -52,14 +57,15 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
engine = new Engine(this.getSettings());
|
||||||
analyzer = new NodePackageAnalyzer();
|
analyzer = new NodePackageAnalyzer();
|
||||||
analyzer.setFilesMatched(true);
|
analyzer.setFilesMatched(true);
|
||||||
analyzer.initialize(getSettings());
|
analyzer.initialize(getSettings());
|
||||||
analyzer.prepare(null);
|
analyzer.prepare(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup the analyzer's temp files, etc.
|
* Cleanup temp files, close resources, etc.
|
||||||
*
|
*
|
||||||
* @throws Exception thrown if there is a problem
|
* @throws Exception thrown if there is a problem
|
||||||
*/
|
*/
|
||||||
@@ -67,6 +73,7 @@ public class NodePackageAnalyzerTest extends BaseTest {
|
|||||||
@Override
|
@Override
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
analyzer.close();
|
analyzer.close();
|
||||||
|
engine.close();
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,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("npm-shrinkwrap.json")), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,17 +100,39 @@ 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/npm-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_project"));
|
assertThat(vendorString, containsString("dns-sync"));
|
||||||
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dns-sync"));
|
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dns-sync"));
|
||||||
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.1.0"));
|
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.1.0"));
|
||||||
assertEquals(NodePackageAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
|
assertEquals(NodePackageAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
|
||||||
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/npm-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;
|
||||||
@@ -11,91 +9,91 @@ import java.io.File;
|
|||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import org.owasp.dependencycheck.Engine;
|
||||||
import org.owasp.dependencycheck.dependency.EvidenceType;
|
import org.owasp.dependencycheck.dependency.EvidenceType;
|
||||||
|
import org.owasp.dependencycheck.exception.InitializationException;
|
||||||
|
|
||||||
public class NspAnalyzerTest extends BaseTest {
|
public class NspAnalyzerTest extends BaseTest {
|
||||||
|
|
||||||
private NspAnalyzer analyzer;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
analyzer = new NspAnalyzer();
|
|
||||||
analyzer.setFilesMatched(true);
|
|
||||||
analyzer.initialize(getSettings());
|
|
||||||
analyzer.prepare(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
@Override
|
|
||||||
public void tearDown() throws Exception {
|
|
||||||
analyzer.close();
|
|
||||||
super.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetName() {
|
public void testGetName() {
|
||||||
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
assertThat(analyzer.getName(), is("Node Security Platform Analyzer"));
|
assertThat(analyzer.getName(), is("Node Security Platform Analyzer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSupportsFiles() {
|
public void testSupportsFiles() {
|
||||||
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
assertThat(analyzer.accept(new File("package.json")), is(true));
|
assertThat(analyzer.accept(new File("package.json")), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzePackage() throws AnalysisException {
|
public void testAnalyzePackage() throws AnalysisException, InitializationException {
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json"));
|
try (Engine engine = new Engine(getSettings())) {
|
||||||
analyzer.analyze(result, null);
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
|
analyzer.setFilesMatched(true);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("owasp-nodejs-goat_project"));
|
analyzer.initialize(getSettings());
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("A tool to learn OWASP Top 10 for node.js developers"));
|
analyzer.prepare(engine);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.3.0"));
|
final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json"));
|
||||||
|
analyzer.analyze(toScan, engine);
|
||||||
|
boolean found = false;
|
||||||
|
assertEquals("4 dependencies should be identified", 4, engine.getDependencies().length);
|
||||||
|
for (Dependency result : engine.getDependencies()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzeEmpty() throws AnalysisException {
|
public void testAnalyzeEmpty() throws AnalysisException, InitializationException {
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/empty.json"));
|
try (Engine engine = new Engine(getSettings())) {
|
||||||
analyzer.analyze(result, null);
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
|
analyzer.setFilesMatched(true);
|
||||||
|
analyzer.initialize(getSettings());
|
||||||
|
analyzer.prepare(engine);
|
||||||
|
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/empty.json"));
|
||||||
|
analyzer.analyze(result, engine);
|
||||||
|
|
||||||
assertEquals(result.getEvidence(EvidenceType.VENDOR).size(), 0);
|
assertEquals(result.getEvidence(EvidenceType.VENDOR).size(), 0);
|
||||||
assertEquals(result.getEvidence(EvidenceType.PRODUCT).size(), 0);
|
assertEquals(result.getEvidence(EvidenceType.PRODUCT).size(), 0);
|
||||||
assertEquals(result.getEvidence(EvidenceType.VERSION).size(), 0);
|
assertEquals(result.getEvidence(EvidenceType.VERSION).size(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzePackageJsonWithBundledDeps() throws AnalysisException {
|
public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException, InitializationException {
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/bundled.deps.package.json"));
|
try (Engine engine = new Engine(getSettings())) {
|
||||||
analyzer.analyze(result, null);
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
|
analyzer.setFilesMatched(true);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Philipp Dunkel <pip@pipobscure.com>"));
|
analyzer.initialize(getSettings());
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("Native Access to Mac OS-X FSEvents"));
|
analyzer.prepare(engine);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.1.1"));
|
final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json"));
|
||||||
|
engine.addDependency(toScan);
|
||||||
|
analyzer.analyze(toScan, engine);
|
||||||
|
assertEquals("No dependencies should exist", 0, engine.getDependencies().length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnalyzePackageJsonWithLicenseObject() throws AnalysisException {
|
public void testAnalyzeInvalidPackageMissingName() throws AnalysisException, InitializationException {
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/license.obj.package.json"));
|
try (Engine engine = new Engine(getSettings())) {
|
||||||
analyzer.analyze(result, null);
|
NspAnalyzer analyzer = new NspAnalyzer();
|
||||||
|
analyzer.setFilesMatched(true);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Twitter, Inc."));
|
analyzer.initialize(getSettings());
|
||||||
assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("The most popular front-end framework for developing responsive, mobile first projects on the web"));
|
analyzer.prepare(engine);
|
||||||
assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("3.2.0"));
|
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/minimal-invalid.json"));
|
||||||
}
|
analyzer.analyze(result, engine);
|
||||||
|
// Upon analysis, not throwing an exception in this case, is all that's required to pass this test
|
||||||
@Test
|
} catch(Throwable ex) {
|
||||||
public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException {
|
fail("This test should not throw an exception");
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json"));
|
throw ex;
|
||||||
analyzer.analyze(result, null);
|
}
|
||||||
// node modules are not scanned - no evidence is collected
|
|
||||||
assertTrue(result.size() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnalyzeInvalidPackageMissingName() throws AnalysisException {
|
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/minimal-invalid.json"));
|
|
||||||
analyzer.analyze(result, null);
|
|
||||||
// Upon analysis, not throwing an exception in this case, is all that's required to pass this test
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,12 +126,12 @@ public class EscapeToolTest {
|
|||||||
public void testCsv() {
|
public void testCsv() {
|
||||||
String text = null;
|
String text = null;
|
||||||
EscapeTool instance = new EscapeTool();
|
EscapeTool instance = new EscapeTool();
|
||||||
String expResult = null;
|
String expResult = "\"\"";
|
||||||
String result = instance.csv(text);
|
String result = instance.csv(text);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
text = "";
|
text = "";
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csv(text);
|
result = instance.csv(text);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
@@ -148,38 +148,38 @@ public class EscapeToolTest {
|
|||||||
public void testCsvIdentifiers() {
|
public void testCsvIdentifiers() {
|
||||||
EscapeTool instance = new EscapeTool();
|
EscapeTool instance = new EscapeTool();
|
||||||
Set<Identifier> ids = null;
|
Set<Identifier> ids = null;
|
||||||
String expResult = "";
|
String expResult = "\"\"";
|
||||||
String result = instance.csvIdentifiers(ids);
|
String result = instance.csvIdentifiers(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvIdentifiers(ids);
|
result = instance.csvIdentifiers(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvIdentifiers(ids);
|
result = instance.csvIdentifiers(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
expResult = "somegroup:something:1.0";
|
expResult = "somegroup:something:1.0";
|
||||||
result = instance.csvIdentifiers(ids);
|
result = instance.csvIdentifiers(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
expResult = "somegroup:something:1.0";
|
expResult = "somegroup:something:1.0";
|
||||||
result = instance.csvIdentifiers(ids);
|
result = instance.csvIdentifiers(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("gav", "somegroup2:something:1.2", ""));
|
ids.add(new Identifier("maven", "somegroup2:something:1.2", ""));
|
||||||
expResult = "\"somegroup:something:1.0, somegroup2:something:1.2\"";
|
expResult = "\"somegroup:something:1.0, somegroup2:something:1.2\"";
|
||||||
String expResult2 = "\"somegroup2:something:1.2, somegroup:something:1.0\"";
|
String expResult2 = "\"somegroup2:something:1.2, somegroup:something:1.0\"";
|
||||||
result = instance.csvIdentifiers(ids);
|
result = instance.csvIdentifiers(ids);
|
||||||
@@ -193,18 +193,18 @@ public class EscapeToolTest {
|
|||||||
public void testCsvCpe() {
|
public void testCsvCpe() {
|
||||||
EscapeTool instance = new EscapeTool();
|
EscapeTool instance = new EscapeTool();
|
||||||
Set<Identifier> ids = null;
|
Set<Identifier> ids = null;
|
||||||
String expResult = "";
|
String expResult = "\"\"";
|
||||||
String result = instance.csvCpe(ids);
|
String result = instance.csvCpe(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvCpe(ids);
|
result = instance.csvCpe(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvCpe(ids);
|
result = instance.csvCpe(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
@@ -216,14 +216,14 @@ public class EscapeToolTest {
|
|||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
expResult = "cpe:/a:somegroup:something:1.0";
|
expResult = "cpe:/a:somegroup:something:1.0";
|
||||||
result = instance.csvCpe(ids);
|
result = instance.csvCpe(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup2:something:1.2", ""));
|
ids.add(new Identifier("cpe", "cpe:/a:somegroup2:something:1.2", ""));
|
||||||
expResult = "\"cpe:/a:somegroup:something:1.0, cpe:/a:somegroup2:something:1.2\"";
|
expResult = "\"cpe:/a:somegroup:something:1.0, cpe:/a:somegroup2:something:1.2\"";
|
||||||
String expResult2 = "\"cpe:/a:somegroup2:something:1.2, cpe:/a:somegroup:something:1.0\"";
|
String expResult2 = "\"cpe:/a:somegroup2:something:1.2, cpe:/a:somegroup:something:1.0\"";
|
||||||
@@ -238,18 +238,18 @@ public class EscapeToolTest {
|
|||||||
public void testCsvCpeConfidence() {
|
public void testCsvCpeConfidence() {
|
||||||
EscapeTool instance = new EscapeTool();
|
EscapeTool instance = new EscapeTool();
|
||||||
Set<Identifier> ids = null;
|
Set<Identifier> ids = null;
|
||||||
String expResult = "";
|
String expResult = "\"\"";
|
||||||
String result = instance.csvCpeConfidence(ids);
|
String result = instance.csvCpeConfidence(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvCpeConfidence(ids);
|
result = instance.csvCpeConfidence(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("maven", "somegroup:something:1.0", ""));
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvCpeConfidence(ids);
|
result = instance.csvCpeConfidence(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ public class EscapeToolTest {
|
|||||||
Identifier i2 = new Identifier("cpe", "cpe:/a:somegroup:something2:1.0", "");
|
Identifier i2 = new Identifier("cpe", "cpe:/a:somegroup:something2:1.0", "");
|
||||||
i2.setConfidence(Confidence.MEDIUM);
|
i2.setConfidence(Confidence.MEDIUM);
|
||||||
ids.add(i2);
|
ids.add(i2);
|
||||||
Identifier i3 = new Identifier("gav", "somegroup:something:1.0", "");
|
Identifier i3 = new Identifier("maven", "somegroup:something:1.0", "");
|
||||||
i3.setConfidence(Confidence.LOW);
|
i3.setConfidence(Confidence.LOW);
|
||||||
ids.add(i3);
|
ids.add(i3);
|
||||||
|
|
||||||
@@ -285,18 +285,18 @@ public class EscapeToolTest {
|
|||||||
public void testCsvGav() {
|
public void testCsvGav() {
|
||||||
EscapeTool instance = new EscapeTool();
|
EscapeTool instance = new EscapeTool();
|
||||||
Set<Identifier> ids = null;
|
Set<Identifier> ids = null;
|
||||||
String expResult = "";
|
String expResult = "\"\"";
|
||||||
String result = instance.csvGav(ids);
|
String result = instance.csvGav(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvGav(ids);
|
result = instance.csvGav(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
ids = new HashSet<>();
|
ids = new HashSet<>();
|
||||||
ids.add(new Identifier("cpe", "somegroup:something:1.0", ""));
|
ids.add(new Identifier("cpe", "somegroup:something:1.0", ""));
|
||||||
expResult = "";
|
expResult = "\"\"";
|
||||||
result = instance.csvGav(ids);
|
result = instance.csvGav(ids);
|
||||||
assertEquals(expResult, result);
|
assertEquals(expResult, result);
|
||||||
|
|
||||||
|
|||||||
@@ -123,3 +123,5 @@ analyzer.nvdcve.enabled=true
|
|||||||
analyzer.vulnerabilitysuppression.enabled=true
|
analyzer.vulnerabilitysuppression.enabled=true
|
||||||
updater.nvdcve.enabled=true
|
updater.nvdcve.enabled=true
|
||||||
updater.versioncheck.enabled=true
|
updater.versioncheck.enabled=true
|
||||||
|
|
||||||
|
ecosystem.skip.cpeanalyzer=npm
|
||||||
18
dependency-check-core/src/test/resources/nodejs/npm-shrinkwrap.json
generated
Normal file
18
dependency-check-core/src/test/resources/nodejs/npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dns-sync": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dns-sync/-/dns-sync-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-gPcpFC513UtfSx0+Upcx7jEplHI=",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"shelljs": "0.5.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
dependency-check-core/src/test/resources/nodejs/package-lock.json
generated
Normal file
17
dependency-check-core/src/test/resources/nodejs/package-lock.json
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dns-sync": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dns-sync/-/dns-sync-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-gPcpFC513UtfSx0+Upcx7jEplHI=",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"shelljs": "0.5.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fsevents",
|
|
||||||
"version": "1.1.1",
|
|
||||||
"description": "Native Access to Mac OS-X FSEvents",
|
|
||||||
"main": "fsevents.js",
|
|
||||||
"dependencies": {
|
|
||||||
"nan": "^2.3.0",
|
|
||||||
"node-pre-gyp": "^0.6.29"
|
|
||||||
},
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"install": "node install",
|
|
||||||
"prepublish": "if [ $(npm -v | head -c 1) -lt 3 ]; then exit 1; fi && npm dedupe",
|
|
||||||
"test": "tap ./test"
|
|
||||||
},
|
|
||||||
"binary": {
|
|
||||||
"module_name": "fse",
|
|
||||||
"module_path": "./lib/binding/{configuration}/{node_abi}-{platform}-{arch}/",
|
|
||||||
"remote_path": "./v{version}/",
|
|
||||||
"package_name": "{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz",
|
|
||||||
"host": "https://fsevents-binaries.s3-us-west-2.amazonaws.com"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/strongloop/fsevents.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"fsevents",
|
|
||||||
"mac"
|
|
||||||
],
|
|
||||||
"author": "Philipp Dunkel <pip@pipobscure.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/strongloop/fsevents/issues"
|
|
||||||
},
|
|
||||||
"bundledDependencies": [
|
|
||||||
"node-pre-gyp"
|
|
||||||
],
|
|
||||||
"homepage": "https://github.com/strongloop/fsevents",
|
|
||||||
"devDependencies": {
|
|
||||||
"tap": "~0.4.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "bootstrap",
|
|
||||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
|
||||||
"version": "3.2.0",
|
|
||||||
"keywords": [
|
|
||||||
"css",
|
|
||||||
"less",
|
|
||||||
"mobile-first",
|
|
||||||
"responsive",
|
|
||||||
"front-end",
|
|
||||||
"framework",
|
|
||||||
"web"
|
|
||||||
],
|
|
||||||
"homepage": "http://getbootstrap.com",
|
|
||||||
"author": "Twitter, Inc.",
|
|
||||||
"scripts": {
|
|
||||||
"test": "grunt test"
|
|
||||||
},
|
|
||||||
"style": "dist/css/bootstrap.css",
|
|
||||||
"less": "less/bootstrap.less",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/twbs/bootstrap.git"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/twbs/bootstrap/issues"
|
|
||||||
},
|
|
||||||
"license": {
|
|
||||||
"type": "MIT",
|
|
||||||
"url": "https://github.com/twbs/bootstrap/blob/master/LICENSE"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"btoa": "~1.1.2",
|
|
||||||
"glob": "~4.0.2",
|
|
||||||
"grunt": "~0.4.5",
|
|
||||||
"grunt-autoprefixer": "~0.7.6",
|
|
||||||
"grunt-banner": "~0.2.3",
|
|
||||||
"grunt-contrib-clean": "~0.5.0",
|
|
||||||
"grunt-contrib-concat": "~0.4.0",
|
|
||||||
"grunt-contrib-connect": "~0.8.0",
|
|
||||||
"grunt-contrib-copy": "~0.5.0",
|
|
||||||
"grunt-contrib-csslint": "~0.2.0",
|
|
||||||
"grunt-contrib-cssmin": "~0.10.0",
|
|
||||||
"grunt-contrib-jade": "~0.12.0",
|
|
||||||
"grunt-contrib-jshint": "~0.10.0",
|
|
||||||
"grunt-contrib-less": "~0.11.3",
|
|
||||||
"grunt-contrib-qunit": "~0.5.1",
|
|
||||||
"grunt-contrib-uglify": "~0.5.0",
|
|
||||||
"grunt-contrib-watch": "~0.6.1",
|
|
||||||
"grunt-csscomb": "~2.0.1",
|
|
||||||
"grunt-exec": "~0.4.5",
|
|
||||||
"grunt-html-validation": "~0.1.18",
|
|
||||||
"grunt-jekyll": "~0.4.2",
|
|
||||||
"grunt-jscs-checker": "~0.6.0",
|
|
||||||
"grunt-saucelabs": "~8.1.0",
|
|
||||||
"grunt-sed": "~0.1.1",
|
|
||||||
"load-grunt-tasks": "~0.6.0",
|
|
||||||
"markdown": "~0.5.0",
|
|
||||||
"npm-shrinkwrap": "~3.1.6",
|
|
||||||
"time-grunt": "~0.3.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "~0.10.1"
|
|
||||||
},
|
|
||||||
"jspm": {
|
|
||||||
"main": "js/bootstrap",
|
|
||||||
"directories": {
|
|
||||||
"example": "examples",
|
|
||||||
"lib": "dist"
|
|
||||||
},
|
|
||||||
"shim": {
|
|
||||||
"js/bootstrap": {
|
|
||||||
"imports": "jquery",
|
|
||||||
"exports": "$"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"buildConfig": {
|
|
||||||
"uglify": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>dependency-check-maven</artifactId>
|
<artifactId>dependency-check-maven</artifactId>
|
||||||
<packaging>maven-plugin</packaging>
|
<packaging>maven-plugin</packaging>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-plugin</artifactId>
|
<artifactId>dependency-check-plugin</artifactId>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved.
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>dependency-check-utils</artifactId>
|
<artifactId>dependency-check-utils</artifactId>
|
||||||
|
|||||||
@@ -442,6 +442,10 @@ public final class Settings {
|
|||||||
* new version available.
|
* new version available.
|
||||||
*/
|
*/
|
||||||
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 CPE analysis.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|||||||
7
pom.xml
7
pom.xml
@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long
|
|||||||
|
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-parent</artifactId>
|
<artifactId>dependency-check-parent</artifactId>
|
||||||
<version>3.0.3-SNAPSHOT</version>
|
<version>3.1.0-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@@ -723,6 +723,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