diff --git a/README.md b/README.md index c924b00c2..d9db978e7 100644 --- a/README.md +++ b/README.md @@ -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 + + +``` + +New: + +```xml + + suppression.xml + +``` + +##### Maven + +Old: + +```xml + + org.owasp + dependency-check-maven + + suppression.xml + + +``` + +New: + +```xml + + org.owasp + dependency-check-maven + + + suppression.xml + + + +``` + + Mailing List ------------ diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java index 65b6cd9a8..5af32b863 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java @@ -461,7 +461,7 @@ public class Check extends Update { */ @Deprecated public void setSuppressionFile(String suppressionFile) { - throw new BuildException("Property form of suppressionFile has been replaced by a nested element, please update your configuration."); + throw new BuildException("Definition of a suppression file via a property has been deprecated. Suppression files are now defined as a nested element, please update your configuration."); } /** diff --git a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java index 87beaf6d9..dfb4c86e5 100644 --- a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java +++ b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java @@ -140,7 +140,7 @@ public class DependencyCheckTaskTest { // WHEN executing the ant task // THEN an exception with a warning is thrown expectedException.expect(BuildException.class); - expectedException.expectMessage("Property form of suppressionFile has been replaced by a nested element, please update your configuration."); + expectedException.expectMessage("Definition of a suppression file via a property has been deprecated. Suppression files are now defined as a nested element, please update your configuration."); buildFileRule.executeTarget(antTaskName); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java index 119d90bd0..704cf32dd 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java @@ -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 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 { diff --git a/dependency-check-core/src/test/resources/other-suppressions.xml b/dependency-check-core/src/test/resources/other-suppressions.xml new file mode 100644 index 000000000..f0b4fad7b --- /dev/null +++ b/dependency-check-core/src/test/resources/other-suppressions.xml @@ -0,0 +1,27 @@ + + + + + + ^com\.fasterxml\.jackson.*:.*:.*$ + cpe:/a:fasterxml:jackson + + diff --git a/dependency-check-maven/src/it/730-multiple-suppression-files/pom.xml b/dependency-check-maven/src/it/730-multiple-suppression-files/pom.xml index 8a534171e..1b64e9518 100644 --- a/dependency-check-maven/src/it/730-multiple-suppression-files/pom.xml +++ b/dependency-check-maven/src/it/730-multiple-suppression-files/pom.xml @@ -44,8 +44,8 @@ Copyright (c) 2017 The OWASP Foundation. All Rights Reserved. dependency-check-maven - ${project.basedir}/test-suppression1.xml - ${project.basedir}/test-suppression2.xml + ${project.basedir}/test-suppression1.xml + ${project.basedir}/test-suppression2.xml diff --git a/dependency-check-maven/src/site/markdown/configuration.md b/dependency-check-maven/src/site/markdown/configuration.md index 08dfab16c..e0e7b38a8 100644 --- a/dependency-check-maven/src/site/markdown/configuration.md +++ b/dependency-check-maven/src/site/markdown/configuration.md @@ -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. |   -suppressionFile | The file path to the XML suppression file \- used to suppress [false positives](../general/suppression.html). |   +suppressionFiles | The file paths to the XML suppression files \- used to suppress [false positives](../general/suppression.html). |   hintsFile | The file path to the XML hints file \- used to resolve [false negatives](../general/hints.html). |   enableExperimental | Enable the [experimental analyzers](../analyzers/index.html). If not enabled the experimental analyzers (see below) will not be loaded or used. | false diff --git a/dependency-check-maven/src/site/markdown/index.md.vm b/dependency-check-maven/src/site/markdown/index.md.vm index 6490307fe..43815f4ab 100644 --- a/dependency-check-maven/src/site/markdown/index.md.vm +++ b/dependency-check-maven/src/site/markdown/index.md.vm @@ -204,3 +204,39 @@ Update the local cache of the NVD data from NIST without analyzing the dependenc ... ``` + +$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 + + ... + + ... + + ... + + org.owasp + dependency-check-maven + ${project.version} + + + http://example.org/suppression.xml + project-suppression.xml + + + + + + check + + + + + ... + + ... + + ... + +```