Merge branch 'master' of github.com:jeremylong/DependencyCheck into stevespringett-master

This commit is contained in:
Jeremy Long
2017-06-02 06:44:13 -04:00
45 changed files with 1502 additions and 448 deletions

View File

@@ -3,11 +3,22 @@ jdk: oraclejdk7
script: mvn install -DreleaseTesting
env:
global:
secure: ZUzhWfpXJw/oAeDlUkDFkEJMT0T7kCN3d7ah8urkL2B0KFfKOqQagkbXkgvDa1SYud8VdcnoGa69LfkEr5IrdqW7R4bEYZAiN5swm4Z0iO8t53szVspm2f+O9jQ44O/sfOfpfLxWUUuhdc7Vbrszp+tSszxdPmssWL+f5a/mfWs=
- secure: ZUzhWfpXJw/oAeDlUkDFkEJMT0T7kCN3d7ah8urkL2B0KFfKOqQagkbXkgvDa1SYud8VdcnoGa69LfkEr5IrdqW7R4bEYZAiN5swm4Z0iO8t53szVspm2f+O9jQ44O/sfOfpfLxWUUuhdc7Vbrszp+tSszxdPmssWL+f5a/mfWs=
- secure: pmFymoI7qH0Kna3NkcHrqLiTVWKmrhwqA4Z9U6XLhWDQxcs5g94wCCKpGB6Lkz9mkvRxBRFpZZelnXJa9W9mnuVOMIa5tQfS5gBuaNXOe7AXXdc+Y2975OR9sSfvf16FxLFvNJILmZq+bpMLs+EXaQvjYQHW2O6OWZdLhAPVG6A=
before_install:
- sudo apt-get install jq
- wget -O ~/codacy-coverage-reporter-assembly-latest.jar $(curl https://api.github.com/repos/codacy/codacy-coverage-reporter/releases/latest | jq -r .assets[0].browser_download_url)
- wget -O ~/codacy-coverage-reporter-assembly.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/1.0.13/codacy-coverage-reporter-1.0.13-assembly.jar
after_success:
- java -cp ~/codacy-coverage-reporter-assembly-latest.jar com.codacy.CodacyCoverageReporter -l Java -r build-reporting/target/site/jacoco-aggregate/jacoco.xml
- java -cp ~/codacy-coverage-reporter-assembly.jar com.codacy.CodacyCoverageReporter
-l Java -r build-reporting/target/coverage-reports/jacoco.xml
matrix:
include:
- env: CRON_ONLY=1
addons:
coverity_scan:
project:
name: OWASP dependency-check
version: 1.0
description: A software composition analsis tools that detects publicly
disclosed vulnerabilities in application dependencies.
build_command: mvn -DskipTests=true package
branch_pattern: master

View File

@@ -29,8 +29,8 @@ $ ./bin/dependency-check.sh --project Testing --out . --scan [path to jar files
```
On Windows
```
> bin/dependency-check.bat -h
> bin/dependency-check.bat --project Testing --out . --scan [path to jar files to be scanned]
> .\bin\dependency-check.bat -h
> .\bin\dependency-check.bat --project Testing --out . --scan [path to jar files to be scanned]
```
On Mac with [Homebrew](http://brew.sh)
```

View File

@@ -20,10 +20,11 @@ Copyright (c) 2017 - Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<name>Dependency-Check Build-Reporting</name>
<artifactId>build-reporting</artifactId>
<packaging>pom</packaging>
<!-- begin copy from http://minds.coremedia.com/2012/09/11/problem-solved-deploy-multi-module-maven-project-site-as-github-pages/ -->
<distributionManagement>
<site>
@@ -62,6 +63,13 @@ Copyright (c) 2017 - Jeremy Long. All Rights Reserved.
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
@@ -72,6 +80,13 @@ Copyright (c) 2017 - Jeremy Long. All Rights Reserved.
<goals>
<goal>report-aggregate</goal>
</goals>
<configuration>
<outputDirectory>target/coverage-reports/</outputDirectory>
<dataFileIncludes>
<dataFileInclude>target/coverage-reports/jacoco-ut.exec</dataFileInclude>
<dataFileInclude>target/coverage-reports/jacoco-it.exec</dataFileInclude>
</dataFileIncludes>
</configuration>
</execution>
</executions>
</plugin>

View File

@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-ant</artifactId>

View File

@@ -940,16 +940,7 @@ public class Check extends Update {
throw new BuildException(ex);
}
}
DatabaseProperties prop = null;
try (CveDB cve = CveDB.getInstance()) {
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
//TODO shouldn't this be a fatal exception
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
}
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
reporter.generateReports(reportOutputDirectory, reportFormat);
engine.writeReports(getProjectName(),new File(reportOutputDirectory), reportFormat);
if (this.failBuildOnCVSS <= 10) {
checkForFailure(engine.getDependencies());

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-cli</artifactId>

View File

@@ -281,18 +281,9 @@ public class App {
}
exCol = ex;
}
final List<Dependency> dependencies = engine.getDependencies();
DatabaseProperties prop = null;
try (CveDB cve = CveDB.getInstance()) {
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
//TODO shouldn't this be a fatal exception
LOGGER.debug("Unable to retrieve DB Properties", ex);
}
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
try {
report.generateReports(reportDirectory, outputFormat);
engine.writeReports(applicationName, new File(reportDirectory), outputFormat);
} catch (ReportException ex) {
if (exCol != null) {
exCol.addException(ex);
@@ -306,7 +297,7 @@ public class App {
}
//Set the exit code based on whether we found a high enough vulnerability
for (Dependency dep : dependencies) {
for (Dependency dep : engine.getDependencies()) {
if (!dep.getVulnerabilities().isEmpty()) {
for (Vulnerability vuln : dep.getVulnerabilities()) {
LOGGER.debug("VULNERABILITY FOUND " + dep.getDisplayFileName());
@@ -316,7 +307,6 @@ public class App {
}
}
}
return retCode;
} finally {
if (engine != null) {
@@ -353,8 +343,7 @@ public class App {
* @throws InvalidSettingException thrown when a user defined properties
* file is unable to be loaded.
*/
private void populateSettings(CliParser cli) throws InvalidSettingException {
final boolean autoUpdate = cli.isAutoUpdate();
protected void populateSettings(CliParser cli) throws InvalidSettingException {
final String connectionTimeout = cli.getConnectionTimeout();
final String proxyServer = cli.getProxyServer();
final String proxyPort = cli.getProxyPort();
@@ -377,7 +366,8 @@ public class App {
final String cveBase12 = cli.getBaseCve12Url();
final String cveBase20 = cli.getBaseCve20Url();
final Integer cveValidForHours = cli.getCveValidForHours();
final boolean experimentalEnabled = cli.isExperimentalEnabled();
final Boolean autoUpdate = cli.isAutoUpdate();
final Boolean experimentalEnabled = cli.isExperimentalEnabled();
if (propertiesFile != null) {
try {
@@ -390,7 +380,7 @@ public class App {
}
// We have to wait until we've merged the properties before attempting to set whether we use
// the proxy for Nexus since it could be disabled in the properties, but not explicitly stated
// on the command line
// on the command line. This is true of other boolean values set below not using the setBooleanIfNotNull.
final boolean nexusUsesProxy = cli.isNexusUsesProxy();
if (dataDirectory != null) {
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
@@ -404,7 +394,7 @@ public class App {
final File dataDir = new File(base, sub);
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
}
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
@@ -415,7 +405,8 @@ public class App {
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
//File Type Analyzer Settings
Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-core</artifactId>

View File

@@ -54,6 +54,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.reporting.ReportGenerator;
/**
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and
@@ -796,4 +799,42 @@ public class Engine implements FileFilter {
exceptions.add(throwable);
throw new ExceptionCollection(message, exceptions, true);
}
/**
* Writes the report to the given output directory.
*
* @param applicationName the name of the application/project
* @param groupId the Maven groupId
* @param artifactId the Maven artifactId
* @param version the Maven version
* @param outputDir the path to the output directory (can include the full
* file name if the format is not ALL)
* @param format the report format (ALL, HTML, CSV, JSON, etc.)
* @throws ReportException thrown if there is an error generating the report
*/
public void writeReports(String applicationName, String groupId, String artifactId,
String version, File outputDir, String format) throws ReportException {
final DatabaseProperties prop = database.getDatabaseProperties();
final ReportGenerator r = new ReportGenerator(applicationName, groupId, artifactId, version, dependencies, getAnalyzers(), prop);
try {
r.write(outputDir.getAbsolutePath(), format);
} catch (ReportException ex) {
final String msg = String.format("Error generating the report for %s", applicationName);
throw new ReportException(msg, ex);
}
}
/**
* Writes the report to the given output directory.
*
* @param applicationName the name of the application/project
* @param outputDir the path to the output directory (can include the full
* file name if the format is not ALL)
* @param format the report format (ALL, HTML, CSV, JSON, etc.)
* @throws ReportException thrown if there is an error generating the report
*/
public void writeReports(String applicationName, File outputDir, String format) throws ReportException {
writeReports(applicationName, null, null, null, outputDir, format);
}
}

View File

@@ -18,12 +18,9 @@
package org.owasp.dependencycheck.agent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability;
@@ -840,21 +837,15 @@ public class DependencyCheckScanAgent {
*
* @param engine a dependency-check engine
* @param outDirectory the directory to write the reports to
* @throw ScanAgentException thrown if there is an error generating the
* report
*/
private void generateExternalReports(Engine engine, File outDirectory) {
DatabaseProperties prop = null;
try (CveDB cve = CveDB.getInstance()) {
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
//TODO shouldn't this be a fatal exception
LOGGER.debug("Unable to retrieve DB Properties", ex);
}
final ReportGenerator r = new ReportGenerator(this.applicationName, engine.getDependencies(), engine.getAnalyzers(), prop);
private void generateExternalReports(Engine engine, File outDirectory) throws ScanAgentException {
try {
r.generateReports(outDirectory.getCanonicalPath(), this.reportFormat.name());
} catch (IOException | ReportException ex) {
LOGGER.error("Unexpected exception occurred during analysis; please see the verbose error log for more details.");
LOGGER.debug("", ex);
engine.writeReports(applicationName, outDirectory, this.reportFormat.name());
} catch (ReportException ex) {
LOGGER.debug("Unexpected exception occurred during analysis; please see the verbose error log for more details.", ex);
throw new ScanAgentException("Error generating the report", ex);
}
}

View File

@@ -24,6 +24,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,6 +37,7 @@ import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.collections.map.ReferenceMap;
import org.owasp.dependencycheck.data.cwe.CweDB;
import org.owasp.dependencycheck.dependency.Reference;
import org.owasp.dependencycheck.dependency.Vulnerability;
@@ -48,6 +50,8 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.commons.collections.map.AbstractReferenceMap.HARD;
import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT;
import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
/**
@@ -91,6 +95,12 @@ public final class CveDB implements AutoCloseable {
*/
private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements = new EnumMap<>(PreparedStatementCveDb.class);
/**
* Cache for CVE lookups; used to speed up the vulnerability search process.
*/
@SuppressWarnings("unchecked")
private final Map<String, List<Vulnerability>> vulnerabilitiesForCpeCache = Collections.synchronizedMap(new ReferenceMap(HARD, SOFT));
/**
* The enum value names must match the keys of the statements in the
* statement bundles "dbStatements*.properties".
@@ -269,6 +279,7 @@ public final class CveDB implements AutoCloseable {
instance.usageCount -= 1;
if (instance.usageCount <= 0 && instance.isOpen()) {
instance.usageCount = 0;
clearCache();
instance.closeStatements();
try {
instance.connection.close();
@@ -474,6 +485,7 @@ public final class CveDB implements AutoCloseable {
* @param value the property value
*/
public synchronized void saveProperty(String key, String value) {
clearCache();
try {
try {
final PreparedStatement mergeProperty = getPreparedStatement(MERGE_PROPERTY);
@@ -498,6 +510,18 @@ public final class CveDB implements AutoCloseable {
}
}
/**
* Clears cache. Should be called whenever something is modified. While this
* is not the optimal cache eviction strategy, this is good enough for
* typical usage (update DB and then only read) and it is easier to maintain
* the code.
*
* It should be also called when DB is closed.
*/
private void clearCache() {
vulnerabilitiesForCpeCache.clear();
}
/**
* Retrieves the vulnerabilities associated with the specified CPE.
*
@@ -506,6 +530,13 @@ public final class CveDB implements AutoCloseable {
* @throws DatabaseException thrown if there is an exception retrieving data
*/
public synchronized List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
final List<Vulnerability> cachedVulnerabilities = vulnerabilitiesForCpeCache.get(cpeStr);
if (cachedVulnerabilities != null) {
LOGGER.debug("Cache hit for {}", cpeStr);
return cachedVulnerabilities;
} else {
LOGGER.debug("Cache miss for {}", cpeStr);
}
final VulnerableSoftware cpe = new VulnerableSoftware();
try {
cpe.parseName(cpeStr);
@@ -554,6 +585,7 @@ public final class CveDB implements AutoCloseable {
} finally {
DBUtils.closeResultSet(rs);
}
vulnerabilitiesForCpeCache.put(cpeStr, vulnerabilities);
return vulnerabilities;
}
@@ -633,6 +665,7 @@ public final class CveDB implements AutoCloseable {
* @throws DatabaseException is thrown if the database
*/
public synchronized void updateVulnerability(Vulnerability vuln) throws DatabaseException {
clearCache();
try {
int vulnerabilityId = 0;
final PreparedStatement selectVulnerabilityId = getPreparedStatement(SELECT_VULNERABILITY_ID);
@@ -799,6 +832,7 @@ public final class CveDB implements AutoCloseable {
* ensure orphan entries are removed.
*/
public synchronized void cleanupDatabase() {
clearCache();
try {
final PreparedStatement ps = getPreparedStatement(CLEANUP_ORPHANS);
if (ps != null) {
@@ -934,6 +968,7 @@ public final class CveDB implements AutoCloseable {
* Deletes unused dictionary entries from the database.
*/
public synchronized void deleteUnusedCpe() {
clearCache();
PreparedStatement ps = null;
try {
ps = connection.prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
@@ -956,6 +991,7 @@ public final class CveDB implements AutoCloseable {
* @param product the CPE product
*/
public synchronized void addCpe(String cpe, String vendor, String product) {
clearCache();
PreparedStatement ps = null;
try {
ps = connection.prepareStatement(statementBundle.getString("ADD_DICT_CPE"));

View File

@@ -124,7 +124,7 @@ public class EscapeTool {
return "";
}
boolean addComma = false;
StringBuilder sb = new StringBuilder();
final StringBuilder sb = new StringBuilder();
for (Identifier id : ids) {
if (!"cpe".equals(id.getType())) {
if (addComma) {
@@ -150,7 +150,7 @@ public class EscapeTool {
return "";
}
boolean addComma = false;
StringBuilder sb = new StringBuilder();
final StringBuilder sb = new StringBuilder();
for (Identifier id : ids) {
if ("cpe".equals(id.getType())) {
if (addComma) {

View File

@@ -107,29 +107,8 @@ public class ReportGenerator {
*/
public ReportGenerator(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
velocityEngine = createVelocityEngine();
context = createContext();
velocityEngine.init();
final EscapeTool enc = new EscapeTool();
final DateTime dt = new DateTime();
final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("MMM d, yyyy 'at' HH:mm:ss z");
final DateTimeFormatter dateFormatXML = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
// final Date d = new Date();
// final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z");
// final DateFormat dateFormatXML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
final String scanDate = dateFormat.print(dt);
final String scanDateXML = dateFormatXML.print(dt);
context.put("applicationName", applicationName);
context.put("dependencies", dependencies);
context.put("analyzers", analyzers);
context.put("properties", properties);
context.put("scanDate", scanDate);
context.put("scanDateXML", scanDateXML);
context.put("enc", enc);
context.put("version", Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
context = createContext(applicationName, dependencies, analyzers, properties);
}
/**
@@ -148,9 +127,15 @@ public class ReportGenerator {
List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
this(applicationName, dependencies, analyzers, properties);
context.put("applicationVersion", version);
context.put("artifactID", artifactID);
context.put("groupID", groupID);
if (version != null) {
context.put("applicationVersion", version);
}
if (artifactID != null) {
context.put("artifactID", artifactID);
}
if (groupID != null) {
context.put("groupID", groupID);
}
}
/**
@@ -166,67 +151,235 @@ public class ReportGenerator {
}
/**
* Creates a new Velocity Context.
* Constructs the velocity context used to generate the dependency-check
* reports.
*
* @return a Velocity Context
* @param applicationName the application name being analyzed
* @param dependencies the list of dependencies
* @param analyzers the list of analyzers used
* @param properties the database properties (containing timestamps of the
* NVD CVE data)
* @return the velocity context
*/
private Context createContext() {
return new VelocityContext();
private VelocityContext createContext(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
final DateTime dt = new DateTime();
final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("MMM d, yyyy 'at' HH:mm:ss z");
final DateTimeFormatter dateFormatXML = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
final String scanDate = dateFormat.print(dt);
final String scanDateXML = dateFormatXML.print(dt);
final VelocityContext ctxt = new VelocityContext();
ctxt.put("applicationName", applicationName);
ctxt.put("dependencies", dependencies);
ctxt.put("analyzers", analyzers);
ctxt.put("properties", properties);
ctxt.put("scanDate", scanDate);
ctxt.put("scanDateXML", scanDateXML);
ctxt.put("enc", new EscapeTool());
ctxt.put("version", Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
return ctxt;
}
/**
* Generates the Dependency Reports for the identified dependencies.
* Writes the dependency-check report to the given output location.
*
* @param outputStream the OutputStream to send the generated report to
* @param format the format the report should be written in
* @throws IOException is thrown when the template file does not exist
* @throws Exception is thrown if there is an error writing out the reports
*/
public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception {
if (format == Format.XML || format == Format.ALL) {
generateReport("XmlReport", outputStream);
}
if (format == Format.HTML || format == Format.ALL) {
generateReport("HtmlReport", outputStream);
}
if (format == Format.VULN || format == Format.ALL) {
generateReport("VulnerabilityReport", outputStream);
}
if (format == Format.JSON || format == Format.ALL) {
generateReport("JsonReport", outputStream);
}
if (format == Format.CSV || format == Format.ALL) {
generateReport("CsvReport", outputStream);
}
}
/**
* Generates the Dependency Reports for the identified dependencies.
*
* @param outputDir the path where the reports should be written
* @param format the format the report should be written in
* @throws ReportException is thrown if there is an error writing out the
* @param outputLocation the path where the reports should be written
* @param format the format the report should be written in (XML, HTML,
* JSON, CSV, ALL) or even the path to a custom velocity template (either
* fully qualified or the template name on the class path).
* @throws ReportException is thrown if there is an error creating out the
* reports
*/
public void generateReports(String outputDir, Format format) throws ReportException {
if (format == Format.XML || format == Format.ALL) {
generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml");
public void write(String outputLocation, String format) throws ReportException {
Format reportFormat = null;
try {
reportFormat = Format.valueOf(format.toUpperCase());
} catch (IllegalArgumentException ex) {
LOGGER.trace("ignore this exception", ex);
}
if (format == Format.JSON || format == Format.ALL) {
generateReport("JsonReport", outputDir + File.separator + "dependency-check-report.json");
pretifyJson(outputDir + File.separator + "dependency-check-report.json");
if (reportFormat != null) {
write(outputLocation, reportFormat);
} else {
final File out = getReportFile(outputLocation, null);
if (out.isDirectory()) {
throw new ReportException("Unable to write non-standard VSL output to a directory, please specify a file name");
}
processTemplate(format, out);
}
if (format == Format.CSV || format == Format.ALL) {
generateReport("CsvReport", outputDir + File.separator + "dependency-check-report.csv");
}
if (format == Format.HTML || format == Format.ALL) {
generateReport("HtmlReport", outputDir + File.separator + "dependency-check-report.html");
}
if (format == Format.VULN || format == Format.ALL) {
generateReport("VulnerabilityReport", outputDir + File.separator + "dependency-check-vulnerability.html");
}
/**
* Writes the dependency-check report(s).
*
* @param outputLocation the path where the reports should be written
* @param format the format the report should be written in (XML, HTML, ALL)
* @throws ReportException is thrown if there is an error creating out the
* reports
*/
public void write(String outputLocation, Format format) throws ReportException {
if (format == Format.ALL) {
for (Format f : Format.values()) {
if (f != Format.ALL) {
write(outputLocation, f);
}
}
} else {
final File out = getReportFile(outputLocation, format);
final String templateName = format.toString().toLowerCase() + "Report";
processTemplate(templateName, out);
if (format == Format.JSON) {
pretifyJson(out.getPath());
}
}
}
// /**
// * Writes the dependency-check report(s).
// *
// * @param outputStream the OutputStream to send the generated report to
// * @param format the format the report should be written in
// * @throws ReportException thrown if the report format is ALL
// * @throws IOException is thrown when the template file does not exist
// * @throws Exception is thrown if there is an error writing out the reports
// */
// public void write(OutputStream outputStream, Format format) throws ReportException, IOException, Exception {
// if (format == Format.ALL) {
// throw new ReportException("Unable to write ALL reports to a single output stream, please check the API");
// }
// final String templateName = format.toString().toLowerCase() + "Report";
// processTemplate(templateName, outputStream);
// }
/**
* Determines the report file name based on the give output location and
* format. If the output location contains a full file name that has the
* correct extension for the given report type then the output location is
* returned. However, if the output location is a directory, this method
* will generate the correct name for the given output format.
*
* @param outputLocation the specified output location
* @param format the report format
* @return the report File
*/
protected File getReportFile(String outputLocation, Format format) {
File outFile = new File(outputLocation);
if (outFile.getParentFile() == null) {
outFile = new File(".", outputLocation);
}
final String pathToCheck = outputLocation.toLowerCase();
if (format == Format.XML && !pathToCheck.endsWith(".xml")) {
return new File(outFile, "dependency-check-report.xml");
}
if (format == Format.HTML && !pathToCheck.endsWith(".html") && !pathToCheck.endsWith(".htm")) {
return new File(outFile, "dependency-check-report.html");
}
if (format == Format.VULN && !pathToCheck.endsWith(".html") && !pathToCheck.endsWith(".htm")) {
return new File(outFile, "dependency-check-vulnerability.html");
}
if (format == Format.JSON && !pathToCheck.endsWith(".json")) {
return new File(outFile, "dependency-check-report.json");
}
if (format == Format.CSV && !pathToCheck.endsWith(".csv")) {
return new File(outFile, "dependency-check-report.csv");
}
return outFile;
}
/**
* Generates a report from a given Velocity Template. The template name
* provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file.
*
* @param template the name of the template to load
* @param file the output file to write the report to
* @throws ReportException is thrown when the report cannot be generated
*/
protected void processTemplate(String template, File file) throws ReportException {
ensureParentDirectoryExists(file);
try (OutputStream output = new FileOutputStream(file)) {
processTemplate(template, output);
} catch (IOException ex) {
throw new ReportException(String.format("Unable to write to file: %s", file), ex);
}
}
/**
* Generates a report from a given Velocity Template. The template name
* provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file.
*
* @param templateName the name of the template to load
* @param outputStream the OutputStream to write the report to
* @throws ReportException is thrown when an exception occurs
*/
protected void processTemplate(String templateName, OutputStream outputStream) throws ReportException {
InputStream input = null;
String logTag = null;
final File f = new File(templateName);
try {
if (f.isFile()) {
try {
logTag = templateName;
input = new FileInputStream(f);
} catch (FileNotFoundException ex) {
throw new ReportException("Unable to locate template file: " + templateName, ex);
}
} else {
logTag = "templates/" + templateName + ".vsl";
input = this.getClass().getClassLoader().getResourceAsStream(logTag);
}
if (input == null) {
logTag = templateName;
input = this.getClass().getClassLoader().getResourceAsStream(templateName);
}
if (input == null) {
throw new ReportException("Template file doesn't exist: " + logTag);
}
try (InputStreamReader reader = new InputStreamReader(input, "UTF-8");
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8")) {
if (!velocityEngine.evaluate(context, writer, logTag, reader)) {
throw new ReportException("Failed to convert the template into html.");
}
writer.flush();
} catch (UnsupportedEncodingException ex) {
throw new ReportException("Unable to generate the report using UTF-8", ex);
} catch (IOException ex) {
throw new ReportException("Unable to write the report", ex);
}
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
LOGGER.trace("Error closing input", ex);
}
}
}
}
/**
* Validates that the given file's parent directory exists. If the directory
* does not exist an attempt to create the necessary path is made; if that
* fails a ReportException will be raised.
*
* @param file the file or directory directory
* @throws ReportException thrown if the parent directory does not exist and
* cannot be created
*/
private void ensureParentDirectoryExists(File file) throws ReportException {
if (!file.getParentFile().exists()) {
final boolean created = file.getParentFile().mkdirs();
if (!created) {
final String msg = String.format("Unable to create directory '%s'.", file.getParentFile().getAbsolutePath());
throw new ReportException(msg);
}
}
}
/**
* Reformats the given JSON file.
*
@@ -311,139 +464,4 @@ public class ReportGenerator {
}
}
}
/**
* Generates the Dependency Reports for the identified dependencies.
*
* @param outputDir the path where the reports should be written
* @param outputFormat the format the report should be written in (XML,
* HTML, ALL)
* @throws ReportException is thrown if there is an error creating out the
* reports
*/
public void generateReports(String outputDir, String outputFormat) throws ReportException {
final String format = outputFormat.toUpperCase();
final String pathToCheck = outputDir.toLowerCase();
if (format.matches("^(XML|HTML|VULN|JSON|ALL)$")) {
if ("XML".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".xml")) {
generateReport("XmlReport", outputDir);
} else {
generateReports(outputDir, Format.XML);
}
}
if ("HTML".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".html") || pathToCheck.endsWith(".htm")) {
generateReport("HtmlReport", outputDir);
} else {
generateReports(outputDir, Format.HTML);
}
}
if ("VULN".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".html") || pathToCheck.endsWith(".htm")) {
generateReport("VulnReport", outputDir);
} else {
generateReports(outputDir, Format.VULN);
}
}
if ("JSON".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".json")) {
generateReport("JsonReport", outputDir);
pretifyJson(outputDir);
} else {
generateReports(outputDir, Format.JSON);
}
}
if ("CSV".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".csv")) {
generateReport("CsvReport", outputDir);
} else {
generateReports(outputDir, Format.JSON);
}
}
if ("ALL".equalsIgnoreCase(format)) {
generateReports(outputDir, Format.ALL);
}
}
}
/**
* Generates a report from a given Velocity Template. The template name
* provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file.
*
* @param templateName the name of the template to load
* @param outputStream the OutputStream to write the report to
* @throws ReportException is thrown when an exception occurs
*/
protected void generateReport(String templateName, OutputStream outputStream) throws ReportException {
InputStream input = null;
String templatePath = null;
final File f = new File(templateName);
try {
if (f.exists() && f.isFile()) {
try {
templatePath = templateName;
input = new FileInputStream(f);
} catch (FileNotFoundException ex) {
throw new ReportException("Unable to locate template file: " + templateName, ex);
}
} else {
templatePath = "templates/" + templateName + ".vsl";
input = this.getClass().getClassLoader().getResourceAsStream(templatePath);
}
if (input == null) {
throw new ReportException("Template file doesn't exist: " + templatePath);
}
try (InputStreamReader reader = new InputStreamReader(input, "UTF-8");
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8")) {
if (!velocityEngine.evaluate(context, writer, templatePath, reader)) {
throw new ReportException("Failed to convert the template into html.");
}
writer.flush();
} catch (UnsupportedEncodingException ex) {
throw new ReportException("Unable to generate the report using UTF-8", ex);
} catch (IOException ex) {
throw new ReportException("Unable to write the report", ex);
}
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
LOGGER.trace("Error closing input", ex);
}
}
}
}
/**
* Generates a report from a given Velocity Template. The template name
* provided can be the name of a template contained in the jar file, such as
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
* template file.
*
* @param templateName the name of the template to load
* @param outFileName the filename and path to write the report to
* @throws ReportException is thrown when the report cannot be generated
*/
protected void generateReport(String templateName, String outFileName) throws ReportException {
File outFile = new File(outFileName);
if (outFile.getParentFile() == null) {
outFile = new File(".", outFileName);
}
if (!outFile.getParentFile().exists()) {
final boolean created = outFile.getParentFile().mkdirs();
if (!created) {
throw new ReportException("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'.");
}
}
try (OutputStream outputSteam = new FileOutputStream(outFile)) {
generateReport(templateName, outputSteam);
} catch (IOException ex) {
throw new ReportException("Unable to write to file: " + outFile, ex);
}
}
}

View File

@@ -0,0 +1,310 @@
package org.owasp.dependencycheck.xml;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* This is a utility class to convert named XML Entities (such as &oslash;) into
* its HTML encoded Unicode code point (i.e. &amp;#248;). This is a slightly
* modified (class/method rename) from an SO answer:
* https://stackoverflow.com/questions/7286428/help-the-java-sax-parser-to-understand-bad-xml
*
* @author https://stackoverflow.com/users/823393/oldcurmudgeon
*/
public class XmlEntity {
/**
* The map of HTML entities.
*/
private static final Map<String, Integer> SPECIALS;
//<editor-fold defaultstate="collapsed" desc="Initialize SPECIALS">
/**
* Create a map HTML Named Entities to their numeric equivalent. Derived
* from Wikipedia
* http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
*/
static {
final Map<String, Integer> map = new HashMap<>();
map.put("quot", 34);
map.put("amp", 38);
map.put("apos", 39);
map.put("lt", 60);
map.put("gt", 62);
map.put("nbsp", 160);
map.put("iexcl", 161);
map.put("cent", 162);
map.put("pound", 163);
map.put("curren", 164);
map.put("yen", 165);
map.put("brvbar", 166);
map.put("sect", 167);
map.put("uml", 168);
map.put("copy", 169);
map.put("ordf", 170);
map.put("laquo", 171);
map.put("not", 172);
map.put("shy", 173);
map.put("reg", 174);
map.put("macr", 175);
map.put("deg", 176);
map.put("plusmn", 177);
map.put("sup2", 178);
map.put("sup3", 179);
map.put("acute", 180);
map.put("micro", 181);
map.put("para", 182);
map.put("middot", 183);
map.put("cedil", 184);
map.put("sup1", 185);
map.put("ordm", 186);
map.put("raquo", 187);
map.put("frac14", 188);
map.put("frac12", 189);
map.put("frac34", 190);
map.put("iquest", 191);
map.put("Agrave", 192);
map.put("Aacute", 193);
map.put("Acirc", 194);
map.put("Atilde", 195);
map.put("Auml", 196);
map.put("Aring", 197);
map.put("AElig", 198);
map.put("Ccedil", 199);
map.put("Egrave", 200);
map.put("Eacute", 201);
map.put("Ecirc", 202);
map.put("Euml", 203);
map.put("Igrave", 204);
map.put("Iacute", 205);
map.put("Icirc", 206);
map.put("Iuml", 207);
map.put("ETH", 208);
map.put("Ntilde", 209);
map.put("Ograve", 210);
map.put("Oacute", 211);
map.put("Ocirc", 212);
map.put("Otilde", 213);
map.put("Ouml", 214);
map.put("times", 215);
map.put("Oslash", 216);
map.put("Ugrave", 217);
map.put("Uacute", 218);
map.put("Ucirc", 219);
map.put("Uuml", 220);
map.put("Yacute", 221);
map.put("THORN", 222);
map.put("szlig", 223);
map.put("agrave", 224);
map.put("aacute", 225);
map.put("acirc", 226);
map.put("atilde", 227);
map.put("auml", 228);
map.put("aring", 229);
map.put("aelig", 230);
map.put("ccedil", 231);
map.put("egrave", 232);
map.put("eacute", 233);
map.put("ecirc", 234);
map.put("euml", 235);
map.put("igrave", 236);
map.put("iacute", 237);
map.put("icirc", 238);
map.put("iuml", 239);
map.put("eth", 240);
map.put("ntilde", 241);
map.put("ograve", 242);
map.put("oacute", 243);
map.put("ocirc", 244);
map.put("otilde", 245);
map.put("ouml", 246);
map.put("divide", 247);
map.put("oslash", 248);
map.put("ugrave", 249);
map.put("uacute", 250);
map.put("ucirc", 251);
map.put("uuml", 252);
map.put("yacute", 253);
map.put("thorn", 254);
map.put("yuml", 255);
map.put("OElig", 338);
map.put("oelig", 339);
map.put("Scaron", 352);
map.put("scaron", 353);
map.put("Yuml", 376);
map.put("fnof", 402);
map.put("circ", 710);
map.put("tilde", 732);
map.put("Alpha", 913);
map.put("Beta", 914);
map.put("Gamma", 915);
map.put("Delta", 916);
map.put("Epsilon", 917);
map.put("Zeta", 918);
map.put("Eta", 919);
map.put("Theta", 920);
map.put("Iota", 921);
map.put("Kappa", 922);
map.put("Lambda", 923);
map.put("Mu", 924);
map.put("Nu", 925);
map.put("Xi", 926);
map.put("Omicron", 927);
map.put("Pi", 928);
map.put("Rho", 929);
map.put("Sigma", 931);
map.put("Tau", 932);
map.put("Upsilon", 933);
map.put("Phi", 934);
map.put("Chi", 935);
map.put("Psi", 936);
map.put("Omega", 937);
map.put("alpha", 945);
map.put("beta", 946);
map.put("gamma", 947);
map.put("delta", 948);
map.put("epsilon", 949);
map.put("zeta", 950);
map.put("eta", 951);
map.put("theta", 952);
map.put("iota", 953);
map.put("kappa", 954);
map.put("lambda", 955);
map.put("mu", 956);
map.put("nu", 957);
map.put("xi", 958);
map.put("omicron", 959);
map.put("pi", 960);
map.put("rho", 961);
map.put("sigmaf", 962);
map.put("sigma", 963);
map.put("tau", 964);
map.put("upsilon", 965);
map.put("phi", 966);
map.put("chi", 967);
map.put("psi", 968);
map.put("omega", 969);
map.put("thetasym", 977);
map.put("upsih", 978);
map.put("piv", 982);
map.put("ensp", 8194);
map.put("emsp", 8195);
map.put("thinsp", 8201);
map.put("zwnj", 8204);
map.put("zwj", 8205);
map.put("lrm", 8206);
map.put("rlm", 8207);
map.put("ndash", 8211);
map.put("mdash", 8212);
map.put("lsquo", 8216);
map.put("rsquo", 8217);
map.put("sbquo", 8218);
map.put("ldquo", 8220);
map.put("rdquo", 8221);
map.put("bdquo", 8222);
map.put("dagger", 8224);
map.put("Dagger", 8225);
map.put("bull", 8226);
map.put("hellip", 8230);
map.put("permil", 8240);
map.put("prime", 8242);
map.put("Prime", 8243);
map.put("lsaquo", 8249);
map.put("rsaquo", 8250);
map.put("oline", 8254);
map.put("frasl", 8260);
map.put("euro", 8364);
map.put("image", 8465);
map.put("weierp", 8472);
map.put("real", 8476);
map.put("trade", 8482);
map.put("alefsym", 8501);
map.put("larr", 8592);
map.put("uarr", 8593);
map.put("rarr", 8594);
map.put("darr", 8595);
map.put("harr", 8596);
map.put("crarr", 8629);
map.put("lArr", 8656);
map.put("uArr", 8657);
map.put("rArr", 8658);
map.put("dArr", 8659);
map.put("hArr", 8660);
map.put("forall", 8704);
map.put("part", 8706);
map.put("exist", 8707);
map.put("empty", 8709);
map.put("nabla", 8711);
map.put("isin", 8712);
map.put("notin", 8713);
map.put("ni", 8715);
map.put("prod", 8719);
map.put("sum", 8721);
map.put("minus", 8722);
map.put("lowast", 8727);
map.put("radic", 8730);
map.put("prop", 8733);
map.put("infin", 8734);
map.put("ang", 8736);
map.put("and", 8743);
map.put("or", 8744);
map.put("cap", 8745);
map.put("cup", 8746);
map.put("int", 8747);
map.put("there4", 8756);
map.put("sim", 8764);
map.put("cong", 8773);
map.put("asymp", 8776);
map.put("ne", 8800);
map.put("equiv", 8801);
map.put("le", 8804);
map.put("ge", 8805);
map.put("sub", 8834);
map.put("sup", 8835);
map.put("nsub", 8836);
map.put("sube", 8838);
map.put("supe", 8839);
map.put("oplus", 8853);
map.put("otimes", 8855);
map.put("perp", 8869);
map.put("sdot", 8901);
map.put("lceil", 8968);
map.put("rceil", 8969);
map.put("lfloor", 8970);
map.put("rfloor", 8971);
map.put("lang", 10216);
map.put("rang", 10217);
map.put("loz", 9674);
map.put("spades", 9824);
map.put("clubs", 9827);
map.put("hearts", 9829);
map.put("diams", 9830);
SPECIALS = Collections.unmodifiableMap(map);
}
//</editor-fold>
/**
* Private constructor for a utility class.
*/
private XmlEntity() {
}
/**
* Converts a named XML entity into its HTML encoded Unicode code point.
*
* @param s the named entity (note, this should not include the leading '&amp;'
* or trailing ';'
* @return the HTML encoded Unicode code point representation of the named
* entity
*/
public static String fromNamedReference(CharSequence s) {
if (s == null) {
return null;
}
final Integer code = SPECIALS.get(s.toString());
if (code != null) {
return "&#" + code + ";";
}
return null;
}
}

View File

@@ -0,0 +1,269 @@
package org.owasp.dependencycheck.xml;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Cleans up often very bad XML. Primarily, this will convert named HTM entities
* into their HTM encoded Unicode code point representation.
*
* <ol>
* <li>Strips leading white space</li>
* <li>Recodes &amp;pound; etc to &amp;#...;</li>
* <li>Recodes lone &amp; as &amp;amp;</li>
* </ol>
* <p>
* This is a slightly modified (class/method rename) from an SO answer:
* https://stackoverflow.com/questions/7286428/help-the-java-sax-parser-to-understand-bad-xml</p>
*
* @author https://stackoverflow.com/users/823393/oldcurmudgeon
*/
public class XmlInputStream extends FilterInputStream {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(XmlInputStream.class);
/**
* The minimum length of characters to read.
*/
private static final int MIN_LENGTH = 2;
/**
* Holder for everything we've read.
*/
private StringBuilder red = new StringBuilder();
/**
* Data that needs to be pushed back.
*/
private StringBuilder pushBack = new StringBuilder();
/**
* How much we've given them.
*/
private int given = 0;
/**
* How much we've read.
*/
private int pulled = 0;
/**
* Constructs a new XML Input Stream.
*
* @param in the base input stream
*/
public XmlInputStream(InputStream in) {
super(in);
}
/**
* NB: This is a Troll length (i.e. it goes 1, 2, many) so 2 actually means
* "at least 2"
*
* @return the length
*/
public int length() {
try {
final StringBuilder s = read(MIN_LENGTH);
pushBack.append(s);
return s.length();
} catch (IOException ex) {
LOGGER.warn("Oops ", ex);
}
return 0;
}
/**
* Read n characters.
*
* @param n the number of characters to read
* @return the characters read
* @throws IOException thrown when an error occurs
*/
private StringBuilder read(int n) throws IOException {
// Input stream finished?
boolean eof = false;
// Read that many.
final StringBuilder s = new StringBuilder(n);
while (s.length() < n && !eof) {
// Always get from the pushBack buffer.
if (pushBack.length() == 0) {
// Read something from the stream into pushBack.
eof = readIntoPushBack();
}
// Pushback only contains deliverable codes.
if (pushBack.length() > 0) {
// Grab one character
s.append(pushBack.charAt(0));
// Remove it from pushBack
pushBack.deleteCharAt(0);
}
}
return s;
}
/**
* Might not actually push back anything but usually will.
*
* @return true if at end-of-file
* @throws IOException thrown if there is an IO exception in the underlying
* steam
*/
private boolean readIntoPushBack() throws IOException {
// File finished?
boolean eof = false;
// Next char.
final int ch = in.read();
if (ch >= 0) {
// Discard whitespace at start?
if (!(pulled == 0 && isWhiteSpace(ch))) {
// Good code.
pulled += 1;
// Parse out the &stuff;
if (ch == '&') {
// Process the &
readAmpersand();
} else {
// Not an '&', just append.
pushBack.append((char) ch);
}
}
} else {
// Hit end of file.
eof = true;
}
return eof;
}
/**
* Deal with an ampersand in the stream.
*
* @throws IOException thrown if an unknown entity is encountered
*/
private void readAmpersand() throws IOException {
// Read the whole word, up to and including the ;
final StringBuilder reference = new StringBuilder();
int ch;
// Should end in a ';'
for (ch = in.read(); isAlphaNumeric(ch); ch = in.read()) {
reference.append((char) ch);
}
// Did we tidily finish?
if (ch == ';') {
// Yes! Translate it into a &#nnn; code.
final String code = XmlEntity.fromNamedReference(reference);
if (code != null) {
// Keep it.
pushBack.append(code);
} else {
throw new IOException("Invalid/Unknown reference '&" + reference + ";'");
}
} else {
// Did not terminate properly!
// Perhaps an & on its own or a malformed reference.
// Either way, escape the &
pushBack.append("&#38;").append(reference).append((char) ch);
}
}
/**
* Keep track of what we've given them.
*
* @param s the sequence of characters given
* @param wanted the number of characters wanted
* @param got the number of characters given
*/
private void given(CharSequence s, int wanted, int got) {
red.append(s);
given += got;
LOGGER.trace("Given: [" + wanted + "," + got + "]-" + s);
}
/**
* Reads the next byte.
*
* @return the byte read
* @throws IOException thrown when there is an problem reading
*/
@Override
public int read() throws IOException {
final StringBuilder s = read(1);
given(s, 1, 1);
return s.length() > 0 ? s.charAt(0) : -1;
}
/**
* Reads the next length of bytes from the stream into the given byte array
* at the given offset.
*
* @param data the buffer to store the data read
* @param offset the offset in the buffer to start writing
* @param length the length of data to read
* @return the number of bytes read
* @throws IOException thrown when there is an issue with the underlying
* stream
*/
@Override
public int read(byte[] data, int offset, int length) throws IOException {
final StringBuilder s = read(length);
int n = 0;
for (int i = 0; i < Math.min(length, s.length()); i++) {
data[offset + i] = (byte) s.charAt(i);
n += 1;
}
given(s, length, n);
return n > 0 ? n : -1;
}
/**
* To string implementation.
*
* @return a string representation of the data given and read from the
* stream.
*/
@Override
public String toString() {
final String s = red.toString();
final StringBuilder h = new StringBuilder();
// Hex dump the small ones.
if (s.length() < 8) {
for (int i = 0; i < s.length(); i++) {
h.append(" ").append(Integer.toHexString(s.charAt(i)));
}
}
return "[" + given + "]-\"" + s + "\"" + (h.length() > 0 ? " (" + h.toString() + ")" : "");
}
/**
* Determines if the character is whitespace.
*
* @param ch the character to check
* @return true if the character is whitespace; otherwise false
*/
private boolean isWhiteSpace(int ch) {
switch (ch) {
case ' ':
case '\r':
case '\n':
case '\t':
return true;
default:
return false;
}
}
/**
* Checks if the given character is alpha-numeric.
*
* @param ch the character to check
* @return true if the character is alpha-numeric; otherwise false.
*/
private boolean isAlphaNumeric(int ch) {
return ('a' <= ch && ch <= 'z')
|| ('A' <= ch && ch <= 'Z')
|| ('0' <= ch && ch <= '9');
}
}

View File

@@ -29,6 +29,7 @@ import javax.xml.parsers.SAXParser;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;
import org.owasp.dependencycheck.utils.XmlUtils;
import org.owasp.dependencycheck.xml.XmlInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,7 +81,8 @@ public class PomParser {
final SAXParser saxParser = XmlUtils.buildSecureSaxParser();
final XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(handler);
final BOMInputStream bomStream = new BOMInputStream(inputStream);
final BOMInputStream bomStream = new BOMInputStream(new XmlInputStream(inputStream));
final ByteOrderMark bom = bomStream.getBOM();
final String defaultEncoding = "UTF-8";
final String charsetName = bom == null ? defaultEncoding : bom.getCharsetName();

View File

@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress base="true">
<notes><![CDATA[
<notes><![CDATA[
This suppresses false positives for Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll.
]]></notes>
<filePath regex="true">.*Microsoft\.VisualStudio\.QualityTools\.UnitTestFramework*\.dll</filePath>
<cve>CVE-2014-3802</cve>
<filePath regex="true">.*Microsoft\.VisualStudio\.QualityTools\.UnitTestFramework*\.dll</filePath>
<cve>CVE-2014-3802</cve>
</suppress>
<suppress base="true">
<notes><![CDATA[
<notes><![CDATA[
This suppresses false positives for EntityFramework.SqlServer.dll.
]]></notes>
<filePath regex="true">.*EntityFramework\.SqlServer*\.dll</filePath>
<cpe>cpe:/a:microsoft:server:6.0.0.0</cpe>
<cpe>cpe:/a:microsoft:sql_server:6.0</cpe>
<filePath regex="true">.*EntityFramework\.SqlServer*\.dll</filePath>
<cpe>cpe:/a:microsoft:server:6.0.0.0</cpe>
<cpe>cpe:/a:microsoft:sql_server:6.0</cpe>
</suppress>
<suppress base="true">
<notes><![CDATA[
@@ -582,24 +582,24 @@
]]></notes>
<gav regex="true">^com\.splunk:splunk:.*$</gav>
<cpe>cpe:/a:splunk:splunk</cpe>
</suppress>
<suppress base="true">
</suppress>
<suppress base="true">
<notes><![CDATA[
False positive per issue #713
]]></notes>
<gav regex="true">^org\.openid4java:openid4java:.*$</gav>
<cpe>cpe:/a:openid:openid</cpe>
<cpe>cpe:/a:openid:openid4java</cpe>
</suppress>
<suppress base="true">
</suppress>
<suppress base="true">
<notes><![CDATA[
False positive per issue #700
]]></notes>
<gav regex="true">^org\.springframework\.cloud:spring-cloud-netflix-core:.*$</gav>
<cpe>cpe:/a:pivotal:spring_framework</cpe>
<cpe>cpe:/a:pivotal_software:spring_framework</cpe>
</suppress>
<suppress base="true">
</suppress>
<suppress base="true">
<notes><![CDATA[
False positive per issue #700
]]></notes>
@@ -607,5 +607,20 @@
<cpe>cpe:/a:pivotal:spring_framework</cpe>
<cpe>cpe:/a:pivotal_software:spring_framework</cpe>
<cpe>cpe:/a:context_project:context</cpe>
</suppress>
</suppress>
<suppress base="true">
<notes><![CDATA[
False Positive per issue #746
]]></notes>
<gav regex="true">^com\.artofsolving:jodconverter:.*$</gav>
<cpe>cpe:/a:openoffice:openoffice.org</cpe>
<cpe>cpe:/a:openoffice:openoffice</cpe>
</suppress>
<suppress base="true">
<notes><![CDATA[
False Positive per issue #743
]]></notes>
<gav regex="true">^org\.xerial:sqlite-jdbc:.*$</gav>
<cve>CVE-2015-3717</cve>
</suppress>
</suppressions>

View File

@@ -17,6 +17,7 @@
*/
package org.owasp.dependencycheck;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@@ -79,12 +80,7 @@ public class EngineIT extends BaseDBTestCase {
}
}
}
DatabaseProperties prop = null;
try (CveDB cve = CveDB.getInstance()) {
prop = cve.getDatabaseProperties();
}
ReportGenerator rg = new ReportGenerator("DependencyCheck", instance.getDependencies(), instance.getAnalyzers(), prop);
rg.generateReports("./target/", "ALL");
instance.writeReports("dependency-check sample", new File("./target/"), "ALL");
instance.cleanup();
}
}

View File

@@ -25,15 +25,12 @@ import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.owasp.dependencycheck.BaseDBTestCase;
import org.owasp.dependencycheck.BaseTest;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.utils.InvalidSettingException;
@@ -47,67 +44,6 @@ import static org.junit.Assert.fail;
*/
public class ReportGeneratorIT extends BaseDBTestCase {
/**
* Test of generateReport method, of class ReportGenerator.
*
* @throws Exception is thrown when an exception occurs.
*/
@Test
public void testGenerateReport() throws Exception {
// String templateName = "HtmlReport";
// File f = new File("target/test-reports");
// if (!f.exists()) {
// f.mkdir();
// }
// String writeTo = "target/test-reports/Report.html";
// Map<String, Object> properties = new HashMap<String, Object>();
// Dependency d = new Dependency();
// d.setFileName("FileName.jar");
// d.setActualFilePath("lib/FileName.jar");
// d.addCPEentry("cpe://a:/some:cpe:1.0");
//
// List<Dependency> dependencies = new ArrayList<Dependency>();
// d.getProductEvidence().addEvidence("jar","filename","<test>test", Confidence.HIGH);
// d.getProductEvidence().addEvidence("manifest","vendor","<test>test", Confidence.HIGH);
//
// for (Evidence e : d.getProductEvidence().iterator(Confidence.HIGH)) {
// String t = e.getValue();
// }
// dependencies.add(d);
//
// Dependency d2 = new Dependency();
// d2.setFileName("Another.jar");
// d2.setActualFilePath("lib/Another.jar");
// d2.addCPEentry("cpe://a:/another:cpe:1.0");
// d2.addCPEentry("cpe://a:/another:cpe:1.1");
// d2.addCPEentry("cpe://a:/another:cpe:1.2");
// d2.getProductEvidence().addEvidence("jar","filename","another.jar", Confidence.HIGH);
// d2.getProductEvidence().addEvidence("manifest","vendor","Company A", Confidence.MEDIUM);
//
// for (Evidence e : d2.getProductEvidence().iterator(Confidence.HIGH)) {
// String t = e.getValue();
// }
//
// dependencies.add(d2);
//
// Dependency d3 = new Dependency();
// d3.setFileName("Third.jar");
// d3.setActualFilePath("lib/Third.jar");
// d3.getProductEvidence().addEvidence("jar","filename","third.jar", Confidence.HIGH);
//
// for (Evidence e : d3.getProductEvidence().iterator(Confidence.HIGH)) {
// String t = e.getValue();
// }
//
// dependencies.add(d3);
//
// properties.put("dependencies",dependencies);
//
// ReportGenerator instance = new ReportGenerator();
// instance.generateReport(templateName, writeTo, properties);
//assertTrue("need to add a real check here", false);
}
/**
* Generates an XML report containing known vulnerabilities and realistic
* data and validates the generated XML document against the XSD.
@@ -115,7 +51,7 @@ public class ReportGeneratorIT extends BaseDBTestCase {
* @throws Exception
*/
@Test
public void testGenerateXMLReport() {
public void testGenerateReport() {
try {
String templateName = "XmlReport";
@@ -123,7 +59,7 @@ public class ReportGeneratorIT extends BaseDBTestCase {
if (!f.exists()) {
f.mkdir();
}
String writeTo = "target/test-reports/Report.xml";
File writeTo = new File("target/test-reports/Report.xml");
File suppressionFile = BaseTest.getResourceAsFile(this, "incorrectSuppressions.xml");
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile.getAbsolutePath());
@@ -135,29 +71,20 @@ public class ReportGeneratorIT extends BaseDBTestCase {
//File jetty = new File(this.getClass().getClassLoader().getResource("org.mortbay.jetty.jar").getPath());
File jetty = BaseTest.getResourceAsFile(this, "org.mortbay.jetty.jar");
boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
Engine engine = new Engine();
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
engine.scan(struts);
engine.scan(axis);
engine.scan(jetty);
engine.analyzeDependencies();
CveDB cveDB = CveDB.getInstance();
DatabaseProperties dbProp = cveDB.getDatabaseProperties();
ReportGenerator generator = new ReportGenerator("Test Report", "org.owasp", "dependency-check-core", "1.4.7",
engine.getDependencies(), engine.getAnalyzers(), dbProp);
generator.generateReport(templateName, writeTo);
cveDB.close();
engine.writeReports("Test Report", "org.owasp", "dependency-check-core", "1.4.7", writeTo, "XML");
engine.cleanup();
InputStream xsdStream = ReportGenerator.class.getClassLoader().getResourceAsStream("schema/dependency-check.1.5.xsd");
StreamSource xsdSource = new StreamSource(xsdStream);
StreamSource xmlSource = new StreamSource(new File(writeTo));
StreamSource xmlSource = new StreamSource(writeTo);
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(xsdSource);
Validator validator = schema.newValidator();

View File

@@ -0,0 +1,55 @@
/*
* 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 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author Jeremy Long
*/
public class XmlEntityTest {
/**
* Test of fromNamedReference method, of class XmlEntity.
*/
@Test
public void testFromNamedReference() {
CharSequence s = null;
String expResult = null;
String result = XmlEntity.fromNamedReference(s);
assertEquals(expResult, result);
s = "somethingWrong";
expResult = null;
result = XmlEntity.fromNamedReference(s);
assertEquals(expResult, result);
s = "amp";
expResult = "&#38;";
result = XmlEntity.fromNamedReference(s);
assertEquals(expResult, result);
s = "acute";
expResult = "&#180;";
result = XmlEntity.fromNamedReference(s);
assertEquals(expResult, result);
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.xml;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author jerem
*/
public class XmlInputStreamTest {
/**
* Test of length method, of class XmlInputStream.
*/
@Test
public void testLength() {
String data = "";
InputStream stream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
XmlInputStream instance = new XmlInputStream(stream);
int expResult = 0;
int result = instance.length();
assertEquals(expResult, result);
data = "Input data";
stream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
instance = new XmlInputStream(stream);
result = instance.length();
assertTrue(result > 0);
}
/**
* Test of read method, of class XmlInputStream.
*/
@Test
public void testRead_0args() throws Exception {
String data = "";
InputStream stream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
XmlInputStream instance = new XmlInputStream(stream);
int expResult = -1;
int result = instance.read();
assertEquals(expResult, result);
data = "*";
stream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
instance = new XmlInputStream(stream);
expResult = 42;
result = instance.read();
assertEquals(expResult, result);
}
/**
* Test of read method, of class XmlInputStream.
*/
@Test
public void testRead_3args() throws Exception {
byte[] data = new byte[10];
int offset = 0;
int length = 10;
byte[] expected = "abcdefghij".getBytes(StandardCharsets.UTF_8);
String text = "abcdefghijklmnopqrstuvwxyz";
InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
XmlInputStream instance = new XmlInputStream(stream);
int expResult = 10;
int result = instance.read(data, offset, length);
assertEquals(expResult, result);
assertArrayEquals(expected, data);
data = new byte[5];
offset = 0;
length = 5;
expected = "&#38;".getBytes(StandardCharsets.UTF_8);
text = "&amp;";
stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
instance = new XmlInputStream(stream);
expResult = 5;
result = instance.read(data, offset, length);
assertEquals(expResult, result);
assertArrayEquals(expected, data);
data = new byte[10];
offset = 0;
length = 10;
expected = "&#38; test".getBytes(StandardCharsets.UTF_8);
text = "& test";
stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
instance = new XmlInputStream(stream);
expResult = 10;
result = instance.read(data, offset, length);
assertEquals(expResult, result);
assertArrayEquals(expected, data);
}
/**
* Test of toString method, of class XmlInputStream.
*/
@Test
public void testToString() throws IOException {
String data = "test";
InputStream stream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
XmlInputStream instance = new XmlInputStream(stream);
int r = instance.read();
assertEquals('t', r);
String expResult = "[1]-\"t\" ( 74)";
String result = instance.toString();
assertEquals(expResult, result);
r = instance.read();
assertEquals('e', r);
expResult = "[2]-\"te\" ( 74 65)";
result = instance.toString();
assertEquals(expResult, result);
}
}

View File

@@ -49,7 +49,7 @@ public class PomUtilsTest extends BaseTest {
assertEquals(expResult, result.getOrganizationUrl());
file = BaseTest.getResourceAsFile(this, "jmockit-1.26.pom");
expResult = "Main";
expResult = "Main ø modified to test issue #710";
result = PomUtils.readPom(file);
assertEquals(expResult, result.getName());
}

View File

@@ -7,7 +7,7 @@
<groupId>org.jmockit</groupId><artifactId>jmockit</artifactId><version>1.26</version>
<packaging>jar</packaging>
<name>Main</name>
<name>Main &oslash; modified to test issue #710</name>
<description>
JMockit is a Java toolkit for automated developer testing.
It contains mocking and faking APIs and a code coverage tool, supporting both JUnit and TestNG.

View File

@@ -20,7 +20,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-maven</artifactId>
<packaging>maven-plugin</packaging>

View File

@@ -0,0 +1,19 @@
#
# This file is part of dependency-check-maven.
#
# 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) 2014 Jeremy Long. All Rights Reserved.
#
invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -Dformat=ALL

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is part of dependency-check-maven.
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 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>
<groupId>org.owasp.test</groupId>
<artifactId>pom-parse-error</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>qdox</groupId>
<artifactId>qdox</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,17 @@
/*
* This file is part of dependency-check-maven.
*
* 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 Jeremy Long. All Rights Reserved.
*/

View File

@@ -0,0 +1,17 @@
/*
* This file is part of dependency-check-maven.
*
* 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) 2014 Jeremy Long. All Rights Reserved.
*/

View File

@@ -123,7 +123,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
outputDir = new File(this.getProject().getBuild().getDirectory());
}
try {
writeReports(engine, this.getProject(), outputDir);
final MavenProject p = this.getProject();
engine.writeReports(p.getName(), p.getGroupId(), p.getArtifactId(), p.getVersion(), outputDir, getFormat());
} catch (ReportException ex) {
if (exCol == null) {
exCol = new ExceptionCollection("Error writing aggregate report", ex);

View File

@@ -1069,35 +1069,6 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
return format;
}
/**
* Generates the reports for a given dependency-check engine.
*
* @param engine a dependency-check engine
* @param p the Maven project
* @param outputDir the directory path to write the report(s)
* @throws ReportException thrown if there is an error writing the report
*/
protected void writeReports(Engine engine, MavenProject p, File outputDir) throws ReportException {
DatabaseProperties prop = null;
try (CveDB cve = CveDB.getInstance()) {
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
//TODO shouldn't this throw an exception?
if (getLog().isDebugEnabled()) {
getLog().debug("Unable to retrieve DB Properties", ex);
}
}
final ReportGenerator r = new ReportGenerator(p.getName(), p.getGroupId(), p.getArtifactId(), p.getVersion(),
engine.getDependencies(), engine.getAnalyzers(), prop);
try {
r.generateReports(outputDir.getAbsolutePath(), format);
} catch (ReportException ex) {
final String msg = String.format("Error generating the report for %s", p.getName());
throw new ReportException(msg, ex);
}
}
//<editor-fold defaultstate="collapsed" desc="Methods to fail build or show summary">
/**
* Checks to see if a vulnerability has been identified with a CVSS score

View File

@@ -25,6 +25,7 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.exception.ExceptionCollection;
@@ -99,19 +100,19 @@ public class CheckMojo extends BaseDependencyCheckMojo {
ExceptionCollection exCol = scanArtifacts(getProject(), engine);
if (engine.getDependencies().isEmpty()) {
getLog().info("No dependencies were identified that could be analyzed by dependency-check");
} else {
try {
engine.analyzeDependencies();
} catch (ExceptionCollection ex) {
if (this.isFailOnError() && ex.isFatal()) {
throw new MojoExecutionException("One or more exceptions occurred during analysis", ex);
}
exCol = ex;
}
try {
engine.analyzeDependencies();
} catch (ExceptionCollection ex) {
if (this.isFailOnError() && ex.isFatal()) {
throw new MojoExecutionException("One or more exceptions occurred during analysis", ex);
}
exCol = ex;
}
if (exCol == null || !exCol.isFatal()) {
try {
writeReports(engine, getProject(), getCorrectOutputDirectory());
final MavenProject p = this.getProject();
engine.writeReports(p.getName(), p.getGroupId(), p.getArtifactId(), p.getVersion(), getCorrectOutputDirectory(), getFormat());
} catch (ReportException ex) {
if (this.isFailOnError()) {
if (exCol != null) {

View File

@@ -21,7 +21,7 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-plugin</artifactId>

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.4.6-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-utils</artifactId>

31
pom.xml
View File

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

View File

@@ -12,4 +12,6 @@
<suppress checks=".*" files=".*[\\/]maven-plugin-plugin-sources[\\/].*.properties" />
<!--suppress checks=".*" files=".*[\\/]org[\\/]owasp[\\/]dependencycheck[\\/]org[\\/]apache[\\/].*.java" /-->
<suppress checks="RegexpHeader" files=".*[\\/]org[\\/]owasp[\\/]dependencycheck[\\/]utils[\\/]SSLSocketFactoryEx.java" />
<suppress checks="RegexpHeader" files=".*[\\/]org[\\/]owasp[\\/]dependencycheck[\\/]xml[\\/]XmlInputStream.java" />
<suppress checks="RegexpHeader" files=".*[\\/]org[\\/]owasp[\\/]dependencycheck[\\/]xml[\\/]XmlEntity.java" />
</suppressions>

View File

@@ -20,8 +20,8 @@ To setup a centralized database the following generalized steps can be used:
<ol><li>Create the database and tables using either <a href="https://github.com/jeremylong/DependencyCheck/blob/master/dependency-check-core/src/main/resources/data/initialize.sql">initialize.sql</a>
or one of the other initialization scripts <a href="https://github.com/jeremylong/DependencyCheck/tree/master/dependency-check-core/src/main/resources/data">found here</a>.</li>
<li>The account that the clients will connect using must have select granted on the tables.
<ul><li>Note, if the clients performing the scans should run with the noupdate setting. A single
instance of the dependency-check client should be setup with update enabled and the account
<ul><li>Note, the clients performing the scans should run with the noupdate setting. A single
instance of the dependency-check client should be setup with updates enabled and the account
used during the update process will need to be granted update rights on the tables.
</li></ul>
</li><li>Dependency-check clients running scans will need to be configured to use the central database: