View Javadoc

1   /*
2    * This file is part of dependency-check-maven.
3    *
4    * Dependency-check-maven is free software: you can redistribute it and/or modify it
5    * under the terms of the GNU General Public License as published by the Free
6    * Software Foundation, either version 3 of the License, or (at your option) any
7    * later version.
8    *
9    * Dependency-check-maven is distributed in the hope that it will be useful, but
10   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12   * details.
13   *
14   * You should have received a copy of the GNU General Public License along with
15   * dependency-check-maven. If not, see http://www.gnu.org/licenses/.
16   *
17   * Copyright (c) 2013 Jeremy Long. All Rights Reserved.
18   */
19  package org.owasp.dependencycheck.maven;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.UnsupportedEncodingException;
25  import java.net.URLEncoder;
26  import java.text.DateFormat;
27  import java.util.Date;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.logging.Level;
31  import java.util.logging.Logger;
32  import org.apache.maven.doxia.sink.SinkFactory;
33  import org.apache.maven.plugin.AbstractMojo;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.project.MavenProject;
36  import java.util.Set;
37  import java.util.logging.LogManager;
38  import org.apache.maven.artifact.Artifact;
39  import org.apache.maven.plugins.annotations.Component;
40  import org.apache.maven.plugins.annotations.LifecyclePhase;
41  import org.apache.maven.plugins.annotations.Mojo;
42  import org.apache.maven.plugins.annotations.Parameter;
43  import org.apache.maven.plugins.annotations.ResolutionScope;
44  import org.apache.maven.reporting.MavenMultiPageReport;
45  import org.apache.maven.reporting.MavenReport;
46  import org.apache.maven.reporting.MavenReportException;
47  import org.apache.maven.doxia.sink.Sink;
48  import org.apache.maven.plugin.MojoFailureException;
49  import org.owasp.dependencycheck.Engine;
50  import org.owasp.dependencycheck.dependency.Dependency;
51  import org.owasp.dependencycheck.dependency.Evidence;
52  import org.owasp.dependencycheck.dependency.Identifier;
53  import org.owasp.dependencycheck.dependency.Reference;
54  import org.owasp.dependencycheck.dependency.Vulnerability;
55  import org.owasp.dependencycheck.dependency.VulnerableSoftware;
56  import org.owasp.dependencycheck.reporting.ReportGenerator;
57  import org.owasp.dependencycheck.utils.Settings;
58  
59  /**
60   * Maven Plugin that checks project dependencies to see if they have any known
61   * published vulnerabilities.
62   *
63   * @author Jeremy Long (jeremy.long@owasp.org)
64   */
65  @Mojo(name = "check", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true,
66          requiresDependencyResolution = ResolutionScope.RUNTIME_PLUS_SYSTEM,
67          requiresOnline = true)
68  public class DependencyCheckMojo extends AbstractMojo implements MavenMultiPageReport {
69  
70      /**
71       * The properties file location.
72       */
73      private static final String PROPERTIES_FILE = "mojo.properties";
74      /**
75       * Name of the logging properties file.
76       */
77      private static final String LOG_PROPERTIES_FILE = "log.properties";
78      /**
79       * The name of the test scope.
80       */
81      public static final String TEST_SCOPE = "test";
82      // <editor-fold defaultstate="collapsed" desc="Maven bound parameters and components">
83      /**
84       * The Maven Project Object.
85       */
86      @Component
87      private MavenProject project;
88      /**
89       * The name of the site report destination.
90       */
91      @Parameter(property = "report-name", defaultValue = "dependency-check-report")
92      private String reportName;
93      /**
94       * The name of the report to be displayed in the Maven Generated Reports
95       * page
96       */
97      @Parameter(property = "name", defaultValue = "Dependency-Check")
98      private String name;
99      /**
100      * The description of the Dependency-Check report to be displayed in the
101      * Maven Generated Reports page
102      */
103     @Parameter(property = "description", defaultValue = "A report providing details on any published "
104             + "vulnerabilities within project dependencies. This report is a best effort but may contain "
105             + "false positives and false negatives.")
106     private String description;
107     /**
108      * Specifies the destination directory for the generated Dependency-Check
109      * report.
110      */
111     @Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
112     private File reportOutputDirectory;
113     /**
114      * Specifies if the build should be failed if a CVSS score above a specified
115      * level is identified. The default is 11 which means since the CVSS scores
116      * are 0-10, by default the build will never fail.
117      */
118     @Parameter(property = "failBuildOnCVSS", defaultValue = "11", required = true)
119     private float failBuildOnCVSS = 11;
120     /**
121      * The output directory.
122      */
123     @Parameter(defaultValue = "${project.build.directory}", required = true)
124     private File outputDirectory;
125     /**
126      * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
127      * recommended that this be turned to false. Default is true.
128      */
129     @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
130     @Parameter(property = "autoupdate", defaultValue = "true", required = true)
131     private boolean autoUpdate = true;
132     /**
133      * The report format to be generated (HTML, XML, VULN, ALL). This
134      * configuration option has no affect if using this within the Site plugin
135      * unless the externalReport is set to true. Default is HTML.
136      */
137     @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
138     @Parameter(property = "format", defaultValue = "HTML", required = true)
139     private String format = "HTML";
140     /**
141      * Sets whether or not the external report format should be used.
142      */
143     @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
144     @Parameter(property = "externalReport", defaultValue = "false", required = true)
145     private boolean externalReport = false;
146     /**
147      * The Proxy URL.
148      */
149     @SuppressWarnings("CanBeFinal")
150     @Parameter(property = "proxyUrl", defaultValue = "", required = false)
151     private String proxyUrl = null;
152     /**
153      * The Proxy Port.
154      */
155     @SuppressWarnings("CanBeFinal")
156     @Parameter(property = "proxyPort", defaultValue = "", required = false)
157     private String proxyPort = null;
158     /**
159      * The Connection Timeout.
160      */
161     @SuppressWarnings("CanBeFinal")
162     @Parameter(property = "connectionTimeout", defaultValue = "", required = false)
163     private String connectionTimeout = null;
164 
165     // </editor-fold>
166     /**
167      * Configures the logger for use by the application.
168      */
169     private static void prepareLogger() {
170         InputStream in = null;
171         try {
172             in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
173             LogManager.getLogManager().reset();
174             LogManager.getLogManager().readConfiguration(in);
175             //TODO add code to disable fine grained log file.
176 //            Logger logger = LogManager.getLogManager().getLogger("");
177 //            for (Handler h : logger.getHandlers()) {
178 //                if (h.getFormatter(). h.toString());
179 //            }
180         } catch (IOException ex) {
181             System.err.println(ex.toString());
182             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
183         } catch (SecurityException ex) {
184             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
185         } finally {
186             if (in != null) {
187                 try {
188                     in.close();
189                 } catch (Exception ex) {
190                     //noinspection UnusedAssignment
191                     in = null;
192                 }
193             }
194         }
195     }
196 
197     /**
198      * Executes the Dependency-Check on the dependent libraries.
199      *
200      * @return the Engine used to scan the dependencies.
201      */
202     private Engine executeDependencyCheck() {
203         prepareLogger();
204         populateSettings();
205         final Engine engine = new Engine();
206         final Set<Artifact> artifacts = project.getArtifacts();
207         for (Artifact a : artifacts) {
208             if (!TEST_SCOPE.equals(a.getScope())) {
209                 engine.scan(a.getFile().getAbsolutePath());
210             }
211         }
212         engine.analyzeDependencies();
213         return engine;
214     }
215 
216     /**
217      * Generates the reports for a given dependency-check engine.
218      *
219      * @param engine a dependency-check engine
220      */
221     private void generateExternalReports(Engine engine) {
222         final ReportGenerator r = new ReportGenerator(project.getName(), engine.getDependencies(), engine.getAnalyzers());
223         try {
224             r.generateReports(outputDirectory.getCanonicalPath(), format);
225         } catch (IOException ex) {
226             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
227         } catch (Exception ex) {
228             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
229         }
230     }
231 
232     /**
233      * Generates a dependency-check report using the Maven Site format.
234      *
235      * @param engine the engine used to scan the dependencies
236      * @param sink the sink to write the data to
237      */
238     private void generateMavenSiteReport(final Engine engine, Sink sink) {
239         final List<Dependency> dependencies = engine.getDependencies();
240 
241         writeSiteReportHeader(sink, project.getName());
242         writeSiteReportTOC(sink, dependencies);
243 
244         int cnt = 0;
245         for (Dependency d : dependencies) {
246             writeSiteReportDependencyHeader(sink, d);
247             cnt = writeSiteReportDependencyAnalysisExceptions(d, cnt, sink);
248             cnt = writeSiteReportDependencyEvidenceUsed(d, cnt, sink);
249             cnt = writeSiteReportDependencyRelatedDependencies(d, cnt, sink);
250             writeSiteReportDependencyIdentifiers(d, sink);
251             writeSiteReportDependencyVulnerabilities(d, sink, cnt);
252         }
253         sink.body_();
254     }
255 
256     // <editor-fold defaultstate="collapsed" desc="various writeXXXXX methods to generate the Site Report">
257     /**
258      * Writes the vulnerabilities to the site report.
259      *
260      * @param d the dependency
261      * @param sink the sink to write the data to
262      * @param collapsibleHeaderCount the collapsible header count
263      */
264     private void writeSiteReportDependencyVulnerabilities(Dependency d, Sink sink, int collapsibleHeaderCount) {
265         int cnt = collapsibleHeaderCount;
266         if (d.getVulnerabilities() != null && !d.getVulnerabilities().isEmpty()) {
267             for (Vulnerability v : d.getVulnerabilities()) {
268 
269                 sink.paragraph();
270                 sink.bold();
271                 try {
272                     sink.link("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + URLEncoder.encode(v.getName(), "US-ASCII"));
273                     sink.text(v.getName());
274                     sink.link_();
275                     sink.bold_();
276                 } catch (UnsupportedEncodingException ex) {
277                     sink.text(v.getName());
278                     sink.bold_();
279                     sink.lineBreak();
280                     sink.text("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + v.getName());
281                 }
282                 sink.paragraph_();
283                 sink.paragraph();
284                 sink.text("Severity: ");
285                 if (v.getCvssScore() < 4.0) {
286                     sink.text("Low");
287                 } else {
288                     if (v.getCvssScore() >= 7.0) {
289                         sink.text("High");
290                     } else {
291                         sink.text("Medium");
292                     }
293                 }
294                 sink.lineBreak();
295                 sink.text("CVSS Score: " + v.getCvssScore());
296                 if (v.getCwe() != null && !v.getCwe().isEmpty()) {
297                     sink.lineBreak();
298                     sink.text("CWE: ");
299                     sink.text(v.getCwe());
300                 }
301                 sink.paragraph_();
302                 sink.paragraph();
303                 sink.text(v.getDescription());
304                 if (v.getReferences() != null && !v.getReferences().isEmpty()) {
305                     sink.list();
306                     for (Reference ref : v.getReferences()) {
307                         sink.listItem();
308                         sink.text(ref.getSource());
309                         sink.text(" - ");
310                         sink.link(ref.getUrl());
311                         sink.text(ref.getName());
312                         sink.link_();
313                         sink.listItem_();
314                     }
315                     sink.list_();
316                 }
317                 sink.paragraph_();
318                 if (v.getVulnerableSoftware() != null && !v.getVulnerableSoftware().isEmpty()) {
319                     sink.paragraph();
320 
321                     cnt += 1;
322                     sink.rawText("Vulnerable Software <a href=\"javascript:toggleElement(this, 'vulnSoft" + cnt + "')\">[-]</a>");
323                     sink.rawText("<div id=\"vulnSoft" + cnt + "\" style=\"display:block\">");
324                     sink.list();
325                     for (VulnerableSoftware vs : v.getVulnerableSoftware()) {
326                         sink.listItem();
327                         try {
328                             sink.link("http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + URLEncoder.encode(vs.getName(), "US-ASCII"));
329                             sink.text(vs.getName());
330                             sink.link_();
331                             if (vs.hasPreviousVersion()) {
332                                 sink.text(" and all previous versions.");
333                             }
334                         } catch (UnsupportedEncodingException ex) {
335                             sink.text(vs.getName());
336                             if (vs.hasPreviousVersion()) {
337                                 sink.text(" and all previous versions.");
338                             }
339                             sink.text(" (http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + vs.getName() + ")");
340                         }
341 
342                         sink.listItem_();
343                     }
344                     sink.list_();
345                     sink.rawText("</div>");
346                     sink.paragraph_();
347                 }
348             }
349         }
350     }
351 
352     /**
353      * Writes the identifiers to the site report.
354      *
355      * @param d the dependency
356      * @param sink the sink to write the data to
357      */
358     private void writeSiteReportDependencyIdentifiers(Dependency d, Sink sink) {
359         if (d.getIdentifiers() != null && !d.getIdentifiers().isEmpty()) {
360             sink.sectionTitle4();
361             sink.text("Identifiers");
362             sink.sectionTitle4_();
363             sink.list();
364             for (Identifier i : d.getIdentifiers()) {
365                 sink.listItem();
366                 sink.text(i.getType());
367                 sink.text(": ");
368                 if (i.getUrl() != null && i.getUrl().length() > 0) {
369                     sink.link(i.getUrl());
370                     sink.text(i.getValue());
371                     sink.link_();
372                 } else {
373                     sink.text(i.getValue());
374                 }
375                 if (i.getDescription() != null && i.getDescription().length() > 0) {
376                     sink.lineBreak();
377                     sink.text(i.getDescription());
378                 }
379                 sink.listItem_();
380             }
381             sink.list_();
382         }
383     }
384 
385     /**
386      * Writes the related dependencies to the site report.
387      *
388      * @param d the dependency
389      * @param sink the sink to write the data to
390      * @param collapsibleHeaderCount the collapsible header count
391      * @return the collapsible header count
392      */
393     private int writeSiteReportDependencyRelatedDependencies(Dependency d, int collapsibleHeaderCount, Sink sink) {
394         int cnt = collapsibleHeaderCount;
395         if (d.getRelatedDependencies() != null && !d.getRelatedDependencies().isEmpty()) {
396             cnt += 1;
397             sink.sectionTitle4();
398             sink.rawText("Related Dependencies <a href=\"javascript:toggleElement(this, 'related" + cnt + "')\">[+]</a>");
399             sink.sectionTitle4_();
400             sink.rawText("<div id=\"related" + cnt + "\" style=\"display:none\">");
401             sink.list();
402             for (Dependency r : d.getRelatedDependencies()) {
403                 sink.listItem();
404                 sink.text(r.getFileName());
405                 sink.list();
406                 writeListItem(sink, "File Path: " + r.getFilePath());
407                 writeListItem(sink, "SHA1: " + r.getSha1sum());
408                 writeListItem(sink, "MD5: " + r.getMd5sum());
409                 sink.list_();
410                 sink.listItem_();
411             }
412             sink.list_();
413             sink.rawText("</div>");
414         }
415         return cnt;
416     }
417 
418     /**
419      * Writes the evidence used to the site report.
420      *
421      * @param d the dependency
422      * @param sink the sink to write the data to
423      * @param collapsibleHeaderCount the collapsible header count
424      * @return the collapsible header count
425      */
426     private int writeSiteReportDependencyEvidenceUsed(Dependency d, int collapsibleHeaderCount, Sink sink) {
427         int cnt = collapsibleHeaderCount;
428         if (d.getEvidenceUsed() != null && d.getEvidenceUsed().size() > 0) {
429             cnt += 1;
430             sink.sectionTitle4();
431             sink.rawText("Evidence Collected <a href=\"javascript:toggleElement(this, 'evidence" + cnt + "')\">[+]</a>");
432             sink.sectionTitle4_();
433             sink.rawText("<div id=\"evidence" + cnt + "\" style=\"display:none\">");
434             sink.table();
435             sink.tableRow();
436             writeTableHeaderCell(sink, "Source");
437             writeTableHeaderCell(sink, "Name");
438             writeTableHeaderCell(sink, "Value");
439             sink.tableRow_();
440             for (Evidence e : d.getEvidenceUsed()) {
441                 sink.tableRow();
442                 writeTableCell(sink, e.getSource());
443                 writeTableCell(sink, e.getName());
444                 writeTableCell(sink, e.getValue());
445                 sink.tableRow_();
446             }
447             sink.table_();
448             sink.rawText("</div>");
449         }
450         return cnt;
451     }
452 
453     /**
454      * Writes the analysis exceptions generated during analysis to the site
455      * report.
456      *
457      * @param d the dependency
458      * @param sink the sink to write the data to
459      * @param collapsibleHeaderCount the collapsible header count
460      * @return the collapsible header count
461      */
462     private int writeSiteReportDependencyAnalysisExceptions(Dependency d, int collapsibleHeaderCount, Sink sink) {
463         int cnt = collapsibleHeaderCount;
464         if (d.getAnalysisExceptions() != null && !d.getAnalysisExceptions().isEmpty()) {
465             cnt += 1;
466             sink.sectionTitle4();
467             sink.rawText("<font style=\"color:red\">Errors occurred during analysis:</font> <a href=\"javascript:toggleElement(this, 'errors"
468                     + cnt + "')\">[+]</a>");
469             sink.sectionTitle4_();
470             sink.rawText("<div id=\"errors" + cnt + "\">");
471             sink.list();
472             for (Exception e : d.getAnalysisExceptions()) {
473                 sink.listItem();
474                 sink.text(e.getMessage());
475                 sink.listItem_();
476             }
477             sink.list_();
478             sink.rawText("</div>");
479         }
480         return cnt;
481     }
482 
483     /**
484      * Writes the dependency header to the site report.
485      *
486      * @param d the dependency
487      * @param sink the sink to write the data to
488      */
489     private void writeSiteReportDependencyHeader(Sink sink, Dependency d) {
490         sink.sectionTitle2();
491         sink.anchor("sha1" + d.getSha1sum());
492         sink.text(d.getFileName());
493         sink.anchor_();
494         sink.sectionTitle2_();
495         if (d.getDescription() != null && d.getDescription().length() > 0) {
496             sink.paragraph();
497             sink.bold();
498             sink.text("Description: ");
499             sink.bold_();
500             sink.text(d.getDescription());
501             sink.paragraph_();
502         }
503         if (d.getLicense() != null && d.getLicense().length() > 0) {
504             sink.paragraph();
505             sink.bold();
506             sink.text("License: ");
507             sink.bold_();
508             if (d.getLicense().startsWith("http://") && !d.getLicense().contains(" ")) {
509                 sink.link(d.getLicense());
510                 sink.text(d.getLicense());
511                 sink.link_();
512             } else {
513                 sink.text(d.getLicense());
514             }
515             sink.paragraph_();
516         }
517     }
518 
519     /**
520      * Adds a list item to the site report.
521      *
522      * @param sink the sink to write the data to
523      * @param text the text to write
524      */
525     private void writeListItem(Sink sink, String text) {
526         sink.listItem();
527         sink.text(text);
528         sink.listItem_();
529     }
530 
531     /**
532      * Adds a table cell to the site report.
533      *
534      * @param sink the sink to write the data to
535      * @param text the text to write
536      */
537     private void writeTableCell(Sink sink, String text) {
538         sink.tableCell();
539         sink.text(text);
540         sink.tableCell_();
541     }
542 
543     /**
544      * Adds a table header cell to the site report.
545      *
546      * @param sink the sink to write the data to
547      * @param text the text to write
548      */
549     private void writeTableHeaderCell(Sink sink, String text) {
550         sink.tableHeaderCell();
551         sink.text(text);
552         sink.tableHeaderCell_();
553     }
554 
555     /**
556      * Writes the TOC for the site report.
557      *
558      * @param sink the sink to write the data to
559      * @param dependencies the dependencies that are being reported on
560      */
561     private void writeSiteReportTOC(Sink sink, final List<Dependency> dependencies) {
562         sink.list();
563         for (Dependency d : dependencies) {
564             sink.listItem();
565             sink.link("#sha1" + d.getSha1sum());
566             sink.text(d.getFileName());
567             sink.link_();
568             if (!d.getVulnerabilities().isEmpty()) {
569                 sink.rawText(" <font style=\"color:red\">•</font>");
570             }
571             if (!d.getRelatedDependencies().isEmpty()) {
572                 sink.list();
573                 for (Dependency r : d.getRelatedDependencies()) {
574                     writeListItem(sink, r.getFileName());
575                 }
576                 sink.list_();
577             }
578             sink.listItem_();
579         }
580         sink.list_();
581     }
582 
583     /**
584      * Writes the site report header.
585      *
586      * @param sink the sink to write the data to
587      * @param projectName the name of the project
588      */
589     private void writeSiteReportHeader(Sink sink, String projectName) {
590         sink.head();
591         sink.title();
592         sink.text("Dependency-Check Report: " + projectName);
593         sink.title_();
594         sink.head_();
595         sink.body();
596         sink.rawText("<script type=\"text/javascript\">");
597         sink.rawText("function toggleElement(el, targetId) {");
598         sink.rawText("if (el.innerText == '[+]') {");
599         sink.rawText("    el.innerText = '[-]';");
600         sink.rawText("    document.getElementById(targetId).style.display='block';");
601         sink.rawText("} else {");
602         sink.rawText("    el.innerText = '[+]';");
603         sink.rawText("    document.getElementById(targetId).style.display='none';");
604         sink.rawText("}");
605 
606         sink.rawText("}");
607         sink.rawText("</script>");
608         sink.section1();
609         sink.sectionTitle1();
610         sink.text("Project: " + projectName);
611         sink.sectionTitle1_();
612         sink.date();
613         final Date now = new Date();
614         sink.text(DateFormat.getDateTimeInstance().format(now));
615         sink.date_();
616         sink.section1_();
617     }
618     // </editor-fold>
619 
620     /**
621      * Takes the properties supplied and updates the dependency-check settings.
622      * Additionally, this sets the system properties required to change the
623      * proxy url, port, and connection timeout.
624      */
625     private void populateSettings() {
626         InputStream mojoProperties = null;
627         try {
628             mojoProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
629             Settings.mergeProperties(mojoProperties);
630         } catch (IOException ex) {
631             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.WARNING, "Unable to load the dependency-check ant task.properties file.");
632             Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.FINE, null, ex);
633         } finally {
634             if (mojoProperties != null) {
635                 try {
636                     mojoProperties.close();
637                 } catch (IOException ex) {
638                     Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.FINEST, null, ex);
639                 }
640             }
641         }
642 
643         Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
644 
645         if (proxyUrl != null && !proxyUrl.isEmpty()) {
646             Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
647         }
648         if (proxyPort != null && !proxyPort.isEmpty()) {
649             Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
650         }
651         if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
652             Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
653         }
654     }
655 
656     /**
657      * Executes the dependency-check and generates the report.
658      *
659      * @throws MojoExecutionException if a maven exception occurs
660      * @throws MojoFailureException thrown if a CVSS score is found that is
661      * higher then the configured level
662      */
663     public void execute() throws MojoExecutionException, MojoFailureException {
664         final Engine engine = executeDependencyCheck();
665         generateExternalReports(engine);
666         if (this.failBuildOnCVSS <= 10) {
667             checkForFailure(engine.getDependencies());
668         }
669     }
670 
671     /**
672      * Generates the Dependency-Check Site Report.
673      *
674      * @param sink the sink to write the report to
675      * @param locale the locale to use when generating the report
676      * @throws MavenReportException if a Maven report exception occurs
677      */
678     public void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink,
679             Locale locale) throws MavenReportException {
680         generate((Sink) sink, null, locale);
681     }
682 
683     /**
684      * Generates the Dependency-Check Site Report.
685      *
686      * @param sink the sink to write the report to
687      * @param sinkFactory the sink factory
688      * @param locale the locale to use when generating the report
689      * @throws MavenReportException if a maven report exception occurs
690      */
691     public void generate(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException {
692         final Engine engine = executeDependencyCheck();
693         generateMavenSiteReport(engine, sink);
694     }
695 
696     // <editor-fold defaultstate="collapsed" desc="required setter/getter methods">
697     /**
698      * Returns the output name.
699      *
700      * @return the output name
701      */
702     public String getOutputName() {
703         return reportName;
704     }
705 
706     /**
707      * Returns the category name.
708      *
709      * @return the category name
710      */
711     public String getCategoryName() {
712         return MavenReport.CATEGORY_PROJECT_REPORTS;
713     }
714 
715     /**
716      * Returns the report name.
717      *
718      * @param locale the location
719      * @return the report name
720      */
721     public String getName(Locale locale) {
722         return name;
723     }
724 
725     /**
726      * Sets the Reporting output directory.
727      *
728      * @param directory the output directory
729      */
730     public void setReportOutputDirectory(File directory) {
731         reportOutputDirectory = directory;
732     }
733 
734     /**
735      * Returns the output directory.
736      *
737      * @return the output directory
738      */
739     public File getReportOutputDirectory() {
740         return reportOutputDirectory;
741     }
742 
743     /**
744      * Gets the description of the Dependency-Check report to be displayed in
745      * the Maven Generated Reports page.
746      *
747      * @param locale The Locale to get the description for
748      * @return the description
749      */
750     public String getDescription(Locale locale) {
751         return description;
752     }
753 
754     /**
755      * Returns whether this is an external report.
756      *
757      * @return true or false;
758      */
759     public boolean isExternalReport() {
760         return externalReport;
761     }
762 
763     /**
764      * Returns whether or not the plugin can generate a report.
765      *
766      * @return true
767      */
768     public boolean canGenerateReport() {
769         return true;
770     }
771     // </editor-fold>
772 
773     /**
774      * Checks to see if a vulnerability has been identified with a CVSS score
775      * that is above the threshold set in the configuration.
776      *
777      * @param dependencies the list of dependency objects
778      * @throws MojoFailureException thrown if a CVSS score is found that is
779      * higher then the threshold set
780      */
781     private void checkForFailure(List<Dependency> dependencies) throws MojoFailureException {
782         final StringBuilder ids = new StringBuilder();
783         for (Dependency d : dependencies) {
784             for (Vulnerability v : d.getVulnerabilities()) {
785                 if (v.getCvssScore() >= failBuildOnCVSS) {
786                     if (ids.length() == 0) {
787                         ids.append(v.getName());
788                     } else {
789                         ids.append(", ").append(v.getName());
790                     }
791                 }
792             }
793         }
794         if (ids.length() > 0) {
795             final String msg = String.format("%n%nDependency-Check Failure:%n"
796                     + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
797                     + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
798             throw new MojoFailureException(msg);
799         }
800     }
801 }