Compare commits

...

7 Commits

Author SHA1 Message Date
stevespringett
8206aa9bfd Added additional check when submitting an invalid payload to nsp. Corrected unit test. 2017-05-23 11:08:54 -05:00
Jeremy Long
dd4a1f2d56 updated for code coverage 2017-05-23 10:44:00 -05:00
Jeremy Long
b0f9935fcb updated to resolve issue #696 2017-05-23 10:44:00 -05:00
Jeremy Long
122c78648a updated code to better handle TLS errors 2017-05-21 18:04:26 -04:00
Jeremy Long
d457fd1452 fixed copyright 2017-05-21 07:45:27 -04:00
Jeremy Long
454a875593 Merge branch 'master' of https://github.com/stevespringett/DependencyCheck into stevespringett-master 2017-05-21 07:29:05 -04:00
stevespringett
9da95e592c Added NSP Analyzer Support 2017-04-26 00:40:15 -05:00
24 changed files with 1515 additions and 88 deletions

View File

@@ -353,8 +353,7 @@ public class App {
* @throws InvalidSettingException thrown when a user defined properties * @throws InvalidSettingException thrown when a user defined properties
* file is unable to be loaded. * file is unable to be loaded.
*/ */
private void populateSettings(CliParser cli) throws InvalidSettingException { protected void populateSettings(CliParser cli) throws InvalidSettingException {
final boolean autoUpdate = cli.isAutoUpdate();
final String connectionTimeout = cli.getConnectionTimeout(); final String connectionTimeout = cli.getConnectionTimeout();
final String proxyServer = cli.getProxyServer(); final String proxyServer = cli.getProxyServer();
final String proxyPort = cli.getProxyPort(); final String proxyPort = cli.getProxyPort();
@@ -377,7 +376,8 @@ public class App {
final String cveBase12 = cli.getBaseCve12Url(); final String cveBase12 = cli.getBaseCve12Url();
final String cveBase20 = cli.getBaseCve20Url(); final String cveBase20 = cli.getBaseCve20Url();
final Integer cveValidForHours = cli.getCveValidForHours(); final Integer cveValidForHours = cli.getCveValidForHours();
final boolean experimentalEnabled = cli.isExperimentalEnabled(); final Boolean autoUpdate = cli.isAutoUpdate();
final Boolean experimentalEnabled = cli.isExperimentalEnabled();
if (propertiesFile != null) { if (propertiesFile != null) {
try { try {
@@ -390,7 +390,7 @@ public class App {
} }
// We have to wait until we've merged the properties before attempting to set whether we use // We have to wait until we've merged the properties before attempting to set whether we use
// the proxy for Nexus since it could be disabled in the properties, but not explicitly stated // the proxy for Nexus since it could be disabled in the properties, but not explicitly stated
// on the command line // on the command line. This is true of other boolean values set below not using the setBooleanIfNotNull.
final boolean nexusUsesProxy = cli.isNexusUsesProxy(); final boolean nexusUsesProxy = cli.isNexusUsesProxy();
if (dataDirectory != null) { if (dataDirectory != null) {
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
@@ -404,7 +404,7 @@ public class App {
final File dataDir = new File(base, sub); final File dataDir = new File(base, sub);
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
} }
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
@@ -415,7 +415,8 @@ public class App {
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
//File Type Analyzer Settings //File Type Analyzer Settings
Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled); Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());

View File

@@ -567,6 +567,32 @@ public final class CliParser {
return value; return value;
} }
/**
* Utility method to determine if one of the disable options has been set.
* If not set, this method will check the currently configured settings for
* the current value to return.
*
* Example given `--disableArchive` on the command line would cause this
* method to return true for the disable archive setting.
*
* @param argument the command line argument
* @param setting the corresponding settings key
* @return true if the disable option was set, if not set the currently
* configured value will be returned
*/
private boolean hasDisableOption(String argument, String setting) {
if (line == null || !line.hasOption(argument)) {
try {
return !Settings.getBoolean(setting);
} catch (InvalidSettingException ise) {
LOGGER.warn("Invalid property setting '{}' defaulting to false", setting);
return false;
}
} else {
return true;
}
}
/** /**
* Returns true if the disableJar command line argument was specified. * Returns true if the disableJar command line argument was specified.
* *
@@ -574,7 +600,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isJarDisabled() { public boolean isJarDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_JAR); return hasDisableOption(ARGUMENT.DISABLE_JAR, Settings.KEYS.ANALYZER_JAR_ENABLED);
} }
/** /**
@@ -584,7 +610,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isArchiveDisabled() { public boolean isArchiveDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ARCHIVE); return hasDisableOption(ARGUMENT.DISABLE_ARCHIVE, Settings.KEYS.ANALYZER_ARCHIVE_ENABLED);
} }
/** /**
@@ -594,7 +620,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNuspecDisabled() { public boolean isNuspecDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NUSPEC); return hasDisableOption(ARGUMENT.DISABLE_NUSPEC, Settings.KEYS.ANALYZER_NUSPEC_ENABLED);
} }
/** /**
@@ -604,7 +630,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isAssemblyDisabled() { public boolean isAssemblyDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY); return hasDisableOption(ARGUMENT.DISABLE_ASSEMBLY, Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED);
} }
/** /**
@@ -615,7 +641,7 @@ public final class CliParser {
* specified; otherwise false * specified; otherwise false
*/ */
public boolean isBundleAuditDisabled() { public boolean isBundleAuditDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_BUNDLE_AUDIT); return hasDisableOption(ARGUMENT.DISABLE_BUNDLE_AUDIT, Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED);
} }
/** /**
@@ -625,7 +651,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isPythonDistributionDisabled() { public boolean isPythonDistributionDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST); return hasDisableOption(ARGUMENT.DISABLE_PY_DIST, Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED);
} }
/** /**
@@ -635,7 +661,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isPythonPackageDisabled() { public boolean isPythonPackageDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG); return hasDisableOption(ARGUMENT.DISABLE_PY_PKG, Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED);
} }
/** /**
@@ -645,7 +671,7 @@ public final class CliParser {
* argument was specified; otherwise false * argument was specified; otherwise false
*/ */
public boolean isRubyGemspecDisabled() { public boolean isRubyGemspecDisabled() {
return (null != line) && line.hasOption(ARGUMENT.DISABLE_RUBYGEMS); return hasDisableOption(ARGUMENT.DISABLE_RUBYGEMS, Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED);
} }
/** /**
@@ -655,7 +681,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isCmakeDisabled() { public boolean isCmakeDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CMAKE); return hasDisableOption(ARGUMENT.DISABLE_CMAKE, Settings.KEYS.ANALYZER_CMAKE_ENABLED);
} }
/** /**
@@ -665,7 +691,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isAutoconfDisabled() { public boolean isAutoconfDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_AUTOCONF); return hasDisableOption(ARGUMENT.DISABLE_AUTOCONF, Settings.KEYS.ANALYZER_AUTOCONF_ENABLED);
} }
/** /**
@@ -675,7 +701,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isComposerDisabled() { public boolean isComposerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_COMPOSER); return hasDisableOption(ARGUMENT.DISABLE_COMPOSER, Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED);
} }
/** /**
@@ -685,7 +711,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNexusDisabled() { public boolean isNexusDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS); return hasDisableOption(ARGUMENT.DISABLE_NEXUS, Settings.KEYS.ANALYZER_NEXUS_ENABLED);
} }
/** /**
@@ -695,7 +721,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isOpenSSLDisabled() { public boolean isOpenSSLDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL); return hasDisableOption(ARGUMENT.DISABLE_OPENSSL, Settings.KEYS.ANALYZER_OPENSSL_ENABLED);
} }
/** /**
@@ -705,7 +731,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNodeJsDisabled() { public boolean isNodeJsDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NODE_JS); return hasDisableOption(ARGUMENT.DISABLE_NODE_JS, Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED);
} }
/** /**
@@ -716,7 +742,7 @@ public final class CliParser {
* specified; otherwise false * specified; otherwise false
*/ */
public boolean isCocoapodsAnalyzerDisabled() { public boolean isCocoapodsAnalyzerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_COCOAPODS); return hasDisableOption(ARGUMENT.DISABLE_COCOAPODS, Settings.KEYS.ANALYZER_COCOAPODS_ENABLED);
} }
/** /**
@@ -727,7 +753,7 @@ public final class CliParser {
* argument was specified; otherwise false * argument was specified; otherwise false
*/ */
public boolean isSwiftPackageAnalyzerDisabled() { public boolean isSwiftPackageAnalyzerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_SWIFT); return hasDisableOption(ARGUMENT.DISABLE_SWIFT, Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED);
} }
/** /**
@@ -737,7 +763,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isCentralDisabled() { public boolean isCentralDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL); return hasDisableOption(ARGUMENT.DISABLE_CENTRAL, Settings.KEYS.ANALYZER_CENTRAL_ENABLED);
} }
/** /**
@@ -1029,10 +1055,10 @@ public final class CliParser {
* disabled via the command line this will return false. * disabled via the command line this will return false.
* *
* @return <code>true</code> if auto-update is allowed; otherwise * @return <code>true</code> if auto-update is allowed; otherwise
* <code>false</code> * <code>null</code>
*/ */
public boolean isAutoUpdate() { public Boolean isAutoUpdate() {
return line != null && !line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE); return (line != null && line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE)) ? false : null;
} }
/** /**
@@ -1134,10 +1160,10 @@ public final class CliParser {
/** /**
* Returns true if the experimental analyzers are enabled. * Returns true if the experimental analyzers are enabled.
* *
* @return true if the experimental analyzers are enabled; otherwise false * @return true if the experimental analyzers are enabled; otherwise null
*/ */
public boolean isExperimentalEnabled() { public Boolean isExperimentalEnabled() {
return line.hasOption(ARGUMENT.EXPERIMENTAL); return (line != null && line.hasOption(ARGUMENT.EXPERIMENTAL)) ? true : null;
} }
/** /**

View File

@@ -13,18 +13,28 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* *
* Copyright (c) 2015 The OWASP Foundatio. All Rights Reserved. * Copyright (c) 2017 The OWASP Foundatio. All Rights Reserved.
*/ */
package org.owasp.dependencycheck; package org.owasp.dependencycheck;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings;
/** /**
* *
* @author jeremy * @author jeremy
*/ */
public class AppTest { public class AppTest {
/** /**
* Test of ensureCanonicalPath method, of class App. * Test of ensureCanonicalPath method, of class App.
*/ */
@@ -35,17 +45,83 @@ public class AppTest {
String result = instance.ensureCanonicalPath(file); String result = instance.ensureCanonicalPath(file);
assertFalse(result.contains("..")); assertFalse(result.contains(".."));
assertTrue(result.endsWith("*.jar")); assertTrue(result.endsWith("*.jar"));
}
/** file = "../some/skip/../path/file.txt";
* Test of ensureCanonicalPath method, of class App.
*/
@Test
public void testEnsureCanonicalPath2() {
String file = "../some/skip/../path/file.txt";
App instance = new App();
String expResult = "/some/path/file.txt"; String expResult = "/some/path/file.txt";
String result = instance.ensureCanonicalPath(file); result = instance.ensureCanonicalPath(file);
assertTrue("result=" + result, result.endsWith(expResult)); assertTrue("result=" + result, result.endsWith(expResult));
} }
@Test(expected = UnrecognizedOptionException.class)
public void testPopulateSettingsException() throws FileNotFoundException, ParseException, InvalidSettingException, URISyntaxException {
String[] args = {"-invalidPROPERTY"};
assertTrue(testBooleanProperties(args, null));
}
@Test
public void testPopulateSettings() throws FileNotFoundException, ParseException, InvalidSettingException, URISyntaxException {
File prop = new File(this.getClass().getClassLoader().getResource("sample.properties").toURI().getPath());
String[] args = {"-P", prop.getAbsolutePath()};
Map<String, Boolean> expected = new HashMap<>();
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args, expected));
String[] args2 = {"-n"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args2, expected));
String[] args3 = {"-h"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args3, expected));
String[] args4 = {"--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args4, expected));
String[] args5 = {"-P", prop.getAbsolutePath(), "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args5, expected));
prop = new File(this.getClass().getClassLoader().getResource("sample2.properties").toURI().getPath());
String[] args6 = {"-P", prop.getAbsolutePath(), "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args6, expected));
String[] args7 = {"-P", prop.getAbsolutePath(), "--noupdate"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args7, expected));
String[] args8 = {"-P", prop.getAbsolutePath(), "--noupdate", "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args8, expected));
}
private boolean testBooleanProperties(String[] args, Map<String, Boolean> expected) throws URISyntaxException, FileNotFoundException, ParseException, InvalidSettingException {
Settings.initialize();
try {
final CliParser cli = new CliParser();
cli.parse(args);
App instance = new App();
instance.populateSettings(cli);
boolean results = true;
for (Map.Entry<String, Boolean> entry : expected.entrySet()) {
results &= Settings.getBoolean(entry.getKey()) == entry.getValue();
}
return results;
} finally {
Settings.cleanup();
}
}
} }

View File

@@ -0,0 +1,33 @@
autoupdate=false
analyzer.experimental.enabled=false
analyzer.jar.enabled=true
analyzer.archive.enabled=true
analyzer.node.package.enabled=true
analyzer.composer.lock.enabled=true
analyzer.python.distribution.enabled=true
analyzer.python.package.enabled=true
analyzer.ruby.gemspec.enabled=true
analyzer.autoconf.enabled=true
analyzer.cmake.enabled=true
analyzer.assembly.enabled=true
analyzer.nuspec.enabled=true
analyzer.openssl.enabled=true
analyzer.central.enabled=true
analyzer.nexus.enabled=false
analyzer.cocoapods.enabled=true
analyzer.swift.package.manager.enabled=true
#whether the nexus analyzer uses the proxy
analyzer.nexus.proxy=true
analyzer.cpe.enabled=true
analyzer.cpesuppression.enabled=true
analyzer.dependencybundling.enabled=true
analyzer.dependencymerging.enabled=true
analyzer.falsepositive.enabled=true
analyzer.filename.enabled=true
analyzer.hint.enabled=true
analyzer.nvdcve.enabled=true
analyzer.vulnerabilitysuppression.enabled=true
updater.nvdcve.enabled=true
updater.versioncheck.enabled=true
analyzer.versionfilter.enabled=true

View File

@@ -0,0 +1,33 @@
autoupdate=true
analyzer.experimental.enabled=true
analyzer.jar.enabled=false
analyzer.archive.enabled=false
analyzer.node.package.enabled=false
analyzer.composer.lock.enabled=false
analyzer.python.distribution.enabled=false
analyzer.python.package.enabled=false
analyzer.ruby.gemspec.enabled=false
analyzer.autoconf.enabled=false
analyzer.cmake.enabled=false
analyzer.assembly.enabled=false
analyzer.nuspec.enabled=false
analyzer.openssl.enabled=false
analyzer.central.enabled=false
analyzer.nexus.enabled=true
analyzer.cocoapods.enabled=false
analyzer.swift.package.manager.enabled=false
#whether the nexus analyzer uses the proxy
analyzer.nexus.proxy=false
analyzer.cpe.enabled=false
analyzer.cpesuppression.enabled=false
analyzer.dependencybundling.enabled=false
analyzer.dependencymerging.enabled=false
analyzer.falsepositive.enabled=false
analyzer.filename.enabled=false
analyzer.hint.enabled=false
analyzer.nvdcve.enabled=false
analyzer.vulnerabilitysuppression.enabled=false
updater.nvdcve.enabled=false
updater.versioncheck.enabled=false
analyzer.versionfilter.enabled=false

View File

@@ -0,0 +1,334 @@
/*
* 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.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nsp.Advisory;
import org.owasp.dependencycheck.data.nsp.NspSearch;
import org.owasp.dependencycheck.data.nsp.SanitizePackage;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceCollection;
import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonException;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import javax.json.JsonString;
import javax.json.JsonValue;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
/**
* Used to analyze Node Package Manager (npm) package.json files via Node
* Security Platform (nsp).
*
* @author Steve Springett
*/
public class NspAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(NspAnalyzer.class);
/**
* The default URL to the NSP check API.
*/
public static final String DEFAULT_URL = "https://api.nodesecurity.io/check";
/**
* The file name to scan.
*/
private static final String PACKAGE_JSON = "package.json";
/**
* Filter that detects files named "package.json".
*/
private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance()
.addFilenames(PACKAGE_JSON).build();
/**
* The NSP Searcher.
*/
private NspSearch searcher;
/**
* Returns the FileFilter
*
* @return the FileFilter
*/
@Override
protected FileFilter getFileFilter() {
return PACKAGE_JSON_FILTER;
}
/**
* Initializes the analyzer once before any analysis is performed.
*
* @throws InitializationException if there's an error during initialization
*/
@Override
public void initializeFileTypeAnalyzer() throws InitializationException {
LOGGER.debug("Initializing " + getName());
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NSP_URL, DEFAULT_URL);
try {
searcher = new NspSearch(new URL(searchUrl));
} catch (MalformedURLException ex) {
setEnabled(false);
throw new InitializationException("The configured URL to Node Security Platform is malformed: " + searchUrl, ex);
}
}
/**
* Returns the name of the analyzer.
*
* @return the name of the analyzer.
*/
@Override
public String getName() {
return "Node Security Platform Analyzer";
}
/**
* Returns the phase that the analyzer is intended to run in.
*
* @return the phase that the analyzer is intended to run in.
*/
@Override
public AnalysisPhase getAnalysisPhase() {
return AnalysisPhase.FINDING_ANALYSIS;
}
/**
* Returns the key used in the properties file to reference the analyzer's
* enabled property.x
*
* @return the analyzer's enabled property setting key
*/
@Override
protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED;
}
@Override
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
final File file = dependency.getActualFile();
try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) {
// Retrieves the contents of package.json from the Dependency
final JsonObject packageJson = jsonReader.readObject();
// Create a sanitized version of the package.json
final JsonObject sanitizedJson = SanitizePackage.sanitize(packageJson);
// Create a new 'package' object that acts as a container for the sanitized package.json
final JsonObjectBuilder builder = Json.createObjectBuilder();
final JsonObject nspPayload = builder.add("package", sanitizedJson).build();
// Submits the package payload to the nsp check service
final List<Advisory> advisories = searcher.submitPackage(nspPayload);
for (Advisory advisory : advisories) {
/*
* Create a new vulnerability out of the advisory returned by nsp.
*/
final Vulnerability vuln = new Vulnerability();
vuln.setCvssScore(advisory.getCvssScore());
vuln.setDescription(advisory.getOverview());
vuln.setName(String.valueOf(advisory.getId()));
vuln.setSource(Vulnerability.Source.NSP);
vuln.addReference(
"NSP",
"Advisory " + advisory.getId() + ": " + advisory.getTitle(),
advisory.getAdvisory()
);
/*
* Create a single vulnerable software object - these do not use CPEs unlike the NVD.
*/
final VulnerableSoftware vs = new VulnerableSoftware();
//vs.setVersion(advisory.getVulnerableVersions());
vs.setUpdate(advisory.getPatchedVersions());
vs.setName(advisory.getModule() + ":" + advisory.getVulnerableVersions());
vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs)));
// Add the vulnerability to package.json
dependency.getVulnerabilities().add(vuln);
}
/*
* Adds evidence about the node package itself, not any of the modules.
*/
final EvidenceCollection productEvidence = dependency.getProductEvidence();
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
if (packageJson.containsKey("name")) {
final Object value = packageJson.get("name");
if (value instanceof JsonString) {
final String valueString = ((JsonString) value).getString();
productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW);
} else {
LOGGER.warn("JSON value not string as expected: {}", value);
}
}
/*
* 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 JsonObject dependencies = packageJson.getJsonObject("bundleDependencies");
processPackage(dependency, dependencies, "bundleDependencies");
}
if (packageJson.containsKey("bundledDependencies")) {
final JsonObject dependencies = packageJson.getJsonObject("bundledDependencies");
processPackage(dependency, dependencies, "bundledDependencies");
}
/*
* Adds the license if defined in package.json
*/
if (packageJson.containsKey("license")) {
dependency.setLicense(packageJson.getString("license"));
}
/*
* Adds general evidence to about the package.
*/
addToEvidence(packageJson, productEvidence, "description");
addToEvidence(packageJson, vendorEvidence, "author");
addToEvidence(packageJson, dependency.getVersionEvidence(), "version");
dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName()));
} catch (URLConnectionFailureException e) {
this.setEnabled(false);
throw new AnalysisException(e.getMessage(), e);
} catch (IOException e) {
LOGGER.debug("Error reading dependency or connecting to Node Security Platform - check API", e);
this.setEnabled(false);
throw new AnalysisException(e.getMessage(), e);
} catch (JsonException e) {
throw new AnalysisException(String.format("Failed to parse %s file.", file.getPath()), e);
}
}
/**
* Processes a part of package.json (as defined by JsobObject) and update
* the specified dependency with relevant info.
*
* @param dependency the Dependency to update
* @param jsonObject the jsonObject to parse
*/
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.
*/
Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "#" + entry.getKey()), true);
nodeModule.setDisplayFileName(entry.getKey());
nodeModule.setIdentifiers(new HashSet<>(Arrays.asList(moduleName, moduleVersion, moduleDepType)));
dependency.addRelatedDependency(nodeModule);
}
}
}
/**
* Adds information to an evidence collection from the node json
* configuration.
*
* @param json information from node.js
* @param collection a set of evidence about a dependency
* @param key the key to obtain the data from the json information
*/
private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) {
if (json.containsKey(key)) {
final JsonValue value = json.get(key);
if (value instanceof JsonString) {
collection.addEvidence(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) {
collection.addEvidence(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);
}
}
}
}

View File

@@ -0,0 +1,344 @@
/*
* 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.data.nsp;
/**
* The response from NSP check API will respond with 0 or more advisories.
* This class defines the Advisory objects returned.
*
* @author Steve Springett
*/
public class Advisory {
/**
* The unique ID of the advisory as issued by Node Security Platform.
*/
private int id;
/**
* The timestamp of the last update to the advisory.
*/
private String updatedAt;
/**
* The timestamp of which the advisory was created.
*/
private String createdAt;
/**
* The timestamp of when the advisory was published.
*/
private String publishDate;
/**
* A detailed description of the advisory.
*/
private String overview;
/**
* Recommendations for mitigation. Typically involves updating to a newer release.
*/
private String recommendation;
/**
* The CVSS vector used to calculate the score.
*/
private String cvssVector;
/**
* The CVSS score.
*/
private float cvssScore;
/**
* The name of the Node module the advisory is for.
*/
private String module;
/**
* The version of the Node module the advisory is for.
*/
private String version;
/**
* A string representation of the versions containing the vulnerability.
*/
private String vulnerableVersions;
/**
* A string representation of the versions that have been patched.
*/
private String patchedVersions;
/**
* The title/name of the advisory.
*/
private String title;
/**
* The linear dependency path that lead to this module.
* [0] is the root with each subsequent array member leading up to the
* final (this) module.
*/
private String[] path;
/**
* The URL to the advisory.
*/
private String advisory;
/**
* Returns the unique ID of the advisory as issued by Node Security Platform.
* @return a unique ID
*/
public int getId() {
return id;
}
/**
* Sets the unique ID of the advisory as issued by Node Security Platform.
* @param id a unique ID
*/
public void setId(int id) {
this.id = id;
}
/**
* Returns the timestamp of the last update to the advisory.
* @return a timestamp
*/
public String getUpdatedAt() {
return updatedAt;
}
/**
* Sets the timestamp of the last update to the advisory.
* @param updatedAt a timestamp
*/
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
/**
* Returns the timestamp of which the advisory was created.
* @return a timestamp
*/
public String getCreatedAt() {
return createdAt;
}
/**
* Sets the timestamp of which the advisory was created.
* @param createdAt a timestamp
*/
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
/**
* Returns the timestamp of when the advisory was published.
* @return a timestamp
*/
public String getPublishDate() {
return publishDate;
}
/**
* Sets the timestamp of when the advisory was published.
* @param publishDate a timestamp
*/
public void setPublishDate(String publishDate) {
this.publishDate = publishDate;
}
/**
* Returns a detailed description of the advisory.
* @return the overview
*/
public String getOverview() {
return overview;
}
/**
* Sets the detailed description of the advisory.
* @param overview the overview
*/
public void setOverview(String overview) {
this.overview = overview;
}
/**
* Returns recommendations for mitigation. Typically involves updating to a newer release.
* @return recommendations
*/
public String getRecommendation() {
return recommendation;
}
/**
* Sets recommendations for mitigation. Typically involves updating to a newer release.
* @param recommendation recommendations
*/
public void setRecommendation(String recommendation) {
this.recommendation = recommendation;
}
/**
* Returns the CVSS vector used to calculate the score.
* @return the CVSS vector
*/
public String getCvssVector() {
return cvssVector;
}
/**
* Sets the CVSS vector used to calculate the score.
* @param cvssVector the CVSS vector
*/
public void setCvssVector(String cvssVector) {
this.cvssVector = cvssVector;
}
/**
* Returns the CVSS score.
* @return the CVSS score
*/
public float getCvssScore() {
return cvssScore;
}
/**
* Sets the CVSS score.
* @param cvssScore the CVSS score
*/
public void setCvssScore(float cvssScore) {
this.cvssScore = cvssScore;
}
/**
* Returns the name of the Node module the advisory is for.
* @return the name of the module
*/
public String getModule() {
return module;
}
/**
* Sets the name of the Node module the advisory is for.
* @param module the name of the4 module
*/
public void setModule(String module) {
this.module = module;
}
/**
* Returns the version of the Node module the advisory is for.
* @return the module version
*/
public String getVersion() {
return version;
}
/**
* Sets the version of the Node module the advisory is for.
* @param version the module version
*/
public void setVersion(String version) {
this.version = version;
}
/**
* Returns a string representation of the versions containing the vulnerability.
* @return the affected versions
*/
public String getVulnerableVersions() {
return vulnerableVersions;
}
/**
* Sets the string representation of the versions containing the vulnerability.
* @param vulnerableVersions the affected versions
*/
public void setVulnerableVersions(String vulnerableVersions) {
this.vulnerableVersions = vulnerableVersions;
}
/**
* Returns a string representation of the versions that have been patched.
* @return the patched versions
*/
public String getPatchedVersions() {
return patchedVersions;
}
/**
* Sets the string representation of the versions that have been patched.
* @param patchedVersions the patched versions
*/
public void setPatchedVersions(String patchedVersions) {
this.patchedVersions = patchedVersions;
}
/**
* Returns the title/name of the advisory.
* @return the title/name of the advisory
*/
public String getTitle() {
return title;
}
/**
* Sets the title/name of the advisory.
* @param title the title/name of the advisory
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Returns the linear dependency path that lead to this module.
* @return the dependency path
*/
public String[] getPath() {
return path;
}
/**
* Sets the linear dependency path that lead to this module.
* @param path the dependency path
*/
public void setPath(String[] path) {
this.path = path;
}
/**
* Returns the URL to the advisory.
* @return the advisory URL
*/
public String getAdvisory() {
return advisory;
}
/**
* Sets the URL to the advisory.
* @param advisory the advisory URL
*/
public void setAdvisory(String advisory) {
this.advisory = advisory;
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.data.nsp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.URLConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
/**
* Class of methods to search via Node Security Platform.
*
* @author Steve Springett
*/
public class NspSearch {
/**
* The URL for the public NSP check API.
*/
private final URL nspCheckUrl;
/**
* Whether to use the Proxy when making requests.
*/
private final boolean useProxy;
/**
* Used for logging.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(NspSearch.class);
/**
* Creates a NspSearch for the given repository URL.
*
* @param nspCheckUrl the URL to the public NSP check API
*/
public NspSearch(URL nspCheckUrl) {
this.nspCheckUrl = nspCheckUrl;
if (null != Settings.getString(Settings.KEYS.PROXY_SERVER)) {
useProxy = true;
LOGGER.debug("Using proxy");
} else {
useProxy = false;
LOGGER.debug("Not using proxy");
}
}
/**
* Submits the package.json file to the NSP public /check API and returns a
* list of zero or more Advisories.
*
* @param packageJson the package.json file retrieved from the Dependency
* @return a List of zero or more Advisory object
* @throws AnalysisException if Node Security Platform is unable to analyze the package
* @throws IOException if it's unable to connect to Node Security Platform
*/
public List<Advisory> submitPackage(JsonObject packageJson) throws AnalysisException, IOException {
try {
List<Advisory> result = new ArrayList<>();
byte[] packageDatabytes = packageJson.toString().getBytes(StandardCharsets.UTF_8);
final HttpURLConnection conn = URLConnectionFactory.createHttpURLConnection(nspCheckUrl, useProxy);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("X-NSP-VERSION", "2.6.2");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Content-Length", Integer.toString(packageDatabytes.length));
conn.connect();
try (OutputStream os = new BufferedOutputStream(conn.getOutputStream())) {
os.write(packageDatabytes);
os.flush();
}
if (conn.getResponseCode() == 200) {
try (InputStream in = new BufferedInputStream(conn.getInputStream())) {
JsonReader jsonReader = Json.createReader(in);
JsonArray array = jsonReader.readArray();
if (array != null) {
for (int i = 0; i < array.size(); i++) {
JsonObject object = array.getJsonObject(i);
Advisory advisory = new Advisory();
advisory.setId(object.getInt("id"));
advisory.setUpdatedAt(object.getString("updated_at", null));
advisory.setCreatedAt(object.getString("created_at", null));
advisory.setPublishDate(object.getString("publish_date", null));
advisory.setOverview(object.getString("overview"));
advisory.setRecommendation(object.getString("recommendation", null));
advisory.setCvssVector(object.getString("cvss_vector", null));
advisory.setCvssScore(Float.parseFloat(object.getJsonNumber("cvss_score").toString()));
advisory.setModule(object.getString("module", null));
advisory.setVersion(object.getString("version", null));
advisory.setVulnerableVersions(object.getString("vulnerable_versions", null));
advisory.setPatchedVersions(object.getString("patched_versions", null));
advisory.setTitle(object.getString("title", null));
advisory.setAdvisory(object.getString("advisory", null));
JsonArray jsonPath = object.getJsonArray("path");
List<String> stringPath = new ArrayList<>();
for (int j = 0; j < jsonPath.size(); j++) {
stringPath.add(jsonPath.getString(j));
}
advisory.setPath(stringPath.toArray(new String[stringPath.size()]));
result.add(advisory);
}
}
}
} else if (conn.getResponseCode() == 400) {
LOGGER.debug("Invalid payload submitted to Node Security Platform. Received response code: {} {}",
conn.getResponseCode(), conn.getResponseMessage());
throw new AnalysisException("Could not perform NSP analysis. Invalid payload submitted to Node Security Platform.");
} else {
LOGGER.debug("Could not connect to Node Security Platform. Received response code: {} {}",
conn.getResponseCode(), conn.getResponseMessage());
throw new IOException("Could not connect to Node Security Platform");
}
return result;
} catch (IOException ex) {
if (ex instanceof javax.net.ssl.SSLHandshakeException
&& ex.getMessage().contains("unable to find valid certification path to requested target")) {
final String msg = String.format("Unable to connect to '%s' - the Java trust store does not contain a trusted root for the cert. "
+ " Please see https://github.com/jeremylong/InstallCert for one method of updating the trusted certificates.", nspCheckUrl);
throw new URLConnectionFailureException(msg, ex);
}
throw ex;
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.data.nsp;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Class used to create a Sanitized version of package.json
* suitable for submission to the nsp/check service.
*
* @author Steve Springett
*/
public class SanitizePackage {
/**
* Specifies a whitelist of allowable objects that package.json should contain.
*/
private static final List<String> WHITELIST = new ArrayList<>(Arrays.asList(
"name",
"version",
"engine",
"dependencies",
"devDependencies",
"optionalDependencies",
"peerDependencies",
"bundleDependencies",
"bundledDependencies"
));
/**
* The NSP API only accepts a subset of objects typically found in package.json.
* This method accepts a JsonObject of a raw package.json file and returns a
* new 'sanitized' version based on a pre-defined whitelist of allowable object
* NSP accepts.
*
* @param rawPackage a raw package.json file
* @return a sanitized version of the package.json file
*/
public static JsonObject sanitize(JsonObject rawPackage) {
JsonObjectBuilder builder = Json.createObjectBuilder();
for (Map.Entry<String, JsonValue> entry: rawPackage.entrySet()) {
if (WHITELIST.contains(entry.getKey())) {
builder.add(entry.getKey(), entry.getValue());
}
}
return builder.build();
}
}

View File

@@ -0,0 +1,7 @@
/**
*
* Contains classes related to searching Node Security Platform (nsp).<br><br>
*
* These are used to abstract NSP searching away from OWASP Dependency Check so they can be reused elsewhere.
*/
package org.owasp.dependencycheck.data.nsp;

View File

@@ -138,6 +138,11 @@ public class Dependency implements Serializable, Comparable<Dependency> {
*/ */
private List<String> availableVersions = new ArrayList<>(); private List<String> availableVersions = new ArrayList<>();
/**
* Defines an actual or virtual dependency.
*/
private boolean isVirtual = false;
/** /**
* Returns the package path. * Returns the package path.
* *
@@ -175,7 +180,18 @@ public class Dependency implements Serializable, Comparable<Dependency> {
* @param file the File to create the dependency object from. * @param file the File to create the dependency object from.
*/ */
public Dependency(File file) { public Dependency(File file) {
this(file, false);
}
/**
* Constructs a new Dependency object.
*
* @param file the File to create the dependency object from.
* @param isVirtual specifies if the dependency is virtual indicating the file doesn't actually exist.
*/
public Dependency(File file, boolean isVirtual) {
this(); this();
this.isVirtual = isVirtual;
this.actualFilePath = file.getAbsolutePath(); this.actualFilePath = file.getAbsolutePath();
this.filePath = this.actualFilePath; this.filePath = this.actualFilePath;
this.fileName = file.getName(); this.fileName = file.getName();
@@ -591,6 +607,9 @@ public class Dependency implements Serializable, Comparable<Dependency> {
private void determineHashes(File file) { private void determineHashes(File file) {
String md5 = null; String md5 = null;
String sha1 = null; String sha1 = null;
if (isVirtual) {
return;
}
try { try {
md5 = Checksum.getMD5Checksum(file); md5 = Checksum.getMD5Checksum(file);
sha1 = Checksum.getSHA1Checksum(file); sha1 = Checksum.getSHA1Checksum(file);

View File

@@ -32,6 +32,11 @@ import org.apache.commons.lang3.builder.CompareToBuilder;
*/ */
public class Vulnerability implements Serializable, Comparable<Vulnerability> { public class Vulnerability implements Serializable, Comparable<Vulnerability> {
public enum Source {
NVD, // National Vulnerability Database
NSP // Node Security Platform
}
/** /**
* The serial version uid. * The serial version uid.
*/ */
@@ -100,6 +105,11 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
*/ */
private String notes; private String notes;
/**
* The source that identified the vulnerability.
*/
private Source source = Source.NVD;
/** /**
* Get the value of name. * Get the value of name.
* *
@@ -516,4 +526,20 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
public boolean hasMatchedAllPreviousCPE() { public boolean hasMatchedAllPreviousCPE() {
return matchedAllPreviousCPE != null; return matchedAllPreviousCPE != null;
} }
/**
* Retruns the source that identified the vulnerability.
* @return the source
*/
public Source getSource() {
return source;
}
/**
* Sets the source that identified the vulnerability.
* @param source the source
*/
public void setSource(Source source) {
this.source = source;
}
} }

View File

@@ -19,6 +19,7 @@ org.owasp.dependencycheck.analyzer.AutoconfAnalyzer
org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer
org.owasp.dependencycheck.analyzer.CMakeAnalyzer org.owasp.dependencycheck.analyzer.CMakeAnalyzer
org.owasp.dependencycheck.analyzer.NodePackageAnalyzer org.owasp.dependencycheck.analyzer.NodePackageAnalyzer
org.owasp.dependencycheck.analyzer.NspAnalyzer
org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer
org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer
org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer

View File

@@ -78,6 +78,9 @@ analyzer.nexus.proxy=true
analyzer.central.enabled=true analyzer.central.enabled=true
analyzer.central.url=https://search.maven.org/solrsearch/select analyzer.central.url=https://search.maven.org/solrsearch/select
# the URL for searching api.nodesecurity.io
analyzer.nsp.url=https://api.nodesecurity.io/check
# the number of nested archives that will be searched. # the number of nested archives that will be searched.
archive.scan.depth=3 archive.scan.depth=3
@@ -89,6 +92,7 @@ analyzer.experimental.enabled=false
analyzer.jar.enabled=true analyzer.jar.enabled=true
analyzer.archive.enabled=true analyzer.archive.enabled=true
analyzer.node.package.enabled=true analyzer.node.package.enabled=true
analyzer.nsp.package.enabled=true
analyzer.composer.lock.enabled=true analyzer.composer.lock.enabled=true
analyzer.python.distribution.enabled=true analyzer.python.distribution.enabled=true
analyzer.python.package.enabled=true analyzer.python.package.enabled=true

View File

@@ -759,8 +759,8 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
<li>$enc.html($related.DisplayFileName) <li>$enc.html($related.DisplayFileName)
<ul> <ul>
<li>File Path:&nbsp;$enc.html($related.FilePath)</li> <li>File Path:&nbsp;$enc.html($related.FilePath)</li>
<li>SHA1:&nbsp;$enc.html($related.Sha1sum)</li> <li>SHA1:&nbsp;#if($related.Sha1sum)$enc.html($related.Sha1sum)#end</li>
<li>MD5:&nbsp;$enc.html($related.Md5sum)</li> <li>MD5:&nbsp;#if($related.Md5sum)$enc.html($related.Md5sum)#end</li>
#foreach($id in $related.getIdentifiers()) #foreach($id in $related.getIdentifiers())
#if( $id.url ) #if( $id.url )
#if ($id.type=="maven") #if ($id.type=="maven")
@@ -771,6 +771,9 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
#end #end
</li> </li>
#end #end
#if ($id.type=="npm")
<li>$enc.html($id.value): $enc.html($id.description)</li>
#end
#end #end
</ul> </ul>
</li> </li>
@@ -837,7 +840,11 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
<div id="content$cnt" class="subsectioncontent standardsubsection"> <div id="content$cnt" class="subsectioncontent standardsubsection">
#foreach($vuln in $dependency.getVulnerabilities()) #foreach($vuln in $dependency.getVulnerabilities())
#set($vsctr=$vsctr+1) #set($vsctr=$vsctr+1)
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a></b>&nbsp;&nbsp;<button class="copybutton" title="Generate Suppression XML for this CCE for this file" onclick="copyText('$enc.html($dependency.FileNameForJavaScript)', '$enc.html($dependency.Sha1sum)', '$enc.html($suppressGav)', 'cve', '$enc.html($vuln.name)')">suppress</button></p> #if($vuln.getSource().name().equals("NVD"))
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a></b>&nbsp;&nbsp;<button class="copybutton" title="Generate Suppression XML for this CCE for this file" onclick="copyText('$enc.html($dependency.FileNameForJavaScript)', '$enc.html($dependency.Sha1sum)', '$enc.html($suppressGav)', 'cve', '$enc.html($vuln.name)')">suppress</button></p>
#elseif($vuln.getSource().name().equals("NSP"))
<p><b><a target="_blank" href="https://nodesecurity.io/advisories/$enc.url($vuln.name)">NSP-$enc.html($vuln.name)</a></b></p>
#end
<p>Severity: <p>Severity:
#if ($vuln.cvssScore<4.0) #if ($vuln.cvssScore<4.0)
Low Low
@@ -846,7 +853,11 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
#else #else
Medium Medium
#end #end
<br/>CVSS Score: $vuln.cvssScore (AV:$enc.html($vuln.cvssAccessVector.substring(0,1))/AC:$enc.html($vuln.cvssAccessComplexity.substring(0,1))/Au:$enc.html($vuln.cvssAuthentication.substring(0,1))/C:$enc.html($vuln.cvssConfidentialityImpact.substring(0,1))/I:$enc.html($vuln.cvssIntegrityImpact.substring(0,1))/A:$enc.html($vuln.cvssAvailabilityImpact.substring(0,1))) <br/>CVSS Score: $vuln.cvssScore
#if ($vuln.getSource().name().equals("NVD"))
<!-- todo: temp workaround as NSP uses CVSSv3 and supplies the full vector -->
(AV:$enc.html($vuln.cvssAccessVector.substring(0,1))/AC:$enc.html($vuln.cvssAccessComplexity.substring(0,1))/Au:$enc.html($vuln.cvssAuthentication.substring(0,1))/C:$enc.html($vuln.cvssConfidentialityImpact.substring(0,1))/I:$enc.html($vuln.cvssIntegrityImpact.substring(0,1))/A:$enc.html($vuln.cvssAvailabilityImpact.substring(0,1)))
#end
#if ($vuln.cwe) #if ($vuln.cwe)
<br/>CWE: $vuln.cwe <br/>CWE: $vuln.cwe
#end #end
@@ -863,18 +874,28 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
#end #end
</p> </p>
#if ($vuln.getVulnerableSoftware().size()<2) #if ($vuln.getSource().name().equals("NVD"))
<p>Vulnerable Software &amp; Versions:<ul> #if ($vuln.getVulnerableSoftware().size()<2)
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li> <p>Vulnerable Software &amp; Versions:<ul>
</ul></p> <li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
#else </ul></p>
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="return toggleDisplay(this,'.vs$vsctr', 'show all', 'show less');">show all</a>)<ul> #else
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li> <p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="return toggleDisplay(this,'.vs$vsctr', 'show all', 'show less');">show all</a>)<ul>
<li class="vs$vsctr">...</li> <li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
#foreach($vs in $vuln.getVulnerableSoftware(true)) <li class="vs$vsctr">...</li>
<li class="vs$vsctr hidden"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vs.name)">$enc.html($vs.name)</a> #if($vs.hasPreviousVersion()) and all previous versions#end</li> #foreach($vs in $vuln.getVulnerableSoftware(true))
<li class="vs$vsctr hidden"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vs.name)">$enc.html($vs.name)</a> #if($vs.hasPreviousVersion()) and all previous versions#end</li>
#end
</ul></p>
#end #end
</ul></p> #elseif ($vuln.getSource().name().equals("NSP"))
<p>Vulnerable Software &amp; Versions:
<ul>
#foreach($vs in $vuln.getVulnerableSoftware())
<li class="vs$vsctr">$enc.html($vs.name)</li>
#end
</ul>
</p>
#end #end
#end #end
</div> </div>
@@ -929,8 +950,8 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
<li>$enc.html($related.DisplayFileName) <li>$enc.html($related.DisplayFileName)
<ul> <ul>
<li>File Path:&nbsp;$enc.html($related.FilePath)</li> <li>File Path:&nbsp;$enc.html($related.FilePath)</li>
<li>SHA1:&nbsp;$enc.html($related.Sha1sum)</li> <li>SHA1:&nbsp;#if($related.Sha1sum)$enc.html($related.Sha1sum)#end</li>
<li>MD5:&nbsp;$enc.html($related.Md5sum)</li> <li>MD5:&nbsp;#if($related.Md5sum)$enc.html($related.Md5sum)#end</li>
</ul> </ul>
</li> </li>
#end #end
@@ -986,7 +1007,11 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
<div id="content$cnt" class="subsectioncontent standardsubsection"> <div id="content$cnt" class="subsectioncontent standardsubsection">
#foreach($vuln in $dependency.getSuppressedVulnerabilities()) #foreach($vuln in $dependency.getSuppressedVulnerabilities())
#set($vsctr=$vsctr+1) #set($vsctr=$vsctr+1)
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a></b>&nbsp;&nbsp;<span class="suppressedLabel" >suppressed</span></p> #if($vuln.getSource().name().equals("NVD"))
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a></b>&nbsp;&nbsp;<span class="suppressedLabel" >suppressed</span></p>
#elseif($vuln.getSource().name().equals("NSP"))
<p><b><a target="_blank" href="https://nodesecurity.io/advisories/$enc.url($vuln.name)">NSP-$enc.html($vuln.name)</a></b>&nbsp;&nbsp;<span class="suppressedLabel" >suppressed</span></p>
#end
<p>Severity: <p>Severity:
#if ($vuln.cvssScore<4.0) #if ($vuln.cvssScore<4.0)
Low Low
@@ -1035,6 +1060,11 @@ Getting Help: <a href="https://groups.google.com/forum/#!forum/dependency-check"
## END SUPPRESSED VULNERABILITIES ## END SUPPRESSED VULNERABILITIES
</div> </div>
</div> </div>
<div><br/><br/>This report contains data retrieved from the <a href="http://nvd.nist.gov">National Vulnerability Database</a>.</div> <div>
<br/><br/>
This report contains data retrieved from the <a href="https://nvd.nist.gov">National Vulnerability Database</a>.
<br/>
This report may contain data retrieved from the <a href="https://nodesecurity.io">Node Security Platform</a>.
</div>
</body> </body>
</html> </html>

View File

@@ -131,7 +131,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
table.lined tr:nth-child(even) { table.lined tr:nth-child(even) {
background-color: #fbfbfb; background-color: #fbfbfb;
} }
th.cve { th.name {
width: 60px; width: 60px;
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
@@ -200,7 +200,7 @@ have been reported. Additionally, the HTML report provides many features not fou
#set($cnt=0) #set($cnt=0)
<table id="vulnTable" class="lined"> <table id="vulnTable" class="lined">
<thead><tr> <thead><tr>
<th class="cve" data-sort="string">CVE</th> <th class="name" data-sort="string">NAME</th>
<th class="cwe" data-sort="string">CWE</th> <th class="cwe" data-sort="string">CWE</th>
<th class="severity" data-sort="severity">Severity (CVSS)</th> <th class="severity" data-sort="severity">Severity (CVSS)</th>
<th class="dependency" data-sort="string">Dependency</th> <th class="dependency" data-sort="string">Dependency</th>
@@ -210,7 +210,13 @@ have been reported. Additionally, the HTML report provides many features not fou
#if($dependency.getVulnerabilities().size()>0) #if($dependency.getVulnerabilities().size()>0)
#foreach($vuln in $dependency.getVulnerabilities()) #foreach($vuln in $dependency.getVulnerabilities())
<tr> <tr>
<td><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a></td> <td>
#if($vuln.getSource().name().equals("NVD"))
<a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vuln.name)">$enc.html($vuln.name)</a>
#elseif($vuln.getSource().name().equals("NSP"))
<a target="_blank" href="https://nodesecurity.io/advisories/$enc.url($vuln.name)">NSP-$enc.html($vuln.name)</a>
#end
</td>
<td> <td>
#if ($vuln.cwe) #if ($vuln.cwe)
$vuln.cwe $vuln.cwe
@@ -241,6 +247,11 @@ have been reported. Additionally, the HTML report provides many features not fou
</tbody> </tbody>
</table> </table>
</div> </div>
<p><br/><br/>This report contains data retrieved from the <a href="http://nvd.nist.gov">National Vulnerability Database</a>.</p> <p>
<br/><br/>
This report contains data retrieved from the <a href="https://nvd.nist.gov">National Vulnerability Database</a>.
<br/>
This report may contain data retrieved from the <a href="https://nodesecurity.io">Node Security Platform</a>.
</p>
</body> </body>
</html> </html>

View File

@@ -41,7 +41,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<version>$enc.xml($version)</version> <version>$enc.xml($version)</version>
#end #end
<reportDate>$scanDateXML</reportDate> <reportDate>$scanDateXML</reportDate>
<credits>This report contains data retrieved from the National Vulnerability Database: http://nvd.nist.gov</credits> <credits>This report contains data retrieved from the National Vulnerability Database: https://nvd.nist.gov and from the Node Security Platform: https://nodesecurity.io</credits>
</projectInfo> </projectInfo>
<dependencies> <dependencies>
#foreach($dependency in $dependencies) #foreach($dependency in $dependencies)
@@ -61,15 +61,18 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
#foreach($related in $dependency.getRelatedDependencies()) #foreach($related in $dependency.getRelatedDependencies())
<relatedDependency> <relatedDependency>
<filePath>$enc.xml($related.FilePath)</filePath> <filePath>$enc.xml($related.FilePath)</filePath>
<sha1>$enc.xml($related.Sha1sum)</sha1> <sha1>#if($related.Sha1sum)$enc.xml($related.Sha1sum)#end</sha1>
<md5>$enc.xml($related.Md5sum)</md5> <md5>#if($related.Md5sum)$enc.xml($related.Md5sum)#end</md5>
#foreach($id in $related.getIdentifiers()) #foreach($id in $related.getIdentifiers())
#if ($id.type=="maven") #if ($id.type=="maven" || $id.type=="npm")
<identifier type="$enc.xml($id.type)"> <identifier type="$enc.xml($id.type)">
<name>($id.value)</name> <name>$enc.xml($id.value)</name>
#if( $id.url ) #if( $id.url )
<url>$enc.xml($id.url)</url> <url>$enc.xml($id.url)</url>
#end #end
#if( $id.description )
<description>$enc.xml($id.description)</description>
#end
#if ($id.notes) #if ($id.notes)
<notes>$enc.xml($id.notes)</notes> <notes>$enc.xml($id.notes)</notes>
#end #end
@@ -139,14 +142,14 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<vulnerabilities> <vulnerabilities>
#foreach($vuln in $dependency.getVulnerabilities()) #foreach($vuln in $dependency.getVulnerabilities())
<vulnerability> <vulnerability>
<name>$enc.xml($vuln.name)</name> <name>#if($vuln.getSource().name().equals("NSP"))NSP-#end$enc.xml($vuln.name)</name>
<cvssScore>$vuln.cvssScore</cvssScore> <cvssScore>$vuln.cvssScore</cvssScore>
<cvssAccessVector>$enc.xml($vuln.cvssAccessVector)</cvssAccessVector> <cvssAccessVector>#if($vuln.cvssAccessVector)$enc.xml($vuln.cvssAccessVector)#end</cvssAccessVector>
<cvssAccessComplexity>$enc.xml($vuln.cvssAccessComplexity)</cvssAccessComplexity> <cvssAccessComplexity>#if($vuln.cvssAccessComplexity)$enc.xml($vuln.cvssAccessComplexity)#end</cvssAccessComplexity>
<cvssAuthenticationr>$enc.xml($vuln.cvssAuthentication)</cvssAuthenticationr> <cvssAuthenticationr>#if($vuln.cvssAuthentication)$enc.xml($vuln.cvssAuthentication)#end</cvssAuthenticationr>
<cvssConfidentialImpact>$enc.xml($vuln.cvssConfidentialityImpact)</cvssConfidentialImpact> <cvssConfidentialImpact>#if($vuln.cvssConfidentialityImpact)$enc.xml($vuln.cvssConfidentialityImpact)#end</cvssConfidentialImpact>
<cvssIntegrityImpact>$enc.xml($vuln.cvssIntegrityImpact)</cvssIntegrityImpact> <cvssIntegrityImpact>#if($vuln.cvssIntegrityImpact)$enc.xml($vuln.cvssIntegrityImpact)#end</cvssIntegrityImpact>
<cvssAvailabilityImpact>$enc.xml($vuln.cvssAvailabilityImpact)</cvssAvailabilityImpact> <cvssAvailabilityImpact>#if($vuln.cvssAvailabilityImpact)$enc.xml($vuln.cvssAvailabilityImpact)#end</cvssAvailabilityImpact>
#if ($vuln.cvssScore<4.0) #if ($vuln.cvssScore<4.0)
<severity>Low</severity> <severity>Low</severity>
#elseif ($vuln.cvssScore>=7.0) #elseif ($vuln.cvssScore>=7.0)

View File

@@ -18,6 +18,8 @@
package org.owasp.dependencycheck; package org.owasp.dependencycheck;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
@@ -58,18 +60,23 @@ public class EngineIT extends BaseDBTestCase {
try { try {
instance.analyzeDependencies(); instance.analyzeDependencies();
} catch (ExceptionCollection ex) { } catch (ExceptionCollection ex) {
if (ex.getExceptions().size() == 1 Set<String> allowedMessages = new HashSet<>();
&& (ex.getExceptions().get(0).getMessage().contains("bundle-audit") allowedMessages.add("bundle-audit");
|| ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer"))) { allowedMessages.add("AssemblyAnalyzer");
//this is fine to ignore //allowedMessages.add("Unable to connect to");
} else if (ex.getExceptions().size() == 2 for (Throwable t : ex.getExceptions()) {
&& ((ex.getExceptions().get(0).getMessage().contains("bundle-audit") boolean isOk = false;
&& ex.getExceptions().get(1).getMessage().contains("AssemblyAnalyzer")) if (t.getMessage()!=null) {
|| (ex.getExceptions().get(1).getMessage().contains("bundle-audit") for (String msg : allowedMessages) {
&& ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer")))) { if (t.getMessage().contains(msg)) {
//this is fine to ignore isOk=true;
} else { break;
throw ex; }
}
}
if (!isOk) {
throw ex;
}
} }
} }
DatabaseProperties prop = null; DatabaseProperties prop = null;

View File

@@ -0,0 +1,81 @@
/*
* 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.data.nsp;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.owasp.dependencycheck.BaseTest;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import static org.junit.Assume.assumeFalse;
import org.owasp.dependencycheck.utils.URLConnectionFailureException;
public class NspSearchTest extends BaseTest {
private static final Logger LOGGER = LoggerFactory.getLogger(NspSearchTest.class);
private NspSearch searcher;
@Before
public void setUp() throws Exception {
String url = Settings.getString(Settings.KEYS.ANALYZER_NSP_URL);
LOGGER.debug(url);
searcher = new NspSearch(new URL(url));
}
@Test
public void testNspSearchPositive() throws Exception {
InputStream in = BaseTest.getResourceAsStream(this, "nsp/package.json");
try (JsonReader jsonReader = Json.createReader(in)) {
final JsonObject packageJson = jsonReader.readObject();
final JsonObject sanitizedJson = SanitizePackage.sanitize(packageJson);
final JsonObjectBuilder builder = Json.createObjectBuilder();
final JsonObject nspPayload = builder.add("package", sanitizedJson).build();
final List<Advisory> advisories = searcher.submitPackage(nspPayload);
Assert.assertTrue(advisories.size() > 0);
} catch (Exception ex) {
assumeFalse(ex instanceof URLConnectionFailureException
&& ex.getMessage().contains("Unable to connect to "));
throw ex;
}
}
@Test(expected = AnalysisException.class)
public void testNspSearchNegative() throws Exception {
InputStream in = BaseTest.getResourceAsStream(this, "nsp/package.json");
try (JsonReader jsonReader = Json.createReader(in)) {
final JsonObject packageJson = jsonReader.readObject();
final JsonObject sanitizedJson = SanitizePackage.sanitize(packageJson);
searcher.submitPackage(sanitizedJson);
} catch (Exception ex) {
assumeFalse(ex instanceof URLConnectionFailureException
&& ex.getMessage().contains("Unable to connect to "));
throw ex;
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.data.nsp;
import org.junit.Assert;
import org.junit.Test;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
public class SanitizePackageTest {
@Test
public void testSanitizer() throws Exception {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder
.add("name", "my app")
.add("version", "1.0.0")
.add("description", "my app does amazing things")
.add("keywords", "best, app, ever")
.add("homepage", "http://example.com")
.add("bugs", "http://example.com/bugs")
.add("license", "Apache-2.0")
.add("main", "myscript")
.add("dependencies", "{ \"foo\" : \"1.0.0 - 2.9999.9999\"}")
.add("devDependencies", "{ \"foo\" : \"1.0.0 - 2.9999.9999\"}")
.add("peerDependencies", "{ \"foo\" : \"1.0.0 - 2.9999.9999\"}")
.add("bundledDependencies", "{ \"foo\" : \"1.0.0 - 2.9999.9999\"}")
.add("optionalDependencies", "{ \"foo\" : \"1.0.0 - 2.9999.9999\"}");
JsonObject packageJson = builder.build();
JsonObject sanitized = SanitizePackage.sanitize(packageJson);
Assert.assertTrue(sanitized.containsKey("name"));
Assert.assertTrue(sanitized.containsKey("version"));
Assert.assertTrue(sanitized.containsKey("dependencies"));
Assert.assertTrue(sanitized.containsKey("devDependencies"));
Assert.assertTrue(sanitized.containsKey("peerDependencies"));
Assert.assertTrue(sanitized.containsKey("bundledDependencies"));
Assert.assertTrue(sanitized.containsKey("optionalDependencies"));
Assert.assertFalse(sanitized.containsKey("description"));
Assert.assertFalse(sanitized.containsKey("keywords"));
Assert.assertFalse(sanitized.containsKey("homepage"));
Assert.assertFalse(sanitized.containsKey("bugs"));
Assert.assertFalse(sanitized.containsKey("license"));
Assert.assertFalse(sanitized.containsKey("main"));
}
}

View File

@@ -73,6 +73,9 @@ analyzer.nexus.proxy=true
analyzer.central.enabled=true analyzer.central.enabled=true
analyzer.central.url=https://search.maven.org/solrsearch/select analyzer.central.url=https://search.maven.org/solrsearch/select
# the URL for searching api.nodesecurity.io
analyzer.nsp.url=https://api.nodesecurity.io/check
# the number of nested archives that will be searched. # the number of nested archives that will be searched.
archive.scan.depth=3 archive.scan.depth=3
@@ -84,6 +87,7 @@ analyzer.experimental.enabled=true
analyzer.jar.enabled=true analyzer.jar.enabled=true
analyzer.archive.enabled=true analyzer.archive.enabled=true
analyzer.node.package.enabled=true analyzer.node.package.enabled=true
analyzer.nsp.package.enabled=true
analyzer.composer.lock.enabled=true analyzer.composer.lock.enabled=true
analyzer.python.distribution.enabled=true analyzer.python.distribution.enabled=true
analyzer.python.package.enabled=true analyzer.python.package.enabled=true

View File

@@ -0,0 +1,59 @@
{
"name": "owasp-nodejs-goat",
"private": true,
"version": "1.3.0",
"description": "A tool to learn OWASP Top 10 for node.js developers",
"main": "server.js",
"dependencies": {
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.15.1",
"consolidate": "^0.14.1",
"csurf": "^1.8.3",
"dont-sniff-mimetype": "^1.0.0",
"express": "^4.13.4",
"express-session": "^1.13.0",
"forever": "^0.15.1",
"helmet": "^2.0.0",
"marked": "0.3.5",
"mongodb": "^2.1.18",
"serve-favicon": "^2.3.0",
"swig": "^1.4.2",
"underscore": "^1.8.3"
},
"comments": {
"//": "do not upgrade the marked package version it is set by purpose",
"//": "to be a vulnerable package to demonstrate an xss introduced through",
"//": "a9 insecure components"
},
"engines": {
"node": "4.4.x",
"npm": "2.15.x"
},
"scripts": {
"start": "node server.js",
"test": "node node_modules/grunt-cli/bin/grunt test",
"db:seed": "grunt db-reset",
"precommit": "grunt precommit"
},
"devDependencies": {
"async": "^2.0.0-rc.4",
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-concurrent": "^2.3.0",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-env": "latest",
"grunt-jsbeautifier": "^0.2.12",
"grunt-mocha-test": "^0.12.7",
"grunt-nodemon": "^0.4.2",
"grunt-if": "https://github.com/binarymist/grunt-if/tarball/master",
"grunt-npm-install": "^0.3.0",
"grunt-retire": "^0.3.12",
"mocha": "^2.4.5",
"selenium-webdriver": "^2.53.2",
"should": "^8.3.1",
"zaproxy": "^0.2.0"
},
"repository": "https://github.com/OWASP/NodejsGoat",
"license": "Apache 2.0"
}

View File

@@ -258,6 +258,14 @@ public final class Settings {
* enabled. * enabled.
*/ */
public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled"; public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled";
/**
* The properties key for whether the Node Security Platform (nsp) analyzer is enabled.
*/
public static final String ANALYZER_NSP_PACKAGE_ENABLED = "analyzer.nsp.package.enabled";
/**
* The properties key for whether the Nexus analyzer is enabled.
*/
public static final String ANALYZER_NSP_URL = "analyzer.nsp.url";
/** /**
* The properties key for whether the composer lock file analyzer is * The properties key for whether the composer lock file analyzer is
* enabled. * enabled.

27
pom.xml
View File

@@ -288,7 +288,7 @@ Copyright (c) 2012 - Jeremy Long
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
<configuration> <configuration>
<!--destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile--> <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
<propertyName>surefireArgLine</propertyName> <propertyName>surefireArgLine</propertyName>
</configuration> </configuration>
</execution> </execution>
@@ -299,12 +299,31 @@ Copyright (c) 2012 - Jeremy Long
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
<configuration> <configuration>
<!--destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile--> <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
<propertyName>failsafeArgLine</propertyName> <propertyName>failsafeArgLine</propertyName>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>add-dynamic-properties</id>
<phase>pre-integration-test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['invoker.mavenOpts']=project.properties.failsafeArgLine
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
@@ -578,6 +597,10 @@ Copyright (c) 2012 - Jeremy Long
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version> <version>0.7.9</version>
<configuration> <configuration>
<dataFileIncludes>
<dataFileInclude>target/coverage-reports/jacoco-ut.exec</dataFileInclude>
<dataFileInclude>target/coverage-reports/jacoco-it.exec</dataFileInclude>
</dataFileIncludes>
</configuration> </configuration>
<reportSets> <reportSets>
<reportSet> <reportSet>