From 29bae6e7d7edf9097420f3a143b0f55b5a213ccf Mon Sep 17 00:00:00 2001 From: Hans Aikema Date: Sun, 25 Mar 2018 19:57:21 +0200 Subject: [PATCH] Implemented a fix for https://github.com/jeremylong/DependencyCheck/issues/1145 --- .../xml/suppression/SuppressionHandler.java | 19 +++++- .../xml/suppression/SuppressionParser.java | 52 ++++++--------- .../xml/suppression/SuppressionRule.java | 29 ++++++++ .../schema/dependency-suppression.1.2.xsd | 66 +++++++++++++++++++ .../suppression/SuppressionParserTest.java | 29 +++++++- core/src/test/resources/suppressions_1_1.xml | 40 +++++++++++ core/src/test/resources/suppressions_1_2.xml | 43 ++++++++++++ 7 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 core/src/main/resources/schema/dependency-suppression.1.2.xsd create mode 100644 core/src/test/resources/suppressions_1_1.xml create mode 100644 core/src/test/resources/suppressions_1_2.xml diff --git a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java index b8d457f88d4..a902c166de8 100644 --- a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java +++ b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java @@ -18,8 +18,12 @@ package org.owasp.dependencycheck.xml.suppression; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; import javax.annotation.concurrent.NotThreadSafe; +import javax.xml.bind.DatatypeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -32,6 +36,11 @@ @NotThreadSafe public class SuppressionHandler extends DefaultHandler { + /** + * The logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionHandler.class); + /** * The suppress node, indicates the start of a new rule. */ @@ -117,6 +126,10 @@ public void startElement(String uri, String localName, String qName, Attributes } else { rule.setBase(false); } + final String until = currentAttributes.getValue("until"); + if (until != null) { + rule.setUntil(DatatypeConverter.parseDate(until)); + } } } @@ -133,7 +146,11 @@ public void endElement(String uri, String localName, String qName) throws SAXExc if (null != qName) { switch (qName) { case SUPPRESS: - suppressionRules.add(rule); + if (rule.getUntil() != null && rule.getUntil().before(Calendar.getInstance())) { + LOGGER.info("Suppression is expired for rule: {}", rule); + } else { + suppressionRules.add(rule); + } rule = null; break; case FILE_PATH: diff --git a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java index 984c9468e08..ff7f4a3e554 100644 --- a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java +++ b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java @@ -51,13 +51,17 @@ public class SuppressionParser { */ private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionParser.class); /** - * The suppression schema file location. + * The suppression schema file location for v 1.2 */ - public static final String SUPPRESSION_SCHEMA = "schema/dependency-suppression.1.1.xsd"; + public static final String SUPPRESSION_SCHEMA_1_2 = "schema/dependency-suppression.1.2.xsd"; /** - * The old suppression schema file location. + * The suppression schema file location for v1.1. */ - private static final String OLD_SUPPRESSION_SCHEMA = "schema/suppression.xsd"; + public static final String SUPPRESSION_SCHEMA_1_1 = "schema/dependency-suppression.1.1.xsd"; + /** + * The old suppression schema file location for v1.0. + */ + private static final String SUPPRESSION_SCHEMA_1_0 = "schema/suppression.xsd"; /** * Parses the given XML file and returns a list of the suppression rules @@ -68,19 +72,11 @@ public class SuppressionParser { * @throws SuppressionParseException thrown if the XML file cannot be parsed */ public List parseSuppressionRules(File file) throws SuppressionParseException { - try { - try (FileInputStream fis = new FileInputStream(file)) { - return parseSuppressionRules(fis); - } catch (IOException ex) { - LOGGER.debug("", ex); - throw new SuppressionParseException(ex); - } - } catch (SAXException ex) { - try (FileInputStream fis = new FileInputStream(file)) { - return parseSuppressionRules(fis, OLD_SUPPRESSION_SCHEMA); - } catch (SAXException | IOException ex1) { - throw new SuppressionParseException(ex); - } + try (FileInputStream fis = new FileInputStream(file)) { + return parseSuppressionRules(fis); + } catch (SAXException | IOException ex) { + LOGGER.debug("", ex); + throw new SuppressionParseException(ex); } } @@ -94,23 +90,13 @@ public List parseSuppressionRules(File file) throws Suppression * @throws SAXException thrown if the XML cannot be parsed */ public List parseSuppressionRules(InputStream inputStream) throws SuppressionParseException, SAXException { - return parseSuppressionRules(inputStream, SUPPRESSION_SCHEMA); - } - - /** - * Parses the given XML stream and returns a list of the suppression rules - * contained. - * - * @param inputStream an InputStream containing suppression rules - * @param schema the schema used to validate the XML stream - * @return a list of suppression rules - * @throws SuppressionParseException thrown if the XML cannot be parsed - * @throws SAXException thrown if the XML cannot be parsed - */ - private List parseSuppressionRules(InputStream inputStream, String schema) throws SuppressionParseException, SAXException { - try (InputStream schemaStream = FileUtils.getResourceAsStream(schema)) { + try ( + InputStream schemaStream12 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_2); + InputStream schemaStream11 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_1); + InputStream schemaStream10 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_0); + ) { final SuppressionHandler handler = new SuppressionHandler(); - final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream); + final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream12, schemaStream11, schemaStream10); final XMLReader xmlReader = saxParser.getXMLReader(); xmlReader.setErrorHandler(new SuppressionErrorHandler()); xmlReader.setContentHandler(handler); diff --git a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java index a0c2f5001ab..6aca3ce8fe7 100644 --- a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java +++ b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java @@ -18,11 +18,13 @@ package org.owasp.dependencycheck.xml.suppression; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; +import javax.xml.bind.DatatypeConverter; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; @@ -76,6 +78,30 @@ public class SuppressionRule { */ private boolean base; + /** + * A date until which the suppression is to be retained. This can be used + * to make a temporary suppression that auto-expires to suppress a CVE + * while waiting for the vulnerability fix of the dependency to be + * released. + */ + private Calendar until; + + /** + * Get the (@code{nullable}) value of until + * @return the value of until + */ + public Calendar getUntil() { + return until; + } + + /** + * Set the value of until + * @param until new value of until + */ + public void setUntil(Calendar until) { + this.until = until; + } + /** * Get the value of filePath. * @@ -502,6 +528,9 @@ protected boolean identifierMatches(String identifierType, PropertyType suppress public String toString() { final StringBuilder sb = new StringBuilder(64); sb.append("SuppressionRule{"); + if (until != null) { + sb.append("until=").append(DatatypeConverter.printDate(until)).append(','); + } if (filePath != null) { sb.append("filePath=").append(filePath).append(','); } diff --git a/core/src/main/resources/schema/dependency-suppression.1.2.xsd b/core/src/main/resources/schema/dependency-suppression.1.2.xsd new file mode 100644 index 00000000000..e1db2e96bab --- /dev/null +++ b/core/src/main/resources/schema/dependency-suppression.1.2.xsd @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When specified the suppression will only be active when the specified date is still in the future. On and after the 'until' date the suppression will no longer be active. + + + + + + + + + diff --git a/core/src/test/java/org/owasp/dependencycheck/xml/suppression/SuppressionParserTest.java b/core/src/test/java/org/owasp/dependencycheck/xml/suppression/SuppressionParserTest.java index d09ac9ee944..e93d20dfba9 100644 --- a/core/src/test/java/org/owasp/dependencycheck/xml/suppression/SuppressionParserTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/xml/suppression/SuppressionParserTest.java @@ -19,6 +19,7 @@ import java.io.File; import java.util.List; +import org.junit.Assert; import static org.junit.Assert.assertTrue; @@ -33,14 +34,36 @@ public class SuppressionParserTest extends BaseTest { /** - * Test of parseSuppressionRules method, of class SuppressionParser. + * Test of parseSuppressionRules method, of class SuppressionParser for the v1.0 suppressions XML Schema. */ @Test - public void testParseSuppressionRules() throws Exception { + public void testParseSuppressionRulesV1_0() throws Exception { //File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath()); File file = BaseTest.getResourceAsFile(this, "suppressions.xml"); SuppressionParser instance = new SuppressionParser(); List result = instance.parseSuppressionRules(file); - assertTrue(result.size() > 3); + Assert.assertEquals(5, result.size()); + } + /** + * Test of parseSuppressionRules method, of class SuppressionParser for the v1.1 suppressions XML Schema. + */ + @Test + public void testParseSuppressionRulesV1_1() throws Exception { + //File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath()); + File file = BaseTest.getResourceAsFile(this, "suppressions_1_1.xml"); + SuppressionParser instance = new SuppressionParser(); + List result = instance.parseSuppressionRules(file); + Assert.assertEquals(5, result.size()); + } + /** + * Test of parseSuppressionRules method, of class SuppressionParser for the v1.2 suppressions XML Schema. + */ + @Test + public void testParseSuppressionRulesV1_2() throws Exception { + //File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath()); + File file = BaseTest.getResourceAsFile(this, "suppressions_1_2.xml"); + SuppressionParser instance = new SuppressionParser(); + List result = instance.parseSuppressionRules(file); + Assert.assertEquals(4, result.size()); } } diff --git a/core/src/test/resources/suppressions_1_1.xml b/core/src/test/resources/suppressions_1_1.xml new file mode 100644 index 00000000000..90034ea0b74 --- /dev/null +++ b/core/src/test/resources/suppressions_1_1.xml @@ -0,0 +1,40 @@ + + + + + c:\path\to\some.jar + cpe:/a:csv:csv:1.0 + + + + .*\btest\.jar + cpe:/a:jboss:jboss + + + + .*\btest\.jar + CVE-2013-1337 + + + + 384FAA82E193D4E4B0546059CA09572654BC3970 + CVE-2013-1337 + + + + 7 + + \ No newline at end of file diff --git a/core/src/test/resources/suppressions_1_2.xml b/core/src/test/resources/suppressions_1_2.xml new file mode 100644 index 00000000000..3e6b1fa1e99 --- /dev/null +++ b/core/src/test/resources/suppressions_1_2.xml @@ -0,0 +1,43 @@ + + + + + c:\path\to\some.jar + cpe:/a:csv:csv:1.0 + + + + .*\btest\.jar + cpe:/a:jboss:jboss + + + + .*\btest\.jar + CVE-2013-1337 + + + + 384FAA82E193D4E4B0546059CA09572654BC3970 + CVE-2013-1337 + + + + 7 + + \ No newline at end of file