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         generatingSite = false;
391         if (skip) {
392             getLog().info("Skipping " + getName(Locale.US));
393         } else {
394             validateAggregate();
395             project.setContextValue(getOutputDirectoryContextKey(), this.outputDirectory);
396             runCheck();
397         }
398     }
399 
400     /**
401      * Checks if the aggregate configuration parameter has been set to true. If it has a MojoExecutionException is thrown because
402      * the aggregate configuration parameter is no longer supported.
403      *
404      * @throws MojoExecutionException thrown if aggregate is set to true
405      */
406     private void validateAggregate() throws MojoExecutionException {
407         if (aggregate != null && aggregate) {
408             final String msg = "Aggregate configuration detected - as of dependency-check 1.2.8 this no longer supported. "
409                     + "Please use the aggregate goal instead.";
410             throw new MojoExecutionException(msg);
411         }
412     }
413 
414     /**
415      * Generates the Dependency-Check Site Report.
416      *
417      * @param sink the sink to write the report to
418      * @param locale the locale to use when generating the report
419      * @throws MavenReportException if a maven report exception occurs
420      * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale)} instead.
421      */
422     @Override
423     @Deprecated
424     public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException {
425         generate((Sink) sink, locale);
426     }
427 
428     /**
429      * A flag indicating whether or not the maven site is being generated.
430      */
431     private boolean generatingSite = false;
432 
433     /**
434      * Returns true if the Maven site is being generated.
435      *
436      * @return true if the Maven site is being generated
437      */
438     protected boolean isGeneratingSite() {
439         return generatingSite;
440     }
441 
442     /**
443      * Generates the Dependency-Check Site Report.
444      *
445      * @param sink the sink to write the report to
446      * @param locale the locale to use when generating the report
447      * @throws MavenReportException if a maven report exception occurs
448      */
449     public void generate(Sink sink, Locale locale) throws MavenReportException {
450         generatingSite = true;
451         try {
452             validateAggregate();
453         } catch (MojoExecutionException ex) {
454             throw new MavenReportException(ex.getMessage());
455         }
456         project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory());
457         try {
458             runCheck();
459         } catch (MojoExecutionException ex) {
460             throw new MavenReportException(ex.getMessage(), ex);
461         } catch (MojoFailureException ex) {
462             getLog().warn("Vulnerabilities were identifies that exceed the CVSS threshold for failing the build");
463         }
464     }
465 
466     /**
467      * Returns the correct output directory depending on if a site is being executed or not.
468      *
469      * @return the directory to write the report(s)
470      * @throws MojoExecutionException thrown if there is an error loading the file path
471      */
472     protected File getCorrectOutputDirectory() throws MojoExecutionException {
473         return getCorrectOutputDirectory(this.project);
474     }
475 
476     /**
477      * Returns the correct output directory depending on if a site is being executed or not.
478      *
479      * @param current the Maven project to get the output directory from
480      * @return the directory to write the report(s)
481      */
482     protected File getCorrectOutputDirectory(MavenProject current) {
483         final Object obj = current.getContextValue(getOutputDirectoryContextKey());
484         if (obj != null && obj instanceof File) {
485             return (File) obj;
486         }
487         File target = new File(current.getBuild().getDirectory());
488         if (target.getParentFile() != null && "target".equals(target.getParentFile().getName())) {
489             target = target.getParentFile();
490         }
491         return target;
492     }
493 
494     /**
495      * Returns the correct output directory depending on if a site is being executed or not.
496      *
497      * @param current the Maven project to get the output directory from
498      * @return the directory to write the report(s)
499      */
500     protected File getDataFile(MavenProject current) {
501         if (getLog().isDebugEnabled()) {
502             getLog().debug(String.format("Getting data filefor %s using key '%s'", current.getName(), getDataFileContextKey()));
503         }
504         final Object obj = current.getContextValue(getDataFileContextKey());
505         if (obj != null) {
506             if (obj instanceof String) {
507                 final File f = new File((String) obj);
508                 return f;
509             }
510         } else {
511             if (getLog().isDebugEnabled()) {
512                 getLog().debug("Context value not found");
513             }
514         }
515         return null;
516     }
517 
518     /**
519      * Scans the project's artifacts and adds them to the engine's dependency list.
520      *
521      * @param project the project to scan the dependencies of
522      * @param engine the engine to use to scan the dependencies
523      */
524     protected void scanArtifacts(MavenProject project, Engine engine) {
525         for (Artifact a : project.getArtifacts()) {
526             if (excludeFromScan(a)) {
527                 continue;
528             }
529             final List<Dependency> deps = engine.scan(a.getFile().getAbsoluteFile());
530             if (deps != null) {
531                 if (deps.size() == 1) {
532                     final Dependency d = deps.get(0);
533                     if (d != null) {
534                         final MavenArtifact ma = new MavenArtifact(a.getGroupId(), a.getArtifactId(), a.getVersion());
535                         d.addAsEvidence("pom", ma, Confidence.HIGHEST);
536                         d.addProjectReference(project.getName());
537                         if (getLog().isDebugEnabled()) {
538                             getLog().debug(String.format("Adding project reference %s on dependency %s", project.getName(),
539                                     d.getDisplayFileName()));
540                         }
541                     }
542                 } else {
543                     if (getLog().isDebugEnabled()) {
544                         final String msg = String.format("More then 1 dependency was identified in first pass scan of '%s:%s:%s'",
545                                 a.getGroupId(), a.getArtifactId(), a.getVersion());
546                         getLog().debug(msg);
547                     }
548                 }
549             }
550         }
551     }
552 
553     /**
554      * Executes the dependency-check scan and generates the necassary report.
555      *
556      * @throws MojoExecutionException thrown if there is an exception running the scan
557      * @throws MojoFailureException thrown if dependency-check is configured to fail the build
558      */
559     public abstract void runCheck() throws MojoExecutionException, MojoFailureException;
560 
561     /**
562      * Sets the Reporting output directory.
563      *
564      * @param directory the output directory
565      */
566     @Override
567     public void setReportOutputDirectory(File directory) {
568         reportOutputDirectory = directory;
569     }
570 
571     /**
572      * Returns the report output directory.
573      *
574      * @return the report output directory
575      */
576     @Override
577     public File getReportOutputDirectory() {
578         return reportOutputDirectory;
579     }
580 
581     /**
582      * Returns the output directory.
583      *
584      * @return the output directory
585      */
586     public File getOutputDirectory() {
587         return outputDirectory;
588     }
589 
590     /**
591      * Returns whether this is an external report. This method always returns true.
592      *
593      * @return <code>true</code>
594      */
595     @Override
596     public final boolean isExternalReport() {
597         return true;
598     }
599 
600     /**
601      * Returns the output name.
602      *
603      * @return the output name
604      */
605     @Override
606     public String getOutputName() {
607         if ("HTML".equalsIgnoreCase(this.format) || "ALL".equalsIgnoreCase(this.format)) {
608             return "dependency-check-report";
609         } else if ("XML".equalsIgnoreCase(this.format)) {
610             return "dependency-check-report.xml#";
611         } else if ("VULN".equalsIgnoreCase(this.format)) {
612             return "dependency-check-vulnerability";
613         } else {
614             getLog().warn("Unknown report format used during site generation.");
615             return "dependency-check-report";
616         }
617     }
618 
619     /**
620      * Returns the category name.
621      *
622      * @return the category name
623      */
624     @Override
625     public String getCategoryName() {
626         return MavenReport.CATEGORY_PROJECT_REPORTS;
627     }
628     //</editor-fold>
629 
630     /**
631      * Initializes a new <code>Engine</code> that can be used for scanning.
632      *
633      * @return a newly instantiated <code>Engine</code>
634      * @throws DatabaseException thrown if there is a database exception
635      */
636     protected Engine initializeEngine() throws DatabaseException {
637         populateSettings();
638         return new Engine(this.project,
639                 this.reactorProjects);
640     }
641 
642     /**
643      * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
644      * required to change the proxy url, port, and connection timeout.
645      */
646     protected void populateSettings() {
647         Settings.initialize();
648         InputStream mojoProperties = null;
649         try {
650             mojoProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
651             Settings.mergeProperties(mojoProperties);
652         } catch (IOException ex) {
653             getLog().warn("Unable to load the dependency-check ant task.properties file.");
654             if (getLog().isDebugEnabled()) {
655                 getLog().debug("", ex);
656             }
657         } finally {
658             if (mojoProperties != null) {
659                 try {
660                     mojoProperties.close();
661                 } catch (IOException ex) {
662                     if (getLog().isDebugEnabled()) {
663                         getLog().debug("", ex);
664                     }
665                 }
666             }
667         }
668         Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
669 
670         if (externalReport != null) {
671             getLog().warn("The 'externalReport' option was set; this configuration option has been removed. "
672                     + "Please update the dependency-check-maven plugin's configuration");
673         }
674 
675         if (proxyUrl != null && !proxyUrl.isEmpty()) {
676             getLog().warn("Deprecated configuration detected, proxyUrl will be ignored; use the maven settings " + "to configure the proxy instead");
677         }
678         final Proxy proxy = getMavenProxy();
679         if (proxy != null) {
680             Settings.setString(Settings.KEYS.PROXY_SERVER, proxy.getHost());
681             Settings.setString(Settings.KEYS.PROXY_PORT, Integer.toString(proxy.getPort()));
682             final String userName = proxy.getUsername();
683             final String password = proxy.getPassword();
684             Settings.setStringIfNotNull(Settings.KEYS.PROXY_USERNAME, userName);
685             Settings.setStringIfNotNull(Settings.KEYS.PROXY_PASSWORD, password);
686             Settings.setStringIfNotNull(Settings.KEYS.PROXY_NON_PROXY_HOSTS, proxy.getNonProxyHosts());
687         }
688 
689         Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
690         Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
691 
692         //File Type Analyzer Settings
693         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
694         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled);
695         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled);
696         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
697         Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
698         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
699         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled);
700         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled);
701         Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
702         Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
703 
704         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled);
705         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled);
706         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled);
707         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled);
708         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled);
709         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled);
710         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
711         Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
712 
713         //Database configuration
714         Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
715         Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
716         Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
717 
718         if (databaseUser == null && databasePassword == null && serverId != null) {
719             final Server server = settingsXml.getServer(serverId);
720             if (server != null) {
721                 databaseUser = server.getUsername();
722                 try {
723                     //The following fix was copied from:
724                     //   https://github.com/bsorrentino/maven-confluence-plugin/blob/master/maven-confluence-reporting-plugin/src/main/java/org/bsc/maven/confluence/plugin/AbstractBaseConfluenceMojo.java
725                     //
726                     // FIX to resolve
727                     // org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException:
728                     // java.io.FileNotFoundException: ~/.settings-security.xml (No such file or directory)
729                     //
730                     if (securityDispatcher instanceof DefaultSecDispatcher) {
731                         ((DefaultSecDispatcher) securityDispatcher).setConfigurationFile("~/.m2/settings-security.xml");
732                     }
733 
734                     databasePassword = securityDispatcher.decrypt(server.getPassword());
735                 } catch (SecDispatcherException ex) {
736                     if (ex.getCause() instanceof FileNotFoundException
737                             || (ex.getCause() != null && ex.getCause().getCause() instanceof FileNotFoundException)) {
738                         //maybe its not encrypted?
739                         final String tmp = server.getPassword();
740                         if (tmp.startsWith("{") && tmp.endsWith("}")) {
741                             getLog().error(String.format(
742                                     "Unable to decrypt the server password for server id '%s' in settings.xml%n\tCause: %s",
743                                     serverId, ex.getMessage()));
744                         } else {
745                             databasePassword = tmp;
746                         }
747                     } else {
748                         getLog().error(String.format(
749                                 "Unable to decrypt the server password for server id '%s' in settings.xml%n\tCause: %s",
750                                 serverId, ex.getMessage()));
751                     }
752                 }
753             } else {
754                 getLog().error(String.format("Server '%s' not found in the settings.xml file", serverId));
755             }
756         }
757 
758         Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser);
759         Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword);
760         Settings.setStringIfNotEmpty(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
761 
762         Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
763         Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
764         Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
765         Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
766         Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
767 
768     }
769 
770     /**
771      * Returns the maven proxy.
772      *
773      * @return the maven proxy
774      */
775     private Proxy getMavenProxy() {
776         if (mavenSettings != null) {
777             final List<Proxy> proxies = mavenSettings.getProxies();
778             if (proxies != null && !proxies.isEmpty()) {
779                 if (mavenSettingsProxyId != null) {
780                     for (Proxy proxy : proxies) {
781                         if (mavenSettingsProxyId.equalsIgnoreCase(proxy.getId())) {
782                             return proxy;
783                         }
784                     }
785                 } else if (proxies.size() == 1) {
786                     return proxies.get(0);
787                 } else {
788                     getLog().warn("Multiple proxy definitions exist in the Maven settings. In the dependency-check "
789                             + "configuration set the mavenSettingsProxyId so that the correct proxy will be used.");
790                     throw new IllegalStateException("Ambiguous proxy definition");
791                 }
792             }
793         }
794         return null;
795     }
796 
797     /**
798      * Tests is the artifact should be included in the scan (i.e. is the dependency in a scope that is being scanned).
799      *
800      * @param a the Artifact to test
801      * @return <code>true</code> if the artifact is in an excluded scope; otherwise <code>false</code>
802      */
803     protected boolean excludeFromScan(Artifact a) {
804         if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) {
805             return true;
806         }
807         if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) {
808             return true;
809         }
810         if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
811             return true;
812         }
813         return false;
814     }
815 
816     /**
817      * Returns a reference to the current project. This method is used instead of auto-binding the project via component
818      * annotation in concrete implementations of this. If the child has a <code>@Component MavenProject project;</code> defined
819      * then the abstract class (i.e. this class) will not have access to the current project (just the way Maven works with the
820      * binding).
821      *
822      * @return returns a reference to the current project
823      */
824     protected MavenProject getProject() {
825         return project;
826     }
827 
828     /**
829      * Returns the list of Maven Projects in this build.
830      *
831      * @return the list of Maven Projects in this build
832      */
833     protected List<MavenProject> getReactorProjects() {
834         return reactorProjects;
835     }
836 
837     /**
838      * Returns the report format.
839      *
840      * @return the report format
841      */
842     protected String getFormat() {
843         return format;
844     }
845 
846     /**
847      * Generates the reports for a given dependency-check engine.
848      *
849      * @param engine a dependency-check engine
850      * @param p the maven project
851      * @param outputDir the directory path to write the report(s).
852      */
853     protected void writeReports(Engine engine, MavenProject p, File outputDir) {
854         DatabaseProperties prop = null;
855         CveDB cve = null;
856         try {
857             cve = new CveDB();
858             cve.open();
859             prop = cve.getDatabaseProperties();
860         } catch (DatabaseException ex) {
861             if (getLog().isDebugEnabled()) {
862                 getLog().debug("Unable to retrieve DB Properties", ex);
863             }
864         } finally {
865             if (cve != null) {
866                 cve.close();
867             }
868         }
869         final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop);
870         try {
871             r.generateReports(outputDir.getAbsolutePath(), format);
872         } catch (IOException ex) {
873             getLog().error(
874                     "Unexpected exception occurred during analysis; please see the verbose error log for more details.");
875             if (getLog().isDebugEnabled()) {
876                 getLog().debug("", ex);
877             }
878         } catch (Throwable ex) {
879             getLog().error(
880                     "Unexpected exception occurred during analysis; please see the verbose error log for more details.");
881             if (getLog().isDebugEnabled()) {
882                 getLog().debug("", ex);
883             }
884         }
885     }
886 
887     //<editor-fold defaultstate="collapsed" desc="Methods to fail build or show summary">
888     /**
889      * Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the
890      * configuration.
891      *
892      * @param dependencies the list of dependency objects
893      * @throws MojoFailureException thrown if a CVSS score is found that is higher then the threshold set
894      */
895     protected void checkForFailure(List<Dependency> dependencies) throws MojoFailureException {
896         if (failBuildOnCVSS <= 10) {
897             final StringBuilder ids = new StringBuilder();
898             for (Dependency d : dependencies) {
899                 boolean addName = true;
900                 for (Vulnerability v : d.getVulnerabilities()) {
901                     if (v.getCvssScore() >= failBuildOnCVSS) {
902                         if (addName) {
903                             addName = false;
904                             ids.append(NEW_LINE).append(d.getFileName()).append(": ");
905                             ids.append(v.getName());
906                         } else {
907                             ids.append(", ").append(v.getName());
908                         }
909                     }
910                 }
911             }
912             if (ids.length() > 0) {
913                 final String msg = String.format("%n%nDependency-Check Failure:%n"
914                         + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
915                         + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
916                 throw new MojoFailureException(msg);
917             }
918         }
919     }
920 
921     /**
922      * Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries.
923      *
924      * @param mp the Maven project for which the summary is shown
925      * @param dependencies a list of dependency objects
926      */
927     protected void showSummary(MavenProject mp, List<Dependency> dependencies) {
928         if (showSummary) {
929             final StringBuilder summary = new StringBuilder();
930             for (Dependency d : dependencies) {
931                 boolean firstEntry = true;
932                 final StringBuilder ids = new StringBuilder();
933                 for (Vulnerability v : d.getVulnerabilities()) {
934                     if (firstEntry) {
935                         firstEntry = false;
936                     } else {
937                         ids.append(", ");
938                     }
939                     ids.append(v.getName());
940                 }
941                 if (ids.length() > 0) {
942                     summary.append(d.getFileName()).append(" (");
943                     firstEntry = true;
944                     for (Identifier id : d.getIdentifiers()) {
945                         if (firstEntry) {
946                             firstEntry = false;
947                         } else {
948                             summary.append(", ");
949                         }
950                         summary.append(id.getValue());
951                     }
952                     summary.append(") : ").append(ids).append(NEW_LINE);
953                 }
954             }
955             if (summary.length() > 0) {
956                 final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities in %s:%n%n%s"
957                         + "%n%nSee the dependency-check report for more details.%n%n", mp.getName(), summary.toString());
958                 getLog().warn(msg);
959             }
960         }
961     }
962 
963     //</editor-fold>
964     //<editor-fold defaultstate="collapsed" desc="Methods to read/write the serialized data file">
965     /**
966      * Returns the key used to store the path to the data file that is saved by <code>writeDataFile()</code>. This key is used in
967      * the <code>MavenProject.(set|get)ContextValue</code>.
968      *
969      * @return the key used to store the path to the data file
970      */
971     protected String getDataFileContextKey() {
972         return "dependency-check-path-" + dataFileName;
973     }
974 
975     /**
976      * Returns the key used to store the path to the output directory. When generating the report in the
977      * <code>executeAggregateReport()</code> the output directory should be obtained by using this key.
978      *
979      * @return the key used to store the path to the output directory
980      */
981     protected String getOutputDirectoryContextKey() {
982         return "dependency-output-dir-" + dataFileName;
983     }
984 
985     /**
986      * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase.
987      *
988      * @param mp the mMven project for which the data file was created
989      * @param writeTo the directory to write the data file
990      * @param dependencies the list of dependencies to serialize
991      */
992     protected void writeDataFile(MavenProject mp, File writeTo, List<Dependency> dependencies) {
993         File file;
994         //check to see if this was already written out
995         if (mp.getContextValue(this.getDataFileContextKey()) == null) {
996             if (writeTo == null) {
997                 file = new File(mp.getBuild().getDirectory());
998                 file = new File(file, dataFileName);
999             } else {
1000                 file = new File(writeTo, dataFileName);
1001             }
1002             final File parent = file.getParentFile();
1003             if (!parent.isDirectory() && parent.mkdirs()) {
1004                 getLog().error(String.format("Directory '%s' does not exist and cannot be created; unable to write data file.",
1005                         parent.getAbsolutePath()));
1006             }
1007 
1008             ObjectOutputStream out = null;
1009             try {
1010                 if (dependencies != null) {
1011                     out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
1012                     out.writeObject(dependencies);
1013                 }
1014                 if (getLog().isDebugEnabled()) {
1015                     getLog().debug(String.format("Serialized data file written to '%s' for %s, referenced by key %s",
1016                             file.getAbsolutePath(), mp.getName(), this.getDataFileContextKey()));
1017                 }
1018                 mp.setContextValue(this.getDataFileContextKey(), file.getAbsolutePath());
1019             } catch (IOException ex) {
1020                 getLog().warn("Unable to create data file used for report aggregation; "
1021                         + "if report aggregation is being used the results may be incomplete.");
1022                 if (getLog().isDebugEnabled()) {
1023                     getLog().debug(ex.getMessage(), ex);
1024                 }
1025             } finally {
1026                 if (out != null) {
1027                     try {
1028                         out.close();
1029                     } catch (IOException ex) {
1030                         if (getLog().isDebugEnabled()) {
1031                             getLog().debug("ignore", ex);
1032                         }
1033                     }
1034                 }
1035             }
1036         }
1037     }
1038 
1039     /**
1040      * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and "aggregate"
1041      * phase.
1042      *
1043      * @param project the Maven project to read the data file from
1044      * @return a <code>Engine</code> object populated with dependencies if the serialized data file exists; otherwise
1045      * <code>null</code> is returned
1046      */
1047     protected List<Dependency> readDataFile(MavenProject project) {
1048         final Object oPath = project.getContextValue(this.getDataFileContextKey());
1049         if (oPath == null) {
1050             return null;
1051         }
1052         List<Dependency> ret = null;
1053         final String path = (String) oPath;
1054         //ObjectInputStream ois = null;
1055         ExpectedOjectInputStream ois = null;
1056         try {
1057             //ois = new ObjectInputStream(new FileInputStream(path));
1058             ois = new ExpectedOjectInputStream(new FileInputStream(path),
1059                     "java.util.ArrayList",
1060                     "java.util.HashSet",
1061                     "java.util.TreeSet",
1062                     "java.lang.AbstractSet",
1063                     "java.lang.AbstractCollection",
1064                     "java.lang.Enum",
1065                     "org.owasp.dependencycheck.dependency.Confidence",
1066                     "org.owasp.dependencycheck.dependency.Dependency",
1067                     "org.owasp.dependencycheck.dependency.Evidence",
1068                     "org.owasp.dependencycheck.dependency.EvidenceCollection",
1069                     "org.owasp.dependencycheck.dependency.Identifier",
1070                     "org.owasp.dependencycheck.dependency.Reference",
1071                     "org.owasp.dependencycheck.dependency.Vulnerability",
1072                     "org.owasp.dependencycheck.dependency.VulnerabilityComparator",
1073                     "org.owasp.dependencycheck.dependency.VulnerableSoftware",
1074                     "org.owasp.dependencycheck.data.cpe.IndexEntry");
1075             ret = (List<Dependency>) ois.readObject();
1076         } catch (FileNotFoundException ex) {
1077             //TODO fix logging
1078             getLog().error("", ex);
1079         } catch (IOException ex) {
1080             getLog().error("", ex);
1081         } catch (ClassNotFoundException ex) {
1082             getLog().error("", ex);
1083         } finally {
1084             if (ois != null) {
1085                 try {
1086                     ois.close();
1087                 } catch (IOException ex) {
1088                     getLog().error("", ex);
1089                 }
1090             }
1091         }
1092         return ret;
1093     }
1094     //</editor-fold>
1095 }