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