Merge branch 'pwhittlesea-issue-730'

This commit is contained in:
Jeremy Long
2017-06-22 07:18:48 -04:00
33 changed files with 1114 additions and 94 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
*/target/**
# IntelliJ test run side-effects
dependency-check-core/data/
dependency-check-ant/data/
# Intellij project files
*.iml
*.ipr

View File

@@ -136,6 +136,69 @@ docker run --rm \
```
Upgrade Notes
-------------
### Upgrading from **1.x.x** to **2.x.x**
Note that when upgrading from version 1.x.x that the following changes will need to be made to your configuration.
#### Suppression file
In order to support multiple suppression files, the mechanism for configuring suppression files has changed.
As such, users that have defined a suppression file in their configuration will need to update.
See the examples below:
##### Ant
Old:
```xml
<dependency-check
failBuildOnCVSS="3"
suppressionFile="suppression.xml">
</dependency-check>
```
New:
```xml
<dependency-check
failBuildOnCVSS="3">
<suppressionFile>suppression.xml</suppressionFile>
</dependency-check>
```
##### Maven
Old:
```xml
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<suppressionFile>suppression.xml</suppressionFile>
</configuration>
</plugin>
```
New:
```xml
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<suppressionFiles>
<suppressionFile>suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
```
Mailing List
------------

View File

@@ -18,7 +18,9 @@
package org.owasp.dependencycheck.taskdefs;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.EnumeratedAttribute;
@@ -62,7 +64,7 @@ public class Check extends Update {
* Whether or not the NSP Analyzer is enabled.
*/
private Boolean nspAnalyzerEnabled;
/**
* Whether or not the Ruby Bundle Audit Analyzer is enabled.
*/
@@ -153,9 +155,14 @@ public class Check extends Update {
*/
private String reportFormat = "HTML";
/**
* The path to the suppression file.
* Suppression file path.
*/
private String suppressionFile;
private String suppressionFile = null;
/**
* Suppression file paths.
*/
private List<String> suppressionFiles = new ArrayList<>();
/**
* The path to the suppression file.
*/
@@ -230,6 +237,17 @@ public class Check extends Update {
getPath().add(rc);
}
/**
* Add a suppression file.
*
* This is called by Ant with the configured {@link SuppressionFile}.
*
* @param suppressionFile the suppression file to add.
*/
public void addConfiguredSuppressionFile(final SuppressionFile suppressionFile) {
suppressionFiles.add(suppressionFile.getPath());
}
/**
* Returns the path. If the path has not been initialized yet, this class is
* synchronized, and will instantiate the path object.
@@ -436,12 +454,12 @@ public class Check extends Update {
}
/**
* Get the value of suppressionFile.
* Gets suppression file paths.
*
* @return the value of suppressionFile
* @return the suppression files.
*/
public String getSuppressionFile() {
return suppressionFile;
public List<String> getSuppressionFiles() {
return suppressionFiles;
}
/**
@@ -451,6 +469,7 @@ public class Check extends Update {
*/
public void setSuppressionFile(String suppressionFile) {
this.suppressionFile = suppressionFile;
suppressionFiles.add(suppressionFile);
}
/**
@@ -742,6 +761,7 @@ public class Check extends Update {
public void setNodeAnalyzerEnabled(Boolean nodeAnalyzerEnabled) {
this.nodeAnalyzerEnabled = nodeAnalyzerEnabled;
}
/**
* Get the value of nspAnalyzerEnabled.
*
@@ -750,6 +770,7 @@ public class Check extends Update {
public Boolean isNspAnalyzerEnabled() {
return nspAnalyzerEnabled;
}
/**
* Set the value of nspAnalyzerEnabled.
*
@@ -1013,7 +1034,7 @@ public class Check extends Update {
protected void populateSettings() throws BuildException {
super.populateSettings();
Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles.toArray(new String[suppressionFiles.size()]));
Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);

View File

@@ -0,0 +1,51 @@
/*
* This file is part of dependency-check-ant.
*
* 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 The OWASP Foundation. All Rights Reserved.
*/
package org.owasp.dependencycheck.taskdefs;
/**
* Class : {@link SuppressionFile} Responsibility : Models a suppression file
* nested XML element where the simple content is its location.
*
* @author Phillip Whittlesea
*/
public class SuppressionFile {
/**
* The path to the suppression file.
*/
private String path;
/**
* Called by ant with the simple content of the suppressionFile xml element.
*
* @param text the simple content.
*/
public final void addText(String text) {
this.path = text;
}
/**
* Gets the path to the suppression file.
*
* @return the path.
*/
public String getPath() {
return path;
}
}

View File

@@ -18,6 +18,7 @@ the project's dependencies.
reportoutputdirectory="${basedir}"
reportformat="ALL">
<suppressionfile>path/to/suppression.xml</suppressionfile>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
@@ -38,14 +39,19 @@ failOnError | Whether the build should fail if there is an error execu
projectName | The name of the project being scanned. | Dependency-Check
reportFormat | The report format to be generated (HTML, XML, CSV, JSON, VULN, ALL). This configuration option has no affect if using this within the Site plugin unless the externalReport is set to true. | HTML
reportOutputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target'
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html) | &nbsp;
hintsFile | The file path to the XML hints file \- used to resolve [false negatives](../general/hints.html) | &nbsp;
proxyServer | The Proxy Server; see the [proxy configuration](../data/proxy.html) page for more information. | &nbsp;
proxyPort | The Proxy Port. | &nbsp;
proxyUsername | Defines the proxy user name. | &nbsp;
proxyPassword | Defines the proxy password. | &nbsp;
connectionTimeout | The URL Connection Timeout. | &nbsp;
enableExperimental | Enable the [experimental analyzers](../analyzers/index.html). If not enabled the experimental analyzers (see below) will not be loaded or used. | false
enableExperimental | Enable the [experimental analyzers](../analyzers/index.html). If not enabled the experimental analyzers (see below) will not be loaded or used. | false
The following nested elements can be set on the dependency-check task.
Property | Description | Default Value
----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html). Element can be specified multiple times. | &nbsp;
Analyzer Configuration
====================

View File

@@ -114,4 +114,54 @@ public class DependencyCheckTaskTest {
expectedException.expect(BuildException.class);
buildFileRule.executeTarget("failCVSS");
}
/**
* Test the DependencyCheckTask where a CVE is suppressed.
*/
@Test
public void testSuppressingCVE() {
// GIVEN an ant task with a vulnerability
final String antTaskName = "suppression";
// WHEN executing the ant task
buildFileRule.executeTarget(antTaskName);
// THEN the ant task executed without error
final File report = new File("target/suppression-report.html");
assertTrue("Expected the DependencyCheck report to be generated", report.exists());
}
/**
* Test the DependencyCheckTask deprecated suppression property throws an
* exception with a warning.
*/
@Test
public void testSuppressingSingle() {
// GIVEN an ant task with a vulnerability using the legacy property
final String antTaskName = "suppression-single";
// WHEN executing the ant task
buildFileRule.executeTarget(antTaskName);
// THEN the ant task executed without error
final File report = new File("target/suppression-single-report.html");
assertTrue("Expected the DependencyCheck report to be generated", report.exists());
}
/**
* Test the DependencyCheckTask deprecated suppression property throws an
* exception with a warning.
*/
@Test
public void testSuppressingMultiple() {
// GIVEN an ant task with a vulnerability using multiple was to configure the suppression file
final String antTaskName = "suppression-multiple";
// WHEN executing the ant task
buildFileRule.executeTarget(antTaskName);
// THEN the ant task executed without error
final File report = new File("target/suppression-multiple-report.html");
assertTrue("Expected the DependencyCheck report to be generated", report.exists());
}
}

View File

@@ -71,4 +71,47 @@
</fileset>
</dependency-check>
</target>
<target name="suppression">
<dependency-check
applicationName="test suppression"
reportOutputDirectory="${project.build.directory}/suppression-report.html"
autoupdate="false"
failBuildOnCVSS="3">
<suppressionfile>${project.build.directory}/test-classes/test-suppression1.xml</suppressionfile>
<suppressionfile>${project.build.directory}/test-classes/test-suppression2.xml</suppressionfile>
<fileset dir="${project.build.directory}/test-classes/jars">
<include name="axis-1.4.jar"/>
</fileset>
<filelist
dir="${project.build.directory}/test-classes/list"
files="jetty-6.1.0.jar,org.mortbay.jetty.jar"/>
</dependency-check>
</target>
<target name="suppression-single">
<dependency-check
applicationName="test suppression"
reportOutputDirectory="${project.build.directory}/suppression-single-report.html"
autoupdate="false"
failBuildOnCVSS="3"
suppressionFile="${project.build.directory}/test-classes/test-suppression1.xml">
<fileset dir="${project.build.directory}/test-classes/jars">
<include name="axis-1.4.jar"/>
</fileset>
</dependency-check>
</target>
<target name="suppression-multiple">
<dependency-check
applicationName="test suppression"
reportOutputDirectory="${project.build.directory}/suppression-multiple-report.html"
autoupdate="false"
failBuildOnCVSS="3"
suppressionFile="${project.build.directory}/test-classes/test-suppression1.xml">
<suppressionfile>${project.build.directory}/test-classes/test-suppression2.xml</suppressionfile>
<fileset dir="${project.build.directory}/test-classes/jars">
<include name="axis-1.4.jar"/>
</fileset>
</dependency-check>
</target>
</project>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: axis-1.4.jar
]]></notes>
<gav regex="true">^org\.apache\.axis:axis:.*$</gav>
<cpe>cpe:/a:apache:axis</cpe>
</suppress>
</suppressions>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: org.mortbay.jetty.jar
]]></notes>
<gav regex="true">^jetty:org\.mortbay\.jetty:.*$</gav>
<cpe>cpe:/a:jetty:jetty</cpe>
</suppress>
<suppress>
<notes><![CDATA[
file name: org.mortbay.jetty.jar
]]></notes>
<gav regex="true">^jetty:org\.mortbay\.jetty:.*$</gav>
<cpe>cpe:/a:mortbay:jetty</cpe>
</suppress>
<suppress>
<notes><![CDATA[
file name: org.mortbay.jetty.jar
]]></notes>
<gav regex="true">^jetty:org\.mortbay\.jetty:.*$</gav>
<cpe>cpe:/a:mortbay_jetty:jetty</cpe>
</suppress>
</suppressions>

View File

@@ -387,7 +387,7 @@ public class App {
final String proxyPass = cli.getProxyPassword();
final String dataDirectory = cli.getDataDirectory();
final File propertiesFile = cli.getPropertiesFile();
final String suppressionFile = cli.getSuppressionFile();
final String[] suppressionFiles = cli.getSuppressionFiles();
final String hintsFile = cli.getHintsFile();
final String nexusUrl = cli.getNexusUrl();
final String databaseDriverName = cli.getDatabaseDriverName();
@@ -436,10 +436,11 @@ public class App {
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPass);
Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles);
//File Type Analyzer Settings
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);

View File

@@ -273,8 +273,9 @@ public final class CliParser {
.desc("Sets how deep nested symbolic links will be followed; 0 indicates symbolic links will not be followed.")
.build();
final Option suppressionFile = Option.builder().argName("file").hasArg().longOpt(ARGUMENT.SUPPRESSION_FILE)
.desc("The file path to the suppression XML file.")
final Option suppressionFile = Option.builder().argName("file").hasArgs().longOpt(ARGUMENT.SUPPRESSION_FILES)
.desc("The file path to the suppression XML file. This can be specified more then once to utilize multiple "
+ "suppression files")
.build();
final Option hintsFile = Option.builder().argName("file").hasArg().longOpt(ARGUMENT.HINTS_FILE)
@@ -735,7 +736,8 @@ public final class CliParser {
public boolean isNodeJsDisabled() {
return hasDisableOption(ARGUMENT.DISABLE_NODE_JS, Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED);
}
/**
/**
* Returns true if the disableNSP command line argument was specified.
*
* @return true if the disableNSP command line argument was specified;
@@ -1031,12 +1033,12 @@ public final class CliParser {
}
/**
* Returns the path to the suppression file.
* Returns the paths to the suppression files.
*
* @return the path to the suppression file
* @return the paths to the suppression files.
*/
public String getSuppressionFile() {
return line.getOptionValue(ARGUMENT.SUPPRESSION_FILE);
public String[] getSuppressionFiles() {
return line.getOptionValues(ARGUMENT.SUPPRESSION_FILES);
}
/**
@@ -1374,9 +1376,9 @@ public final class CliParser {
public static final String SYM_LINK_DEPTH = "symLink";
/**
* The CLI argument name for setting the location of the suppression
* file.
* file(s).
*/
public static final String SUPPRESSION_FILE = "suppression";
public static final String SUPPRESSION_FILES = "suppression";
/**
* The CLI argument name for setting the location of the hint file.
*/

View File

@@ -14,7 +14,7 @@ Short | Argument&nbsp;Name&nbsp;&nbsp; | Parameter | Description | Requir
| \-\-failOnCvss | \<score\> | If the score set between 0 and 10 the exit code from dependency-check will indicate if a vulnerability with a CVSS score equal to or higher was identified. | Optional
\-l | \-\-log | \<file\> | The file path to write verbose logging information. | Optional
\-n | \-\-noupdate | | Disables the automatic updating of the CPE data. | Optional
| \-\-suppression | \<file\> | The file path to the suppression XML file; used to suppress [false positives](../general/suppression.html). | Optional
| \-\-suppression | \<files\> | The file paths to the suppression XML files; used to suppress [false positives](../general/suppression.html). This can be specified more then once to utilize multiple suppression files. | Optional
\-h | \-\-help | | Print the help message. | Optional
| \-\-advancedHelp | | Print the advanced help message. | Optional
\-v | \-\-version | | Print the version information. | Optional

View File

@@ -13,28 +13,59 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2017 The OWASP Foundatio. All Rights Reserved.
* Copyright (c) 2017 The OWASP Foundation. All Rights Reserved.
*/
package org.owasp.dependencycheck;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
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.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.rules.ExpectedException;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.Settings.KEYS;
/**
*
* @author jeremy
* Tests for the {@link AppTest} class.
*/
public class AppTest {
/**
* Test rule for asserting exceptions and their contents.
*/
@Rule
public ExpectedException expectedException = ExpectedException.none();
/**
* Initialize the {@link Settings} singleton.
*/
@Before
public void setUp() {
Settings.initialize();
}
/**
* Clean the {@link Settings} singleton.
*/
@After
public void tearDown() {
Settings.cleanup();
}
/**
* Test of ensureCanonicalPath method, of class App.
*/
@@ -52,20 +83,20 @@ public class AppTest {
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));
}
/**
* Assert that boolean properties can be set on the CLI and parsed into the
* {@link Settings} singleton.
*
* @throws Exception the unexpected {@link Exception}.
*/
@Test
public void testPopulateSettings() throws FileNotFoundException, ParseException, InvalidSettingException, URISyntaxException {
public void testPopulateSettings() throws Exception {
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"};
@@ -103,8 +134,67 @@ public class AppTest {
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args8, expected));
}
/**
* Assert that an {@link UnrecognizedOptionException} is thrown when a
* property that is not supported is specified on the CLI.
*
* @throws Exception the unexpected {@link Exception}.
*/
@Test
public void testPopulateSettingsException() throws Exception {
String[] args = {"-invalidPROPERTY"};
expectedException.expect(UnrecognizedOptionException.class);
expectedException.expectMessage("Unrecognized option: -invalidPROPERTY");
testBooleanProperties(args, null);
}
/**
* Assert that a single suppression file can be set using the CLI.
*
* @throws Exception the unexpected {@link Exception}.
*/
@Test
public void testPopulatingSuppressionSettingsWithASingleFile() throws Exception {
// GIVEN CLI properties with the mandatory arguments
File prop = new File(this.getClass().getClassLoader().getResource("sample.properties").toURI().getPath());
// AND a single suppression file
String[] args = {"-P", prop.getAbsolutePath(), "--suppression", "another-file.xml"};
// WHEN parsing the CLI arguments
final CliParser cli = new CliParser();
cli.parse(args);
final App classUnderTest = new App();
classUnderTest.populateSettings(cli);
// THEN the suppression file is set in the settings singleton for use in the application core
assertThat("Expected the suppression file to be set in the Settings singleton", Settings.getString(KEYS.SUPPRESSION_FILE), is("another-file.xml"));
}
/**
* Assert that multiple suppression files can be set using the CLI.
*
* @throws Exception the unexpected {@link Exception}.
*/
@Test
public void testPopulatingSuppressionSettingsWithMultipleFiles() throws Exception {
// GIVEN CLI properties with the mandatory arguments
File prop = new File(this.getClass().getClassLoader().getResource("sample.properties").toURI().getPath());
// AND a single suppression file
String[] args = {"-P", prop.getAbsolutePath(), "--suppression", "first-file.xml", "another-file.xml"};
// WHEN parsing the CLI arguments
final CliParser cli = new CliParser();
cli.parse(args);
final App classUnderTest = new App();
classUnderTest.populateSettings(cli);
// THEN the suppression file is set in the settings singleton for use in the application core
assertThat("Expected the suppression files to be set in the Settings singleton with a separator", Settings.getString(KEYS.SUPPRESSION_FILE), is("first-file.xml,another-file.xml"));
}
private boolean testBooleanProperties(String[] args, Map<String, Boolean> expected) throws URISyntaxException, FileNotFoundException, ParseException, InvalidSettingException {
@@ -124,4 +214,5 @@ public class AppTest {
Settings.cleanup();
}
}
}

View File

@@ -71,7 +71,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
try {
loadSuppressionData();
} catch (SuppressionParseException ex) {
throw new InitializationException("Error initializing the suppression analyzer", ex);
throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex);
}
}
@@ -99,23 +99,41 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
}
/**
* Loads the suppression rules file.
* Loads all the suppression rules files configured in the {@link Settings} singleton.
*
* @throws SuppressionParseException thrown if the XML cannot be parsed.
*/
private void loadSuppressionData() throws SuppressionParseException {
final SuppressionParser parser = new SuppressionParser();
File file = null;
try {
final InputStream in = FileUtils.getResourceAsStream("dependencycheck-base-suppression.xml");
rules = parser.parseSuppressionRules(in);
} catch (SAXException ex) {
throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
}
final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
if (suppressionFilePath == null) {
final String[] suppressionFilePaths = Settings.getArray(Settings.KEYS.SUPPRESSION_FILE);
if (suppressionFilePaths == null || suppressionFilePaths.length == 0) {
return;
}
// Load all the suppression file paths
for (final String suppressionFilePath : suppressionFilePaths) {
loadSuppressionFile(parser, suppressionFilePath);
}
LOGGER.debug("{} suppression rules were loaded.", rules.size());
}
/**
* Load a single suppression rules file from the path provided using the parser provided.
*
* @param parser the parser to use for loading the file.
* @param suppressionFilePath the path to load.
* @throws SuppressionParseException thrown if the suppression file cannot be loaded and parsed.
*/
private void loadSuppressionFile(final SuppressionParser parser, final String suppressionFilePath) throws SuppressionParseException {
LOGGER.debug("Loading suppression rules from '{}'", suppressionFilePath);
File file = null;
boolean deleteTempFile = false;
try {
final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE);
@@ -153,7 +171,6 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
}
try {
rules.addAll(parser.parseSuppressionRules(file));
LOGGER.debug("{} suppression rules were loaded.", rules.size());
} catch (SuppressionParseException ex) {
LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath());
LOGGER.warn(ex.getMessage());

View File

@@ -17,30 +17,34 @@
*/
package org.owasp.dependencycheck.analyzer;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.owasp.dependencycheck.BaseTest;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.Settings.KEYS;
/**
* @author Jeremy Long
*/
public class AbstractSuppressionAnalyzerTest extends BaseTest {
/** A second suppression file to test with. */
private static final String OTHER_SUPPRESSIONS_FILE = "other-suppressions.xml";
/** Suppression file to test with. */
private static final String SUPPRESSIONS_FILE = "suppressions.xml";
private AbstractSuppressionAnalyzer instance;
@Before
@@ -64,24 +68,42 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
*/
@Test
public void testGetRulesFromSuppressionFileFromURL() throws Exception {
setSupressionFileFromURL();
instance.initialize();
int expCount = 5;
List<SuppressionRule> result = instance.getRules();
assertTrue(expCount <= result.size());
final String fileUrl = getClass().getClassLoader().getResource(SUPPRESSIONS_FILE).toURI().toURL().toString();
final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(fileUrl) - getNumberOfRulesLoadedInCoreFile();
assertEquals("Expected 5 extra rules in the given path", 5, numberOfExtraLoadedRules);
}
/**
* Test of getRules method, of class AbstractSuppressionAnalyzer for
* suppression file declared as URL.
* suppression file on the classpath.
*/
@Test
public void testGetRulesFromSuppressionFileInClasspath() throws Exception {
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, "suppressions.xml");
final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(SUPPRESSIONS_FILE) - getNumberOfRulesLoadedInCoreFile();
assertEquals("Expected 5 extra rules in the given file", 5, numberOfExtraLoadedRules);
}
/**
* Assert that rules are loaded from multiple files if multiple files are denfined in the {@link Settings} singleton.
*/
@Test
public void testGetRulesFromMultipleSuppressionFiles() throws Exception {
final int rulesInCoreFile = getNumberOfRulesLoadedInCoreFile();
// GIVEN suppression rules from one file
final int rulesInFirstFile = getNumberOfRulesLoadedFromPath(SUPPRESSIONS_FILE) - rulesInCoreFile;
// AND suppression rules from another file
final int rulesInSecondFile = getNumberOfRulesLoadedFromPath(OTHER_SUPPRESSIONS_FILE) - rulesInCoreFile;
// WHEN initializing with both suppression files
final String[] suppressionFiles = { SUPPRESSIONS_FILE, OTHER_SUPPRESSIONS_FILE };
Settings.setArrayIfNotEmpty(KEYS.SUPPRESSION_FILE, suppressionFiles);
instance.initialize();
int expCount = 5;
int currentSize = instance.getRules().size();
assertTrue(expCount <= currentSize);
// THEN rules from both files were loaded
final int expectedSize = rulesInFirstFile + rulesInSecondFile + rulesInCoreFile;
assertThat("Expected suppressions from both files", instance.getRules().size(), is(expectedSize));
}
@Test(expected = InitializationException.class)
@@ -90,15 +112,33 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
instance.initialize();
}
private void setSupressionFileFromURL() throws Exception {
try {
final String uri = this.getClass().getClassLoader().getResource("suppressions.xml").toURI().toURL().toString();
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, uri);
} catch (URISyntaxException ex) {
LoggerFactory.getLogger(AbstractSuppressionAnalyzerTest.class).error("", ex);
} catch (MalformedURLException ex) {
LoggerFactory.getLogger(AbstractSuppressionAnalyzerTest.class).error("", ex);
}
/**
* Return the number of rules that are loaded from the core suppression file.
*
* @return the number of rules defined in the core suppresion file.
* @throws Exception if loading the rules fails.
*/
private int getNumberOfRulesLoadedInCoreFile() throws Exception {
Settings.removeProperty(KEYS.SUPPRESSION_FILE);
final AbstractSuppressionAnalyzerImpl coreFileAnalyzer = new AbstractSuppressionAnalyzerImpl();
coreFileAnalyzer.initialize();
return coreFileAnalyzer.getRules().size();
}
/**
* Load a file into the {@link AbstractSuppressionAnalyzer} and return the number of rules loaded.
*
* @param path the path to load.
* @return the number of rules that were loaded (including the core rules).
* @throws Exception if loading the rules fails.
*/
private int getNumberOfRulesLoadedFromPath(final String path) throws Exception {
Settings.setString(KEYS.SUPPRESSION_FILE, path);
final AbstractSuppressionAnalyzerImpl fileAnalyzer = new AbstractSuppressionAnalyzerImpl();
fileAnalyzer.initialize();
return fileAnalyzer.getRules().size();
}
public class AbstractSuppressionAnalyzerImpl extends AbstractSuppressionAnalyzer {

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: jackson-dataformat-xml-2.4.5.jar
]]></notes>
<gav regex="true">^com\.fasterxml\.jackson.*:.*:.*$</gav>
<cpe>cpe:/a:fasterxml:jackson</cpe>
</suppress>
</suppressions>

View File

@@ -14,7 +14,7 @@ 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) 2013 Jeremy Long. All Rights Reserved.
Copyright (c) 2017 Jeremy Long. All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -22,8 +22,8 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<artifactId>test-dataformat-jackson</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.5</version>
@@ -38,10 +38,10 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<artifactId>jackson-dataformat-cbor</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
</project>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
#
# 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 The OWASP Foundation. All Rights Reserved.
#
invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.owasp.test</groupId>
<artifactId>test-multiple-suppression-files</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- These can be replaced by any other vulnerable dependency -->
<dependency>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<suppressionFile>${project.basedir}/test-suppression1.xml</suppressionFile>
<suppressionFiles>
<suppressionFile>${project.basedir}/test-suppression2.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,35 @@
/*
* 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 The OWASP Foundation. All Rights Reserved.
*/
import org.apache.commons.io.FileUtils
import org.apache.commons.lang.StringUtils
import java.nio.charset.Charset
// Check that suppression worked.
String log = FileUtils.readFileToString(new File(basedir, "build.log"), Charset.defaultCharset().name());
int count = StringUtils.countMatches(log, "CVE-2016-5696");
if (count > 0) {
System.out.println(String.format("CVE-2016-5696 (android-json-0.0.20131108.vaadin1.jar) was identified and should be suppressed"));
return false;
}
count = StringUtils.countMatches(log, "CVE-2016-7051");
if (count > 0) {
System.out.println(String.format("CVE-2016-7051 (jackson-module-jaxb-annotations-2.4.5.jar) was identified and should be suppressed"));
return false;
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: android-json-0.0.20131108.vaadin1.jar
]]></notes>
<gav regex="true">^com\.vaadin\.external\.google:android-json:.*$</gav>
<cpe>cpe:/a:google:android</cpe>
</suppress>
</suppressions>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: jackson-dataformat-xml-2.4.5.jar
]]></notes>
<gav regex="true">^com\.fasterxml\.jackson.*:.*:.*$</gav>
<cpe>cpe:/a:fasterxml:jackson</cpe>
</suppress>
</suppressions>

View File

@@ -0,0 +1,18 @@
#
# 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 The OWASP Foundation. All Rights Reserved.
#
invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.owasp.test</groupId>
<artifactId>test-multiple-suppression-files</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- These can be replaced by any other vulnerable dependency -->
<dependency>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<suppressionFiles>
<suppressionFile>${project.basedir}/test-suppression1.xml</suppressionFile>
<suppressionFile>${project.basedir}/test-suppression2.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,35 @@
/*
* 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 The OWASP Foundation. All Rights Reserved.
*/
import org.apache.commons.io.FileUtils
import org.apache.commons.lang.StringUtils
import java.nio.charset.Charset
// Check that suppression worked.
String log = FileUtils.readFileToString(new File(basedir, "build.log"), Charset.defaultCharset().name());
int count = StringUtils.countMatches(log, "CVE-2016-5696");
if (count > 0) {
System.out.println(String.format("CVE-2016-5696 (android-json-0.0.20131108.vaadin1.jar) was identified and should be suppressed"));
return false;
}
count = StringUtils.countMatches(log, "CVE-2016-7051");
if (count > 0) {
System.out.println(String.format("CVE-2016-7051 (jackson-module-jaxb-annotations-2.4.5.jar) was identified and should be suppressed"));
return false;
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: android-json-0.0.20131108.vaadin1.jar
]]></notes>
<gav regex="true">^com\.vaadin\.external\.google:android-json:.*$</gav>
<cpe>cpe:/a:google:android</cpe>
</suppress>
</suppressions>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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 The OWASP Foundation. All Rights Reserved.
-->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<notes><![CDATA[
file name: jackson-dataformat-xml-2.4.5.jar
]]></notes>
<gav regex="true">^com\.fasterxml\.jackson.*:.*:.*$</gav>
<cpe>cpe:/a:fasterxml:jackson</cpe>
</suppress>
</suppressions>

View File

@@ -21,6 +21,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.maven.artifact.Artifact;
@@ -199,11 +200,15 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
@Parameter(property = "connectionTimeout", defaultValue = "", required = false)
private String connectionTimeout;
/**
* The path to the suppression file.
* The paths to the suppression files.
*/
@Parameter(property = "suppressionFile", defaultValue = "", required = false)
@Parameter(required = false)
private String[] suppressionFiles;
/**
* The paths to the suppression file.
*/
@Parameter(required = false)
private String suppressionFile;
/**
* The path to the hints file.
*/
@@ -415,7 +420,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
private boolean skipSystemScope = false;
/**
* Skip analysis for dependencies which type matches this regular expression.
* Skip analysis for dependencies which type matches this regular
* expression.
*/
@SuppressWarnings("CanBeFinal")
@Parameter(property = "skipArtifactType", required = false)
@@ -488,7 +494,6 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
*/
private Filter<String> artifactTypeExcluded;
// </editor-fold>
//<editor-fold defaultstate="collapsed" desc="Base Maven implementation">
/**
@@ -660,8 +665,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
List<DependencyNode> nodes, ProjectBuildingRequest buildingRequest) {
ExceptionCollection exCol = null;
for (DependencyNode dependencyNode : nodes) {
if (artifactScopeExcluded.passes(dependencyNode.getArtifact().getScope()) ||
artifactTypeExcluded.passes(dependencyNode.getArtifact().getType())) {
if (artifactScopeExcluded.passes(dependencyNode.getArtifact().getScope())
|| artifactTypeExcluded.passes(dependencyNode.getArtifact().getType())) {
continue;
}
exCol = collectDependencies(engine, project, dependencyNode.getChildren(), buildingRequest);
@@ -686,7 +691,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
}
if (!isResolved) {
getLog().error("Unable to resolve system scoped dependency: " + dependencyNode.toNodeString());
exCol.addException(new DependencyNotFoundException("Unable to resolve system scoped dependency: " + dependencyNode.toNodeString()));
exCol.addException(new DependencyNotFoundException("Unable to resolve system scoped dependency: "
+ dependencyNode.toNodeString()));
}
} else {
final ArtifactCoordinate coordinate = TransferUtils.toArtifactCoordinate(dependencyNode.getArtifact());
@@ -924,9 +930,10 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
Settings.setStringIfNotNull(Settings.KEYS.PROXY_PASSWORD, password);
Settings.setStringIfNotNull(Settings.KEYS.PROXY_NON_PROXY_HOSTS, proxy.getNonProxyHosts());
}
final String[] suppressions = determineSuppressions();
Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressions);
Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
//File Type Analyzer Settings
@@ -1014,6 +1021,25 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
artifactTypeExcluded = new ArtifactTypeExcluded(skipArtifactType);
}
/**
* Combines the configured suppressionFile and suppressionFiles into a
* single array.
*
* @return an array of suppression file paths
*/
private String[] determineSuppressions() {
String[] suppressions = suppressionFiles;
if (suppressionFile != null) {
if (suppressions == null) {
suppressions = new String[]{suppressionFile};
} else {
suppressions = Arrays.copyOf(suppressions, suppressions.length + 1);
suppressions[suppressions.length - 1] = suppressionFile;
}
}
return suppressions;
}
/**
* Returns the maven proxy.
*

View File

@@ -28,7 +28,7 @@ skipRuntimeScope | Skip analysis for artifacts with Runtime Scope.
skipSystemScope | Skip analysis for artifacts with System Scope. | false
skipTestScope | Skip analysis for artifacts with Test Scope. | true
skipArtifactType | A regular expression used to filter/skip artifact types. | &nbsp;
suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html). | &nbsp;
suppressionFiles | The file paths to the XML suppression files \- used to suppress [false positives](../general/suppression.html). | &nbsp;
hintsFile | The file path to the XML hints file \- used to resolve [false negatives](../general/hints.html). | &nbsp;
enableExperimental | Enable the [experimental analyzers](../analyzers/index.html). If not enabled the experimental analyzers (see below) will not be loaded or used. | false

View File

@@ -204,3 +204,39 @@ Update the local cache of the NVD data from NIST without analyzing the dependenc
...
</project>
```
$H$H$H Example 7:
Suppress false positives using multiple suppression files (E.g. a company-wide suppression file and a local project file).
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${project.version}</version>
<configuration>
<suppressionFiles>
<suppressionFile>http://example.org/suppression.xml</suppressionFile>
<suppressionFile>project-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>
```

View File

@@ -151,10 +151,10 @@ public final class FileUtils {
}
/**
* Gets the {@link InputStream} for this resource
* Gets the {@link InputStream} for this resource.
*
* @param resource path
* @return
* @return the input stream for the given resource
*/
public static InputStream getResourceAsStream(String resource) {
return FileUtils.class.getClassLoader() != null

View File

@@ -33,6 +33,8 @@ import java.security.ProtectionDomain;
import java.util.Enumeration;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
/**
* A simple settings container that wraps the dependencycheck.properties file.
*
@@ -48,6 +50,10 @@ public final class Settings {
* The properties file location.
*/
private static final String PROPERTIES_FILE = "dependencycheck.properties";
/**
* Array separator.
*/
private static final String ARRAY_SEP = ",";
/**
* Thread local settings.
*/
@@ -592,6 +598,18 @@ public final class Settings {
}
}
/**
* Sets a property value only if the array value is not null and not empty.
*
* @param key the key for the property
* @param value the value for the property
*/
public static void setArrayIfNotEmpty(String key, String[] value) {
if (null != value && value.length > 0) {
setString(key, StringUtils.join(value, ARRAY_SEP));
}
}
/**
* Sets a property value.
*
@@ -745,7 +763,7 @@ public final class Settings {
private static File getJarPath() {
String decodedPath = ".";
String jarPath = "";
ProtectionDomain domain = Settings.class.getProtectionDomain();
final ProtectionDomain domain = Settings.class.getProtectionDomain();
if (domain != null && domain.getCodeSource() != null && domain.getCodeSource().getLocation() != null) {
jarPath = Settings.class.getProtectionDomain().getCodeSource().getLocation().getPath();
}
@@ -805,6 +823,22 @@ public final class Settings {
return System.getProperty(key, LOCAL_SETTINGS.get().props.getProperty(key));
}
/**
* Returns a list with the given key.
*
* If the propery is not set then {@code null} will be returned.
*
* @param key the key to get from this {@link Settings} singleton.
* @return the list or {@code null} if the key wasn't present.
*/
public static String[] getArray(final String key) {
final String string = getString(key);
if (string != null) {
return string.split(ARRAY_SEP);
}
return null;
}
/**
* Removes a property from the local properties collection. This is mainly
* used in test cases.

View File

@@ -17,10 +17,18 @@
*/
package org.owasp.dependencycheck.utils;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
@@ -29,6 +37,22 @@ import org.junit.Test;
*/
public class SettingsTest extends BaseTest {
/**
* Initialize the {@link Settings} singleton.
*/
@Before
public void setUp() {
Settings.initialize();
}
/**
* Clean the {@link Settings} singleton.
*/
@After
public void tearDown() {
Settings.cleanup();
}
/**
* Test of getString method, of class Settings.
*/
@@ -220,4 +244,101 @@ public class SettingsTest extends BaseTest {
File tmp = Settings.getTempDirectory();
Assert.assertTrue(tmp.exists());
}
/**
* Assert {@link Settings#getArray(String)} from a delimited string returns
* multiple values in an array.
*/
@Test
public void testGetArrayFromADelimitedString() {
// GIVEN a delimited string
final String delimitedString = "value1,value2";
Settings.setString("key", delimitedString);
// WHEN getting the array
final String[] array = Settings.getArray("key");
// THEN the split array is returned
assertThat("Expected the array to be non-null", array, notNullValue());
assertThat("Expected the array to have two values", array.length, is(2));
assertThat("Expected the first array value to be value1", array[0], is("value1"));
assertThat("Expected the second array value to be value2", array[1], is("value2"));
}
/**
* Assert {@link Settings#getArray(String)} returns {@code null} if the
* property is not set.
*/
@Test
public void testGetArrayWhereThePropertyIsNotSet() {
// WHEN getting the array
final String[] array = Settings.getArray("key");
// THEN null is returned
assertThat("Expected the array to be null", array, nullValue());
}
/**
* Assert {@link Settings#setArrayIfNotEmpty(String, String[])} with an
* empty array is ignored.
*/
@Test
public void testSetArrayNotEmptyIgnoresAnEmptyArray() {
// GIVEN an empty array
final String[] array = {};
// WHEN setting the array
Settings.setArrayIfNotEmpty("key", array);
// THEN the property was not set
assertThat("Expected the property to not be set", Settings.getString("key"), nullValue());
}
/**
* Assert {@link Settings#setArrayIfNotEmpty(String, String[])} with a null
* array is ignored.
*/
@Test
public void testSetArrayNotEmptyIgnoresAnNullArray() {
// GIVEN a null array
final String[] array = null;
// WHEN setting the array
Settings.setArrayIfNotEmpty("key", array);
// THEN the property was not set
assertThat("Expected the property to not be set", Settings.getString("key"), nullValue());
}
/**
* Assert {@link Settings#setArrayIfNotEmpty(String, String[])} with
* multiple values sets a delimited string.
*/
@Test
public void testSetArrayNotEmptySetsADelimitedString() {
// GIVEN an array with values
final String[] array = {"value1", "value2"};
// WHEN setting the array
Settings.setArrayIfNotEmpty("key", array);
// THEN the property is set
assertThat("Expected the property to be set", Settings.getString("key"), is("value1,value2"));
}
/**
* Assert {@link Settings#setArrayIfNotEmpty(String, String[])} with a
* single values sets a string.
*/
@Test
public void testSetArrayNotEmptyWithSingleValueSetsAString() {
// GIVEN an array with a value
final String[] array = {"value1"};
// WHEN setting the array
Settings.setArrayIfNotEmpty("key", array);
// THEN the property is set
assertThat("Expected the property to be set", Settings.getString("key"), is("value1"));
}
}