updated to address issue #193

Former-commit-id: 8361c2fdbec4191e52db16b870406e3e45d97d0d
This commit is contained in:
Jeremy Long
2015-02-07 18:16:07 -05:00
parent a841027d48
commit d5753b9589
3 changed files with 74 additions and 60 deletions

View File

@@ -41,8 +41,8 @@ import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.utils.Settings; 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 * Maven Plugin that checks project dependencies and the dependencies of all child modules to see if they have any known published
* published vulnerabilities. * vulnerabilities.
* *
* @author Jeremy Long <jeremy.long@owasp.org> * @author Jeremy Long <jeremy.long@owasp.org>
*/ */
@@ -73,21 +73,27 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
if (getProject() == getReactorProjects().get(getReactorProjects().size() - 1)) { if (getProject() == getReactorProjects().get(getReactorProjects().size() - 1)) {
final Map<MavenProject, Set<MavenProject>> children = buildAggregateInfo(); final Map<MavenProject, Set<MavenProject>> children = buildAggregateInfo();
boolean hasOrchestration = false;
for (MavenProject current : getReactorProjects()) { for (MavenProject current : getReactorProjects()) {
final List<Dependency> dependencies = readDataFile(current); final File outputDir = getCorrectOutputDirectory(current);
final List<MavenProject> childProjects = getAllChildren(current, children); if (outputDir == null) { //dc was never run on this project. write the ser to the target.
//check for orchestration build - execution root with no children or dependencies engine.getDependencies().clear();
if ((dependencies == null || dependencies.isEmpty()) && childProjects.isEmpty() && current.isExecutionRoot()) { engine.resetFileTypeAnalyzers();
hasOrchestration = true; scanArtifacts(current, engine);
engine.analyzeDependencies();
final File target = new File(current.getBuild().getOutputDirectory()).getParentFile();
writeDataFile(current, target, engine.getDependencies());
showSummary(current, engine.getDependencies());
} }
} }
for (MavenProject current : getReactorProjects()) { for (MavenProject current : getReactorProjects()) {
List<Dependency> dependencies = readDataFile(current); List<Dependency> dependencies = readDataFile(current);
final List<MavenProject> childProjects = getAllChildren(current, children); final List<MavenProject> childProjects = getAllChildren(current, children);
//check for orchestration build - execution root with no children or dependencies //check for orchestration build - execution root with no children or dependencies
if ((dependencies == null || dependencies.isEmpty()) && childProjects.isEmpty() && current.isExecutionRoot()) { if ((dependencies == null || dependencies.isEmpty()) && childProjects.isEmpty() && current.isExecutionRoot()) {
engine.getDependencies().clear();
engine.resetFileTypeAnalyzers(); engine.resetFileTypeAnalyzers();
for (MavenProject mod : getReactorProjects()) { for (MavenProject mod : getReactorProjects()) {
scanArtifacts(mod, engine); scanArtifacts(mod, engine);
@@ -113,14 +119,13 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
LOGGER.log(Level.FINE, "Bundling Exception", ex); LOGGER.log(Level.FINE, "Bundling Exception", ex);
} }
} }
try { File outputDir = getCorrectOutputDirectory(current);
final File outputDir = getCorrectOutputDirectory(current); if (outputDir == null) {
writeReports(engine, current, outputDir); //in some regards we shouldn't be writting this, but we are anyway.
} catch (MojoExecutionException ex) { //we shouldn't write this because nothing is configured to generate this report.
if (!hasOrchestration) { outputDir = new File(current.getBuild().getOutputDirectory()).getParentFile();
throw ex;
} // else ignore this
} }
writeReports(engine, current, outputDir);
} }
} }
engine.cleanup(); engine.cleanup();
@@ -183,8 +188,7 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
* *
* @return the Engine used to execute dependency-check * @return the Engine used to execute dependency-check
* @throws MojoExecutionException thrown if there is an exception running the mojo * @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 * @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are identified.
* identified.
*/ */
protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException { protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException {
final Engine engine; final Engine engine;
@@ -196,8 +200,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
} }
scanArtifacts(getProject(), engine); scanArtifacts(getProject(), engine);
engine.analyzeDependencies(); engine.analyzeDependencies();
writeDataFile(engine.getDependencies()); writeDataFile(getProject(), null, engine.getDependencies());
showSummary(engine.getDependencies()); showSummary(getProject(), engine.getDependencies());
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
return engine; return engine;
} }

View File

@@ -101,21 +101,29 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Parameter(property = "logFile", defaultValue = "") @Parameter(property = "logFile", defaultValue = "")
private String logFile = null; private String logFile = null;
//"project.reporting.outputDirectory"
/** /**
* The output directory. This generally maps to "target". * The output directory. This generally maps to "target".
*/ */
@Parameter(defaultValue = "${project.build.directory}", required = true) @Parameter(defaultValue = "${project.build.directory}", required = true)
private File outputDirectory; private File outputDirectory;
/** /**
* Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 * Specifies the destination directory for the generated Dependency-Check report. This generally maps to "target/site".
* which means since the CVSS scores are 0-10, by default the build will never fail. */
//Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
@Parameter(property = "project.reporting.outputDirectory", required = true)
private File reportOutputDirectory;
/**
* 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.
*/ */
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Parameter(property = "failBuildOnCVSS", defaultValue = "11", required = true) @Parameter(property = "failBuildOnCVSS", defaultValue = "11", required = true)
private float failBuildOnCVSS = 11; private float failBuildOnCVSS = 11;
/** /**
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. Default
* false. Default is true. * is true.
*/ */
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Parameter(property = "autoupdate", defaultValue = "true", required = true) @Parameter(property = "autoupdate", defaultValue = "true", required = true)
@@ -129,8 +137,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
@Deprecated @Deprecated
private boolean aggregate; private boolean aggregate;
/** /**
* The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this * The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the
* within the Site plug-in unless the externalReport is set to true. Default is HTML. * Site plug-in unless the externalReport is set to true. Default is HTML.
*/ */
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Parameter(property = "format", defaultValue = "HTML", required = true) @Parameter(property = "format", defaultValue = "HTML", required = true)
@@ -317,13 +325,6 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
@Parameter(property = "externalReport") @Parameter(property = "externalReport")
@Deprecated @Deprecated
private String externalReport = null; private String externalReport = null;
/**
* 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;
// </editor-fold> // </editor-fold>
//<editor-fold defaultstate="collapsed" desc="Base Maven implementation"> //<editor-fold defaultstate="collapsed" desc="Base Maven implementation">
@@ -341,8 +342,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
} }
/** /**
* Checks if the aggregate configuration parameter has been set to true. If it has a MojoExecutionException is * Checks if the aggregate configuration parameter has been set to true. If it has a MojoExecutionException is thrown because
* thrown because the aggregate configuration parameter is no longer supported. * the aggregate configuration parameter is no longer supported.
* *
* @throws MojoExecutionException thrown if aggregate is set to true * @throws MojoExecutionException thrown if aggregate is set to true
*/ */
@@ -405,15 +406,13 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
* *
* @param current the Maven project to get the output directory from * @param current the Maven project to get the output directory from
* @return the directory to write the report(s) * @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 { protected File getCorrectOutputDirectory(MavenProject current) {
final Object obj = current.getContextValue(getOutputDirectoryContextKey()); final Object obj = current.getContextValue(getOutputDirectoryContextKey());
if (obj != null && obj instanceof File) { if (obj != null && obj instanceof File) {
return (File) obj; return (File) obj;
} else {
throw new MojoExecutionException(String.format("Unable to determine output directory for '%s'", current.getName()));
} }
return null;
} }
/** /**
@@ -526,15 +525,19 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
* @throws DatabaseException thrown if there is a database exception * @throws DatabaseException thrown if there is a database exception
*/ */
protected Engine initializeEngine() throws DatabaseException { protected Engine initializeEngine() throws DatabaseException {
final InputStream in = BaseDependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE); final InputStream in = BaseDependencyCheckMojo.class
.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
LogUtils.prepareLogger(in, logFile); LogUtils.prepareLogger(in, logFile);
populateSettings(); populateSettings();
return new Engine(this.project, this.reactorProjects);
return new Engine(this.project,
this.reactorProjects);
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
* properties required to change the proxy url, port, and connection timeout. * required to change the proxy url, port, and connection timeout.
*/ */
private void populateSettings() { private void populateSettings() {
Settings.initialize(); Settings.initialize();
@@ -699,9 +702,9 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
/** /**
* Returns a reference to the current project. This method is used instead of auto-binding the project via component * 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 <code>@Component MavenProject project;</code> * annotation in concrete implementations of this. If the child has a <code>@Component MavenProject project;</code> defined
* defined then the abstract class (i.e. this class) will not have access to the current project (just the way Maven * then the abstract class (i.e. this class) will not have access to the current project (just the way Maven works with the
* works with the binding). * binding).
* *
* @return returns a reference to the current project * @return returns a reference to the current project
*/ */
@@ -799,9 +802,10 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
/** /**
* Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries. * Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries.
* *
* @param mp the Maven project for which the summary is shown
* @param dependencies a list of dependency objects * @param dependencies a list of dependency objects
*/ */
protected void showSummary(List<Dependency> dependencies) { protected void showSummary(MavenProject mp, List<Dependency> dependencies) {
if (showSummary) { if (showSummary) {
final StringBuilder summary = new StringBuilder(); final StringBuilder summary = new StringBuilder();
for (Dependency d : dependencies) { for (Dependency d : dependencies) {
@@ -830,8 +834,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
} }
} }
if (summary.length() > 0) { if (summary.length() > 0) {
final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities:%n%n%s" final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities in %s:%n%n%s"
+ "%n%nSee the dependency-check report for more details.%n%n", summary.toString()); + "%n%nSee the dependency-check report for more details.%n%n", mp.getName(), summary.toString());
LOGGER.log(Level.WARNING, msg); LOGGER.log(Level.WARNING, msg);
} }
} }
@@ -840,8 +844,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
//</editor-fold> //</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Methods to read/write the serialized data file"> //<editor-fold defaultstate="collapsed" desc="Methods to read/write the serialized data file">
/** /**
* Returns the key used to store the path to the data file that is saved by <code>writeDataFile()</code>. This key * Returns the key used to store the path to the data file that is saved by <code>writeDataFile()</code>. This key is used in
* is used in the <code>MavenProject.(set|get)ContextValue</code>. * the <code>MavenProject.(set|get)ContextValue</code>.
* *
* @return the key used to store the path to the data file * @return the key used to store the path to the data file
*/ */
@@ -862,12 +866,18 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
/** /**
* Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase. * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase.
* *
* @param mp the mMven project for which the data file was created
* @param writeTo the directory to write the data file
* @param dependencies the list of dependencies to serialize * @param dependencies the list of dependencies to serialize
*/ */
protected void writeDataFile(List<Dependency> dependencies) { protected void writeDataFile(MavenProject mp, File writeTo, List<Dependency> dependencies) {
File file = null; File file;
if (dependencies != null && project.getContextValue(this.getDataFileContextKey()) == null) { if (dependencies != null && mp.getContextValue(this.getDataFileContextKey()) == null) {
file = new File(project.getBuild().getDirectory(), dataFileName); if (writeTo != null) {
file = new File(mp.getBuild().getDirectory(), dataFileName);
} else {
file = new File(writeTo, dataFileName);
}
OutputStream os = null; OutputStream os = null;
OutputStream bos = null; OutputStream bos = null;
ObjectOutputStream out = null; ObjectOutputStream out = null;
@@ -881,8 +891,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
//call reset to prevent resource leaks per //call reset to prevent resource leaks per
//https://www.securecoding.cert.org/confluence/display/java/SER10-J.+Avoid+memory+and+resource+leaks+during+serialization //https://www.securecoding.cert.org/confluence/display/java/SER10-J.+Avoid+memory+and+resource+leaks+during+serialization
out.reset(); out.reset();
project.setContextValue(this.getDataFileContextKey(), file.getAbsolutePath()); mp.setContextValue(this.getDataFileContextKey(), file.getAbsolutePath());
LOGGER.fine(String.format("Serialized data file written to '%s'", file.getAbsolutePath())); LOGGER.fine(String.format("Serialized data file written to '%s' for %s", file.getAbsolutePath(), mp.getName()));
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.WARNING, "Unable to create data file used for report aggregation; " LOGGER.log(Level.WARNING, "Unable to create data file used for report aggregation; "
+ "if report aggregation is being used the results may be incomplete."); + "if report aggregation is being used the results may be incomplete.");
@@ -914,8 +924,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
} }
/** /**
* Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and "aggregate"
* "aggregate" phase. * phase.
* *
* @param project the Maven project to read the data file from * @param project the Maven project to read the data file from
* @return a <code>Engine</code> object populated with dependencies if the serialized data file exists; otherwise * @return a <code>Engine</code> object populated with dependencies if the serialized data file exists; otherwise

View File

@@ -86,8 +86,8 @@ public class CheckMojo extends BaseDependencyCheckMojo {
} else { } else {
engine.analyzeDependencies(); engine.analyzeDependencies();
writeReports(engine, getProject(), getCorrectOutputDirectory()); writeReports(engine, getProject(), getCorrectOutputDirectory());
writeDataFile(engine.getDependencies()); writeDataFile(getProject(), null, engine.getDependencies());
showSummary(engine.getDependencies()); showSummary(getProject(), engine.getDependencies());
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
} }
engine.cleanup(); engine.cleanup();