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