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