View Javadoc
1   /*
2    * This file is part of dependency-check-maven.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2014 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.maven;
19  
20  import java.io.BufferedOutputStream;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileNotFoundException;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.ObjectInputStream;
28  import java.io.ObjectOutputStream;
29  import java.io.OutputStream;
30  import java.util.List;
31  import java.util.Locale;
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
34  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
35  import org.apache.maven.artifact.repository.ArtifactRepository;
36  import org.apache.maven.artifact.versioning.ArtifactVersion;
37  import org.apache.maven.doxia.sink.Sink;
38  import org.apache.maven.plugin.AbstractMojo;
39  import org.apache.maven.plugin.MojoExecutionException;
40  import org.apache.maven.plugin.MojoFailureException;
41  import org.apache.maven.plugins.annotations.Component;
42  import org.apache.maven.plugins.annotations.Parameter;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.reporting.MavenReport;
45  import org.apache.maven.reporting.MavenReportException;
46  import org.apache.maven.settings.Proxy;
47  import org.owasp.dependencycheck.data.nexus.MavenArtifact;
48  import org.owasp.dependencycheck.data.nvdcve.CveDB;
49  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
50  import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
51  import org.owasp.dependencycheck.dependency.Confidence;
52  import org.owasp.dependencycheck.dependency.Dependency;
53  import org.owasp.dependencycheck.dependency.Identifier;
54  import org.owasp.dependencycheck.dependency.Vulnerability;
55  import org.owasp.dependencycheck.reporting.ReportGenerator;
56  import org.owasp.dependencycheck.utils.DependencyVersion;
57  import org.owasp.dependencycheck.utils.Settings;
58  
59  /**
60   *
61   * @author Jeremy Long
62   */
63  public abstract class BaseDependencyCheckMojo extends AbstractMojo implements MavenReport {
64  
65      //<editor-fold defaultstate="collapsed" desc="Private fields">
66      /**
67       * The properties file location.
68       */
69      private static final String PROPERTIES_FILE = "mojo.properties";
70      /**
71       * System specific new line character.
72       */
73      private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
74      /**
75       * Sets whether or not the external report format should be used.
76       */
77      @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true)
78      private String dataFileName;
79  
80      //</editor-fold>
81      // <editor-fold defaultstate="collapsed" desc="Maven bound parameters and components">
82      /**
83       * The Maven Project Object.
84       */
85      @Component
86      private MavenProject project;
87      /**
88       * The meta data source for retrieving artifact version information.
89       */
90      @Component
91      private ArtifactMetadataSource metadataSource;
92      /**
93       * A reference to the local repository.
94       */
95      @Parameter(property = "localRepository", readonly = true)
96      private ArtifactRepository localRepository;
97      /**
98       * References to the remote repositories.
99       */
100     @Parameter(property = "project.remoteArtifactRepositories", readonly = true)
101     private List<ArtifactRepository> remoteRepositories;
102     /**
103      * List of Maven project of the current build
104      */
105     @Parameter(readonly = true, required = true, property = "reactorProjects")
106     private List<MavenProject> reactorProjects;
107 
108     /**
109      * The output directory. This generally maps to "target".
110      */
111     @Parameter(defaultValue = "${project.build.directory}", required = true)
112     private File outputDirectory;
113     /**
114      * Specifies the destination directory for the generated Dependency-Check report. This generally maps to "target/site".
115      */
116     @Parameter(property = "project.reporting.outputDirectory", required = true)
117     private File reportOutputDirectory;
118     /**
119      * Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which
120      * means since the CVSS scores are 0-10, by default the build will never fail.
121      */
122     @SuppressWarnings("CanBeFinal")
123     @Parameter(property = "failBuildOnCVSS", defaultValue = "11", required = true)
124     private float failBuildOnCVSS = 11;
125     /**
126      * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. Default
127      * is true.
128      */
129     @SuppressWarnings("CanBeFinal")
130     @Parameter(property = "autoupdate", defaultValue = "true", required = true)
131     private boolean autoUpdate = true;
132     /**
133      * Generate aggregate reports in multi-module projects.
134      *
135      * @deprecated use the aggregate goal instead
136      */
137     @Parameter(property = "aggregate", defaultValue = "false")
138     @Deprecated
139     private boolean aggregate;
140     /**
141      * The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the
142      * Site plug-in unless the externalReport is set to true. Default is HTML.
143      */
144     @SuppressWarnings("CanBeFinal")
145     @Parameter(property = "format", defaultValue = "HTML", required = true)
146     private String format = "HTML";
147     /**
148      * The Maven settings.
149      */
150     @Parameter(property = "mavenSettings", defaultValue = "${settings}", required = false)
151     private org.apache.maven.settings.Settings mavenSettings;
152 
153     /**
154      * The maven settings proxy id.
155      */
156     @SuppressWarnings("CanBeFinal")
157     @Parameter(property = "mavenSettingsProxyId", required = false)
158     private String mavenSettingsProxyId;
159 
160     /**
161      * The Connection Timeout.
162      */
163     @SuppressWarnings("CanBeFinal")
164     @Parameter(property = "connectionTimeout", defaultValue = "", required = false)
165     private String connectionTimeout = null;
166     /**
167      * The path to the suppression file.
168      */
169     @SuppressWarnings("CanBeFinal")
170     @Parameter(property = "suppressionFile", defaultValue = "", required = false)
171     private String suppressionFile = null;
172     /**
173      * Flag indicating whether or not to show a summary in the output.
174      */
175     @SuppressWarnings("CanBeFinal")
176     @Parameter(property = "showSummary", defaultValue = "true", required = false)
177     private boolean showSummary = true;
178 
179     /**
180      * Whether or not the Jar Analyzer is enabled.
181      */
182     @SuppressWarnings("CanBeFinal")
183     @Parameter(property = "jarAnalyzerEnabled", defaultValue = "true", required = false)
184     private boolean jarAnalyzerEnabled = true;
185 
186     /**
187      * Whether or not the Archive Analyzer is enabled.
188      */
189     @SuppressWarnings("CanBeFinal")
190     @Parameter(property = "archiveAnalyzerEnabled", defaultValue = "true", required = false)
191     private boolean archiveAnalyzerEnabled = true;
192 
193     /**
194      * Whether or not the .NET Assembly Analyzer is enabled.
195      */
196     @SuppressWarnings("CanBeFinal")
197     @Parameter(property = "assemblyAnalyzerEnabled", defaultValue = "true", required = false)
198     private boolean assemblyAnalyzerEnabled = true;
199 
200     /**
201      * Whether or not the .NET Nuspec Analyzer is enabled.
202      */
203     @SuppressWarnings("CanBeFinal")
204     @Parameter(property = "nuspecAnalyzerEnabled", defaultValue = "true", required = false)
205     private boolean nuspecAnalyzerEnabled = true;
206 
207     /**
208      * Whether or not the Central Analyzer is enabled.
209      */
210     @SuppressWarnings("CanBeFinal")
211     @Parameter(property = "centralAnalyzerEnabled", defaultValue = "true", required = false)
212     private boolean centralAnalyzerEnabled = true;
213 
214     /**
215      * Whether or not the Nexus Analyzer is enabled.
216      */
217     @SuppressWarnings("CanBeFinal")
218     @Parameter(property = "nexusAnalyzerEnabled", defaultValue = "true", required = false)
219     private boolean nexusAnalyzerEnabled = true;
220 
221     /**
222      * The URL of a Nexus server's REST API end point (http://domain/nexus/service/local).
223      */
224     @Parameter(property = "nexusUrl", defaultValue = "", required = false)
225     private String nexusUrl;
226     /**
227      * Whether or not the configured proxy is used to connect to Nexus.
228      */
229     @Parameter(property = "nexusUsesProxy", defaultValue = "true", required = false)
230     private boolean nexusUsesProxy = true;
231     /**
232      * The database connection string.
233      */
234     @Parameter(property = "connectionString", defaultValue = "", required = false)
235     private String connectionString;
236     /**
237      * The database driver name. An example would be org.h2.Driver.
238      */
239     @Parameter(property = "databaseDriverName", defaultValue = "", required = false)
240     private String databaseDriverName;
241     /**
242      * The path to the database driver if it is not on the class path.
243      */
244     @Parameter(property = "databaseDriverPath", defaultValue = "", required = false)
245     private String databaseDriverPath;
246     /**
247      * The database user name.
248      */
249     @Parameter(property = "databaseUser", defaultValue = "", required = false)
250     private String databaseUser;
251     /**
252      * The password to use when connecting to the database.
253      */
254     @Parameter(property = "databasePassword", defaultValue = "", required = false)
255     private String databasePassword;
256     /**
257      * A comma-separated list of file extensions to add to analysis next to jar, zip, ....
258      */
259     @Parameter(property = "zipExtensions", required = false)
260     private String zipExtensions;
261     /**
262      * Skip Analysis for Test Scope Dependencies.
263      */
264     @SuppressWarnings("CanBeFinal")
265     @Parameter(property = "skipTestScope", defaultValue = "true", required = false)
266     private boolean skipTestScope = true;
267     /**
268      * Skip Analysis for Runtime Scope Dependencies.
269      */
270     @SuppressWarnings("CanBeFinal")
271     @Parameter(property = "skipRuntimeScope", defaultValue = "false", required = false)
272     private boolean skipRuntimeScope = false;
273     /**
274      * Skip Analysis for Provided Scope Dependencies.
275      */
276     @SuppressWarnings("CanBeFinal")
277     @Parameter(property = "skipProvidedScope", defaultValue = "false", required = false)
278     private boolean skipProvidedScope = false;
279     /**
280      * The data directory, hold DC SQL DB.
281      */
282     @Parameter(property = "dataDirectory", defaultValue = "", required = false)
283     private String dataDirectory;
284     /**
285      * Data Mirror URL for CVE 1.2.
286      */
287     @Parameter(property = "cveUrl12Modified", defaultValue = "", required = false)
288     private String cveUrl12Modified;
289     /**
290      * Data Mirror URL for CVE 2.0.
291      */
292     @Parameter(property = "cveUrl20Modified", defaultValue = "", required = false)
293     private String cveUrl20Modified;
294     /**
295      * Base Data Mirror URL for CVE 1.2.
296      */
297     @Parameter(property = "cveUrl12Base", defaultValue = "", required = false)
298     private String cveUrl12Base;
299     /**
300      * Data Mirror URL for CVE 2.0.
301      */
302     @Parameter(property = "cveUrl20Base", defaultValue = "", required = false)
303     private String cveUrl20Base;
304 
305     /**
306      * The path to mono for .NET Assembly analysis on non-windows systems.
307      */
308     @Parameter(property = "pathToMono", defaultValue = "", required = false)
309     private String pathToMono;
310 
311     /**
312      * The Proxy URL.
313      *
314      * @deprecated Please use mavenSettings instead
315      */
316     @SuppressWarnings("CanBeFinal")
317     @Parameter(property = "proxyUrl", defaultValue = "", required = false)
318     @Deprecated
319     private String proxyUrl = null;
320     /**
321      * Sets whether or not the external report format should be used.
322      *
323      * @deprecated the internal report is no longer supported
324      */
325     @SuppressWarnings("CanBeFinal")
326     @Parameter(property = "externalReport")
327     @Deprecated
328     private String externalReport = null;
329     // </editor-fold>
330     //<editor-fold defaultstate="collapsed" desc="Base Maven implementation">
331 
332     /**
333      * Executes dependency-check.
334      *
335      * @throws MojoExecutionException thrown if there is an exception executing the mojo
336      * @throws MojoFailureException thrown if dependency-check failed the build
337      */
338     @Override
339     public void execute() throws MojoExecutionException, MojoFailureException {
340         validateAggregate();
341         project.setContextValue(getOutputDirectoryContextKey(), this.outputDirectory);
342         runCheck();
343     }
344 
345     /**
346      * Checks if the aggregate configuration parameter has been set to true. If it has a MojoExecutionException is thrown because
347      * the aggregate configuration parameter is no longer supported.
348      *
349      * @throws MojoExecutionException thrown if aggregate is set to true
350      */
351     private void validateAggregate() throws MojoExecutionException {
352         if (aggregate) {
353             final String msg = "Aggregate configuration detected - as of dependency-check 1.2.8 this no longer supported. "
354                     + "Please use the aggregate goal instead.";
355             throw new MojoExecutionException(msg);
356         }
357     }
358 
359     /**
360      * Generates the Dependency-Check Site Report.
361      *
362      * @param sink the sink to write the report to
363      * @param locale the locale to use when generating the report
364      * @throws MavenReportException if a maven report exception occurs
365      * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale)} instead.
366      */
367     @Deprecated
368     public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException {
369         generate((Sink) sink, locale);
370     }
371 
372     /**
373      * Generates the Dependency-Check Site Report.
374      *
375      * @param sink the sink to write the report to
376      * @param locale the locale to use when generating the report
377      * @throws MavenReportException if a maven report exception occurs
378      */
379     public void generate(Sink sink, Locale locale) throws MavenReportException {
380         try {
381             validateAggregate();
382         } catch (MojoExecutionException ex) {
383             throw new MavenReportException(ex.getMessage());
384         }
385         project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory());
386         try {
387             runCheck();
388         } catch (MojoExecutionException ex) {
389             throw new MavenReportException(ex.getMessage(), ex);
390         } catch (MojoFailureException ex) {
391             getLog().warn("Vulnerabilities were identifies that exceed the CVSS threshold for failing the build");
392         }
393     }
394 
395     /**
396      * Returns the correct output directory depending on if a site is being executed or not.
397      *
398      * @return the directory to write the report(s)
399      * @throws MojoExecutionException thrown if there is an error loading the file path
400      */
401     protected File getCorrectOutputDirectory() throws MojoExecutionException {
402         return getCorrectOutputDirectory(this.project);
403     }
404 
405     /**
406      * Returns the correct output directory depending on if a site is being executed or not.
407      *
408      * @param current the Maven project to get the output directory from
409      * @return the directory to write the report(s)
410      */
411     protected File getCorrectOutputDirectory(MavenProject current) {
412         final Object obj = current.getContextValue(getOutputDirectoryContextKey());
413         if (obj != null && obj instanceof File) {
414             return (File) obj;
415         }
416         File target = new File(current.getBuild().getDirectory());
417         if (target.getParentFile() != null && "target".equals(target.getParentFile().getName())) {
418             target = target.getParentFile();
419         }
420         return target;
421     }
422 
423     /**
424      * Returns the correct output directory depending on if a site is being executed or not.
425      *
426      * @param current the Maven project to get the output directory from
427      * @return the directory to write the report(s)
428      */
429     protected File getDataFile(MavenProject current) {
430         if (getLog().isDebugEnabled()) {
431             getLog().debug(String.format("Getting data filefor %s using key '%s'", current.getName(), getDataFileContextKey()));
432         }
433         final Object obj = current.getContextValue(getDataFileContextKey());
434         if (obj != null) {
435             if (obj instanceof File) {
436                 return (File) obj;
437             }
438         } else {
439             if (getLog().isDebugEnabled()) {
440                 getLog().debug("Context value not found");
441             }
442         }
443         return null;
444     }
445 
446     /**
447      * Scans the project's artifacts and adds them to the engine's dependency list.
448      *
449      * @param project the project to scan the dependencies of
450      * @param engine the engine to use to scan the dependencies
451      */
452     protected void scanArtifacts(MavenProject project, Engine engine) {
453         for (Artifact a : project.getArtifacts()) {
454             if (excludeFromScan(a)) {
455                 continue;
456             }
457             final List<Dependency> deps = engine.scan(a.getFile().getAbsoluteFile());
458             if (deps != null) {
459                 if (deps.size() == 1) {
460                     final Dependency d = deps.get(0);
461                     if (d != null) {
462                         final MavenArtifact ma = new MavenArtifact(a.getGroupId(), a.getArtifactId(), a.getVersion());
463                         d.addAsEvidence("pom", ma, Confidence.HIGHEST);
464                         d.addProjectReference(project.getName());
465                         if (getLog().isDebugEnabled()) {
466                             getLog().debug(String.format("Adding project reference %s on dependency %s", project.getName(),
467                                     d.getDisplayFileName()));
468                         }
469                         if (metadataSource != null) {
470                             try {
471                                 final DependencyVersion currentVersion = new DependencyVersion(a.getVersion());
472                                 final List<ArtifactVersion> versions = metadataSource.retrieveAvailableVersions(a,
473                                         localRepository, remoteRepositories);
474                                 for (ArtifactVersion av : versions) {
475                                     final DependencyVersion newVersion = new DependencyVersion(av.toString());
476                                     if (currentVersion.compareTo(newVersion) < 0) {
477                                         d.addAvailableVersion(av.toString());
478                                     }
479                                 }
480                             } catch (ArtifactMetadataRetrievalException ex) {
481                                 getLog().warn(
482                                         "Unable to check for new versions of dependencies; see the log for more details.");
483                                 if (getLog().isDebugEnabled()) {
484                                     getLog().debug("", ex);
485                                 }
486                             } catch (Throwable t) {
487                                 getLog().warn(
488                                         "Unexpected error occured checking for new versions; see the log for more details.");
489                                 if (getLog().isDebugEnabled()) {
490                                     getLog().debug("", t);
491                                 }
492                             }
493                         }
494                     }
495                 } else {
496                     if (getLog().isDebugEnabled()) {
497                         final String msg = String.format("More then 1 dependency was identified in first pass scan of '%s:%s:%s'",
498                                 a.getGroupId(), a.getArtifactId(), a.getVersion());
499                         getLog().debug(msg);
500                     }
501                 }
502             }
503         }
504     }
505 
506     /**
507      * Executes the dependency-check scan and generates the necassary report.
508      *
509      * @throws MojoExecutionException thrown if there is an exception running the scan
510      * @throws MojoFailureException thrown if dependency-check is configured to fail the build
511      */
512     public abstract void runCheck() throws MojoExecutionException, MojoFailureException;
513 
514     /**
515      * Sets the Reporting output directory.
516      *
517      * @param directory the output directory
518      */
519     @Override
520     public void setReportOutputDirectory(File directory) {
521         reportOutputDirectory = directory;
522     }
523 
524     /**
525      * Returns the report output directory.
526      *
527      * @return the report output directory
528      */
529     @Override
530     public File getReportOutputDirectory() {
531         return reportOutputDirectory;
532     }
533 
534     /**
535      * Returns the output directory.
536      *
537      * @return the output directory
538      */
539     public File getOutputDirectory() {
540         return outputDirectory;
541     }
542 
543     /**
544      * Returns whether this is an external report. This method always returns true.
545      *
546      * @return <code>true</code>
547      */
548     @Override
549     public final boolean isExternalReport() {
550         return true;
551     }
552 
553     /**
554      * Returns the output name.
555      *
556      * @return the output name
557      */
558     public String getOutputName() {
559         if ("HTML".equalsIgnoreCase(this.format) || "ALL".equalsIgnoreCase(this.format)) {
560             return "dependency-check-report";
561         } else if ("XML".equalsIgnoreCase(this.format)) {
562             return "dependency-check-report.xml#";
563         } else if ("VULN".equalsIgnoreCase(this.format)) {
564             return "dependency-check-vulnerability";
565         } else {
566             getLog().warn("Unknown report format used during site generation.");
567             return "dependency-check-report";
568         }
569     }
570 
571     /**
572      * Returns the category name.
573      *
574      * @return the category name
575      */
576     public String getCategoryName() {
577         return MavenReport.CATEGORY_PROJECT_REPORTS;
578     }
579     //</editor-fold>
580 
581     /**
582      * Initializes a new <code>Engine</code> that can be used for scanning.
583      *
584      * @return a newly instantiated <code>Engine</code>
585      * @throws DatabaseException thrown if there is a database exception
586      */
587     protected Engine initializeEngine() throws DatabaseException {
588         populateSettings();
589         return new Engine(this.project,
590                 this.reactorProjects);
591     }
592 
593     /**
594      * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
595      * required to change the proxy url, port, and connection timeout.
596      */
597     private void populateSettings() {
598         Settings.initialize();
599         InputStream mojoProperties = null;
600         try {
601             mojoProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
602             Settings.mergeProperties(mojoProperties);
603         } catch (IOException ex) {
604             getLog().warn("Unable to load the dependency-check ant task.properties file.");
605             if (getLog().isDebugEnabled()) {
606                 getLog().debug("", ex);
607             }
608         } finally {
609             if (mojoProperties != null) {
610                 try {
611                     mojoProperties.close();
612                 } catch (IOException ex) {
613                     if (getLog().isDebugEnabled()) {
614                         getLog().debug("", ex);
615                     }
616                 }
617             }
618         }
619 
620         Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
621         if (externalReport != null) {
622             getLog().warn("The 'externalReport' option was set; this configuration option has been removed. "
623                     + "Please update the dependency-check-maven plugin's configuration");
624         }
625 
626         if (proxyUrl != null && !proxyUrl.isEmpty()) {
627             getLog().warn("Deprecated configuration detected, proxyUrl will be ignored; use the maven settings " + "to configure the proxy instead");
628         }
629         final Proxy proxy = getMavenProxy();
630         if (proxy != null) {
631             Settings.setString(Settings.KEYS.PROXY_SERVER, proxy.getHost());
632             Settings.setString(Settings.KEYS.PROXY_PORT, Integer.toString(proxy.getPort()));
633             final String userName = proxy.getUsername();
634             final String password = proxy.getPassword();
635             if (userName != null) {
636                 Settings.setString(Settings.KEYS.PROXY_USERNAME, userName);
637             }
638             if (password != null) {
639                 Settings.setString(Settings.KEYS.PROXY_PASSWORD, password);
640             }
641 
642         }
643 
644         if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
645             Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
646         }
647         if (suppressionFile != null && !suppressionFile.isEmpty()) {
648             Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
649         }
650 
651         //File Type Analyzer Settings
652         //JAR ANALYZER
653         Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
654         //NUSPEC ANALYZER
655         Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled);
656         //NEXUS ANALYZER
657         Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled);
658         //NEXUS ANALYZER
659         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
660         if (nexusUrl != null && !nexusUrl.isEmpty()) {
661             Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
662         }
663         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
664         //ARCHIVE ANALYZER
665         Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled);
666         if (zipExtensions != null && !zipExtensions.isEmpty()) {
667             Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
668         }
669         //ASSEMBLY ANALYZER
670         Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled);
671         if (pathToMono != null && !pathToMono.isEmpty()) {
672             Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
673         }
674 
675         //Database configuration
676         if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
677             Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
678         }
679         if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
680             Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
681         }
682         if (connectionString != null && !connectionString.isEmpty()) {
683             Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
684         }
685         if (databaseUser != null && !databaseUser.isEmpty()) {
686             Settings.setString(Settings.KEYS.DB_USER, databaseUser);
687         }
688         if (databasePassword != null && !databasePassword.isEmpty()) {
689             Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
690         }
691         // Data Directory
692         if (dataDirectory != null && !dataDirectory.isEmpty()) {
693             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
694         }
695 
696         // Scope Exclusion
697         Settings.setBoolean(Settings.KEYS.SKIP_TEST_SCOPE, skipTestScope);
698         Settings.setBoolean(Settings.KEYS.SKIP_RUNTIME_SCOPE, skipRuntimeScope);
699         Settings.setBoolean(Settings.KEYS.SKIP_PROVIDED_SCOPE, skipProvidedScope);
700 
701         // CVE Data Mirroring
702         if (cveUrl12Modified != null && !cveUrl12Modified.isEmpty()) {
703             Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
704         }
705         if (cveUrl20Modified != null && !cveUrl20Modified.isEmpty()) {
706             Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
707         }
708         if (cveUrl12Base != null && !cveUrl12Base.isEmpty()) {
709             Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
710         }
711         if (cveUrl20Base != null && !cveUrl20Base.isEmpty()) {
712             Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
713         }
714     }
715 
716     /**
717      * Returns the maven proxy.
718      *
719      * @return the maven proxy
720      */
721     private Proxy getMavenProxy() {
722         if (mavenSettings != null) {
723             final List<Proxy> proxies = mavenSettings.getProxies();
724             if (proxies != null && !proxies.isEmpty()) {
725                 if (mavenSettingsProxyId != null) {
726                     for (Proxy proxy : proxies) {
727                         if (mavenSettingsProxyId.equalsIgnoreCase(proxy.getId())) {
728                             return proxy;
729                         }
730                     }
731                 } else if (proxies.size() == 1) {
732                     return proxies.get(0);
733                 } else {
734                     getLog().warn("Multiple proxy definitions exist in the Maven settings. In the dependency-check "
735                             + "configuration set the mavenSettingsProxyId so that the correct proxy will be used.");
736                     throw new IllegalStateException("Ambiguous proxy definition");
737                 }
738             }
739         }
740         return null;
741     }
742 
743     /**
744      * Tests is the artifact should be included in the scan (i.e. is the dependency in a scope that is being scanned).
745      *
746      * @param a the Artifact to test
747      * @return <code>true</code> if the artifact is in an excluded scope; otherwise <code>false</code>
748      */
749     protected boolean excludeFromScan(Artifact a) {
750         if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) {
751             return true;
752         }
753         if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) {
754             return true;
755         }
756         if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
757             return true;
758         }
759         return false;
760     }
761 
762     /**
763      * Returns a reference to the current project. This method is used instead of auto-binding the project via component
764      * annotation in concrete implementations of this. If the child has a <code>@Component MavenProject project;</code> defined
765      * then the abstract class (i.e. this class) will not have access to the current project (just the way Maven works with the
766      * binding).
767      *
768      * @return returns a reference to the current project
769      */
770     protected MavenProject getProject() {
771         return project;
772     }
773 
774     /**
775      * Returns the list of Maven Projects in this build.
776      *
777      * @return the list of Maven Projects in this build
778      */
779     protected List<MavenProject> getReactorProjects() {
780         return reactorProjects;
781     }
782 
783     /**
784      * Returns the report format.
785      *
786      * @return the report format
787      */
788     protected String getFormat() {
789         return format;
790     }
791 
792     /**
793      * Generates the reports for a given dependency-check engine.
794      *
795      * @param engine a dependency-check engine
796      * @param p the maven project
797      * @param outputDir the directory path to write the report(s).
798      */
799     protected void writeReports(Engine engine, MavenProject p, File outputDir) {
800         DatabaseProperties prop = null;
801         CveDB cve = null;
802         try {
803             cve = new CveDB();
804             cve.open();
805             prop = cve.getDatabaseProperties();
806         } catch (DatabaseException ex) {
807             if (getLog().isDebugEnabled()) {
808                 getLog().debug("Unable to retrieve DB Properties", ex);
809             }
810         } finally {
811             if (cve != null) {
812                 cve.close();
813             }
814         }
815         final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop);
816         try {
817             r.generateReports(outputDir.getAbsolutePath(), format);
818         } catch (IOException ex) {
819             getLog().error(
820                     "Unexpected exception occurred during analysis; please see the verbose error log for more details.");
821             if (getLog().isDebugEnabled()) {
822                 getLog().debug("", ex);
823             }
824         } catch (Throwable ex) {
825             getLog().error(
826                     "Unexpected exception occurred during analysis; please see the verbose error log for more details.");
827             if (getLog().isDebugEnabled()) {
828                 getLog().debug("", ex);
829             }
830         }
831     }
832 
833     //<editor-fold defaultstate="collapsed" desc="Methods to fail build or show summary">
834     /**
835      * Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the
836      * configuration.
837      *
838      * @param dependencies the list of dependency objects
839      * @throws MojoFailureException thrown if a CVSS score is found that is higher then the threshold set
840      */
841     protected void checkForFailure(List<Dependency> dependencies) throws MojoFailureException {
842         if (failBuildOnCVSS <= 10) {
843             final StringBuilder ids = new StringBuilder();
844             for (Dependency d : dependencies) {
845                 boolean addName = true;
846                 for (Vulnerability v : d.getVulnerabilities()) {
847                     if (v.getCvssScore() >= failBuildOnCVSS) {
848                         if (addName) {
849                             addName = false;
850                             ids.append(NEW_LINE).append(d.getFileName()).append(": ");
851                             ids.append(v.getName());
852                         } else {
853                             ids.append(", ").append(v.getName());
854                         }
855                     }
856                 }
857             }
858             if (ids.length() > 0) {
859                 final String msg = String.format("%n%nDependency-Check Failure:%n"
860                         + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
861                         + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
862                 throw new MojoFailureException(msg);
863             }
864         }
865     }
866 
867     /**
868      * Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries.
869      *
870      * @param mp the Maven project for which the summary is shown
871      * @param dependencies a list of dependency objects
872      */
873     protected void showSummary(MavenProject mp, List<Dependency> dependencies) {
874         if (showSummary) {
875             final StringBuilder summary = new StringBuilder();
876             for (Dependency d : dependencies) {
877                 boolean firstEntry = true;
878                 final StringBuilder ids = new StringBuilder();
879                 for (Vulnerability v : d.getVulnerabilities()) {
880                     if (firstEntry) {
881                         firstEntry = false;
882                     } else {
883                         ids.append(", ");
884                     }
885                     ids.append(v.getName());
886                 }
887                 if (ids.length() > 0) {
888                     summary.append(d.getFileName()).append(" (");
889                     firstEntry = true;
890                     for (Identifier id : d.getIdentifiers()) {
891                         if (firstEntry) {
892                             firstEntry = false;
893                         } else {
894                             summary.append(", ");
895                         }
896                         summary.append(id.getValue());
897                     }
898                     summary.append(") : ").append(ids).append(NEW_LINE);
899                 }
900             }
901             if (summary.length() > 0) {
902                 final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities in %s:%n%n%s"
903                         + "%n%nSee the dependency-check report for more details.%n%n", mp.getName(), summary.toString());
904                 getLog().warn(msg);
905             }
906         }
907     }
908 
909     //</editor-fold>
910     //<editor-fold defaultstate="collapsed" desc="Methods to read/write the serialized data file">
911     /**
912      * Returns the key used to store the path to the data file that is saved by <code>writeDataFile()</code>. This key is used in
913      * the <code>MavenProject.(set|get)ContextValue</code>.
914      *
915      * @return the key used to store the path to the data file
916      */
917     protected String getDataFileContextKey() {
918         return "dependency-check-path-" + dataFileName;
919     }
920 
921     /**
922      * Returns the key used to store the path to the output directory. When generating the report in the
923      * <code>executeAggregateReport()</code> the output directory should be obtained by using this key.
924      *
925      * @return the key used to store the path to the output directory
926      */
927     protected String getOutputDirectoryContextKey() {
928         return "dependency-output-dir-" + dataFileName;
929     }
930 
931     /**
932      * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase.
933      *
934      * @param mp the mMven project for which the data file was created
935      * @param writeTo the directory to write the data file
936      * @param dependencies the list of dependencies to serialize
937      */
938     protected void writeDataFile(MavenProject mp, File writeTo, List<Dependency> dependencies) {
939         File file;
940         //check to see if this was already written out
941         if (mp.getContextValue(this.getDataFileContextKey()) == null) {
942             if (writeTo == null) {
943                 file = new File(mp.getBuild().getDirectory());
944                 file = new File(file, dataFileName);
945             } else {
946                 file = new File(writeTo, dataFileName);
947             }
948             final File parent = file.getParentFile();
949             if (!parent.isDirectory()) {
950                 if (parent.mkdirs()) {
951                     getLog().error(String.format("Directory '%s' does not exist and cannot be created; unable to write data file.",
952                             parent.getAbsolutePath()));
953                 }
954             }
955 
956             OutputStream os = null;
957             OutputStream bos = null;
958             ObjectOutputStream out = null;
959             try {
960                 if (dependencies != null) {
961                     os = new FileOutputStream(file);
962                     bos = new BufferedOutputStream(os);
963                     out = new ObjectOutputStream(bos);
964                     out.writeObject(dependencies);
965                     out.flush();
966 
967                     //call reset to prevent resource leaks per
968                     //https://www.securecoding.cert.org/confluence/display/java/SER10-J.+Avoid+memory+and+resource+leaks+during+serialization
969                     out.reset();
970                 }
971                 if (getLog().isDebugEnabled()) {
972                     getLog().debug(String.format("Serialized data file written to '%s' for %s, referenced by key %s",
973                             file.getAbsolutePath(), mp.getName(), this.getDataFileContextKey()));
974                 }
975                 mp.setContextValue(this.getDataFileContextKey(), file.getAbsolutePath());
976             } catch (IOException ex) {
977                 getLog().warn("Unable to create data file used for report aggregation; "
978                         + "if report aggregation is being used the results may be incomplete.");
979                 if (getLog().isDebugEnabled()) {
980                     getLog().debug(ex.getMessage(), ex);
981                 }
982             } finally {
983                 if (out != null) {
984                     try {
985                         out.close();
986                     } catch (IOException ex) {
987                         if (getLog().isDebugEnabled()) {
988                             getLog().debug("ignore", ex);
989                         }
990                     }
991                 }
992                 if (bos != null) {
993                     try {
994                         bos.close();
995                     } catch (IOException ex) {
996                         if (getLog().isDebugEnabled()) {
997                             getLog().debug("ignore", ex);
998                         }
999                     }
1000                 }
1001                 if (os != null) {
1002                     try {
1003                         os.close();
1004                     } catch (IOException ex) {
1005                         if (getLog().isDebugEnabled()) {
1006                             getLog().debug("ignore", ex);
1007                         }
1008                     }
1009                 }
1010             }
1011         }
1012     }
1013 
1014     /**
1015      * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and "aggregate"
1016      * phase.
1017      *
1018      * @param project the Maven project to read the data file from
1019      * @return a <code>Engine</code> object populated with dependencies if the serialized data file exists; otherwise
1020      * <code>null</code> is returned
1021      */
1022     protected List<Dependency> readDataFile(MavenProject project) {
1023         final Object oPath = project.getContextValue(this.getDataFileContextKey());
1024         if (oPath == null) {
1025             return null;
1026         }
1027         List<Dependency> ret = null;
1028         final String path = (String) oPath;
1029         ObjectInputStream ois = null;
1030         try {
1031             ois = new ObjectInputStream(new FileInputStream(path));
1032             ret = (List<Dependency>) ois.readObject();
1033         } catch (FileNotFoundException ex) {
1034             //TODO fix logging
1035             getLog().error("", ex);
1036         } catch (IOException ex) {
1037             getLog().error("", ex);
1038         } catch (ClassNotFoundException ex) {
1039             getLog().error("", ex);
1040         } finally {
1041             if (ois != null) {
1042                 try {
1043                     ois.close();
1044                 } catch (IOException ex) {
1045                     getLog().error("", ex);
1046                 }
1047             }
1048         }
1049         return ret;
1050     }
1051     //</editor-fold>
1052 }