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