From f95ce8c7b52c1512db41daa145f12cfc84ab5ffc Mon Sep 17 00:00:00 2001 From: Jeremy Long Date: Sat, 30 Nov 2013 08:56:44 -0500 Subject: [PATCH] added support for suppression rules, initial version Former-commit-id: c58bea577282155661b4c6e1991178ea07e7eb98 --- .../analyzer/AbstractSuppressionAnalyzer.java | 115 +++++ .../analyzer/CpeSuppressionAnalyzer.java | 76 +++ .../VulnerabilitySuppressionAnalyzer.java | 76 +++ .../suppression/PropertyType.java | 186 +++++++ .../suppression/SuppressionErrorHandler.java | 79 +++ .../suppression/SuppressionHandler.java | 173 +++++++ .../suppression/SuppressionParser.java | 107 ++++ .../suppression/SuppressionRule.java | 334 +++++++++++++ .../suppression/PropertyTypeTest.java | 108 ++++ .../suppression/SuppressionHandlerTest.java | 95 ++++ .../suppression/SuppressionParserTest.java | 66 +++ .../suppression/SuppressionRuleTest.java | 471 ++++++++++++++++++ 12 files changed, 1886 insertions(+) create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/PropertyType.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionErrorHandler.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParser.java create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/PropertyTypeTest.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionHandlerTest.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionParserTest.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionRuleTest.java diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java new file mode 100644 index 000000000..86a539682 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java @@ -0,0 +1,115 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.owasp.dependencycheck.suppression.SuppressionParseException; +import org.owasp.dependencycheck.suppression.SuppressionParser; +import org.owasp.dependencycheck.suppression.SuppressionRule; +import org.owasp.dependencycheck.utils.Settings; + +/** + * Abstract base suppression analyzer that contains methods for parsing the + * suppression xml file. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { + + // + /** + * Returns a list of file EXTENSIONS supported by this analyzer. + * + * @return a list of file EXTENSIONS supported by this analyzer. + */ + public Set getSupportedExtensions() { + return null; + } + + /** + * Returns whether or not this analyzer can process the given extension. + * + * @param extension the file extension to test for support. + * @return whether or not the specified file extension is supported by this + * analyzer. + */ + @Override + public boolean supportsExtension(String extension) { + return true; + } + + // + /** + * The initialize method loads the suppression XML file. + * + * @throws Exception thrown if there is an exception + */ + @Override + public void initialize() throws Exception { + super.initialize(); + loadSuppressionData(); + } + /** + * The list of suppression rules + */ + private List rules; + + /** + * Get the value of rules. + * + * @return the value of rules + */ + public List getRules() { + return rules; + } + + /** + * Set the value of rules. + * + * @param rules new value of rules + */ + public void setRules(List rules) { + this.rules = rules; + } + + /** + * Loads the suppression rules file. + * + * @throws SuppressionParseException thrown if the XML cannot be parsed. + */ + private void loadSuppressionData() throws SuppressionParseException { + final File file = Settings.getFile(Settings.KEYS.SUPPRESSION_FILE); + if (file != null) { + final SuppressionParser parser = new SuppressionParser(); + try { + rules = parser.parseSuppressionRules(file); + } catch (SuppressionParseException ex) { + final String msg = String.format("Unable to parse suppression xml file '%s'", file.getPath()); + Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.WARNING, msg); + Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.WARNING, ex.getMessage()); + Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.FINE, null, ex); + throw ex; + } + } + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java new file mode 100644 index 000000000..42c07b044 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java @@ -0,0 +1,76 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.suppression.SuppressionRule; + +/** + * The suppression analyzer processes an externally defined XML document that + * complies with the suppressions.xsd schema. Any identified CPE entries within + * the dependencies that match will be removed. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class CpeSuppressionAnalyzer extends AbstractSuppressionAnalyzer { + + // + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Cpe Suppression Analyzer"; + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_IDENTIFIER_ANALYSIS; + + /** + * Returns the name of the analyzer. + * + * @return the name of the analyzer. + */ + @Override + public String getName() { + return ANALYZER_NAME; + } + + /** + * 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 ANALYSIS_PHASE; + } + // + + @Override + public void analyze(final Dependency dependency, final Engine engine) throws AnalysisException { + + if (getRules() == null || getRules().size() <= 0) { + return; + } + + for (final SuppressionRule rule : getRules()) { + rule.process(dependency); + } + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java new file mode 100644 index 000000000..65726ad8b --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java @@ -0,0 +1,76 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.suppression.SuppressionRule; + +/** + * The suppression analyzer processes an externally defined XML document that + * complies with the suppressions.xsd schema. Any identified Vulnerability + * entries within the dependencies that match will be removed. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class VulnerabilitySuppressionAnalyzer extends AbstractSuppressionAnalyzer { + + // + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Vulnerability Suppression Analyzer"; + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_FINDING_ANALYSIS; + + /** + * Returns the name of the analyzer. + * + * @return the name of the analyzer. + */ + @Override + public String getName() { + return ANALYZER_NAME; + } + + /** + * 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 ANALYSIS_PHASE; + } + // + + @Override + public void analyze(final Dependency dependency, final Engine engine) throws AnalysisException { + + if (getRules() == null || getRules().size() <= 0) { + return; + } + + for (final SuppressionRule rule : getRules()) { + rule.process(dependency); + } + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/PropertyType.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/PropertyType.java new file mode 100644 index 000000000..eb3b52050 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/PropertyType.java @@ -0,0 +1,186 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.util.regex.Pattern; + +/** + * A simple PropertyType used to represent a string value that could be used as + * a regular expression or could be case insensitive. The equals method has been + * over-ridden so that the object will correctly compare to strings. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class PropertyType { + + // + /** + * The value. + */ + private String value; + + /** + * Gets the value of the value property. + * + * @return the value of the value property + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value of the value property + */ + public void setValue(String value) { + this.value = value; + } + /** + * Whether or not the expression is a regex. + */ + private boolean regex = false; + + /** + * Returns whether or not the value is a regex. + * + * @return true if the value is a regex, otherwise false + * + */ + public boolean isRegex() { + return regex; + } + + /** + * Sets whether the value property is a regex. + * + * @param value true if the value is a regex, otherwise false + * + */ + public void setRegex(boolean value) { + this.regex = value; + } + /** + * Indicates case sensitivity. + */ + protected boolean caseSensitive = false; + + /** + * Gets the value of the caseSensitive property. + * + * @return true if the value is case sensitive + * + */ + public boolean isCaseSensitive() { + return caseSensitive; + } + + /** + * Sets the value of the caseSensitive property. + * + * @param value whether the value is case sensitive + * + */ + public void setCaseSensitive(boolean value) { + this.caseSensitive = value; + } + // + + /** + * Uses the object's properties to determine if the supplied string matches + * the value of this property. + * + * @param text the String to validate + * @return whether the text supplied is matched by the value of the property + */ + public boolean matches(String text) { + if (text == null) { + return false; + } + if (this.regex) { + Pattern rx; + if (this.caseSensitive) { + rx = Pattern.compile(this.value); + } else { + rx = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE); + } + return rx.matcher(text).matches(); + } else { + if (this.caseSensitive) { + return value.equals(text); + } else { + return value.equalsIgnoreCase(text); + } + } + } + + // + /** + * Default implementation of hashCode. + * + * @return the hash code + */ + @Override + public int hashCode() { + int hash = 3; + hash = 59 * hash + (this.value != null ? this.value.hashCode() : 0); + hash = 59 * hash + (this.regex ? 1 : 0); + hash = 59 * hash + (this.caseSensitive ? 1 : 0); + return hash; + } + + /** + * Default implementation of equals. + * + * @param obj the object to compare + * @return whether the objects are equivalent + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PropertyType other = (PropertyType) obj; + if ((this.value == null) ? (other.value != null) : !this.value.equals(other.value)) { + return false; + } + if (this.regex != other.regex) { + return false; + } + if (this.caseSensitive != other.caseSensitive) { + return false; + } + return true; + } + + /** + * Default implementation of toString(). + * + * @return the string representation of the object + */ + @Override + public String toString() { + return "PropertyType{" + "value=" + value + ", regex=" + regex + ", caseSensitive=" + caseSensitive + '}'; + } + // +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionErrorHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionErrorHandler.java new file mode 100644 index 000000000..cfc2710b8 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionErrorHandler.java @@ -0,0 +1,79 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.owasp.dependencycheck.suppression; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +/** + * An XML parsing error handler. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionErrorHandler implements ErrorHandler { + + /** + * Builds a prettier exception message. + * + * @param ex the SAXParseException + * @return an easier to read exception message + */ + private String getPrettyParseExceptionInfo(SAXParseException ex) { + + final StringBuffer sb = new StringBuffer(); + + if (ex.getSystemId() != null) { + sb.append("systemId=").append(ex.getSystemId()).append(", "); + } + if (ex.getPublicId() != null) { + sb.append("publicId=").append(ex.getPublicId()).append(", "); + } + if (ex.getLineNumber() > 0) { + sb.append("Line=").append(ex.getLineNumber()); + } + if (ex.getColumnNumber() > 0) { + sb.append(", Column=").append(ex.getColumnNumber()); + } + sb.append(": ").append(ex.getMessage()); + + return sb.toString(); + } + + /** + * Logs warnings. + * + * @param ex the warning to log + * @throws SAXException is never thrown + */ + @Override + public void warning(SAXParseException ex) throws SAXException { + Logger.getLogger(SuppressionErrorHandler.class.getName()).log(Level.FINE, null, ex); + } + + /** + * Handles errors. + * + * @param ex the error to handle + * @throws SAXException is always thrown + */ + @Override + public void error(SAXParseException ex) throws SAXException { + throw new SAXException(getPrettyParseExceptionInfo(ex)); + } + + /** + * Handles fatal exceptions. + * + * @param ex a fatal exception + * @throws SAXException is always + */ + @Override + public void fatalError(SAXParseException ex) throws SAXException { + throw new SAXException(getPrettyParseExceptionInfo(ex)); + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java new file mode 100644 index 000000000..2acab08f7 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java @@ -0,0 +1,173 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.util.ArrayList; +import java.util.List; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * A handler to load suppression rules. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionHandler extends DefaultHandler { + + /** + * The suppress node, indicates the start of a new rule. + */ + public static final String SUPPRESS = "suppress"; + /** + * The file path element name. + */ + public static final String FILE_PATH = "filePath"; + /** + * The sha1 hash element name. + */ + public static final String SHA1 = "sha1"; + /** + * The CVE element name. + */ + public static final String CVE = "cve"; + /** + * The CPE element name. + */ + public static final String CPE = "cpe"; + /** + * The CWE element name. + */ + public static final String CWE = "cwe"; + /** + * The cvssBelow element name. + */ + public static final String CVSS_BELOW = "cvssBelow"; + /** + * A list of suppression rules. + */ + private List supressionRules = new ArrayList(); + + /** + * Get the value of supressionRules + * + * @return the value of supressionRules + */ + public List getSupressionRules() { + return supressionRules; + } + /** + * The current rule being read. + */ + private SuppressionRule rule; + /** + * The attributes of the node being read. + */ + private Attributes currentAttributes; + /** + * The current node text being extracted from the element. + */ + private StringBuffer currentText; + + /** + * Handles the start element event. + * + * @param uri the uri of the element being processed + * @param localName the local name of the element being processed + * @param qName the qName of the element being processed + * @param attributes the attributes of the element being processed + * @throws SAXException thrown if there is an exception processing + */ + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + currentAttributes = null; + currentText = new StringBuffer(); + + if (SUPPRESS.equals(qName)) { + rule = new SuppressionRule(); + } else if (FILE_PATH.equals(qName)) { + currentAttributes = attributes; + } + } + + /** + * Handles the end element event. + * + * @param uri the uri of the element + * @param localName the local name of the element + * @param qName the qName of the element + * @throws SAXException thrown if there is an exception processing + */ + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (SUPPRESS.equals(qName)) { + supressionRules.add(rule); + rule = null; + } else if (FILE_PATH.equals(qName)) { + PropertyType pt = processPropertyType(); + rule.setFilePath(pt); + } else if (SHA1.equals(qName)) { + rule.setSha1(currentText.toString()); + } else if (CPE.equals(qName)) { + PropertyType pt = processPropertyType(); + rule.addCpe(pt); + } else if (CWE.equals(qName)) { + rule.addCwe(currentText.toString()); + } else if (CVE.equals(qName)) { + rule.addCve(currentText.toString()); + } else if (CVSS_BELOW.equals(qName)) { + float cvss = Float.parseFloat(currentText.toString()); + } + } + + /** + * Collects the body text of the node being processed. + * + * @param ch the char array of text + * @param start the start position to copy text from in the char array + * @param length the number of characters to copy from the char array + * @throws SAXException thrown if there is a parsing exception + */ + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + currentText.append(ch, start, length); + } + + /** + * Processes field members that have been collected during the characters + * and startElement method to construct a PropertyType object. + * + * @return a PropertyType object + */ + private PropertyType processPropertyType() { + PropertyType pt = new PropertyType(); + pt.setValue(currentText.toString()); + if (currentAttributes != null && currentAttributes.getLength() > 0) { + final String regex = currentAttributes.getValue("regex"); + if (regex != null) { + pt.setRegex(Boolean.parseBoolean(regex)); + } + final String caseSensitive = currentAttributes.getValue("caseSensitive"); + if (regex != null) { + pt.setCaseSensitive(Boolean.parseBoolean(caseSensitive)); + } + } + return pt; + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParser.java new file mode 100644 index 000000000..464039869 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParser.java @@ -0,0 +1,107 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * A simple validating parser for XML Suppression Rules. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionParser { + + /** + * JAXP Schema Language, source: + * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html + */ + public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; + /** + * W3C XML Schema, source: + * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html + */ + public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; + /** + * JAXP Schema Source, source: + * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html + */ + public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; + + /** + * Parses the given xml file and returns a list of the suppression rules + * contained. + * + * @param file an xml file containing suppression rules + * @return a list of suppression rules + * @throws SuppressionParseException thrown if the xml file cannot be parsed + */ + public List parseSuppressionRules(File file) throws SuppressionParseException { + try { + File schema = new File(this.getClass().getClassLoader().getResource("schema/suppression.xsd").getPath()); + SuppressionHandler handler = new SuppressionHandler(); + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(true); + SAXParser saxParser = factory.newSAXParser(); + saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA); + saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, schema); + XMLReader xmlReader = saxParser.getXMLReader(); + xmlReader.setErrorHandler(new SuppressionErrorHandler()); + xmlReader.setContentHandler(handler); + + InputStream inputStream = new FileInputStream(file); + Reader reader = new InputStreamReader(inputStream); //, "UTF-8"); + InputSource in = new InputSource(reader); + //in.setEncoding("UTF-8"); + + xmlReader.parse(in); + + + return handler.getSupressionRules(); + } catch (ParserConfigurationException ex) { + Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex); + throw new SuppressionParseException(ex); + } catch (SAXException ex) { + Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex); + throw new SuppressionParseException(ex); + } catch (FileNotFoundException ex) { + Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex); + throw new SuppressionParseException(ex); + } catch (IOException ex) { + Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex); + throw new SuppressionParseException(ex); + } + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java new file mode 100644 index 000000000..e8ce2b061 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java @@ -0,0 +1,334 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Identifier; +import org.owasp.dependencycheck.dependency.Vulnerability; + +/** + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionRule { + + /** + * The file path for the suppression. + */ + private PropertyType filePath; + + /** + * Get the value of filePath. + * + * @return the value of filePath + */ + public PropertyType getFilePath() { + return filePath; + } + + /** + * Set the value of filePath. + * + * @param filePath new value of filePath + */ + public void setFilePath(PropertyType filePath) { + this.filePath = filePath; + } + /** + * The sha1 hash. + */ + private String sha1; + + /** + * Get the value of sha1. + * + * @return the value of sha1 + */ + public String getSha1() { + return sha1; + } + + /** + * Set the value of sha1. + * + * @param sha1 new value of sha1 + */ + public void setSha1(String sha1) { + this.sha1 = sha1; + } + /** + * A list of CPEs to suppression + */ + private List cpe = new ArrayList(); + + /** + * Get the value of cpe. + * + * @return the value of cpe + */ + public List getCpe() { + return cpe; + } + + /** + * Set the value of cpe. + * + * @param cpe new value of cpe + */ + public void setCpe(List cpe) { + this.cpe = cpe; + } + + /** + * Adds the cpe to the cpe list. + * + * @param cpe the cpe to add + */ + public void addCpe(PropertyType cpe) { + this.cpe.add(cpe); + } + + /** + * Returns whether or not this suppression rule as CPE entries. + * + * @return whether or not this suppression rule as CPE entries + */ + public boolean hasCpe() { + return cpe.size() > 0; + } + /** + * The list of cvssBelow scores. + */ + private List cvssBelow = new ArrayList(); + + /** + * Get the value of cvssBelow + * + * @return the value of cvssBelow + */ + public List getCvssBelow() { + return cvssBelow; + } + + /** + * Set the value of cvssBelow + * + * @param cvssBelow new value of cvssBelow + */ + public void setCvssBelow(List cvssBelow) { + this.cvssBelow = cvssBelow; + } + + /** + * Adds the cvss to the cvssBelow list. + * + * @param cvss the cvss to add + */ + public void addCvssBelow(Float cvss) { + this.cvssBelow.add(cvss); + } + + /** + * Returns whether or not this suppression rule has cvss suppressions. + * + * @return whether or not this suppression rule has cvss suppressions + */ + public boolean hasCvssBelow() { + return cvssBelow.size() > 0; + } + /** + * The list of cwe entries to suppress. + */ + private List cwe = new ArrayList(); + + /** + * Get the value of cwe. + * + * @return the value of cwe + */ + public List getCwe() { + return cwe; + } + + /** + * Set the value of cwe. + * + * @param cwe new value of cwe + */ + public void setCwe(List cwe) { + this.cwe = cwe; + } + + /** + * Adds the cwe to the cwe list. + * + * @param cwe the cwe to add + */ + public void addCwe(String cwe) { + this.cwe.add(cwe); + } + + /** + * Returns whether this suppression rule has CWE entries. + * + * @return whether this suppression rule has CWE entries + */ + public boolean hasCwe() { + return cwe.size() > 0; + } + /** + * The list of cve entries to suppress. + */ + private List cve = new ArrayList(); + + /** + * Get the value of cve. + * + * @return the value of cve + */ + public List getCve() { + return cve; + } + + /** + * Set the value of cve. + * + * @param cve new value of cve + */ + public void setCve(List cve) { + this.cve = cve; + } + + /** + * Adds the cve to the cve list. + * + * @param cve the cve to add + */ + public void addCve(String cve) { + this.cve.add(cve); + } + + /** + * Returns whether this suppression rule has CVE entries. + * + * @return whether this suppression rule has CVE entries + */ + public boolean hasCve() { + return cve.size() > 0; + } + + public void process(Dependency dependency) { + if (filePath != null && !filePath.matches(dependency.getFilePath())) { + return; + } + if (sha1 != null && !sha1.equalsIgnoreCase(dependency.getSha1sum())) { + return; + } + if (this.hasCpe()) { + Iterator itr = dependency.getIdentifiers().iterator(); + while (itr.hasNext()) { + Identifier i = itr.next(); + for (PropertyType c : this.cpe) { + if (cpeMatches(c, i)) { + itr.remove(); + break; + } + } + } + } + if (hasCve() || hasCwe() || hasCvssBelow()) { + Iterator itr = dependency.getVulnerabilities().iterator(); + boolean remove = false; + while (!remove && itr.hasNext()) { + Vulnerability v = itr.next(); + for (String entry : this.cve) { + if (entry.equalsIgnoreCase(v.getName())) { + remove = true; + break; + } + } + if (!remove) { + for (String entry : this.cwe) { + if (v.getCwe() != null) { + final String toMatch = String.format("CWE-%s ", entry); + final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase(); + if (toTest.equals(toMatch)) { + remove = true; + break; + } + } + } + } + if (!remove) { + for (float cvss : this.cvssBelow) { + if (v.getCvssScore() < cvss) { + remove = true; + break; + } + } + } + if (remove) { + itr.remove(); + } + } + } + } + + boolean cpeHasNoVersion(PropertyType c) { + if (c.isRegex()) { + return false; + } // cpe:/a:jboss:jboss:1.0.0: + if (countCharacter(c.getValue(), ':') == 3) { + return true; + } + return false; + } + + int countCharacter(String str, char c) { + int count = 0; + int pos = str.indexOf(c) + 1; + while (pos > 0) { + count += 1; + pos = str.indexOf(c, pos) + 1; + } + return count; + } + + boolean cpeMatches(PropertyType cpeEntry, Identifier identifier) { + if (cpeEntry.matches(identifier.getValue())) { + return true; + } else if (cpeHasNoVersion(cpeEntry)) { + if (cpeEntry.isCaseSensitive()) { + if (identifier.getValue().startsWith(cpeEntry.getValue())) { + return true; + } + } else { + final String id = identifier.getValue().toLowerCase(); + final String check = cpeEntry.getValue().toLowerCase(); + if (id.startsWith(check)) { + return true; + } + } + } + return false; + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/PropertyTypeTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/PropertyTypeTest.java new file mode 100644 index 000000000..e18b130da --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/PropertyTypeTest.java @@ -0,0 +1,108 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class PropertyTypeTest { + + public PropertyTypeTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of set and getValue method, of class PropertyType. + */ + @Test + public void testSetGetValue() { + + PropertyType instance = new PropertyType(); + String expResult = "test"; + instance.setValue(expResult); + String result = instance.getValue(); + assertEquals(expResult, result); + } + + /** + * Test of isRegex method, of class PropertyType. + */ + @Test + public void testIsRegex() { + PropertyType instance = new PropertyType(); + boolean result = instance.isRegex(); + assertFalse(instance.isRegex()); + instance.setRegex(true); + assertTrue(instance.isRegex()); + } + + /** + * Test of isCaseSensitive method, of class PropertyType. + */ + @Test + public void testIsCaseSensitive() { + PropertyType instance = new PropertyType(); + assertFalse(instance.isCaseSensitive()); + instance.setCaseSensitive(true); + assertTrue(instance.isCaseSensitive()); + } + + /** + * Test of matches method, of class PropertyType. + */ + @Test + public void testMatches() { + String text = "Simple"; + + PropertyType instance = new PropertyType(); + instance.setValue("simple"); + assertTrue(instance.matches(text)); + instance.setCaseSensitive(true); + assertFalse(instance.matches(text)); + + instance.setValue("s.*le"); + instance.setRegex(true); + assertFalse(instance.matches(text)); + instance.setCaseSensitive(false); + assertTrue(instance.matches(text)); + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionHandlerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionHandlerTest.java new file mode 100644 index 000000000..a47dc4515 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionHandlerTest.java @@ -0,0 +1,95 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; + +/** + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionHandlerTest { + + public SuppressionHandlerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getSupressionRules method, of class SuppressionHandler. + * + * @throws Exception thrown if there is an exception.... + */ + @Test + public void testHandler() throws Exception { + File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath()); + + File schema = new File(this.getClass().getClassLoader().getResource("schema/suppression.xsd").getPath()); + SuppressionHandler handler = new SuppressionHandler(); + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(true); + SAXParser saxParser = factory.newSAXParser(); + saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA); + saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, schema); + XMLReader xmlReader = saxParser.getXMLReader(); + xmlReader.setErrorHandler(new SuppressionErrorHandler()); + xmlReader.setContentHandler(handler); + + InputStream inputStream = new FileInputStream(file); + Reader reader = new InputStreamReader(inputStream); //, "UTF-8"); + InputSource in = new InputSource(reader); + //in.setEncoding("UTF-8"); + + xmlReader.parse(in); + + List result = handler.getSupressionRules(); + assertTrue(result.size() > 3); + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionParserTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionParserTest.java new file mode 100644 index 000000000..7c5ce205c --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionParserTest.java @@ -0,0 +1,66 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test of the suppression parser. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionParserTest { + + public SuppressionParserTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of parseSuppressionRules method, of class SuppressionParser. + */ + @Test + public void testParseSuppressionRules() throws Exception { + File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath()); + SuppressionParser instance = new SuppressionParser(); + List result = instance.parseSuppressionRules(file); + assertTrue(result.size() > 3); + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionRuleTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionRuleTest.java new file mode 100644 index 000000000..294bf6f4a --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/suppression/SuppressionRuleTest.java @@ -0,0 +1,471 @@ +/* + * This file is part of dependency-check-core. + * + * Dependency-check-core is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Dependency-check-core is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * dependency-check-core. If not, see http://www.gnu.org/licenses/. + * + * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.suppression; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Identifier; +import org.owasp.dependencycheck.dependency.Vulnerability; + +/** + * Test of the suppression rule. + * + * @author Jeremy Long (jeremy.long@owasp.org) + */ +public class SuppressionRuleTest { + + public SuppressionRuleTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + // + /** + * Test of FilePath property, of class SuppressionRule. + */ + @Test + public void testFilePath() { + SuppressionRule instance = new SuppressionRule(); + PropertyType expResult = new PropertyType(); + expResult.setValue("test"); + instance.setFilePath(expResult); + PropertyType result = instance.getFilePath(); + assertEquals(expResult, result); + } + + /** + * Test of Sha1 property, of class SuppressionRule. + */ + @Test + public void testSha1() { + SuppressionRule instance = new SuppressionRule(); + String expResult = "384FAA82E193D4E4B0546059CA09572654BC3970"; + instance.setSha1(expResult); + String result = instance.getSha1(); + assertEquals(expResult, result); + } + + /** + * Test of Cpe property, of class SuppressionRule. + */ + @Test + public void testCpe() { + SuppressionRule instance = new SuppressionRule(); + ArrayList cpe = new ArrayList(); + instance.setCpe(cpe); + assertFalse(instance.hasCpe()); + PropertyType pt = new PropertyType(); + pt.setValue("one"); + instance.addCpe(pt); + assertTrue(instance.hasCpe()); + List result = instance.getCpe(); + assertEquals(cpe, result); + + } + + /** + * Test of CvssBelow property, of class SuppressionRule. + */ + @Test + public void testGetCvssBelow() { + SuppressionRule instance = new SuppressionRule(); + ArrayList cvss = new ArrayList(); + instance.setCvssBelow(cvss); + assertFalse(instance.hasCvssBelow()); + instance.addCvssBelow(0.7f); + assertTrue(instance.hasCvssBelow()); + List result = instance.getCvssBelow(); + assertEquals(cvss, result); + } + + /** + * Test of Cwe property, of class SuppressionRule. + */ + @Test + public void testCwe() { + SuppressionRule instance = new SuppressionRule(); + ArrayList cwe = new ArrayList(); + instance.setCwe(cwe); + assertFalse(instance.hasCwe()); + instance.addCwe("2"); + assertTrue(instance.hasCwe()); + List result = instance.getCwe(); + assertEquals(cwe, result); + } + + /** + * Test of Cve property, of class SuppressionRule. + */ + @Test + public void testCve() { + SuppressionRule instance = new SuppressionRule(); + ArrayList cve = new ArrayList(); + instance.setCve(cve); + assertFalse(instance.hasCve()); + instance.addCve("CVE-2013-1337"); + assertTrue(instance.hasCve()); + List result = instance.getCve(); + assertEquals(cve, result); + } + // + + // + /** + * Test of getFilePath method, of class SuppressionRule. + */ + @Test + public void testGetFilePath() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setFilePath method, of class SuppressionRule. + */ + @Test + public void testSetFilePath() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of getSha1 method, of class SuppressionRule. + */ + @Test + public void testGetSha1() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setSha1 method, of class SuppressionRule. + */ + @Test + public void testSetSha1() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of getCpe method, of class SuppressionRule. + */ + @Test + public void testGetCpe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setCpe method, of class SuppressionRule. + */ + @Test + public void testSetCpe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of addCpe method, of class SuppressionRule. + */ + @Test + public void testAddCpe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of hasCpe method, of class SuppressionRule. + */ + @Test + public void testHasCpe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setCvssBelow method, of class SuppressionRule. + */ + @Test + public void testSetCvssBelow() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of addCvssBelow method, of class SuppressionRule. + */ + @Test + public void testAddCvssBelow() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of hasCvssBelow method, of class SuppressionRule. + */ + @Test + public void testHasCvssBelow() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of getCwe method, of class SuppressionRule. + */ + @Test + public void testGetCwe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setCwe method, of class SuppressionRule. + */ + @Test + public void testSetCwe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of addCwe method, of class SuppressionRule. + */ + @Test + public void testAddCwe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of hasCwe method, of class SuppressionRule. + */ + @Test + public void testHasCwe() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of getCve method, of class SuppressionRule. + */ + @Test + public void testGetCve() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of setCve method, of class SuppressionRule. + */ + @Test + public void testSetCve() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of addCve method, of class SuppressionRule. + */ + @Test + public void testAddCve() { + //already tested, this is just left so the IDE doesn't recreate it. + } + + /** + * Test of hasCve method, of class SuppressionRule. + */ + @Test + public void testHasCve() { + //already tested, this is just left so the IDE doesn't recreate it. + } + // + + /** + * Test of cpeHasNoVersion method, of class SuppressionRule. + */ + @Test + public void testCpeHasNoVersion() { + PropertyType c = new PropertyType(); + c.setValue("cpe:/a:microsoft:.net_framework:4.5"); + SuppressionRule instance = new SuppressionRule(); + assertFalse(instance.cpeHasNoVersion(c)); + c.setValue("cpe:/a:microsoft:.net_framework:"); + assertFalse(instance.cpeHasNoVersion(c)); + c.setValue("cpe:/a:microsoft:.net_framework"); + assertTrue(instance.cpeHasNoVersion(c)); + } + + /** + * Test of countCharacter method, of class SuppressionRule. + */ + @Test + public void testCountCharacter() { + String str = "cpe:/a:microsoft:.net_framework:4.5"; + char c = ':'; + SuppressionRule instance = new SuppressionRule(); + int expResult = 4; + int result = instance.countCharacter(str, c); + assertEquals(expResult, result); + str = "::"; + expResult = 2; + result = instance.countCharacter(str, c); + assertEquals(expResult, result); + str = "these are not the characters you are looking for"; + expResult = 0; + result = instance.countCharacter(str, c); + assertEquals(expResult, result); + } + + /** + * Test of cpeMatches method, of class SuppressionRule. + */ + @Test + public void testCpeMatches() { + Identifier identifier = new Identifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test"); + + PropertyType cpe = new PropertyType(); + cpe.setValue("cpe:/a:microsoft:.net_framework:4.5"); + + SuppressionRule instance = new SuppressionRule(); + boolean expResult = true; + boolean result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("cpe:/a:microsoft:.net_framework:4.0"); + expResult = false; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("CPE:/a:microsoft:.net_framework:4.5"); + cpe.setCaseSensitive(true); + expResult = false; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("cpe:/a:microsoft:.net_framework"); + cpe.setCaseSensitive(false); + expResult = true; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("cpe:/a:microsoft:.*"); + cpe.setRegex(true); + expResult = true; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("CPE:/a:microsoft:.*"); + cpe.setRegex(true); + cpe.setCaseSensitive(true); + expResult = false; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + + cpe.setValue("cpe:/a:apache:.*"); + cpe.setRegex(true); + cpe.setCaseSensitive(false); + expResult = false; + result = instance.cpeMatches(cpe, identifier); + assertEquals(expResult, result); + } + + /** + * Test of process method, of class SuppressionRule. + */ + @Test + public void testProcess() { + File struts = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath()); + Dependency dependency = new Dependency(struts); + dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test"); + String sha1 = dependency.getSha1sum(); + dependency.setSha1sum("384FAA82E193D4E4B0546059CA09572654BC3970"); + Vulnerability v = createVulnerability(); + dependency.addVulnerability(v); + + //cwe + SuppressionRule instance = new SuppressionRule(); + instance.setSha1(sha1); + instance.addCwe("287"); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().size() == 1); + dependency.setSha1sum(sha1); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().isEmpty()); + + //cvss + dependency.addVulnerability(v); + instance = new SuppressionRule(); + instance.addCvssBelow(5f); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().size() == 1); + instance.addCvssBelow(8f); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().isEmpty()); + + //cve + dependency.addVulnerability(v); + instance = new SuppressionRule(); + instance.addCve("CVE-2012-1337"); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().size() == 1); + instance.addCve("CVE-2013-1337"); + instance.process(dependency); + assertTrue(dependency.getVulnerabilities().isEmpty()); + + //cpe + instance = new SuppressionRule(); + PropertyType pt = new PropertyType(); + pt.setValue("cpe:/a:microsoft:.net_framework:4.0"); + instance.addCpe(pt); + instance.process(dependency); + assertTrue(dependency.getIdentifiers().size() == 1); + pt = new PropertyType(); + pt.setValue("cpe:/a:microsoft:.net_framework:4.5"); + instance.addCpe(pt); + pt = new PropertyType(); + pt.setValue(".*"); + pt.setRegex(true); + instance.setFilePath(pt); + instance.process(dependency); + assertTrue(dependency.getIdentifiers().isEmpty()); + + dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.0", "some url not needed for this test"); + dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test"); + dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:5.0", "some url not needed for this test"); + pt = new PropertyType(); + pt.setValue("cpe:/a:microsoft:.net_framework"); + instance.addCpe(pt); + assertTrue(dependency.getIdentifiers().size() == 3); + instance.process(dependency); + assertTrue(dependency.getIdentifiers().isEmpty()); + } + + private Vulnerability createVulnerability() { + Vulnerability v = new Vulnerability(); + v.setCwe("CWE-287 Improper Authentication"); + v.setName("CVE-2013-1337"); + v.setCvssScore(7.5f); + return v; + } +}