Coverage Report - org.owasp.dependencycheck.taskdefs.DependencyCheckTask
 
Classes in this File Line Coverage Branch Coverage Complexity
DependencyCheckTask
55%
77/138
37%
21/56
2.433
DependencyCheckTask$ReportFormats
100%
7/7
100%
2/2
2.433
 
 1  
 /*
 2  
  * This file is part of dependency-check-ant.
 3  
  *
 4  
  * Dependency-check-ant 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-ant 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-ant. If not, see http://www.gnu.org/licenses/.
 16  
  *
 17  
  * Copyright (c) 2013 Jeremy Long. All Rights Reserved.
 18  
  */
 19  
 package org.owasp.dependencycheck.taskdefs;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.util.List;
 25  
 import java.util.logging.Level;
 26  
 import java.util.logging.LogManager;
 27  
 import java.util.logging.Logger;
 28  
 import org.apache.tools.ant.BuildException;
 29  
 import org.apache.tools.ant.Task;
 30  
 import org.apache.tools.ant.types.EnumeratedAttribute;
 31  
 import org.apache.tools.ant.types.Reference;
 32  
 import org.apache.tools.ant.types.Resource;
 33  
 import org.apache.tools.ant.types.ResourceCollection;
 34  
 import org.apache.tools.ant.types.resources.FileProvider;
 35  
 import org.apache.tools.ant.types.resources.Resources;
 36  
 import org.owasp.dependencycheck.Engine;
 37  
 import org.owasp.dependencycheck.dependency.Dependency;
 38  
 import org.owasp.dependencycheck.dependency.Vulnerability;
 39  
 import org.owasp.dependencycheck.reporting.ReportGenerator;
 40  
 import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
 41  
 import org.owasp.dependencycheck.utils.Settings;
 42  
 
 43  
 /**
 44  
  * An Ant task definition to execute dependency-check during an Ant build.
 45  
  *
 46  
  * @author Jeremy Long (jeremy.long@owasp.org)
 47  
  */
 48  
 public class DependencyCheckTask extends Task {
 49  
 
 50  
     /**
 51  
      * The properties file location.
 52  
      */
 53  
     private static final String PROPERTIES_FILE = "task.properties";
 54  
     /**
 55  
      * Name of the logging properties file.
 56  
      */
 57  
     private static final String LOG_PROPERTIES_FILE = "log.properties";
 58  
 
 59  
     /**
 60  
      * Construct a new DependencyCheckTask.
 61  
      */
 62  
     public DependencyCheckTask() {
 63  12
         super();
 64  12
     }
 65  
     //The following code was copied Apache Ant PathConvert
 66  
     //BEGIN COPY from org.apache.tools.ant.taskdefs.PathConvert
 67  
     /**
 68  
      * Path to be converted
 69  
      */
 70  12
     private Resources path = null;
 71  
     /**
 72  
      * Reference to path/fileset to convert
 73  
      */
 74  12
     private Reference refid = null;
 75  
 
 76  
     /**
 77  
      * Add an arbitrary ResourceCollection.
 78  
      *
 79  
      * @param rc the ResourceCollection to add.
 80  
      * @since Ant 1.7
 81  
      */
 82  
     public void add(ResourceCollection rc) {
 83  12
         if (isReference()) {
 84  0
             throw new BuildException("Nested elements are not allowed when using the refid attribute.");
 85  
         }
 86  12
         getPath().add(rc);
 87  12
     }
 88  
 
 89  
     /**
 90  
      * Returns the path. If the path has not been initialized yet, this class is
 91  
      * synchronized, and will instantiate the path object.
 92  
      *
 93  
      * @return the path
 94  
      */
 95  
     private synchronized Resources getPath() {
 96  12
         if (path == null) {
 97  9
             path = new Resources(getProject());
 98  9
             path.setCache(true);
 99  
         }
 100  12
         return path;
 101  
     }
 102  
 
 103  
     /**
 104  
      * Learn whether the refid attribute of this element been set.
 105  
      *
 106  
      * @return true if refid is valid.
 107  
      */
 108  
     public boolean isReference() {
 109  24
         return refid != null;
 110  
     }
 111  
 
 112  
     /**
 113  
      * Add a reference to a Path, FileSet, DirSet, or FileList defined
 114  
      * elsewhere.
 115  
      *
 116  
      * @param r the reference to a path, fileset, dirset or filelist.
 117  
      */
 118  
     public void setRefid(Reference r) {
 119  0
         if (path != null) {
 120  0
             throw new BuildException("Nested elements are not allowed when using the refid attribute.");
 121  
         }
 122  0
         refid = r;
 123  0
     }
 124  
 
 125  
     /**
 126  
      * If this is a reference, this method will add the referenced resource
 127  
      * collection to the collection of paths.
 128  
      *
 129  
      * @throws BuildException if the reference is not to a resource collection
 130  
      */
 131  
     private void dealWithReferences() throws BuildException {
 132  12
         if (isReference()) {
 133  0
             final Object o = refid.getReferencedObject(getProject());
 134  0
             if (!(o instanceof ResourceCollection)) {
 135  0
                 throw new BuildException("refid '" + refid.getRefId()
 136  
                         + "' does not refer to a resource collection.");
 137  
             }
 138  0
             getPath().add((ResourceCollection) o);
 139  
         }
 140  12
     }
 141  
     // END COPY from org.apache.tools.ant.taskdefs
 142  
     /**
 143  
      * The application name for the report.
 144  
      */
 145  12
     private String applicationName = "Dependency-Check";
 146  
 
 147  
     /**
 148  
      * Get the value of applicationName.
 149  
      *
 150  
      * @return the value of applicationName
 151  
      */
 152  
     public String getApplicationName() {
 153  0
         return applicationName;
 154  
     }
 155  
 
 156  
     /**
 157  
      * Set the value of applicationName.
 158  
      *
 159  
      * @param applicationName new value of applicationName
 160  
      */
 161  
     public void setApplicationName(String applicationName) {
 162  12
         this.applicationName = applicationName;
 163  12
     }
 164  
     /**
 165  
      * The location of the data directory that contains
 166  
      */
 167  12
     private String dataDirectory = null;
 168  
 
 169  
     /**
 170  
      * Get the value of dataDirectory.
 171  
      *
 172  
      * @return the value of dataDirectory
 173  
      */
 174  
     public String getDataDirectory() {
 175  0
         return dataDirectory;
 176  
     }
 177  
 
 178  
     /**
 179  
      * Set the value of dataDirectory.
 180  
      *
 181  
      * @param dataDirectory new value of dataDirectory
 182  
      */
 183  
     public void setDataDirectory(String dataDirectory) {
 184  0
         this.dataDirectory = dataDirectory;
 185  0
     }
 186  
     /**
 187  
      * Specifies the destination directory for the generated Dependency-Check
 188  
      * report.
 189  
      */
 190  12
     private String reportOutputDirectory = ".";
 191  
 
 192  
     /**
 193  
      * Get the value of reportOutputDirectory.
 194  
      *
 195  
      * @return the value of reportOutputDirectory
 196  
      */
 197  
     public String getReportOutputDirectory() {
 198  0
         return reportOutputDirectory;
 199  
     }
 200  
 
 201  
     /**
 202  
      * Set the value of reportOutputDirectory.
 203  
      *
 204  
      * @param reportOutputDirectory new value of reportOutputDirectory
 205  
      */
 206  
     public void setReportOutputDirectory(String reportOutputDirectory) {
 207  12
         this.reportOutputDirectory = reportOutputDirectory;
 208  12
     }
 209  
     /**
 210  
      * Specifies if the build should be failed if a CVSS score above a specified
 211  
      * level is identified. The default is 11 which means since the CVSS scores
 212  
      * are 0-10, by default the build will never fail and the CVSS score is set
 213  
      * to 11. The valid range for the fail build on CVSS is 0 to 11, where
 214  
      * anything above 10 will not cause the build to fail.
 215  
      */
 216  12
     private float failBuildOnCVSS = 11;
 217  
 
 218  
     /**
 219  
      * Get the value of failBuildOnCVSS.
 220  
      *
 221  
      * @return the value of failBuildOnCVSS
 222  
      */
 223  
     public float getFailBuildOnCVSS() {
 224  0
         return failBuildOnCVSS;
 225  
     }
 226  
 
 227  
     /**
 228  
      * Set the value of failBuildOnCVSS.
 229  
      *
 230  
      * @param failBuildOnCVSS new value of failBuildOnCVSS
 231  
      */
 232  
     public void setFailBuildOnCVSS(float failBuildOnCVSS) {
 233  3
         this.failBuildOnCVSS = failBuildOnCVSS;
 234  3
     }
 235  
     /**
 236  
      * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
 237  
      * recommended that this be turned to false. Default is true.
 238  
      */
 239  12
     private boolean autoUpdate = true;
 240  
 
 241  
     /**
 242  
      * Get the value of autoUpdate.
 243  
      *
 244  
      * @return the value of autoUpdate
 245  
      */
 246  
     public boolean isAutoUpdate() {
 247  0
         return autoUpdate;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Set the value of autoUpdate.
 252  
      *
 253  
      * @param autoUpdate new value of autoUpdate
 254  
      */
 255  
     public void setAutoUpdate(boolean autoUpdate) {
 256  12
         this.autoUpdate = autoUpdate;
 257  12
     }
 258  
     /**
 259  
      * The report format to be generated (HTML, XML, VULN, ALL). This
 260  
      * configuration option has no affect if using this within the Site plugin
 261  
      * unless the externalReport is set to true. Default is HTML.
 262  
      */
 263  12
     private String reportFormat = "HTML";
 264  
 
 265  
     /**
 266  
      * Get the value of reportFormat.
 267  
      *
 268  
      * @return the value of reportFormat
 269  
      */
 270  
     public String getReportFormat() {
 271  0
         return reportFormat;
 272  
     }
 273  
 
 274  
     /**
 275  
      * Set the value of reportFormat.
 276  
      *
 277  
      * @param reportFormat new value of reportFormat
 278  
      */
 279  
     public void setReportFormat(ReportFormats reportFormat) {
 280  12
         this.reportFormat = reportFormat.getValue();
 281  12
     }
 282  
     /**
 283  
      * The Proxy URL.
 284  
      */
 285  
     private String proxyUrl;
 286  
 
 287  
     /**
 288  
      * Get the value of proxyUrl.
 289  
      *
 290  
      * @return the value of proxyUrl
 291  
      */
 292  
     public String getProxyUrl() {
 293  0
         return proxyUrl;
 294  
     }
 295  
 
 296  
     /**
 297  
      * Set the value of proxyUrl.
 298  
      *
 299  
      * @param proxyUrl new value of proxyUrl
 300  
      */
 301  
     public void setProxyUrl(String proxyUrl) {
 302  0
         this.proxyUrl = proxyUrl;
 303  0
     }
 304  
     /**
 305  
      * The Proxy Port.
 306  
      */
 307  
     private String proxyPort;
 308  
 
 309  
     /**
 310  
      * Get the value of proxyPort.
 311  
      *
 312  
      * @return the value of proxyPort
 313  
      */
 314  
     public String getProxyPort() {
 315  0
         return proxyPort;
 316  
     }
 317  
 
 318  
     /**
 319  
      * Set the value of proxyPort.
 320  
      *
 321  
      * @param proxyPort new value of proxyPort
 322  
      */
 323  
     public void setProxyPort(String proxyPort) {
 324  0
         this.proxyPort = proxyPort;
 325  0
     }
 326  
     /**
 327  
      * The Connection Timeout.
 328  
      */
 329  
     private String connectionTimeout;
 330  
 
 331  
     /**
 332  
      * Get the value of connectionTimeout.
 333  
      *
 334  
      * @return the value of connectionTimeout
 335  
      */
 336  
     public String getConnectionTimeout() {
 337  0
         return connectionTimeout;
 338  
     }
 339  
 
 340  
     /**
 341  
      * Set the value of connectionTimeout.
 342  
      *
 343  
      * @param connectionTimeout new value of connectionTimeout
 344  
      */
 345  
     public void setConnectionTimeout(String connectionTimeout) {
 346  0
         this.connectionTimeout = connectionTimeout;
 347  0
     }
 348  
 
 349  
     /**
 350  
      * Configures the logger for use by the application.
 351  
      */
 352  
     private static void prepareLogger() {
 353  12
         InputStream in = null;
 354  
         try {
 355  12
             in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
 356  12
             LogManager.getLogManager().reset();
 357  12
             LogManager.getLogManager().readConfiguration(in);
 358  
             //TODO add code to disable fine grained log file.
 359  
 //            Logger logger = LogManager.getLogManager().getLogger("");
 360  
 //            for (Handler h : logger.getHandlers()) {
 361  
 //                if (h.getFormatter(). h.toString());
 362  
 //            }
 363  0
         } catch (IOException ex) {
 364  0
             System.err.println(ex.toString());
 365  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
 366  0
         } catch (SecurityException ex) {
 367  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
 368  
         } finally {
 369  12
             if (in != null) {
 370  
                 try {
 371  12
                     in.close();
 372  0
                 } catch (Exception ex) {
 373  
                     //noinspection UnusedAssignment
 374  0
                     in = null;
 375  12
                 }
 376  
             }
 377  
         }
 378  12
     }
 379  
 
 380  
     @Override
 381  
     public void execute() throws BuildException {
 382  12
         prepareLogger();
 383  
 
 384  12
         dealWithReferences();
 385  12
         validateConfiguration();
 386  9
         populateSettings();
 387  
 
 388  9
         final Engine engine = new Engine();
 389  9
         for (Resource resource : path) {
 390  15
             final FileProvider provider = resource.as(FileProvider.class);
 391  15
             if (provider != null) {
 392  15
                 final File file = provider.getFile();
 393  15
                 if (file != null && file.exists()) {
 394  15
                     engine.scan(file);
 395  
                 }
 396  
             }
 397  15
         }
 398  
         try {
 399  9
             engine.analyzeDependencies();
 400  9
             final ReportGenerator reporter = new ReportGenerator(applicationName, engine.getDependencies(), engine.getAnalyzers());
 401  9
             reporter.generateReports(reportOutputDirectory, reportFormat);
 402  
 
 403  9
             if (this.failBuildOnCVSS <= 10) {
 404  0
                 checkForFailure(engine.getDependencies());
 405  
             }
 406  0
         } catch (IOException ex) {
 407  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
 408  0
             throw new BuildException("Unable to generate dependency-check report", ex);
 409  0
         } catch (Exception ex) {
 410  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
 411  0
             throw new BuildException("An exception occured; unable to continue task", ex);
 412  9
         }
 413  9
     }
 414  
 
 415  
     /**
 416  
      * Validate the configuration to ensure the parameters have been properly
 417  
      * configured/initialized.
 418  
      *
 419  
      * @throws BuildException if the task was not configured correctly.
 420  
      */
 421  
     private void validateConfiguration() throws BuildException {
 422  12
         if (path == null) {
 423  3
             throw new BuildException("No project dependencies have been defined to analyze.");
 424  
         }
 425  9
         if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
 426  0
             throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
 427  
         }
 428  9
     }
 429  
 
 430  
     /**
 431  
      * Takes the properties supplied and updates the dependency-check settings.
 432  
      * Additionally, this sets the system properties required to change the
 433  
      * proxy url, port, and connection timeout.
 434  
      */
 435  
     private void populateSettings() {
 436  9
         InputStream taskProperties = null;
 437  
         try {
 438  9
             taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
 439  9
             Settings.mergeProperties(taskProperties);
 440  0
         } catch (IOException ex) {
 441  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, "Unable to load the dependency-check ant task.properties file.");
 442  0
             Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
 443  
         } finally {
 444  9
             if (taskProperties != null) {
 445  
                 try {
 446  9
                     taskProperties.close();
 447  0
                 } catch (IOException ex) {
 448  0
                     Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINEST, null, ex);
 449  9
                 }
 450  
             }
 451  
         }
 452  9
         if (dataDirectory != null) {
 453  0
             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
 454  
         } else {
 455  9
             final File jarPath = new File(DependencyCheckTask.class.getProtectionDomain().getCodeSource().getLocation().getPath());
 456  9
             final File base = jarPath.getParentFile();
 457  9
             final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
 458  9
             final File dataDir = new File(base, sub);
 459  9
             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
 460  
         }
 461  
 
 462  9
         Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
 463  
 
 464  9
         if (proxyUrl != null && !proxyUrl.isEmpty()) {
 465  0
             Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
 466  
         }
 467  9
         if (proxyPort != null && !proxyPort.isEmpty()) {
 468  0
             Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
 469  
         }
 470  9
         if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
 471  0
             Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
 472  
         }
 473  9
     }
 474  
 
 475  
     /**
 476  
      * Checks to see if a vulnerability has been identified with a CVSS score
 477  
      * that is above the threshold set in the configuration.
 478  
      *
 479  
      * @param dependencies the list of dependency objects
 480  
      * @throws BuildException thrown if a CVSS score is found that is higher
 481  
      * then the threshold set
 482  
      */
 483  
     private void checkForFailure(List<Dependency> dependencies) throws BuildException {
 484  0
         final StringBuilder ids = new StringBuilder();
 485  0
         for (Dependency d : dependencies) {
 486  0
             for (Vulnerability v : d.getVulnerabilities()) {
 487  0
                 if (v.getCvssScore() >= failBuildOnCVSS) {
 488  0
                     if (ids.length() == 0) {
 489  0
                         ids.append(v.getName());
 490  
                     } else {
 491  0
                         ids.append(", ").append(v.getName());
 492  
                     }
 493  
                 }
 494  
             }
 495  
         }
 496  0
         if (ids.length() > 0) {
 497  0
             final String msg = String.format("%n%nDependency-Check Failure:%n"
 498  
                     + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
 499  
                     + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
 500  0
             throw new BuildException(msg);
 501  
         }
 502  0
     }
 503  
 
 504  
     /**
 505  
      * An enumeration of supported report formats: "ALL", "HTML", "XML", "VULN",
 506  
      * etc..
 507  
      */
 508  12
     public static class ReportFormats extends EnumeratedAttribute {
 509  
 
 510  
         /**
 511  
          * Returns the list of values for the report format.
 512  
          *
 513  
          * @return the list of values for the report format
 514  
          */
 515  
         public String[] getValues() {
 516  12
             int i = 0;
 517  12
             final Format[] formats = Format.values();
 518  12
             final String[] values = new String[formats.length];
 519  60
             for (Format format : formats) {
 520  48
                 values[i++] = format.name();
 521  
             }
 522  12
             return values;
 523  
         }
 524  
     }
 525  
 }