diff --git a/dependency-check-ant/pom.xml b/dependency-check-ant/pom.xml index e409815de..413f13c74 100644 --- a/dependency-check-ant/pom.xml +++ b/dependency-check-ant/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 dependency-check-ant @@ -456,12 +456,12 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.apache.ant ant - 1.9.3 + 1.9.4 org.apache.ant ant-testutil - 1.9.3 + 1.9.4 test diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTask.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTask.java index 47e83bc10..f03c6c443 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTask.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTask.java @@ -559,6 +559,28 @@ public class DependencyCheckTask extends Task { public void setNuspecAnalyzerEnabled(boolean nuspecAnalyzerEnabled) { this.nuspecAnalyzerEnabled = nuspecAnalyzerEnabled; } + /** + * Whether or not the central analyzer is enabled. + */ + private boolean centralAnalyzerEnabled = false; + + /** + * Get the value of centralAnalyzerEnabled + * + * @return the value of centralAnalyzerEnabled + */ + public boolean isCentralAnalyzerEnabled() { + return centralAnalyzerEnabled; + } + + /** + * Set the value of centralAnalyzerEnabled + * + * @param centralAnalyzerEnabled new value of centralAnalyzerEnabled + */ + public void setCentralAnalyzerEnabled(boolean centralAnalyzerEnabled) { + this.centralAnalyzerEnabled = centralAnalyzerEnabled; + } /** * Whether or not the nexus analyzer is enabled. @@ -1015,6 +1037,8 @@ public class DependencyCheckTask extends Task { Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); //NUSPEC ANALYZER Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); + //CENTRAL ANALYZER + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); //NEXUS ANALYZER Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); if (nexusUrl != null && !nexusUrl.isEmpty()) { diff --git a/dependency-check-ant/src/site/markdown/configuration.md b/dependency-check-ant/src/site/markdown/configuration.md index c0065a7e8..b283590ce 100644 --- a/dependency-check-ant/src/site/markdown/configuration.md +++ b/dependency-check-ant/src/site/markdown/configuration.md @@ -50,8 +50,9 @@ Property | Description ------------------------|---------------------------------------------------------------------------|------------------ archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   -jarAnalyzer | Sets whether Jar Analyzer will be used. | true -nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true +jarAnalyzer | Sets whether the Jar Analyzer will be used. | true +centralAnalyzerEnabled | Sets whether the Central Analyzer will be used. If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer (see below). | true +nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. This analyzer is superceded by the Central Analyzer; however, you can configure this to run against a Nexus Pro installation. | true nexusUrl | Defines the Nexus Pro URL. If not set the Nexus Analyzer will be disabled. |   nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true diff --git a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java index f13d93df2..d78cae5f6 100644 --- a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java +++ b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java @@ -30,6 +30,9 @@ import org.owasp.dependencycheck.utils.Settings; * @author Jeremy Long */ public class DependencyCheckTaskTest extends BuildFileTest { + //TODO: The use of deprecated class BuildFileTestcan possibly + //be replaced with BuildFileRule. However, it doesn't seem to be included + //in the ant-testutil jar. @Before @Override diff --git a/dependency-check-cli/pom.xml b/dependency-check-cli/pom.xml index 1df3c5429..e3d0f98fe 100644 --- a/dependency-check-cli/pom.xml +++ b/dependency-check-cli/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 dependency-check-cli diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index bf258397d..6ba1da344 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -233,6 +233,7 @@ public class App { final boolean archiveDisabled = cli.isArchiveDisabled(); final boolean assemblyDisabled = cli.isAssemblyDisabled(); final boolean nuspecDisabled = cli.isNuspecDisabled(); + final boolean centralDisabled = cli.isCentralDisabled(); final boolean nexusDisabled = cli.isNexusDisabled(); final String nexusUrl = cli.getNexusUrl(); final String databaseDriverName = cli.getDatabaseDriverName(); @@ -298,6 +299,7 @@ public class App { Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !centralDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !nexusDisabled); if (nexusUrl != null && !nexusUrl.isEmpty()) { Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 9bec420a4..c2cb5e938 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -323,6 +323,10 @@ public final class CliParser { .withDescription("Disable the .NET Assembly Analyzer.") .create(); + final Option disableCentralAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_CENTRAL) + .withDescription("Disable the Central Analyzer. If this analyzer is disabled it is likely you also want to disable the Nexus Analyzer.") + .create(); + final Option disableNexusAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_NEXUS) .withDescription("Disable the Nexus Analyzer.") .create(); @@ -360,6 +364,7 @@ public final class CliParser { .addOption(disableArchiveAnalyzer) .addOption(disableAssemblyAnalyzer) .addOption(disableNuspecAnalyzer) + .addOption(disableCentralAnalyzer) .addOption(disableNexusAnalyzer) .addOption(nexusUrl) .addOption(nexusUsesProxy) @@ -456,6 +461,15 @@ public final class CliParser { return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS); } + /** + * Returns true if the disableCentral command line argument was specified. + * + * @return true if the disableCentral command line argument was specified; otherwise false + */ + public boolean isCentralDisabled() { + return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL); + } + /** * Returns the url to the nexus server if one was specified. * @@ -876,6 +890,10 @@ public final class CliParser { * Disables the Nuspec Analyzer. */ public static final String DISABLE_NUSPEC = "disableNuspec"; + /** + * Disables the Central Analyzer. + */ + public static final String DISABLE_CENTRAL = "disableCentral"; /** * Disables the Nexus Analyzer. */ diff --git a/dependency-check-cli/src/site/markdown/arguments.md b/dependency-check-cli/src/site/markdown/arguments.md index c4a3f4b7d..bc92b9267 100644 --- a/dependency-check-cli/src/site/markdown/arguments.md +++ b/dependency-check-cli/src/site/markdown/arguments.md @@ -23,8 +23,9 @@ Short | Argument Name        | Paramete -------|-----------------------|-----------------|-----------------------------------------------------------------------------|--------------- | \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | false | \-\-zipExtensions | \ | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   - | \-\-disableJar | | Sets whether Jar Analyzer will be used. | false - | \-\-disableNexus | | Sets whether Nexus Analyzer will be used. | false + | \-\-disableJar | | Sets whether the Jar Analyzer will be used. | false + | \-\-disableCentral | | Sets whether the Central Analyzer will be used. If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer. | false + | \-\-disableNexus | | Sets whether the Nexus Analyzer will be used. Note, this has been superceded by the Central Analyzer. However, you can configure the Nexus URL to utilize an internally hosted Nexus Pro server. | false | \-\-nexus | \ | The url to the Nexus Pro Server. If not set the Nexus Analyzer will be disabled. |   | \-\-nexusUsesProxy | \ | Whether or not the defined proxy should be used when connecting to Nexus. | true | \-\-disableNuspec | | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | false diff --git a/dependency-check-core/pom.xml b/dependency-check-core/pom.xml index 8ce92f6c7..058a3b5b0 100644 --- a/dependency-check-core/pom.xml +++ b/dependency-check-core/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 dependency-check-core @@ -416,30 +416,25 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.apache.lucene lucene-test-framework - 4.3.1 + ${apache.lucene.version} test org.jmockit jmockit - 1.12 + 1.14 test com.google.code.findbugs annotations - 2.0.1 + 3.0.0 true - - commons-cli - commons-cli - 1.2 - org.apache.commons commons-compress - 1.8.1 + 1.9 commons-io @@ -449,22 +444,22 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. commons-lang commons-lang - 2.5 + 2.6 org.apache.lucene lucene-core - 4.5.1 + ${apache.lucene.version} org.apache.lucene lucene-analyzers-common - 4.5.1 + ${apache.lucene.version} org.apache.lucene lucene-queryparser - 4.5.1 + ${apache.lucene.version} org.apache.velocity @@ -474,7 +469,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. com.h2database h2 - 1.3.172 + 1.3.176 org.jsoup @@ -732,4 +727,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. + + 4.10.3 + diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java index 5c417bf38..8f645b0c9 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java @@ -153,6 +153,11 @@ public class Engine { return dependencies; } + /** + * Sets the dependencies. + * + * @param dependencies the dependencies + */ public void setDependencies(List dependencies) { this.dependencies = dependencies; } @@ -323,7 +328,7 @@ public class Engine { } } else { final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.", file.toString()); - LOGGER.log(Level.FINEST, msg); + LOGGER.log(Level.FINE, msg); } return dependency; } @@ -513,6 +518,15 @@ public class Engine { return scan; } + /** + * Returns the set of file type analyzers. + * + * @return the set of file type analyzers + */ + public Set getFileTypeAnalyzers() { + return this.fileTypeAnalyzers; + } + /** * Checks the CPE Index to ensure documents exists. If none exist a NoDataException is thrown. * diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java index 935a820b7..21c8aabd7 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java @@ -41,16 +41,7 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen * enabled. */ public AbstractFileTypeAnalyzer() { - final String key = getAnalyzerEnabledSettingKey(); - try { - enabled = Settings.getBoolean(key, true); - } catch (InvalidSettingException ex) { - String msg = String.format("Invalid setting for property '%s'", key); - LOGGER.log(Level.WARNING, msg); - LOGGER.log(Level.FINE, "", ex); - msg = String.format("%s has been disabled", getName()); - LOGGER.log(Level.WARNING, msg); - } + reset(); } // @@ -164,6 +155,23 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen } } + /** + * Resets the enabled flag on the analyzer. + */ + @Override + public final void reset() { + final String key = getAnalyzerEnabledSettingKey(); + try { + enabled = Settings.getBoolean(key, true); + } catch (InvalidSettingException ex) { + String msg = String.format("Invalid setting for property '%s'", key); + LOGGER.log(Level.WARNING, msg); + LOGGER.log(Level.FINE, "", ex); + msg = String.format("%s has been disabled", getName()); + LOGGER.log(Level.WARNING, msg); + } + } + /** * Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, * scanned, and added to the list of dependencies within the engine. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileTypeAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileTypeAnalyzer.java index d22aad210..55f3c2f7b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileTypeAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileTypeAnalyzer.java @@ -31,4 +31,9 @@ public interface FileTypeAnalyzer extends Analyzer { * @return whether or not the specified file extension is supported by this analyzer. */ boolean supportsExtension(String extension); + + /** + * Resets the analyzers state. + */ + void reset(); } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java index 857abb6cc..c330cc3e1 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java @@ -62,7 +62,7 @@ public final class CpeMemoryIndex { /** * singleton instance. */ - private static CpeMemoryIndex instance = new CpeMemoryIndex(); + private static final CpeMemoryIndex INSTANCE = new CpeMemoryIndex(); /** * private constructor for singleton. @@ -76,7 +76,7 @@ public final class CpeMemoryIndex { * @return the instance of the CpeMemoryIndex */ public static CpeMemoryIndex getInstance() { - return instance; + return INSTANCE; } /** * The in memory Lucene index. @@ -114,18 +114,20 @@ public final class CpeMemoryIndex { * @throws IndexException thrown if there is an error creating the index */ public void open(CveDB cve) throws IndexException { - if (!openState) { - index = new RAMDirectory(); - buildIndex(cve); - try { - indexReader = DirectoryReader.open(index); - } catch (IOException ex) { - throw new IndexException(ex); + synchronized (INSTANCE) { + if (!openState) { + index = new RAMDirectory(); + buildIndex(cve); + try { + indexReader = DirectoryReader.open(index); + } catch (IOException ex) { + throw new IndexException(ex); + } + indexSearcher = new IndexSearcher(indexReader); + searchingAnalyzer = createSearchingAnalyzer(); + queryParser = new QueryParser(LuceneUtils.CURRENT_VERSION, Fields.DOCUMENT_KEY, searchingAnalyzer); + openState = true; } - indexSearcher = new IndexSearcher(indexReader); - searchingAnalyzer = createSearchingAnalyzer(); - queryParser = new QueryParser(LuceneUtils.CURRENT_VERSION, Fields.DOCUMENT_KEY, searchingAnalyzer); - openState = true; } } /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java index 1a58f22a7..09435d7ab 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java @@ -38,17 +38,6 @@ public class AlphaNumericTokenizer extends CharTokenizer { super(matchVersion, in); } - /** - * Constructs a new AlphaNumericTokenizer. - * - * @param matchVersion the lucene version - * @param factory the AttributeFactory - * @param in the Reader - */ - public AlphaNumericTokenizer(Version matchVersion, AttributeFactory factory, Reader in) { - super(matchVersion, factory, in); - } - /** * Determines if the char passed in is part of a token. * diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java index 1b59283d8..b64544ebb 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java @@ -31,7 +31,7 @@ public final class LuceneUtils { * The current version of Lucene being used. Declaring this one place so an upgrade doesn't require hunting through * the code base. */ - public static final Version CURRENT_VERSION = Version.LUCENE_45; + public static final Version CURRENT_VERSION = Version.LATEST; /** * Private constructor as this is a utility class. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/task/DownloadTask.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/task/DownloadTask.java index 270baa95f..e15541312 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/task/DownloadTask.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/task/DownloadTask.java @@ -261,7 +261,7 @@ public class DownloadTask implements Callable> { */ private void extractGzip(File file) throws FileNotFoundException, IOException { final String originalPath = file.getPath(); - File gzip = new File(originalPath + ".gz"); + final File gzip = new File(originalPath + ".gz"); if (gzip.isFile() && !gzip.delete()) { gzip.deleteOnExit(); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java index c53277180..40934c674 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java @@ -134,6 +134,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { Dependency dependency = new Dependency(file); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); int initial_size = engine.getDependencies().size(); @@ -165,6 +166,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { Dependency dependency = new Dependency(file); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); int initial_size = engine.getDependencies().size(); @@ -193,6 +195,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { //Dependency dependency = new Dependency(file); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); int initial_size = engine.getDependencies().size(); @@ -220,6 +223,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { // File file = new File(this.getClass().getClassLoader().getResource("nested.zip").getPath()); // Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); // Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); +// Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); // Engine engine = new Engine(); // // engine.scan(file); @@ -242,6 +246,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { File file = new File(this.getClass().getClassLoader().getResource("file.tgz").getPath()); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); int initial_size = engine.getDependencies().size(); @@ -269,6 +274,7 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase { Dependency dependency = new Dependency(file); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); int initial_size = engine.getDependencies().size(); // boolean failed = false; diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java index f4a8cb63d..80608fdb4 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java @@ -75,6 +75,7 @@ public class HintAnalyzerTest extends BaseTest { //Dependency spring = new Dependency(files); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); engine.scan(guice); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIntegrationTest.java index 6ae7ace04..26bddba31 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIntegrationTest.java @@ -65,6 +65,7 @@ public class VulnerabilitySuppressionAnalyzerIntegrationTest extends AbstractDat File suppression = new File(this.getClass().getClassLoader().getResource("commons-fileupload-1.2.1.suppression.xml").getPath()); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); Engine engine = new Engine(); engine.scan(file); engine.analyzeDependencies(); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilterTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilterTest.java index cd13e0e15..ce7afb437 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilterTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilterTest.java @@ -20,16 +20,10 @@ package org.owasp.dependencycheck.data.lucene; import java.io.IOException; import java.io.Reader; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.Analyzer.TokenStreamComponents; import org.apache.lucene.analysis.BaseTokenStreamTestCase; -import static org.apache.lucene.analysis.BaseTokenStreamTestCase.checkOneTerm; import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.KeywordTokenizer; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; /** * @@ -50,24 +44,6 @@ public class UrlTokenizingFilterTest extends BaseTokenStreamTestCase { }; } - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() throws Exception { - super.setUp(); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); - } - /** * test some example domains */ @@ -102,6 +78,6 @@ public class UrlTokenizingFilterTest extends BaseTokenStreamTestCase { return new TokenStreamComponents(tokenizer, new UrlTokenizingFilter(tokenizer)); } }; - checkOneTermReuse(a, "", ""); + checkOneTerm(a, "", ""); } } diff --git a/dependency-check-jenkins/pom.xml b/dependency-check-jenkins/pom.xml index 306b2a7a7..0ec47fcfd 100644 --- a/dependency-check-jenkins/pom.xml +++ b/dependency-check-jenkins/pom.xml @@ -3,7 +3,7 @@ org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 org.owasp dependency-check-jenkins diff --git a/dependency-check-maven/pom.xml b/dependency-check-maven/pom.xml index 40de32aa6..5d77b89c9 100644 --- a/dependency-check-maven/pom.xml +++ b/dependency-check-maven/pom.xml @@ -22,7 +22,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 dependency-check-maven @@ -86,6 +86,23 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. + + org.apache.maven.plugins + maven-surefire-plugin + 2.16 + + + + data.directory + ${project.build.directory}/dependency-check-data + + + temp.directory + ${project.build.directory}/temp + + + + true org.apache.maven.plugins @@ -287,27 +304,27 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. org.apache.maven maven-plugin-api - 3.0 + 3.2.5 org.apache.maven maven-settings - 3.0 + 3.2.5 org.apache.maven maven-core - 3.0 + 3.2.5 org.apache.maven.plugins maven-site-plugin - 3.0 + 3.4 org.apache.maven.plugin-tools maven-plugin-annotations - 3.0 + 3.3 compile @@ -316,16 +333,15 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. 3.0 - junit - junit - 4.11 + org.jmockit + jmockit + 1.14 test - jar org.apache.maven.plugin-testing maven-plugin-testing-harness - 2.1 + 3.3.0 test diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java new file mode 100644 index 000000000..cff72167b --- /dev/null +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java @@ -0,0 +1,231 @@ +/* + * 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) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.maven; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.utils.Settings; + +/** + * Maven Plugin that checks project dependencies and the dependencies of all child modules to see if they have any known + * published vulnerabilities. + * + * @author Jeremy Long + */ +@Mojo( + name = "aggregate", + defaultPhase = LifecyclePhase.SITE, + aggregator = true, + threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresOnline = true +) +public class AggregateMojo extends BaseDependencyCheckMojo { + + /** + * Logger field reference. + */ + private static final Logger LOGGER = Logger.getLogger(AggregateMojo.class.getName()); + + /** + * Executes the aggregate dependency-check goal. This runs dependency-check and generates the subsequent reports. + * + * @throws MojoExecutionException thrown if there is ane exception running the mojo + * @throws MojoFailureException thrown if dependency-check is configured to fail the build + */ + @Override + public void runCheck() throws MojoExecutionException, MojoFailureException { + final Engine engine = generateDataFile(); + + if (getProject() == getReactorProjects().get(getReactorProjects().size() - 1)) { + final Map> children = buildAggregateInfo(); + boolean hasOrchestration = false; + for (MavenProject current : getReactorProjects()) { + final List dependencies = readDataFile(current); + final List childProjects = getAllChildren(current, children); + //check for orchestration build - execution root with no children or dependencies + if ((dependencies == null || dependencies.isEmpty()) && childProjects.isEmpty() && current.isExecutionRoot()) { + hasOrchestration = true; + } + } + + for (MavenProject current : getReactorProjects()) { + List dependencies = readDataFile(current); + final List childProjects = getAllChildren(current, children); + //check for orchestration build - execution root with no children or dependencies + if ((dependencies == null || dependencies.isEmpty()) && childProjects.isEmpty() && current.isExecutionRoot()) { + engine.resetFileTypeAnalyzers(); + for (MavenProject mod : getReactorProjects()) { + scanArtifacts(mod, engine); + } + engine.analyzeDependencies(); + } else { + if (dependencies == null) { + dependencies = new ArrayList(); + } + for (MavenProject reportOn : childProjects) { + final List childDeps = readDataFile(reportOn); + if (childDeps != null && !childDeps.isEmpty()) { + dependencies.addAll(childDeps); + } + } + engine.getDependencies().clear(); + engine.getDependencies().addAll(dependencies); + final DependencyBundlingAnalyzer bundler = new DependencyBundlingAnalyzer(); + try { + bundler.analyze(null, engine); + } catch (AnalysisException ex) { + LOGGER.log(Level.WARNING, "An error occured grouping the dependencies; duplicate entries may exist in the report", ex); + LOGGER.log(Level.FINE, "Bundling Exception", ex); + } + } + try { + final File outputDir = getCorrectOutputDirectory(current); + writeReports(engine, current, outputDir); + } catch (MojoExecutionException ex) { + if (!hasOrchestration) { + throw ex; + } // else ignore this + } + } + } + engine.cleanup(); + Settings.cleanup(); + } + + /** + * Returns a list containing all the recursive, non-pom children of the given project, never null. + * + * @param project the parent project to collect the child project references + * @param childMap a map of the parent-child relationships + * @return a list of child projects + */ + protected List getAllChildren(MavenProject project, Map> childMap) { + final Set children = childMap.get(project); + if (children == null) { + return Collections.emptyList(); + } + final List result = new ArrayList(); + for (MavenProject child : children) { + if (isMultiModule(child)) { + result.addAll(getAllChildren(child, childMap)); + } else { + result.add(child); + } + } + return result; + } + + /** + * Test if the project has pom packaging + * + * @param mavenProject Project to test + * @return true if it has a pom packaging; otherwise false + */ + protected boolean isMultiModule(MavenProject mavenProject) { + return "pom".equals(mavenProject.getPackaging()); + } + + /** + * Builds the parent-child map. + * + * @return a map of the parent/child relationships + */ + private Map> buildAggregateInfo() { + final Map> parentChildMap = new HashMap>(); + for (MavenProject proj : getReactorProjects()) { + Set depList = parentChildMap.get(proj.getParent()); + if (depList == null) { + depList = new HashSet(); + parentChildMap.put(proj.getParent(), depList); + } + depList.add(proj); + } + return parentChildMap; + } + + /** + * Runs dependency-check's Engine and writes the serialized dependencies to disk. + * + * @return the Engine used to execute dependency-check + * @throws MojoExecutionException thrown if there is an exception running the mojo + * @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are + * identified. + */ + protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException { + final Engine engine; + try { + engine = initializeEngine(); + } catch (DatabaseException ex) { + LOGGER.log(Level.FINE, "Database connection error", ex); + throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex); + } + scanArtifacts(getProject(), engine); + engine.analyzeDependencies(); + writeDataFile(engine.getDependencies()); + showSummary(engine.getDependencies()); + checkForFailure(engine.getDependencies()); + return engine; + } + + @Override + public boolean canGenerateReport() { + return true; //aggregate always returns true for now - we can look at a more complicated/acurate solution later + } + + /** + * Returns the report name. + * + * @param locale the location + * @return the report name + */ + public String getName(Locale locale) { + return "dependency-check:aggregate"; + } + + /** + * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * + * @param locale The Locale to get the description for + * @return the description + */ + public String getDescription(Locale locale) { + return "Generates an aggregate report of all child Maven projects providing details on any " + + "published vulnerabilities within project dependencies. This report is a best " + + "effort and may contain false positives and false negatives."; + } +} diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java similarity index 65% rename from dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java rename to dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java index 50c3ba2bb..c75ed37fb 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Copyright (c) 2013 Jeremy Long. All Rights Reserved. + * Copyright (c) 2014 Jeremy Long. All Rights Reserved. */ package org.owasp.dependencycheck.maven; @@ -29,50 +29,42 @@ import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.List; import java.util.Locale; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.maven.artifact.Artifact; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.MavenReport; import org.apache.maven.reporting.MavenReportException; import org.apache.maven.settings.Proxy; -import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer; -import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.nexus.MavenArtifact; +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.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.utils.LogUtils; import org.owasp.dependencycheck.utils.Settings; /** - * Maven Plugin that checks project dependencies to see if they have any known published vulnerabilities. * * @author Jeremy Long */ -@Mojo( - name = "check", - defaultPhase = LifecyclePhase.COMPILE, - threadSafe = true, - requiresDependencyResolution = ResolutionScope.RUNTIME_PLUS_SYSTEM, - requiresOnline = true -) -public class DependencyCheckMojo extends ReportAggregationMojo { +public abstract class BaseDependencyCheckMojo extends AbstractMojo implements MavenReport { // /** * Logger field reference. */ - private static final Logger LOGGER = Logger.getLogger(DependencyCheckMojo.class.getName()); + private static final Logger LOGGER = Logger.getLogger(BaseDependencyCheckMojo.class.getName()); /** * The properties file location. */ @@ -86,12 +78,23 @@ public class DependencyCheckMojo extends ReportAggregationMojo { */ private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern(); /** - * The dependency-check engine used to scan the project. + * Sets whether or not the external report format should be used. */ - private Engine engine = null; - // + @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true) + private String dataFileName; + // // + /** + * The Maven Project Object. + */ + @Component + private MavenProject project; + /** + * List of Maven project of the current build + */ + @Parameter(readonly = true, required = true, property = "reactorProjects") + private List reactorProjects; /** * The path to the verbose log. */ @@ -117,6 +120,14 @@ public class DependencyCheckMojo extends ReportAggregationMojo { @SuppressWarnings("CanBeFinal") @Parameter(property = "autoupdate", defaultValue = "true", required = true) private boolean autoUpdate = true; + /** + * Generate aggregate reports in multi-module projects. + * + * @deprecated use the aggregate goal instead + */ + @Parameter(property = "aggregate", defaultValue = "false") + @Deprecated + private boolean aggregate; /** * The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this * within the Site plug-in unless the externalReport is set to true. Default is HTML. @@ -184,14 +195,22 @@ public class DependencyCheckMojo extends ReportAggregationMojo { @Parameter(property = "nuspecAnalyzerEnabled", defaultValue = "true", required = false) private boolean nuspecAnalyzerEnabled = true; + /** + * Whether or not the Central Analyzer is enabled. + */ + @SuppressWarnings("CanBeFinal") + @Parameter(property = "centralAnalyzerEnabled", defaultValue = "true", required = false) + private boolean centralAnalyzerEnabled = true; + /** * Whether or not the Nexus Analyzer is enabled. */ @SuppressWarnings("CanBeFinal") @Parameter(property = "nexusAnalyzerEnabled", defaultValue = "true", required = false) private boolean nexusAnalyzerEnabled = true; + /** - * Whether or not the Nexus Analyzer is enabled. + * The URL of a Nexus Pro server. */ @Parameter(property = "nexusUrl", defaultValue = "", required = false) private String nexusUrl; @@ -299,38 +318,116 @@ public class DependencyCheckMojo extends ReportAggregationMojo { @Deprecated private String externalReport = null; - // /** - * Executes the Dependency-Check on the dependent libraries. - * - * @return the Engine used to scan the dependencies. - * @throws DatabaseException thrown if there is an exception connecting to the database + * Specifies the destination directory for the generated Dependency-Check report. This generally maps to + * "target/site". */ - private Engine executeDependencyCheck() throws DatabaseException { - return executeDependencyCheck(getProject()); + @Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true) + private File reportOutputDirectory; + // + // + + /** + * Executes dependency-check. + * + * @throws MojoExecutionException thrown if there is an exception executing the mojo + * @throws MojoFailureException thrown if dependency-check failed the build + */ + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + validateAggregate(); + project.setContextValue(getOutputDirectoryContextKey(), this.outputDirectory); + runCheck(); } /** - * Executes the Dependency-Check on the dependent libraries. + * Checks if the aggregate configuration parameter has been set to true. If it has a MojoExecutionException is + * thrown because the aggregate configuration parameter is no longer supported. * - * @param project the project to run dependency-check on - * @return the Engine used to scan the dependencies. - * @throws DatabaseException thrown if there is an exception connecting to the database + * @throws MojoExecutionException thrown if aggregate is set to true */ - private Engine executeDependencyCheck(MavenProject project) throws DatabaseException { - final Engine localEngine; - if (engine == null) { - localEngine = initializeEngine(project); - } else { - localEngine = engine; + private void validateAggregate() throws MojoExecutionException { + if (aggregate) { + final String msg = "Aggregate configuration detected - as of dependency-check 1.2.8 this no longer supported. " + + "Please use the aggregate goal instead."; + throw new MojoExecutionException(msg); } + } - final Set artifacts = project.getArtifacts(); - for (Artifact a : artifacts) { + /** + * Generates the Dependency-Check Site Report. + * + * @param sink the sink to write the report to + * @param locale the locale to use when generating the report + * @throws MavenReportException if a maven report exception occurs + * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale) instead. + */ + @Deprecated + public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException { + generate((Sink) sink, locale); + } + + /** + * Generates the Dependency-Check Site Report. + * + * @param sink the sink to write the report to + * @param locale the locale to use when generating the report + * @throws MavenReportException if a maven report exception occurs + */ + public void generate(Sink sink, Locale locale) throws MavenReportException { + try { + validateAggregate(); + } catch (MojoExecutionException ex) { + throw new MavenReportException(ex.getMessage()); + } + project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory()); + try { + runCheck(); + } catch (MojoExecutionException ex) { + throw new MavenReportException(ex.getMessage(), ex); + } catch (MojoFailureException ex) { + LOGGER.warning("Vulnerabilities were identifies that exceed the CVSS threshold for failing the build"); + } + } + + /** + * Returns the correct output directory depending on if a site is being executed or not. + * + * @return the directory to write the report(s) + * @throws MojoExecutionException thrown if there is an error loading the file path + */ + protected File getCorrectOutputDirectory() throws MojoExecutionException { + return getCorrectOutputDirectory(this.project); + } + + /** + * Returns the correct output directory depending on if a site is being executed or not. + * + * @param current the Maven project to get the output directory from + * @return the directory to write the report(s) + * @throws MojoExecutionException thrown if there is an error loading the file path + */ + protected File getCorrectOutputDirectory(MavenProject current) throws MojoExecutionException { + final Object obj = current.getContextValue(getOutputDirectoryContextKey()); + if (obj != null && obj instanceof File) { + return (File) obj; + } else { + throw new MojoExecutionException(String.format("Unable to determine output directory for '%s'", current.getName())); + } + } + + /** + * Scans the project's artifacts and adds them to the engine's dependency list. + * + * @param project the project to scan the dependencies of + * @param engine the engine to use to scan the dependencies + */ + protected void scanArtifacts(MavenProject project, Engine engine) { + for (Artifact a : project.getArtifacts()) { if (excludeFromScan(a)) { continue; } - final List deps = localEngine.scan(a.getFile().getAbsoluteFile()); + final List deps = engine.scan(a.getFile().getAbsoluteFile()); if (deps != null) { if (deps.size() == 1) { final Dependency d = deps.get(0); @@ -345,44 +442,96 @@ public class DependencyCheckMojo extends ReportAggregationMojo { } } } - localEngine.analyzeDependencies(); - - return localEngine; } + /** + * Executes the dependency-check scan and generates the necassary report. + * + * @throws MojoExecutionException thrown if there is an exception running the scan + * @throws MojoFailureException thrown if dependency-check is configured to fail the build + */ + public abstract void runCheck() throws MojoExecutionException, MojoFailureException; + + /** + * Sets the Reporting output directory. + * + * @param directory the output directory + */ + @Override + public void setReportOutputDirectory(File directory) { + reportOutputDirectory = directory; + } + + /** + * Returns the report output directory. + * + * @return the report output directory + */ + @Override + public File getReportOutputDirectory() { + return reportOutputDirectory; + } + + /** + * Returns the output directory. + * + * @return the output directory + */ + public File getOutputDirectory() { + return outputDirectory; + } + + /** + * Returns whether this is an external report. This method always returns true. + * + * @return true + */ + @Override + public final boolean isExternalReport() { + return true; + } + + /** + * Returns the output name. + * + * @return the output name + */ + public String getOutputName() { + if ("HTML".equalsIgnoreCase(this.format) || "ALL".equalsIgnoreCase(this.format)) { + return "dependency-check-report"; + } else if ("XML".equalsIgnoreCase(this.format)) { + return "dependency-check-report.xml#"; + } else if ("VULN".equalsIgnoreCase(this.format)) { + return "dependency-check-vulnerability"; + } else { + LOGGER.log(Level.WARNING, "Unknown report format used during site generation."); + return "dependency-check-report"; + } + } + + /** + * Returns the category name. + * + * @return the category name + */ + public String getCategoryName() { + return MavenReport.CATEGORY_PROJECT_REPORTS; + } + // + /** * Initializes a new Engine that can be used for scanning. * - * @param project the current MavenProject * @return a newly instantiated Engine * @throws DatabaseException thrown if there is a database exception */ - private Engine initializeEngine(MavenProject project) throws DatabaseException { + protected Engine initializeEngine() throws DatabaseException { + final InputStream in = BaseDependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE); + LogUtils.prepareLogger(in, logFile); populateSettings(); - final Engine localEngine = new Engine(project); - return localEngine; + return new Engine(this.project, this.reactorProjects); } - /** - * Tests is the artifact should be included in the scan (i.e. is the dependency in a scope that is being scanned). - * - * @param a the Artifact to test - * @return true if the artifact is in an excluded scope; otherwise false - */ - private boolean excludeFromScan(Artifact a) { - if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) { - return true; - } - if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) { - return true; - } - if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) { - return true; - } - return false; - } - - // /** * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system * properties required to change the proxy url, port, and connection timeout. @@ -443,6 +592,8 @@ public class DependencyCheckMojo extends ReportAggregationMojo { //NUSPEC ANALYZER Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); //NEXUS ANALYZER + Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); + //NEXUS ANALYZER Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); if (nexusUrl != null && !nexusUrl.isEmpty()) { Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); @@ -527,248 +678,90 @@ public class DependencyCheckMojo extends ReportAggregationMojo { return null; } - // /** - * Initialize the mojo. - */ - @Override - protected void initialize() { - final InputStream in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE); - LogUtils.prepareLogger(in, logFile); - } - - /** - * Executes the dependency-check and generates the report. + * Tests is the artifact should be included in the scan (i.e. is the dependency in a scope that is being scanned). * - * @throws MojoExecutionException if a maven exception occurs - * @throws MojoFailureException thrown if a CVSS score is found that is higher then the configured level + * @param a the Artifact to test + * @return true if the artifact is in an excluded scope; otherwise false */ - @Override - protected void performExecute() throws MojoExecutionException, MojoFailureException { - try { - engine = executeDependencyCheck(); - ReportingUtil.generateExternalReports(engine, outputDirectory, getProject().getName(), format); - if (this.showSummary) { - showSummary(engine.getDependencies()); - } - if (this.failBuildOnCVSS <= 10) { - checkForFailure(engine.getDependencies()); - } - } catch (DatabaseException ex) { - LOGGER.log(Level.SEVERE, "Unable to connect to the dependency-check database; analysis has stopped"); - LOGGER.log(Level.FINE, "", ex); - } - } - - @Override - protected void postExecute() throws MojoExecutionException, MojoFailureException { - try { - super.postExecute(); - } finally { - cleanupEngine(); - } - } - - @Override - protected void postGenerate() throws MavenReportException { - try { - super.postGenerate(); - } finally { - cleanupEngine(); - } - } - - /** - * Calls engine.cleanup() to release resources. - */ - private void cleanupEngine() { - if (engine != null) { - engine.cleanup(); - engine = null; - } - Settings.cleanup(true); - } - - /** - * Generates the Dependency-Check Site Report. - * - * @param locale the locale to use when generating the report - * @throws MavenReportException if a maven report exception occurs - */ - @Override - protected void executeNonAggregateReport(Locale locale) throws MavenReportException { - final List deps = readDataFile(); - if (deps != null) { - try { - if (engine != null) { - engine = initializeEngine(getProject()); - } - engine.getDependencies().addAll(deps); - } catch (DatabaseException ex) { - final String msg = String.format("An unrecoverable exception with the dependency-check initialization occured while scanning %s", - getProject().getName()); - throw new MavenReportException(msg, ex); - } - } else { - try { - engine = executeDependencyCheck(); - } catch (DatabaseException ex) { - final String msg = String.format("An unrecoverable exception with the dependency-check scan occured while scanning %s", - getProject().getName()); - throw new MavenReportException(msg, ex); - } - } - ReportingUtil.generateExternalReports(engine, getReportOutputDirectory(), getProject().getName(), format); - } - - @Override - protected void executeAggregateReport(MavenProject project, Locale locale) throws MavenReportException { - List deps = readDataFile(project); - if (deps != null) { - try { - if (engine != null) { - engine = initializeEngine(project); - } - engine.getDependencies().addAll(deps); - } catch (DatabaseException ex) { - final String msg = String.format("An unrecoverable exception with the dependency-check initialization occured while scanning %s", - project.getName()); - throw new MavenReportException(msg, ex); - } - } else { - try { - engine = executeDependencyCheck(project); - } catch (DatabaseException ex) { - final String msg = String.format("An unrecoverable exception with the dependency-check scan occured while scanning %s", - project.getName()); - throw new MavenReportException(msg, ex); - } - } - for (MavenProject child : getAllChildren(project)) { - deps = readDataFile(child); - if (deps == null) { - final String msg = String.format("Unable to include information on %s in the dependency-check aggregate report", - child.getName()); - LOGGER.severe(msg); - } else { - engine.getDependencies().addAll(deps); - } - } - final DependencyBundlingAnalyzer bundler = new DependencyBundlingAnalyzer(); - try { - bundler.analyze(null, engine); - } catch (AnalysisException ex) { - LOGGER.log(Level.WARNING, "An error occured grouping the dependencies; duplicate entries may exist in the report", ex); - LOGGER.log(Level.FINE, "Bundling Exception", ex); - } - final File outputDir = getReportOutputDirectory(project); - if (outputDir != null) { - ReportingUtil.generateExternalReports(engine, outputDir, project.getName(), format); - } - } - - // - /** - * Returns the output name. - * - * @return the output name - */ - public String getOutputName() { - if ("HTML".equalsIgnoreCase(this.format) || "ALL".equalsIgnoreCase(this.format)) { - return "dependency-check-report"; - } else if ("XML".equalsIgnoreCase(this.format)) { - return "dependency-check-report.xml#"; - } else if ("VULN".equalsIgnoreCase(this.format)) { - return "dependency-check-vulnerability"; - } else { - LOGGER.log(Level.WARNING, "Unknown report format used during site generation."); - return "dependency-check-report"; - } - } - - /** - * Returns the category name. - * - * @return the category name - */ - public String getCategoryName() { - return MavenReport.CATEGORY_PROJECT_REPORTS; - } - - /** - * Returns the report name. - * - * @param locale the location - * @return the report name - */ - public String getName(Locale locale) { - return "dependency-check"; - } - - /** - * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. - * - * @param locale The Locale to get the description for - * @return the description - */ - public String getDescription(Locale locale) { - return "A report providing details on any published " - + "vulnerabilities within project dependencies. This report is a best effort but may contain " - + "false positives and false negatives."; - } - - /** - * Returns whether or not a report can be generated. - * - * @return true if a report can be generated; otherwise false - */ - public boolean canGenerateReport() { - if (canGenerateAggregateReport() || (isAggregate() && isMultiModule())) { + protected boolean excludeFromScan(Artifact a) { + if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) { return true; } - if (canGenerateNonAggregateReport()) { + if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) { + return true; + } + if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) { return true; - } else { - final String msg; - if (getProject().getArtifacts().size() > 0) { - msg = "No project dependencies exist in the included scope - dependency-check:check is unable to generate a report."; - } else { - msg = "No project dependencies exist - dependency-check:check is unable to generate a report."; - } - LOGGER.warning(msg); } - return false; } /** - * Returns whether or not a non-aggregate report can be generated. + * Returns a reference to the current project. This method is used instead of auto-binding the project via component + * annotation in concrete implementations of this. If the child has a @Component MavenProject project; + * defined then the abstract class (i.e. this class) will not have access to the current project (just the way Maven + * works with the binding). * - * @return true if a non-aggregate report can be generated; otherwise false + * @return returns a reference to the current project */ - @Override - protected boolean canGenerateNonAggregateReport() { - boolean ability = false; - for (Artifact a : getProject().getArtifacts()) { - if (!excludeFromScan(a)) { - ability = true; - break; - } - } - return ability; + protected MavenProject getProject() { + return project; } /** - * Returns whether or not an aggregate report can be generated. + * Returns the list of Maven Projects in this build. * - * @return true if an aggregate report can be generated; otherwise false + * @return the list of Maven Projects in this build */ - @Override - protected boolean canGenerateAggregateReport() { - return isAggregate() && isLastProject(); + protected List getReactorProjects() { + return reactorProjects; + } + + /** + * Returns the report format. + * + * @return the report format + */ + protected String getFormat() { + 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). + */ + protected void writeReports(Engine engine, MavenProject p, File outputDir) { + DatabaseProperties prop = null; + CveDB cve = null; + try { + cve = new CveDB(); + cve.open(); + prop = cve.getDatabaseProperties(); + } catch (DatabaseException ex) { + LOGGER.log(Level.FINE, "Unable to retrieve DB Properties", ex); + } finally { + if (cve != null) { + cve.close(); + } + } + final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop); + try { + r.generateReports(outputDir.getAbsolutePath(), format); + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, + "Unexpected exception occurred during analysis; please see the verbose error log for more details."); + LOGGER.log(Level.FINE, null, ex); + } catch (Throwable ex) { + LOGGER.log(Level.SEVERE, + "Unexpected exception occurred during analysis; please see the verbose error log for more details."); + LOGGER.log(Level.FINE, null, ex); + } } - // // /** * Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the @@ -777,27 +770,29 @@ public class DependencyCheckMojo extends ReportAggregationMojo { * @param dependencies the list of dependency objects * @throws MojoFailureException thrown if a CVSS score is found that is higher then the threshold set */ - private void checkForFailure(List dependencies) throws MojoFailureException { - final StringBuilder ids = new StringBuilder(); - for (Dependency d : dependencies) { - boolean addName = true; - for (Vulnerability v : d.getVulnerabilities()) { - if (v.getCvssScore() >= failBuildOnCVSS) { - if (addName) { - addName = false; - ids.append(NEW_LINE).append(d.getFileName()).append(": "); - ids.append(v.getName()); - } else { - ids.append(", ").append(v.getName()); + protected void checkForFailure(List dependencies) throws MojoFailureException { + if (failBuildOnCVSS <= 10) { + final StringBuilder ids = new StringBuilder(); + for (Dependency d : dependencies) { + boolean addName = true; + for (Vulnerability v : d.getVulnerabilities()) { + if (v.getCvssScore() >= failBuildOnCVSS) { + if (addName) { + addName = false; + ids.append(NEW_LINE).append(d.getFileName()).append(": "); + ids.append(v.getName()); + } else { + ids.append(", ").append(v.getName()); + } } } } - } - if (ids.length() > 0) { - final String msg = String.format("%n%nDependency-Check Failure:%n" - + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n" - + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString()); - throw new MojoFailureException(msg); + if (ids.length() > 0) { + final String msg = String.format("%n%nDependency-Check Failure:%n" + + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n" + + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString()); + throw new MojoFailureException(msg); + } } } @@ -806,52 +801,73 @@ public class DependencyCheckMojo extends ReportAggregationMojo { * * @param dependencies a list of dependency objects */ - private void showSummary(List dependencies) { - final StringBuilder summary = new StringBuilder(); - for (Dependency d : dependencies) { - boolean firstEntry = true; - final StringBuilder ids = new StringBuilder(); - for (Vulnerability v : d.getVulnerabilities()) { - if (firstEntry) { - firstEntry = false; - } else { - ids.append(", "); - } - ids.append(v.getName()); - } - if (ids.length() > 0) { - summary.append(d.getFileName()).append(" ("); - firstEntry = true; - for (Identifier id : d.getIdentifiers()) { + protected void showSummary(List dependencies) { + if (showSummary) { + final StringBuilder summary = new StringBuilder(); + for (Dependency d : dependencies) { + boolean firstEntry = true; + final StringBuilder ids = new StringBuilder(); + for (Vulnerability v : d.getVulnerabilities()) { if (firstEntry) { firstEntry = false; } else { - summary.append(", "); + ids.append(", "); } - summary.append(id.getValue()); + ids.append(v.getName()); + } + if (ids.length() > 0) { + summary.append(d.getFileName()).append(" ("); + firstEntry = true; + for (Identifier id : d.getIdentifiers()) { + if (firstEntry) { + firstEntry = false; + } else { + summary.append(", "); + } + summary.append(id.getValue()); + } + summary.append(") : ").append(ids).append(NEW_LINE); } - summary.append(") : ").append(ids).append(NEW_LINE); } - } - if (summary.length() > 0) { - final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities:%n%n%s" - + "%n%nSee the dependency-check report for more details.%n%n", summary.toString()); - LOGGER.log(Level.WARNING, msg); + if (summary.length() > 0) { + final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities:%n%n%s" + + "%n%nSee the dependency-check report for more details.%n%n", summary.toString()); + LOGGER.log(Level.WARNING, msg); + } } } // // + /** + * Returns the key used to store the path to the data file that is saved by writeDataFile(). This key + * is used in the MavenProject.(set|get)ContextValue. + * + * @return the key used to store the path to the data file + */ + protected String getDataFileContextKey() { + return "dependency-check-path-" + dataFileName; + } + + /** + * Returns the key used to store the path to the output directory. When generating the report in the + * executeAggregateReport() the output directory should be obtained by using this key. + * + * @return the key used to store the path to the output directory + */ + protected String getOutputDirectoryContextKey() { + return "dependency-output-dir-" + dataFileName; + } + /** * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase. * - * @return the File object referencing the data file that was written + * @param dependencies the list of dependencies to serialize */ - @Override - protected File writeDataFile() { + protected void writeDataFile(List dependencies) { File file = null; - if (engine != null && getProject().getContextValue(this.getDataFileContextKey()) == null) { - file = new File(getProject().getBuild().getDirectory(), getDataFileName()); + if (dependencies != null && project.getContextValue(this.getDataFileContextKey()) == null) { + file = new File(project.getBuild().getDirectory(), dataFileName); OutputStream os = null; OutputStream bos = null; ObjectOutputStream out = null; @@ -859,13 +875,14 @@ public class DependencyCheckMojo extends ReportAggregationMojo { os = new FileOutputStream(file); bos = new BufferedOutputStream(os); out = new ObjectOutputStream(bos); - out.writeObject(engine.getDependencies()); + out.writeObject(dependencies); out.flush(); //call reset to prevent resource leaks per //https://www.securecoding.cert.org/confluence/display/java/SER10-J.+Avoid+memory+and+resource+leaks+during+serialization out.reset(); - + project.setContextValue(this.getDataFileContextKey(), file.getAbsolutePath()); + LOGGER.fine(String.format("Serialized data file written to '%s'", file.getAbsolutePath())); } catch (IOException ex) { LOGGER.log(Level.WARNING, "Unable to create data file used for report aggregation; " + "if report aggregation is being used the results may be incomplete."); @@ -894,18 +911,6 @@ public class DependencyCheckMojo extends ReportAggregationMojo { } } } - return file; - } - - /** - * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and - * "aggregate" phase. - * - * @return a Engine object populated with dependencies if the serialized data file exists; otherwise - * null is returned - */ - protected List readDataFile() { - return readDataFile(getProject()); } /** @@ -946,5 +951,4 @@ public class DependencyCheckMojo extends ReportAggregationMojo { return ret; } // - } diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java new file mode 100644 index 000000000..2ecb2bc63 --- /dev/null +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java @@ -0,0 +1,118 @@ +/* + * 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) 2013 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.maven; + +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.utils.Settings; + +/** + * Maven Plugin that checks the project dependencies to see if they have any known published vulnerabilities. + * + * @author Jeremy Long + */ +@Mojo( + name = "check", + defaultPhase = LifecyclePhase.COMPILE, + threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresOnline = true +) +public class CheckMojo extends BaseDependencyCheckMojo { + + /** + * Logger field reference. + */ + private static final Logger LOGGER = Logger.getLogger(CheckMojo.class.getName()); + + /** + * Returns whether or not a the report can be generated. + * + * @return true if the report can be generated; otherwise false + */ + @Override + public boolean canGenerateReport() { + boolean isCapable = false; + for (Artifact a : getProject().getArtifacts()) { + if (!excludeFromScan(a)) { + isCapable = true; + break; + } + } + return isCapable; + } + + /** + * Executes the dependency-check engine on the project's dependencies and generates the report. + * + * @throws MojoExecutionException thrown if there is an exception executing the goal + * @throws MojoFailureException thrown if dependency-check is configured to fail the build + */ + @Override + public void runCheck() throws MojoExecutionException, MojoFailureException { + final Engine engine; + try { + engine = initializeEngine(); + } catch (DatabaseException ex) { + LOGGER.log(Level.FINE, "Database connection error", ex); + throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex); + } + scanArtifacts(getProject(), engine); + if (engine.getDependencies().isEmpty()) { + LOGGER.info("No dependencies were identified that could be analyzed by dependency-check"); + } else { + engine.analyzeDependencies(); + writeReports(engine, getProject(), getCorrectOutputDirectory()); + writeDataFile(engine.getDependencies()); + showSummary(engine.getDependencies()); + checkForFailure(engine.getDependencies()); + } + engine.cleanup(); + Settings.cleanup(); + } + + /** + * Returns the report name. + * + * @param locale the location + * @return the report name + */ + public String getName(Locale locale) { + return "dependency-check"; + } + + /** + * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * + * @param locale The Locale to get the description for + * @return the description + */ + public String getDescription(Locale locale) { + return "Generates a report providing details on any published vulnerabilities within project dependencies. " + + "This report is a best effort and may contain false positives and false negatives."; + } + +} diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java index efd6be00c..499e9ab05 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java @@ -17,10 +17,12 @@ */ package org.owasp.dependencycheck.maven; +import java.util.List; import java.util.logging.Logger; import org.apache.maven.project.MavenProject; import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.analyzer.CPEAnalyzer; +import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.utils.Settings; @@ -44,22 +46,37 @@ public class Engine extends org.owasp.dependencycheck.Engine { * The current MavenProject. */ private MavenProject currentProject; + /** + * The list of MavenProjects that are part of the current build. + */ + private List reactorProjects; + /** + * Key used in the MavenProject context values to note whether or not an update has been executed. + */ + public static final String UPDATE_EXECUTED_FLAG = "dependency-check-update-executed"; /** * Creates a new Engine to perform anyalsis on dependencies. * * @param project the current Maven project + * @param reactorProjects the reactor projects for the current Maven execution * @throws DatabaseException thrown if there is an issue connecting to the database */ - public Engine(MavenProject project) throws DatabaseException { + public Engine(MavenProject project, List reactorProjects) throws DatabaseException { this.currentProject = project; - final MavenProject parent = getRootParent(); - if (parent != null && parent.getContextValue("dependency-check-data-was-updated") != null) { + this.reactorProjects = reactorProjects; + final MavenProject root = getExecutionRoot(); + if (root != null) { + LOGGER.fine(String.format("Checking root project, %s, if updates have already been completed", root.getArtifactId())); + } else { + LOGGER.fine("Checking root project, null, if updates have already been completed"); + } + if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) { System.setProperty(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE.toString()); } initializeEngine(); - if (parent != null) { - parent.setContextValue("dependency-check-data-was-updated", Boolean.valueOf(true)); + if (root != null) { + root.setContextValue(UPDATE_EXECUTED_FLAG, Boolean.TRUE); } } @@ -81,7 +98,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { @Override protected Analyzer initializeAnalyzer(Analyzer analyzer) { if ((analyzer instanceof CPEAnalyzer)) { - CPEAnalyzer cpe = getPreviouslyLoadedAnalyzer(); + CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer(); if (cpe != null) { return cpe; } @@ -91,6 +108,23 @@ public class Engine extends org.owasp.dependencycheck.Engine { return super.initializeAnalyzer(analyzer); } + /** + * Releases resources used by the analyzers by calling close() on each analyzer. + */ + @Override + public void cleanup() { + super.cleanup(); + if (currentProject == null || reactorProjects == null) { + return; + } + if (this.currentProject == reactorProjects.get(reactorProjects.size() - 1)) { + final CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer(); + if (cpe != null) { + cpe.close(); + } + } + } + /** * Closes the given analyzer. This skips closing the CPEAnalyzer. * @@ -99,7 +133,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { @Override protected void closeAnalyzer(Analyzer analyzer) { if ((analyzer instanceof CPEAnalyzer)) { - if (getPreviouslyLoadedAnalyzer() == null) { + if (getPreviouslyLoadedCPEAnalyzer() == null) { super.closeAnalyzer(analyzer); } } else { @@ -107,26 +141,19 @@ public class Engine extends org.owasp.dependencycheck.Engine { } } - /** - * Closes the CPEAnalyzer if it has been created and persisted in the root parent MavenProject context. - */ - public void cleanupFinal() { - final CPEAnalyzer cpe = getPreviouslyLoadedAnalyzer(); - if (cpe != null) { - cpe.close(); - } - } - /** * Gets the CPEAnalyzer from the root Maven Project. * * @return an initialized CPEAnalyzer */ - private CPEAnalyzer getPreviouslyLoadedAnalyzer() { + private CPEAnalyzer getPreviouslyLoadedCPEAnalyzer() { CPEAnalyzer cpe = null; - final MavenProject project = getRootParent(); + final MavenProject project = getExecutionRoot(); if (project != null) { - cpe = (CPEAnalyzer) project.getContextValue(CPE_ANALYZER_KEY); + final Object obj = project.getContextValue(CPE_ANALYZER_KEY); + if (obj != null && obj instanceof CPEAnalyzer) { + cpe = (CPEAnalyzer) project.getContextValue(CPE_ANALYZER_KEY); + } } return cpe; } @@ -137,7 +164,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { * @param cpe the CPEAnalyzer to store */ private void storeCPEAnalyzer(CPEAnalyzer cpe) { - final MavenProject p = getRootParent(); + final MavenProject p = getExecutionRoot(); if (p != null) { p.setContextValue(CPE_ANALYZER_KEY, cpe); } @@ -148,7 +175,16 @@ public class Engine extends org.owasp.dependencycheck.Engine { * * @return the root Maven Project */ - private MavenProject getRootParent() { + private MavenProject getExecutionRoot() { + if (reactorProjects == null) { + return null; + } + for (MavenProject p : reactorProjects) { + if (p.isExecutionRoot()) { + return p; + } + } + //the following should never run, but leaving it as a failsafe. if (this.currentProject == null) { return null; } @@ -158,4 +194,15 @@ public class Engine extends org.owasp.dependencycheck.Engine { } return p; } + + /** + * Resets the file type analyzers so that they can be re-used to scan additional directories. Without the reset the + * analyzer might be disabled because the first scan/analyze did not identify any files that could be processed by + * the analyzer. + */ + public void resetFileTypeAnalyzers() { + for (FileTypeAnalyzer a : getFileTypeAnalyzers()) { + a.reset(); + } + } } diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java deleted file mode 100644 index 58db453e7..000000000 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * 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. - */ -package org.owasp.dependencycheck.maven; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.logging.Logger; -import org.apache.maven.doxia.sink.Sink; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apache.maven.reporting.MavenReport; -import org.apache.maven.reporting.MavenReportException; - -/** - *

- * This is an abstract reporting mojo that enables report aggregation. Some of the code in the this class was copied - * from the CoberturaReportMojo (http://mojo.codehaus.org/cobertura-maven-plugin/, version 2.6). The authors of the - * CoberturaReportMojo were Will Gwaltney and - * Joakim Erdfelt. There working example of how to do report aggregation was - * invaluable.

- *

- * An important point about using this abstract class is that it is intended for one to write some form of serialized - * data (via the {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#writeDataFile() }; note that the - * writeDataFile() function is called automatically after either {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#executeNonAggregateReport(org.apache.maven.doxia.sink.Sink, - * org.apache.maven.doxia.sink.SinkFactory, java.util.Locale) - * } or {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#executeAggregateReport(org.apache.maven.doxia.sink.Sink, - * org.apache.maven.doxia.sink.SinkFactory, java.util.Locale) - * } are called. When executeAggregateReport() is implemented, one can call {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#getChildDataFiles() - * } to obtain a list of the data files to aggregate.

- * - * - * @author Jeremy Long - */ -public abstract class ReportAggregationMojo extends AbstractMojo implements MavenReport { - - /** - * The Maven Project Object. - */ - @Component - private MavenProject project; - - /** - * Logger field reference. - */ - private static final Logger LOGGER = Logger.getLogger(ReportAggregationMojo.class.getName()); - - /** - * List of Maven project of the current build - */ - @Parameter(readonly = true, required = true, property = "reactorProjects") - private List reactorProjects; - - /** - * Generate aggregate reports in multi-module projects. - */ - @Parameter(property = "aggregate", defaultValue = "false") - private boolean aggregate; - - /** - * Sets whether or not the external report format should be used. - */ - @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true) - private String dataFileName; - /** - * Specifies the destination directory for the generated Dependency-Check report. This generally maps to - * "target/site". - */ - @Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true) - private File reportOutputDirectory; - - /** - * Sets the Reporting output directory. - * - * @param directory the output directory - */ - @Override - public void setReportOutputDirectory(File directory) { - reportOutputDirectory = directory; - } - - /** - * Returns the output directory. - * - * @return the output directory - */ - @Override - public File getReportOutputDirectory() { - return reportOutputDirectory; - } - - /** - * Returns the output directory for the given project. - * - * @param project the Maven project to get the output directory for - * @return the output directory for the given project - */ - public File getReportOutputDirectory(MavenProject project) { - final Object o = project.getContextValue(getOutputDirectoryContextKey()); - if (o != null && o instanceof File) { - return (File) o; - } - return null; - } - - /** - * Returns whether this is an external report. This method always returns true. - * - * @return true - */ - @Override - public final boolean isExternalReport() { - return true; - } - - /** - * Initializes the mojo. - */ - protected abstract void initialize(); - - /** - * The collection of child projects. - */ - private final Map> projectChildren = new HashMap>(); - - /** - * Called before execute; allows for any setup that is needed. If this is overridden you must call - * super.preExecute(). - * - * @throws MojoExecutionException thrown if there is an issue executing the mojo - * @throws MojoFailureException thrown if there is an issue executing the mojo - */ - protected void preExecute() throws MojoExecutionException, MojoFailureException { - buildAggregateInfo(); - } - - /** - * Called when the mojo is being executed. - * - * @throws MojoExecutionException thrown if there is an issue executing the mojo - * @throws MojoFailureException thrown if there is an issue executing the mojo - */ - protected abstract void performExecute() throws MojoExecutionException, MojoFailureException; - - /** - * Runs after the mojo has executed. This implementation will call writeDataFile(). As such, it is - * important that if this method is overriden that super.postExecute() is called. - * - * @throws MojoExecutionException thrown if there is an issue executing the mojo - * @throws MojoFailureException thrown if there is an issue executing the mojo - */ - protected void postExecute() throws MojoExecutionException, MojoFailureException { - final File written = writeDataFile(); - if (written != null) { - project.setContextValue(getDataFileContextKey(), written.getAbsolutePath()); - } - } - - /** - * Returns the key used to store the path to the data file that is saved by writeDataFile(). This key - * is used in the MavenProject.(set|get)ContextValue. - * - * @return the key used to store the path to the data file - */ - protected String getDataFileContextKey() { - return "dependency-check-path-" + this.getDataFileName(); - } - - /** - * Returns the key used to store the path to the output directory. When generating the report in the - * executeAggregateReport() the output directory should be obtained by using this key. - * - * @return the key used to store the path to the output directory - */ - protected String getOutputDirectoryContextKey() { - return "dependency-output-dir-" + this.getDataFileName(); - } - - /** - * Is called by Maven to execute the mojo. - * - * @throws MojoExecutionException thrown if there is an issue executing the mojo - * @throws MojoFailureException thrown if there is an issue executing the mojo - */ - public final void execute() throws MojoExecutionException, MojoFailureException { - try { - initialize(); - preExecute(); - performExecute(); - } finally { - postExecute(); - } - } - - /** - * Runs prior to the site report generation. - * - * @throws MavenReportException if a maven report exception occurs - */ - protected void preGenerate() throws MavenReportException { - buildAggregateInfo(); - - project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory()); - } - - /** - * Executes after the site report has been generated. - * - * @throws MavenReportException if a maven report exception occurs - */ - protected void postGenerate() throws MavenReportException { - final File written = writeDataFile(); - if (written != null) { - project.setContextValue(getDataFileContextKey(), written.getAbsolutePath()); - } - } - - /** - * Generates the non aggregate report. - * - * @param locale the locale to use when generating the report - * @throws MavenReportException if a maven report exception occurs - */ - protected abstract void executeNonAggregateReport(Locale locale) throws MavenReportException; - - /** - * Generates the aggregate Site Report. - * - * @param project the maven project used to generate the aggregate report - * @param locale the locale to use when generating the report - * @throws MavenReportException if a maven report exception occurs - */ - protected abstract void executeAggregateReport(MavenProject project, Locale locale) throws MavenReportException; - - /** - * Generates the Dependency-Check Site Report. - * - * @param sink the sink to write the report to - * @param locale the locale to use when generating the report - * @throws MavenReportException if a maven report exception occurs - * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale) instead. - */ - @Deprecated - public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException { - generate((Sink) sink, locale); - } - - /** - * Generates the Dependency-Check Site Report. - * - * @param sink the sink to write the report to - * @param locale the locale to use when generating the report - * @throws MavenReportException if a maven report exception occurs - */ - public final void generate(Sink sink, Locale locale) throws MavenReportException { - try { - initialize(); - preGenerate(); - if (canGenerateNonAggregateReport()) { - executeNonAggregateReport(locale); - } - } finally { - postGenerate(); - } - if (canGenerateAggregateReport()) { - for (MavenProject proj : reactorProjects) { - if (!isMultiModule(proj)) { - continue; - } - executeAggregateReport(proj, locale); - } - } - } - - /** - * Returns whether or not the mojo can generate a non-aggregate report for this project. - * - * @return true if a non-aggregate report can be generated, otherwise false - */ - protected abstract boolean canGenerateNonAggregateReport(); - - /** - * Returns whether or not we can generate any aggregate reports at this time. - * - * @return true if an aggregate report can be generated, otherwise false - */ - protected abstract boolean canGenerateAggregateReport(); - - /** - * Returns the name of the data file that contains the serialized data. - * - * @return the name of the data file that contains the serialized data - */ - protected String getDataFileName() { - return dataFileName; - } - - /** - * Writes the data file to disk in the target directory. - * - * @return the File object referencing the data file that was written - */ - protected abstract File writeDataFile(); - - /** - * Collects the information needed for building aggregate reports. - */ - private void buildAggregateInfo() { - // build parent-child map - for (MavenProject proj : reactorProjects) { - Set depList = projectChildren.get(proj.getParent()); - if (depList == null) { - depList = new HashSet(); - projectChildren.put(proj.getParent(), depList); - } - depList.add(proj); - } - } - - /** - * Returns a list containing all the recursive, non-pom children of the given project, never null. - * - * @return a list of child projects - */ - protected List getAllChildren() { - return getAllChildren(project); - } - - /** - * Returns a list containing all the recursive, non-pom children of the given project, never null. - * - * @param parentProject the parent project to collect the child project references - * @return a list of child projects - */ - protected List getAllChildren(MavenProject parentProject) { - final Set children = projectChildren.get(parentProject); - if (children == null) { - return Collections.emptyList(); - } - - final List result = new ArrayList(); - for (MavenProject child : children) { - if (isMultiModule(child)) { - result.addAll(getAllChildren(child)); - } else { - result.add(child); - } - } - return result; - } - - /** - * Returns a list of data files that were produced by the direct children of the given MavenProject. - * - * @param project the Maven project to obtain the child data files from - * @return a list of the data files - */ - protected List getAllChildDataFiles(MavenProject project) { - final List children = getAllChildren(project); - return getDataFiles(children); - } - - /** - * Returns any existing output files from the given list of projects. - * - * @param projects the list of projects to obtain the output files from - * @return a list of output files - */ - protected List getDataFiles(List projects) { - final List files = new ArrayList(); - for (MavenProject proj : projects) { - final Object path = project.getContextValue(getDataFileContextKey()); - if (path == null) { - final String msg = String.format("Unable to aggregate data for '%s' - aggregate data file was not generated", - proj.getName()); - LOGGER.warning(msg); - } else { - final File outputFile = new File((String) path); - if (outputFile.exists()) { - files.add(outputFile); - } else { - if (!isMultiModule(project)) { - final String msg = String.format("Unable to aggregate data for '%s' - missing data file '%s'", - proj.getName(), outputFile.getPath()); - LOGGER.warning(msg); - } - } - } - } - return files; - } - - /** - * Test if the project has pom packaging - * - * @param mavenProject Project to test - * @return true if it has a pom packaging; otherwise false - */ - protected boolean isMultiModule(MavenProject mavenProject) { - return "pom".equals(mavenProject.getPackaging()); - } - - /** - * Test if the current project has pom packaging - * - * @return true if it has a pom packaging; otherwise false - */ - protected boolean isMultiModule() { - return isMultiModule(project); - } - - /** - * Check whether the current project is the last project in a multi-module build. If the maven build is not a - * multi-module project then this will always return true. - * - * @return true if the current project is the last project in a multi-module build; otherwise - * false - */ - protected boolean isLastProject() { - return project.equals(reactorProjects.get(reactorProjects.size() - 1)); - } - - /** - * Returns whether or not the mojo is configured to perform report aggregation. - * - * @return true if report aggregation is enabled; otherwise false - */ - public boolean isAggregate() { - return aggregate; - } - - /** - * Returns a reference to the current project. This method is used instead of auto-binding the project via component - * annotation in concrete implementations of this. If the child has a @Component MavenProject project; - * defined then the abstract class (i.e. this class) will not have access to the current project (just the way Maven - * works with the binding). - * - * @return returns a reference to the current project - */ - protected MavenProject getProject() { - return project; - } -} diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportingUtil.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportingUtil.java deleted file mode 100644 index e7a081fb9..000000000 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportingUtil.java +++ /dev/null @@ -1,455 +0,0 @@ -/* - * 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. - */ -package org.owasp.dependencycheck.maven; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.DateFormat; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.maven.doxia.sink.Sink; -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.Evidence; -import org.owasp.dependencycheck.dependency.Identifier; -import org.owasp.dependencycheck.dependency.Reference; -import org.owasp.dependencycheck.dependency.Vulnerability; -import org.owasp.dependencycheck.dependency.VulnerableSoftware; -import org.owasp.dependencycheck.reporting.ReportGenerator; - -/** - * A utility class that encapsulates the report generation for dependency-check-maven. - * - * @author Jeremy Long - */ -final class ReportingUtil { - - /** - * Logger field reference. - */ - private static final Logger LOGGER = Logger.getLogger(ReportingUtil.class.getName()); - - /** - * Empty private constructor for this utility class. - */ - private ReportingUtil() { - } - - /** - * Generates the reports for a given dependency-check engine. - * - * @param engine a dependency-check engine - * @param outDirectory the directory to write the reports to - * @param projectName the name of the project that a report is being generated for - * @param format the format of the report to generate - */ - static void generateExternalReports(Engine engine, File outDirectory, String projectName, String format) { - DatabaseProperties prop = null; - CveDB cve = null; - try { - cve = new CveDB(); - cve.open(); - prop = cve.getDatabaseProperties(); - } catch (DatabaseException ex) { - LOGGER.log(Level.FINE, "Unable to retrieve DB Properties", ex); - } finally { - if (cve != null) { - cve.close(); - } - } - final ReportGenerator r = new ReportGenerator(projectName, engine.getDependencies(), engine.getAnalyzers(), prop); - try { - r.generateReports(outDirectory.getCanonicalPath(), format); - } catch (IOException ex) { - LOGGER.log(Level.SEVERE, - "Unexpected exception occurred during analysis; please see the verbose error log for more details."); - LOGGER.log(Level.FINE, null, ex); - } catch (Throwable ex) { - LOGGER.log(Level.SEVERE, - "Unexpected exception occurred during analysis; please see the verbose error log for more details."); - LOGGER.log(Level.FINE, null, ex); - } - } - - /** - * Generates a dependency-check report using the Maven Site format. - * - * @param engine the engine used to scan the dependencies - * @param sink the sink to write the data to - * @param projectName the name of the project - */ - static void generateMavenSiteReport(final Engine engine, Sink sink, String projectName) { - final List dependencies = engine.getDependencies(); - - writeSiteReportHeader(sink, projectName); - writeSiteReportTOC(sink, dependencies); - - int cnt = 0; - for (Dependency d : dependencies) { - writeSiteReportDependencyHeader(sink, d); - cnt = writeSiteReportDependencyEvidenceUsed(d, cnt, sink); - cnt = writeSiteReportDependencyRelatedDependencies(d, cnt, sink); - writeSiteReportDependencyIdentifiers(d, sink); - writeSiteReportDependencyVulnerabilities(d, sink, cnt); - } - sink.body_(); - } - - // - /** - * Writes the vulnerabilities to the site report. - * - * @param d the dependency - * @param sink the sink to write the data to - * @param collapsibleHeaderCount the collapsible header count - */ - private static void writeSiteReportDependencyVulnerabilities(Dependency d, Sink sink, int collapsibleHeaderCount) { - int cnt = collapsibleHeaderCount; - if (d.getVulnerabilities() != null && !d.getVulnerabilities().isEmpty()) { - for (Vulnerability v : d.getVulnerabilities()) { - - sink.paragraph(); - sink.bold(); - try { - sink.link("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + URLEncoder.encode(v.getName(), "US-ASCII")); - sink.text(v.getName()); - sink.link_(); - sink.bold_(); - } catch (UnsupportedEncodingException ex) { - sink.text(v.getName()); - sink.bold_(); - sink.lineBreak(); - sink.text("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + v.getName()); - } - sink.paragraph_(); - sink.paragraph(); - sink.text("Severity: "); - if (v.getCvssScore() < 4.0) { - sink.text("Low"); - } else { - if (v.getCvssScore() >= 7.0) { - sink.text("High"); - } else { - sink.text("Medium"); - } - } - sink.lineBreak(); - sink.text("CVSS Score: " + v.getCvssScore()); - if (v.getCwe() != null && !v.getCwe().isEmpty()) { - sink.lineBreak(); - sink.text("CWE: "); - sink.text(v.getCwe()); - } - sink.paragraph_(); - sink.paragraph(); - sink.text(v.getDescription()); - if (v.getReferences() != null && !v.getReferences().isEmpty()) { - sink.list(); - for (Reference ref : v.getReferences()) { - sink.listItem(); - sink.text(ref.getSource()); - sink.text(" - "); - sink.link(ref.getUrl()); - sink.text(ref.getName()); - sink.link_(); - sink.listItem_(); - } - sink.list_(); - } - sink.paragraph_(); - if (v.getVulnerableSoftware() != null && !v.getVulnerableSoftware().isEmpty()) { - sink.paragraph(); - - cnt += 1; - sink.rawText("Vulnerable Software [-]"); - sink.rawText("
"); - sink.list(); - for (VulnerableSoftware vs : v.getVulnerableSoftware()) { - sink.listItem(); - try { - sink.link("http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + URLEncoder.encode(vs.getName(), "US-ASCII")); - sink.text(vs.getName()); - sink.link_(); - if (vs.hasPreviousVersion()) { - sink.text(" and all previous versions."); - } - } catch (UnsupportedEncodingException ex) { - sink.text(vs.getName()); - if (vs.hasPreviousVersion()) { - sink.text(" and all previous versions."); - } - sink.text(" (http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + vs.getName() + ")"); - } - - sink.listItem_(); - } - sink.list_(); - sink.rawText("
"); - sink.paragraph_(); - } - } - } - } - - /** - * Writes the identifiers to the site report. - * - * @param d the dependency - * @param sink the sink to write the data to - */ - private static void writeSiteReportDependencyIdentifiers(Dependency d, Sink sink) { - if (d.getIdentifiers() != null && !d.getIdentifiers().isEmpty()) { - sink.sectionTitle4(); - sink.text("Identifiers"); - sink.sectionTitle4_(); - sink.list(); - for (Identifier i : d.getIdentifiers()) { - sink.listItem(); - sink.text(i.getType()); - sink.text(": "); - if (i.getUrl() != null && i.getUrl().length() > 0) { - sink.link(i.getUrl()); - sink.text(i.getValue()); - sink.link_(); - } else { - sink.text(i.getValue()); - } - if (i.getDescription() != null && i.getDescription().length() > 0) { - sink.lineBreak(); - sink.text(i.getDescription()); - } - sink.listItem_(); - } - sink.list_(); - } - } - - /** - * Writes the related dependencies to the site report. - * - * @param d the dependency - * @param sink the sink to write the data to - * @param collapsibleHeaderCount the collapsible header count - * @return the collapsible header count - */ - private static int writeSiteReportDependencyRelatedDependencies(Dependency d, int collapsibleHeaderCount, Sink sink) { - int cnt = collapsibleHeaderCount; - if (d.getRelatedDependencies() != null && !d.getRelatedDependencies().isEmpty()) { - cnt += 1; - sink.sectionTitle4(); - sink.rawText("Related Dependencies [+]"); - sink.sectionTitle4_(); - sink.rawText("
"); - sink.list(); - for (Dependency r : d.getRelatedDependencies()) { - sink.listItem(); - sink.text(r.getFileName()); - sink.list(); - writeListItem(sink, "File Path: " + r.getFilePath()); - writeListItem(sink, "SHA1: " + r.getSha1sum()); - writeListItem(sink, "MD5: " + r.getMd5sum()); - sink.list_(); - sink.listItem_(); - } - sink.list_(); - sink.rawText("
"); - } - return cnt; - } - - /** - * Writes the evidence used to the site report. - * - * @param d the dependency - * @param sink the sink to write the data to - * @param collapsibleHeaderCount the collapsible header count - * @return the collapsible header count - */ - private static int writeSiteReportDependencyEvidenceUsed(Dependency d, int collapsibleHeaderCount, Sink sink) { - int cnt = collapsibleHeaderCount; - final Set evidence = d.getEvidenceForDisplay(); - if (evidence != null && evidence.size() > 0) { - cnt += 1; - sink.sectionTitle4(); - sink.rawText("Evidence Collected [+]"); - sink.sectionTitle4_(); - sink.rawText("
"); - sink.table(); - sink.tableRow(); - writeTableHeaderCell(sink, "Source"); - writeTableHeaderCell(sink, "Name"); - writeTableHeaderCell(sink, "Value"); - sink.tableRow_(); - for (Evidence e : evidence) { - sink.tableRow(); - writeTableCell(sink, e.getSource()); - writeTableCell(sink, e.getName()); - writeTableCell(sink, e.getValue()); - sink.tableRow_(); - } - sink.table_(); - sink.rawText("
"); - } - return cnt; - } - - /** - * Writes the dependency header to the site report. - * - * @param d the dependency - * @param sink the sink to write the data to - */ - private static void writeSiteReportDependencyHeader(Sink sink, Dependency d) { - sink.sectionTitle2(); - sink.anchor("sha1" + d.getSha1sum()); - sink.text(d.getFileName()); - sink.anchor_(); - sink.sectionTitle2_(); - if (d.getDescription() != null && d.getDescription().length() > 0) { - sink.paragraph(); - sink.bold(); - sink.text("Description: "); - sink.bold_(); - sink.text(d.getDescription()); - sink.paragraph_(); - } - if (d.getLicense() != null && d.getLicense().length() > 0) { - sink.paragraph(); - sink.bold(); - sink.text("License: "); - sink.bold_(); - if (d.getLicense().startsWith("http://") && !d.getLicense().contains(" ")) { - sink.link(d.getLicense()); - sink.text(d.getLicense()); - sink.link_(); - } else { - sink.text(d.getLicense()); - } - sink.paragraph_(); - } - } - - /** - * Adds a list item to the site report. - * - * @param sink the sink to write the data to - * @param text the text to write - */ - private static void writeListItem(Sink sink, String text) { - sink.listItem(); - sink.text(text); - sink.listItem_(); - } - - /** - * Adds a table cell to the site report. - * - * @param sink the sink to write the data to - * @param text the text to write - */ - private static void writeTableCell(Sink sink, String text) { - sink.tableCell(); - sink.text(text); - sink.tableCell_(); - } - - /** - * Adds a table header cell to the site report. - * - * @param sink the sink to write the data to - * @param text the text to write - */ - private static void writeTableHeaderCell(Sink sink, String text) { - sink.tableHeaderCell(); - sink.text(text); - sink.tableHeaderCell_(); - } - - /** - * Writes the TOC for the site report. - * - * @param sink the sink to write the data to - * @param dependencies the dependencies that are being reported on - */ - private static void writeSiteReportTOC(Sink sink, final List dependencies) { - sink.list(); - for (Dependency d : dependencies) { - sink.listItem(); - sink.link("#sha1" + d.getSha1sum()); - sink.text(d.getFileName()); - sink.link_(); - if (!d.getVulnerabilities().isEmpty()) { - sink.rawText(" "); - } - if (!d.getRelatedDependencies().isEmpty()) { - sink.list(); - for (Dependency r : d.getRelatedDependencies()) { - writeListItem(sink, r.getFileName()); - } - sink.list_(); - } - sink.listItem_(); - } - sink.list_(); - } - - /** - * Writes the site report header. - * - * @param sink the sink to write the data to - * @param projectName the name of the project - */ - private static void writeSiteReportHeader(Sink sink, String projectName) { - sink.head(); - sink.title(); - sink.text("Dependency-Check Report: " + projectName); - sink.title_(); - sink.head_(); - sink.body(); - sink.rawText(""); - sink.section1(); - sink.sectionTitle1(); - sink.text("Project: " + projectName); - sink.sectionTitle1_(); - sink.date(); - final Date now = new Date(); - sink.text(DateFormat.getDateTimeInstance().format(now)); - sink.date_(); - sink.section1_(); - } - //
- -} diff --git a/dependency-check-maven/src/site/markdown/configuration.md b/dependency-check-maven/src/site/markdown/configuration.md index da7710d80..10b6d4b53 100644 --- a/dependency-check-maven/src/site/markdown/configuration.md +++ b/dependency-check-maven/src/site/markdown/configuration.md @@ -1,10 +1,18 @@ +Goals +==================== + +Goal | Description +-----------|----------------------- +aggregate | Runs dependency-check against the child projects and aggregates the results into a single report. +check | Runs dependency-check against the project and generates a report. + Configuration ==================== The following properties can be set on the dependency-check-maven plugin. Property | Description | Default Value ---------------------|------------------------------------|------------------ -aggregate | Sets whether report aggregation will be performed for multi-module site reports. This option only affects the report generation when configured within the reporting section. | false +aggregate | Deprecated - use the aggregate goal instead. |   autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true outputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target' failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11 @@ -28,7 +36,8 @@ Property | Description archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   jarAnalyzer | Sets whether Jar Analyzer will be used. | true -nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true +centralAnalyzerEnabled | Sets whether Central Analyzer will be used. If this analyzer is being disabled there is a good chance you also want to disable the Nexus Analyzer (see below). | true +nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. This analyzer is superceded by the Central Analyzer; however, you can configure this to run against a Nexus Pro installation. | true nexusUrl | Defines the Nexus Pro Server URL. If not set the Nexus Analyzer will be disabled. |   nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true diff --git a/dependency-check-maven/src/site/markdown/usage.md.vm b/dependency-check-maven/src/site/markdown/usage.md.vm index 6c2c7e10c..8ff9ce975 100644 --- a/dependency-check-maven/src/site/markdown/usage.md.vm +++ b/dependency-check-maven/src/site/markdown/usage.md.vm @@ -13,7 +13,7 @@ seven days the update will only take a few seconds. #set( $H = '#' ) $H$H$H Example 1: -Create the DependencyCheck-report.html in the target directory +Create the DependencyCheck-report.html in the target directory. ```xml @@ -43,7 +43,7 @@ Create the DependencyCheck-report.html in the target directory ``` $H$H$H Example 2: -Create an aggregated dependency-check report within the site +Create an aggregated dependency-check report within the site. ```xml @@ -57,13 +57,10 @@ Create an aggregated dependency-check report within the site org.owasp dependency-check-maven ${project.version} - - true - - check + aggregate @@ -78,7 +75,7 @@ Create an aggregated dependency-check report within the site ``` $H$H$H Example 3: -Create the DependencyCheck-report.html and fail the build for CVSS greater then 8 +Create the DependencyCheck-report.html and fail the build for CVSS greater then 8. ```xml @@ -111,7 +108,7 @@ Create the DependencyCheck-report.html and fail the build for CVSS greater then ``` $H$H$H Example 4: -Create the DependencyCheck-report.html and skip artifacts not bundled in distribution (Provided and Runtime scope) +Create the DependencyCheck-report.html and skip artifacts not bundled in distribution (Provided and Runtime scope). ```xml @@ -145,7 +142,7 @@ Create the DependencyCheck-report.html and skip artifacts not bundled in distrib ``` $H$H$H Example 5: -Create the DependencyCheck-report.html and use internal mirroring of CVE contents +Create the DependencyCheck-report.html and use internal mirroring of CVE contents. ```xml diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java new file mode 100644 index 000000000..97adbc5f2 --- /dev/null +++ b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java @@ -0,0 +1,106 @@ +/* + * 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. + */ +package org.owasp.dependencycheck.maven; + +import java.io.File; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import mockit.Mock; +import mockit.MockUp; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.project.MavenProject; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; + +/** + * + * @author Jeremy Long + */ +public class BaseDependencyCheckMojoTest extends BaseTest { + + /** + * Test of scanArtifacts method, of class BaseDependencyCheckMojo. + */ + @Test + public void testScanArtifacts() throws DatabaseException, InvalidSettingException { + MavenProject project = new MockUp() { + @Mock + public Set getArtifacts() { + Set artifacts = new HashSet(); + Artifact a = new ArtifactStub(); + try { + File file = new File(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + a.setFile(file); + artifacts.add(a); + } catch (URISyntaxException ex) { + Logger.getLogger(BaseDependencyCheckMojoTest.class.getName()).log(Level.SEVERE, null, ex); + } + //File file = new File(this.getClass().getClassLoader().getResource("daytrader-ear-2.1.7.ear").getPath()); + + return artifacts; + } + }.getMockInstance(); + + boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); + Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); + Engine engine = new Engine(null, null); + Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); + + assertTrue(engine.getDependencies().isEmpty()); + BaseDependencyCheckMojoImpl instance = new BaseDependencyCheckMojoImpl(); + instance.scanArtifacts(project, engine); + assertFalse(engine.getDependencies().isEmpty()); + engine.cleanup(); + } + + public class BaseDependencyCheckMojoImpl extends BaseDependencyCheckMojo { + + @Override + public void runCheck() throws MojoExecutionException, MojoFailureException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getName(Locale locale) { + return "test implementation"; + } + + @Override + public String getDescription(Locale locale) { + return "test implementation"; + } + + @Override + public boolean canGenerateReport() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + } + +} diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java new file mode 100644 index 000000000..4d0ca1381 --- /dev/null +++ b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +package org.owasp.dependencycheck.maven; + +import java.io.InputStream; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.owasp.dependencycheck.utils.Settings; + +/** + * + * @author Jeremy Long + */ +public class BaseTest { + + /** + * The properties file location. + */ + public static final String PROPERTIES_FILE = "mojo.properties"; + + @BeforeClass + public static void setUpClass() throws Exception { + Settings.initialize(); + InputStream mojoProperties = BaseTest.class.getClassLoader().getResourceAsStream(BaseTest.PROPERTIES_FILE); + Settings.mergeProperties(mojoProperties); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Settings.cleanup(true); + } +} diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/DependencyCheckMojoTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/DependencyCheckMojoTest.java deleted file mode 100644 index 2c161ecaa..000000000 --- a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/DependencyCheckMojoTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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) 2013 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.maven; - -import org.apache.maven.plugin.testing.AbstractMojoTestCase; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * TODO - figure out how to get the test harness to work. ATM no tests are running. - * - * @author Jeremy Long - */ -public class DependencyCheckMojoTest extends AbstractMojoTestCase { - - public DependencyCheckMojoTest() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } -// -// /** -// * Test of execute method, of class DependencyCheckMojo. -// */ -// @Test -// public void testExecute() throws Exception { -// System.out.println("execute"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// instance.execute(); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of generate method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGenerate_Sink_Locale() throws Exception { -// System.out.println("generate"); -// org.codehaus.doxia.sink.Sink sink = null; -// Locale locale = null; -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// instance.generate(sink, locale); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } - - /** - * Test of generate method, of class DependencyCheckMojo. - */ - @Test - public void testGenerate_Sink_SinkFactory_Locale() throws Exception { - //can't get the test-harness to work. -// File samplePom = new File(this.getClass().getClassLoader().getResource("sample.xml").toURI()); -// DependencyCheckMojo mojo = (DependencyCheckMojo) lookupMojo("check", samplePom); -// assertNotNull("Unable to load mojo", mojo); -// -// File out = mojo.getReportOutputDirectory(); -// OutputStream os = new FileOutputStream(out); -// MySink sink = new MySink(os); -// Locale locale = new Locale("en"); -// -// -// mojo.generate(sink, null, locale); -// sink.close(); - } -// /** -// * Test of getOutputName method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGetOutputName() { -// System.out.println("getOutputName"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// String expResult = ""; -// String result = instance.getOutputName(); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of getCategoryName method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGetCategoryName() { -// System.out.println("getCategoryName"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// String expResult = ""; -// String result = instance.getCategoryName(); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of getName method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGetName() { -// System.out.println("getName"); -// Locale locale = null; -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// String expResult = ""; -// String result = instance.getName(locale); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of setReportOutputDirectory method, of class DependencyCheckMojo. -// */ -// @Test -// public void testSetReportOutputDirectory() { -// System.out.println("setReportOutputDirectory"); -// File directory = null; -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// instance.setReportOutputDirectory(directory); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of getReportOutputDirectory method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGetReportOutputDirectory() { -// System.out.println("getReportOutputDirectory"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// File expResult = null; -// File result = instance.getReportOutputDirectory(); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of getDescription method, of class DependencyCheckMojo. -// */ -// @Test -// public void testGetDescription() { -// System.out.println("getDescription"); -// Locale locale = null; -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// String expResult = ""; -// String result = instance.getDescription(locale); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of isExternalReport method, of class DependencyCheckMojo. -// */ -// @Test -// public void testIsExternalReport() { -// System.out.println("isExternalReport"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// boolean expResult = false; -// boolean result = instance.isExternalReport(); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -// -// /** -// * Test of canGenerateReport method, of class DependencyCheckMojo. -// */ -// @Test -// public void testCanGenerateReport() { -// System.out.println("canGenerateReport"); -// DependencyCheckMojo instance = new DependencyCheckMojo(); -// boolean expResult = false; -// boolean result = instance.canGenerateReport(); -// assertEquals(expResult, result); -// // TODO review the generated test code and remove the default call to fail. -// fail("The test case is a prototype."); -// } -} diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/MySink.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/MySink.java deleted file mode 100644 index dfdad6270..000000000 --- a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/MySink.java +++ /dev/null @@ -1,601 +0,0 @@ -/* - * 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) 2013 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.maven; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.maven.doxia.logging.Log; -import org.apache.maven.doxia.sink.Sink; -import org.apache.maven.doxia.sink.SinkEventAttributes; - -/** - * - * @author Jeremy Long - */ -public class MySink implements Sink { - - private OutputStreamWriter out = null; - - public MySink(OutputStream os) { - out = new OutputStreamWriter(os); - - } - - private void writeTag(String tag) { - try { - out.write(tag); - } catch (IOException ex) { - Logger.getLogger(MySink.class.getName()).log(Level.SEVERE, "Error writing a tag; unable to generate the report"); - Logger.getLogger(MySink.class.getName()).log(Level.FINE, null, ex); - } - } - - public void head() { - writeTag(""); - } - - public void head_() { - writeTag(""); - } - - public void title() { - writeTag(""); - } - - public void title_() { - writeTag(""); - } - - public void author() { - writeTag(""); - } - - public void author_() { - writeTag(""); - } - - public void date() { - writeTag(""); - } - - public void body() { - writeTag(""); - } - - public void body_() { - writeTag(""); - } - - public void sectionTitle() { - writeTag("

"); - } - - public void sectionTitle_() { - writeTag("

"); - } - - public void section1() { - writeTag("
"); - } - - public void section1_() { - writeTag("
"); - } - - public void sectionTitle1() { - writeTag("

"); - } - - public void sectionTitle1_() { - writeTag("

"); - } - - public void section2() { - writeTag("
"); - } - - public void section2_() { - writeTag("
"); - } - - public void sectionTitle2() { - writeTag("

"); - } - - public void sectionTitle2_() { - writeTag("

"); - } - - public void section3() { - writeTag("
"); - } - - public void section3_() { - writeTag("
"); - } - - public void sectionTitle3() { - writeTag("

"); - } - - public void sectionTitle3_() { - writeTag("

"); - } - - public void section4() { - writeTag("
"); - } - - public void section4_() { - writeTag("
"); - } - - public void sectionTitle4() { - writeTag("
"); - } - - public void sectionTitle4_() { - writeTag("
"); - } - - public void section5() { - writeTag("
"); - } - - public void section5_() { - writeTag("
"); - } - - public void sectionTitle5() { - writeTag("
"); - } - - public void sectionTitle5_() { - writeTag("
"); - } - - public void list() { - writeTag("
    "); - } - - public void list_() { - writeTag("
"); - } - - public void listItem() { - writeTag("
  • "); - } - - public void listItem_() { - writeTag("
  • "); - } - - public void numberedList(int numbering) { - writeTag("
      "); - } - - public void numberedList_() { - writeTag("
    "); - } - - public void numberedListItem() { - writeTag("
  • "); - } - - public void numberedListItem_() { - writeTag("
  • "); - } - - public void definitionList() { - writeTag("
    "); - } - - public void definitionList_() { - writeTag("
    "); - } - - public void definitionListItem() { - writeTag("
    "); - } - - public void definitionListItem_() { - writeTag("
    "); - } - - public void definition() { - writeTag("
    "); - } - - public void definition_() { - writeTag("
    "); - } - - public void definedTerm() { - writeTag("
    "); - } - - public void definedTerm_() { - writeTag("
    "); - } - - public void figure() { - throw new UnsupportedOperationException("Not supported yet."); - } - - public void figure_() { - throw new UnsupportedOperationException("Not supported yet."); - } - - public void figureCaption() { - throw new UnsupportedOperationException("Not supported yet."); - } - - public void figureCaption_() { - throw new UnsupportedOperationException("Not supported yet."); - } - - public void figureGraphics(String name) { - throw new UnsupportedOperationException("Not supported yet."); - } - - public void table() { - writeTag(""); - } - - public void table_() { - writeTag("
    "); - } - - public void tableRows(int[] justification, boolean grid) { - writeTag(""); - } - - public void tableRows_() { - writeTag(""); - } - - public void tableRow() { - writeTag(""); - } - - public void tableRow_() { - writeTag(""); - } - - public void tableCell() { - writeTag(""); - } - - public void tableCell(String width) { - writeTag(""); - } - - public void tableCell_() { - writeTag(""); - } - - public void tableHeaderCell() { - writeTag(""); - } - - public void tableHeaderCell(String width) { - writeTag(""); - } - - public void tableHeaderCell_() { - writeTag(""); - } - - public void tableCaption() { - writeTag(""); - } - - public void tableCaption_() { - writeTag(""); - } - - public void paragraph() { - writeTag("

    "); - } - - public void paragraph_() { - writeTag("

    "); - } - - public void verbatim(boolean boxed) { - writeTag("
    ");
    -    }
    -
    -    public void verbatim_() {
    -        writeTag("
    "); - } - - public void horizontalRule() { - writeTag("
    "); - } - - public void pageBreak() { - writeTag("
    "); - } - - public void anchor(String name) { - writeTag(""); - } - - public void anchor_() { - writeTag(""); - } - - public void link(String name) { - writeTag(""); - } - - public void link_() { - writeTag(""); - } - - public void italic() { - writeTag(""); - } - - public void italic_() { - writeTag(""); - } - - public void bold() { - writeTag(""); - } - - public void bold_() { - writeTag(""); - } - - public void monospaced() { - writeTag("
    ");
    -    }
    -
    -    public void monospaced_() {
    -        writeTag("
    "); - } - - public void lineBreak() { - writeTag("
    "); - } - - public void nonBreakingSpace() { - writeTag(" "); - } - - public void text(String text) { - try { - //TODO add HTML Encoding - or figure out how to get the doxia xhtmlsink to work. - out.write(text); - } catch (IOException ex) { - Logger.getLogger(MySink.class.getName()).log(Level.SEVERE, "Error writing a text; unable to generate the report"); - Logger.getLogger(MySink.class.getName()).log(Level.FINE, null, ex); - } - } - - public void rawText(String text) { - try { - out.write(text); - } catch (IOException ex) { - Logger.getLogger(MySink.class.getName()).log(Level.SEVERE, "Error writing raw text; unable to generate the report"); - Logger.getLogger(MySink.class.getName()).log(Level.FINE, null, ex); - } - } - - public void flush() { - try { - out.flush(); - } catch (IOException ex) { - Logger.getLogger(MySink.class.getName()).log(Level.FINEST, null, ex); - } - } - - public void close() { - flush(); - try { - out.close(); - } catch (IOException ex) { - Logger.getLogger(MySink.class.getName()).log(Level.FINEST, null, ex); - } - } - - @Override - public void head(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void title(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void author(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void date(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void body(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void section(int i, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void section_(int i) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void sectionTitle(int i, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void sectionTitle_(int i) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void list(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void listItem(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void numberedList(int i, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void numberedListItem(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void definitionList(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void definitionListItem(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void definition(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void definedTerm(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void figure(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void figureCaption(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void figureGraphics(String string, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void table(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void tableRow(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void tableCell(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void tableHeaderCell(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void tableCaption(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void paragraph(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void verbatim(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void horizontalRule(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void anchor(String string, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void link(String string, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void lineBreak(SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void text(String string, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void comment(String string) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void unknown(String string, Object[] os, SinkEventAttributes sea) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void enableLogging(Log log) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } -} diff --git a/dependency-check-maven/src/test/resources/mojo.properties b/dependency-check-maven/src/test/resources/mojo.properties new file mode 100644 index 000000000..98d17c44c --- /dev/null +++ b/dependency-check-maven/src/test/resources/mojo.properties @@ -0,0 +1,2 @@ +# the path to the data directory +data.directory=[JAR]/dependency-check-data diff --git a/dependency-check-utils/pom.xml b/dependency-check-utils/pom.xml index 717d249a8..01a0ddac9 100644 --- a/dependency-check-utils/pom.xml +++ b/dependency-check-utils/pom.xml @@ -21,7 +21,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 dependency-check-utils @@ -279,11 +279,5 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. commons-io 2.4
    - - junit - junit - 4.11 - test - diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java index 70b988ec8..f4e5d8849 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; +import java.security.InvalidAlgorithmParameterException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; @@ -80,11 +81,11 @@ public final class Downloader { try { org.apache.commons.io.FileUtils.copyFile(file, outputPath); } catch (IOException ex) { - final String msg = String.format("Download failed, unable to copy '%s'", url.toString()); + final String msg = String.format("Download failed, unable to copy '%s' to '%s'", url.toString(), outputPath.getAbsolutePath()); throw new DownloadFailedException(msg); } } else { - final String msg = String.format("Download failed, file does not exist '%s'", url.toString()); + final String msg = String.format("Download failed, file ('%s') does not exist", url.toString()); throw new DownloadFailedException(msg); } } else { @@ -101,7 +102,8 @@ public final class Downloader { } finally { conn = null; } - throw new DownloadFailedException("Error downloading file.", ex); + final String msg = String.format("Error downloading file %s; unable to connect.", url.toString()); + throw new DownloadFailedException(msg, ex); } final String encoding = conn.getContentEncoding(); @@ -122,13 +124,20 @@ public final class Downloader { while ((bytesRead = reader.read(buffer)) > 0) { writer.write(buffer, 0, bytesRead); } + } catch (IOException ex) { + analyzeException(ex); + final String msg = String.format("Error saving '%s' to file '%s'%nConnection Timeout: %d%nEncoding: %s%n", + url.toString(), outputPath.getAbsolutePath(), conn.getConnectTimeout(), encoding); + throw new DownloadFailedException(msg, ex); } catch (Throwable ex) { - throw new DownloadFailedException("Error saving downloaded file.", ex); + final String msg = String.format("Unexpected exception saving '%s' to file '%s'%nConnection Timeout: %d%nEncoding: %s%n", + url.toString(), outputPath.getAbsolutePath(), conn.getConnectTimeout(), encoding); + throw new DownloadFailedException(msg, ex); } finally { if (writer != null) { try { writer.close(); - } catch (Throwable ex) { + } catch (IOException ex) { LOGGER.log(Level.FINEST, "Error closing the writer in Downloader.", ex); } @@ -136,7 +145,7 @@ public final class Downloader { if (reader != null) { try { reader.close(); - } catch (Throwable ex) { + } catch (IOException ex) { LOGGER.log(Level.FINEST, "Error closing the reader in Downloader.", ex); } @@ -185,6 +194,7 @@ public final class Downloader { } catch (URLConnectionFailureException ex) { throw new DownloadFailedException("Error creating URL Connection for HTTP HEAD request.", ex); } catch (IOException ex) { + analyzeException(ex); throw new DownloadFailedException("Error making HTTP HEAD request.", ex); } finally { if (conn != null) { @@ -198,4 +208,22 @@ public final class Downloader { } return timestamp; } + + protected static void analyzeException(IOException ex) throws DownloadFailedException { + Throwable cause = ex; + do { + if (cause instanceof InvalidAlgorithmParameterException) { + String keystore = System.getProperty("javax.net.ssl.keyStore"); + String version = System.getProperty("java.version"); + String vendor = System.getProperty("java.vendor"); + LOGGER.info("Error making HTTPS request - InvalidAlgorithmParameterException"); + LOGGER.info("There appears to be an issue with the installation of Java and the cacerts." + + "See closed issue #177 here: https://github.com/jeremylong/DependencyCheck/issues/177"); + LOGGER.info(String.format("Java Info:%njavax.net.ssl.keyStore='%s'%njava.version='%s'%njava.vendor='%s'", + keystore, version, vendor)); + throw new DownloadFailedException("Error making HTTPS request. Please see the log for more details."); + } + cause = cause.getCause(); + } while (cause.getCause() != null); + } } diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/LogUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/LogUtils.java index 8a60524c2..af4b82fed 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/LogUtils.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/LogUtils.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.utils; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.logging.FileHandler; @@ -57,6 +58,13 @@ public final class LogUtils { if (verboseLogFile != null && !verboseLogFile.isEmpty()) { verboseLoggingEnabled = true; final Logger logger = Logger.getLogger(""); + final File logFile = new File(verboseLogFile); + final File logDir = logFile.getParentFile(); + if (logDir != null && !logDir.isDirectory() && !logDir.mkdirs()) { + final String msg = String.format("Unable to create directory '%s', verbose logging will be disabled.", + logDir.getAbsolutePath()); + throw new IOException(msg); + } final FileHandler fileHandler = new FileHandler(verboseLogFile, true); fileHandler.setFormatter(new SimpleFormatter()); fileHandler.setLevel(Level.FINE); diff --git a/pom.xml b/pom.xml index daa3b0e7e..5493a81f4 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long org.owasp dependency-check-parent - 1.2.8-SNAPSHOT + 1.2.8 pom @@ -256,9 +256,8 @@ Copyright (c) 2012 - Jeremy Long junit junit - 4.11 + 4.12 test - jar - \ No newline at end of file + diff --git a/src/site/markdown/central-analyzer.md b/src/site/markdown/central-analyzer.md new file mode 100644 index 000000000..ec70e36a3 --- /dev/null +++ b/src/site/markdown/central-analyzer.md @@ -0,0 +1,11 @@ +Central Analyzer +============== + +Dependency-check includes an analyzer that will check for the Maven GAV +(Group/Artifact/Version) information for artifacts in the scanned area. By +default the information comes from [Maven Central][1]. If the artifact's hash +is found in the configured Nexus repository, its GAV is recorded as an Identifier +and the Group is collected as Vendor evidence, the Artifact is collected as Product +evidence, and the Version is collected as Version evidence. + +[1]: http://search.maven.org/ "Maven Central" diff --git a/src/site/markdown/nexus-analyzer.md b/src/site/markdown/nexus-analyzer.md index 5c3ebb8b6..b8992182b 100644 --- a/src/site/markdown/nexus-analyzer.md +++ b/src/site/markdown/nexus-analyzer.md @@ -1,54 +1,17 @@ Nexus Analyzer ============== -Dependency-check includes an analyzer that will check for the Maven GAV -(Group/Artifact/Version) information for artifacts in the scanned area. By -default the information comes from [Maven Central][1], but can be configured to -use a local repository if necessary. If the artifact's hash is found in the -configured Nexus repository, its GAV is recorded as an Identifier and the Group -is collected as Vendor evidence, the Artifact is collected as Product evidence, -and the Version is collected as Version evidence. +The Nexus Analyzer has been superceded by the Central Analyzer. If both the +Central Analyzer and Nexus Analyzer are enabled and the Nexus URL has not +been configured to point to an instance of Nexus Pro the Nexus Analyzer will +disable itself. -Default Configuration ---------------------- - -By default, the Nexus analyzer uses the [Sonatype Nexus Repository][2] to search -for SHA-1 hashes of dependencies. If the proxy is configured for Dependency -Check, that proxy is used in order to connect to the Nexus Central repository. -So if you're using `--proxyurl` on the command-line, the `proxyUrl` setting in -the Maven plugin, or the `proxyUrl` attribute in the Ant task, that proxy will -be used by default. Also, the proxy port, user, and password configured globally -are used as well. - -Overriding Defaults -------------------- - -If you have an internal Nexus repository you want to use, Dependency Check can -be configured to use this repository rather than Sonatype. This needs to be a -Nexus repository (support for Artifactory is planned). For a normal installation -of Nexus, you would append `/service/local/` to the root of the URL to your -Nexus repository. This URL can be set as: - -* `analyzer.nexus.url` in `dependencycheck.properties` -* `--nexus ` in the CLI -* The `nexusUrl` property in the Maven plugin -* The `nexusUrl` attribute in the Ant task - -If this repository is internal and should not use the proxy, you can disable the -proxy for just the Nexus analyzer. Setting this makes no difference if a proxy -is not configured. - -* `analyzer.nexus.proxy=false` in `dependencycheck.properties` -* `--nexusUsesProxy false` in the CLI -* The `nexusUsesProxy` property in the Maven plugin -* The `nexusUsesProxy` attribute in the Ant task - -Finally, the Nexus analyzer can be disabled altogether. - -* `analyzer.nexus.enabled=false` in `dependencycheck.properties` -* `--disableNexus` in the CLI -* `nexusAnalyzerEnabled` property in the Maven plugin -* `nexusAnalyzerEnabled` attribute in the Ant task +The Nexus Analyzer will check for the Maven GAV (Group/Artifact/Version) information +for artifacts in the scanned area. This is done by determining if an artifact exists +in a Nexus Pro installation using the SHA-1 hash of the artifact scanned. If the +artifact's hash is found in the configured Nexus repository, its GAV is recorded as +an Identifier and the Group is collected as Vendor evidence, the Artifact is +collected as Product evidence, and the Version is collected as Version evidence. Logging ------- @@ -60,9 +23,9 @@ You may see a log message similar to the following during analysis: At the beginning of analysis, a check is made by the Nexus analyzer to see if it is able to reach the configured Nexus service, and if it cannot be reached, the -analyzer will be disabled. If you see this message, you can use the -configuration settings described above to resolve the issue, or disable the -analyzer altogether. +analyzer will be disabled. If you see this message, you can use the configuration +settings described in either the CLI, Ant, Maven, or Jenkins plugins to resolve +the issue, or disable the analyzer altogether. [1]: http://search.maven.org/ "Maven Central" [2]: https://repository.sonatype.org/ "Sonatype Nexus Repository" diff --git a/src/site/site.xml b/src/site/site.xml index 3ad2fb0c2..9967446cb 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -98,6 +98,9 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. Jar Analyzer + + Central Analyzer + Nexus Analyzer