Coverage Report - org.owasp.dependencycheck.cli.CliParser
 
Classes in this File Line Coverage Branch Coverage Complexity
CliParser
76%
73/95
71%
23/32
1.68
CliParser$ArgumentName
0%
0/1
N/A
1.68
 
 1  
 /*
 2  
  * This file is part of dependency-check-cli.
 3  
  *
 4  
  * Dependency-check-cli 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-cli 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-cli. If not, see http://www.gnu.org/licenses/.
 16  
  *
 17  
  * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
 18  
  */
 19  
 package org.owasp.dependencycheck.cli;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.FileNotFoundException;
 23  
 import org.apache.commons.cli.CommandLine;
 24  
 import org.apache.commons.cli.CommandLineParser;
 25  
 import org.apache.commons.cli.HelpFormatter;
 26  
 import org.apache.commons.cli.Option;
 27  
 import org.apache.commons.cli.OptionBuilder;
 28  
 import org.apache.commons.cli.OptionGroup;
 29  
 import org.apache.commons.cli.Options;
 30  
 import org.apache.commons.cli.ParseException;
 31  
 import org.apache.commons.cli.PosixParser;
 32  
 import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
 33  
 import org.owasp.dependencycheck.utils.Settings;
 34  
 
 35  
 /**
 36  
  * A utility to parse command line arguments for the DependencyCheck.
 37  
  *
 38  
  * @author Jeremy Long (jeremy.long@owasp.org)
 39  
  */
 40  9
 public final class CliParser {
 41  
 
 42  
     /**
 43  
      * The command line.
 44  
      */
 45  
     private CommandLine line;
 46  
     /**
 47  
      * The options for the command line parser.
 48  
      */
 49  9
     private final Options options = createCommandLineOptions();
 50  
     /**
 51  
      * Indicates whether the arguments are valid.
 52  
      */
 53  9
     private boolean isValid = true;
 54  
 
 55  
     /**
 56  
      * Parses the arguments passed in and captures the results for later use.
 57  
      *
 58  
      * @param args the command line arguments
 59  
      * @throws FileNotFoundException is thrown when a 'file' argument does not
 60  
      * point to a file that exists.
 61  
      * @throws ParseException is thrown when a Parse Exception occurs.
 62  
      */
 63  
     public void parse(String[] args) throws FileNotFoundException, ParseException {
 64  9
         line = parseArgs(args);
 65  
 
 66  8
         if (line != null) {
 67  8
             validateArgs();
 68  
         }
 69  7
     }
 70  
 
 71  
     /**
 72  
      * Parses the command line arguments.
 73  
      *
 74  
      * @param args the command line arguments
 75  
      * @return the results of parsing the command line arguments
 76  
      * @throws ParseException if the arguments are invalid
 77  
      */
 78  
     private CommandLine parseArgs(String[] args) throws ParseException {
 79  9
         final CommandLineParser parser = new PosixParser();
 80  9
         return parser.parse(options, args);
 81  
     }
 82  
 
 83  
     /**
 84  
      * Validates that the command line arguments are valid.
 85  
      *
 86  
      * @throws FileNotFoundException if there is a file specified by either the
 87  
      * SCAN or CPE command line arguments that does not exist.
 88  
      * @throws ParseException is thrown if there is an exception parsing the
 89  
      * command line.
 90  
      */
 91  
     private void validateArgs() throws FileNotFoundException, ParseException {
 92  8
         if (isRunScan()) {
 93  2
             validatePathExists(getScanFiles(), "scan");
 94  1
             validatePathExists(getReportDirectory(), "out");
 95  1
             if (!line.hasOption(ArgumentName.APP_NAME)) {
 96  0
                 throw new ParseException("Missing 'app' argument; the scan cannot be run without the an application name.");
 97  
             }
 98  1
             if (line.hasOption(ArgumentName.OUTPUT_FORMAT)) {
 99  0
                 final String format = line.getOptionValue(ArgumentName.OUTPUT_FORMAT);
 100  
                 try {
 101  0
                     Format.valueOf(format);
 102  0
                 } catch (IllegalArgumentException ex) {
 103  0
                     final String msg = String.format("An invalid 'format' of '%s' was specified. Supported output formats are XML, HTML, VULN, or ALL", format);
 104  0
                     throw new ParseException(msg);
 105  0
                 }
 106  
             }
 107  
         }
 108  7
     }
 109  
 
 110  
     /**
 111  
      * Validates whether or not the path(s) points at a file that exists; if the
 112  
      * path(s) does not point to an existing file a FileNotFoundException is
 113  
      * thrown.
 114  
      *
 115  
      * @param paths the paths to validate if they exists
 116  
      * @param optType the option being validated (e.g. scan, out, etc.)
 117  
      * @throws FileNotFoundException is thrown if one of the paths being
 118  
      * validated does not exist.
 119  
      */
 120  
     private void validatePathExists(String[] paths, String optType) throws FileNotFoundException {
 121  3
         for (String path : paths) {
 122  2
             validatePathExists(path, optType);
 123  
         }
 124  1
     }
 125  
 
 126  
     /**
 127  
      * Validates whether or not the path points at a file that exists; if the
 128  
      * path does not point to an existing file a FileNotFoundException is
 129  
      * thrown.
 130  
      *
 131  
      * @param path the paths to validate if they exists
 132  
      * @param optType the option being validated (e.g. scan, out, etc.)
 133  
      * @throws FileNotFoundException is thrown if the path being validated does
 134  
      * not exist.
 135  
      */
 136  
     private void validatePathExists(String path, String optType) throws FileNotFoundException {
 137  3
         final File f = new File(path);
 138  3
         if (!f.exists()) {
 139  1
             isValid = false;
 140  1
             final String msg = String.format("Invalid '%s' argument: '%s'", optType, path);
 141  1
             throw new FileNotFoundException(msg);
 142  
         }
 143  2
     }
 144  
 
 145  
     /**
 146  
      * Generates an Options collection that is used to parse the command line
 147  
      * and to display the help message.
 148  
      *
 149  
      * @return the command line options used for parsing the command line
 150  
      */
 151  
     @SuppressWarnings("static-access")
 152  
     private Options createCommandLineOptions() {
 153  9
         final Option help = new Option(ArgumentName.HELP_SHORT, ArgumentName.HELP, false,
 154  
                 "Print this message.");
 155  
 
 156  9
         final Option version = new Option(ArgumentName.VERSION_SHORT, ArgumentName.VERSION,
 157  
                 false, "Print the version information.");
 158  
 
 159  9
         final Option noUpdate = new Option(ArgumentName.DISABLE_AUTO_UPDATE_SHORT, ArgumentName.DISABLE_AUTO_UPDATE,
 160  
                 false, "Disables the automatic updating of the CPE data.");
 161  
 
 162  9
         final Option appName = OptionBuilder.withArgName("name").hasArg().withLongOpt(ArgumentName.APP_NAME)
 163  
                 .withDescription("The name of the application being scanned. This is a required argument.")
 164  
                 .create(ArgumentName.APP_NAME_SHORT);
 165  
 
 166  9
         final Option connectionTimeout = OptionBuilder.withArgName("timeout").hasArg().withLongOpt(ArgumentName.CONNECTION_TIMEOUT)
 167  
                 .withDescription("The connection timeout (in milliseconds) to use when downloading resources.")
 168  
                 .create(ArgumentName.CONNECTION_TIMEOUT_SHORT);
 169  
 
 170  9
         final Option proxyUrl = OptionBuilder.withArgName("url").hasArg().withLongOpt(ArgumentName.PROXY_URL)
 171  
                 .withDescription("The proxy url to use when downloading resources.")
 172  
                 .create(ArgumentName.PROXY_URL_SHORT);
 173  
 
 174  9
         final Option proxyPort = OptionBuilder.withArgName("port").hasArg().withLongOpt(ArgumentName.PROXY_PORT)
 175  
                 .withDescription("The proxy port to use when downloading resources.")
 176  
                 .create(ArgumentName.PROXY_PORT_SHORT);
 177  
 
 178  9
         final Option proxyUsername = OptionBuilder.withArgName("user").hasArg().withLongOpt(ArgumentName.PROXY_USERNAME)
 179  
                 .withDescription("The proxy username to use when downloading resources.")
 180  
                 .create(ArgumentName.PROXY_USERNAME_SHORT);
 181  
 
 182  9
         final Option proxyPassword = OptionBuilder.withArgName("pass").hasArg().withLongOpt(ArgumentName.PROXY_PASSWORD)
 183  
                 .withDescription("The proxy password to use when downloading resources.")
 184  
                 .create(ArgumentName.PROXY_PASSWORD_SHORT);
 185  
 
 186  9
         final Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN)
 187  
                 .withDescription("The path to scan - this option can be specified multiple times.")
 188  
                 .create(ArgumentName.SCAN_SHORT);
 189  
 
 190  9
         final Option props = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.PROP)
 191  
                 .withDescription("A property file to load.")
 192  
                 .create(ArgumentName.PROP_SHORT);
 193  
 
 194  9
         final Option data = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.DATA_DIRECTORY)
 195  
                 .withDescription("The location of the data directory used to store persistent data. This option should generally not be set.")
 196  
                 .create(ArgumentName.DATA_DIRECTORY_SHORT);
 197  
 
 198  9
         final Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ArgumentName.OUT)
 199  
                 .withDescription("The folder to write reports to. This defaults to the current directory.")
 200  
                 .create(ArgumentName.OUT_SHORT);
 201  
 
 202  9
         final Option outputFormat = OptionBuilder.withArgName("format").hasArg().withLongOpt(ArgumentName.OUTPUT_FORMAT)
 203  
                 .withDescription("The output format to write to (XML, HTML, VULN, ALL). The default is HTML.")
 204  
                 .create(ArgumentName.OUTPUT_FORMAT_SHORT);
 205  
 
 206  9
         final Option verboseLog = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.VERBOSE_LOG)
 207  
                 .withDescription("The file path to write verbose logging information.")
 208  
                 .create(ArgumentName.VERBOSE_LOG_SHORT);
 209  
 
 210  9
         final Option suppressionFile = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.SUPPRESION_FILE)
 211  
                 .withDescription("The file path to the suppression XML file.")
 212  
                 .create(ArgumentName.SUPPRESION_FILE_SHORT);
 213  
 
 214  
 
 215  9
         final OptionGroup og = new OptionGroup();
 216  9
         og.addOption(path);
 217  
 
 218  9
         final Options opts = new Options();
 219  9
         opts.addOptionGroup(og);
 220  9
         opts.addOption(out);
 221  9
         opts.addOption(outputFormat);
 222  9
         opts.addOption(appName);
 223  9
         opts.addOption(version);
 224  9
         opts.addOption(help);
 225  9
         opts.addOption(noUpdate);
 226  9
         opts.addOption(props);
 227  9
         opts.addOption(data);
 228  9
         opts.addOption(verboseLog);
 229  9
         opts.addOption(suppressionFile);
 230  9
         opts.addOption(proxyPort);
 231  9
         opts.addOption(proxyUrl);
 232  9
         opts.addOption(proxyUsername);
 233  9
         opts.addOption(proxyPassword);
 234  9
         opts.addOption(connectionTimeout);
 235  
 
 236  9
         return opts;
 237  
     }
 238  
 
 239  
     /**
 240  
      * Determines if the 'version' command line argument was passed in.
 241  
      *
 242  
      * @return whether or not the 'version' command line argument was passed in
 243  
      */
 244  
     public boolean isGetVersion() {
 245  7
         return (line != null) && line.hasOption(ArgumentName.VERSION);
 246  
     }
 247  
 
 248  
     /**
 249  
      * Determines if the 'help' command line argument was passed in.
 250  
      *
 251  
      * @return whether or not the 'help' command line argument was passed in
 252  
      */
 253  
     public boolean isGetHelp() {
 254  7
         return (line != null) && line.hasOption(ArgumentName.HELP);
 255  
     }
 256  
 
 257  
     /**
 258  
      * Determines if the 'scan' command line argument was passed in.
 259  
      *
 260  
      * @return whether or not the 'scan' command line argument was passed in
 261  
      */
 262  
     public boolean isRunScan() {
 263  15
         return (line != null) && isValid && line.hasOption(ArgumentName.SCAN);
 264  
     }
 265  
 
 266  
     /**
 267  
      * Displays the command line help message to the standard output.
 268  
      */
 269  
     public void printHelp() {
 270  2
         final HelpFormatter formatter = new HelpFormatter();
 271  2
         final String nl = System.getProperty("line.separator");
 272  
 
 273  2
         formatter.printHelp(Settings.getString("application.name", "DependencyCheck"),
 274  
                 nl + Settings.getString("application.name", "DependencyCheck")
 275  
                 + " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. "
 276  
                 + Settings.getString("application.name", "DependencyCheck")
 277  
                 + " will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov." + nl + nl,
 278  
                 options,
 279  
                 "",
 280  
                 true);
 281  2
     }
 282  
 
 283  
     /**
 284  
      * Retrieves the file command line parameter(s) specified for the 'scan'
 285  
      * argument.
 286  
      *
 287  
      * @return the file paths specified on the command line for scan
 288  
      */
 289  
     public String[] getScanFiles() {
 290  3
         return line.getOptionValues(ArgumentName.SCAN);
 291  
     }
 292  
 
 293  
     /**
 294  
      * Returns the directory to write the reports to specified on the command
 295  
      * line.
 296  
      *
 297  
      * @return the path to the reports directory.
 298  
      */
 299  
     public String getReportDirectory() {
 300  1
         return line.getOptionValue(ArgumentName.OUT, ".");
 301  
     }
 302  
 
 303  
     /**
 304  
      * Returns the output format specified on the command line. Defaults to HTML
 305  
      * if no format was specified.
 306  
      *
 307  
      * @return the output format name.
 308  
      */
 309  
     public String getReportFormat() {
 310  0
         return line.getOptionValue(ArgumentName.OUTPUT_FORMAT, "HTML");
 311  
     }
 312  
 
 313  
     /**
 314  
      * Returns the application name specified on the command line.
 315  
      *
 316  
      * @return the application name.
 317  
      */
 318  
     public String getApplicationName() {
 319  0
         return line.getOptionValue(ArgumentName.APP_NAME);
 320  
     }
 321  
 
 322  
     /**
 323  
      * Returns the connection timeout.
 324  
      *
 325  
      * @return the connection timeout
 326  
      */
 327  
     public String getConnectionTimeout() {
 328  0
         return line.getOptionValue(ArgumentName.CONNECTION_TIMEOUT);
 329  
     }
 330  
 
 331  
     /**
 332  
      * Returns the proxy url.
 333  
      *
 334  
      * @return the proxy url
 335  
      */
 336  
     public String getProxyUrl() {
 337  0
         return line.getOptionValue(ArgumentName.PROXY_URL);
 338  
     }
 339  
 
 340  
     /**
 341  
      * Returns the proxy port.
 342  
      *
 343  
      * @return the proxy port
 344  
      */
 345  
     public String getProxyPort() {
 346  0
         return line.getOptionValue(ArgumentName.PROXY_PORT);
 347  
     }
 348  
 
 349  
     /**
 350  
      * Returns the proxy username.
 351  
      *
 352  
      * @return the proxy username
 353  
      */
 354  
     public String getProxyUsername() {
 355  0
         return line.getOptionValue(ArgumentName.PROXY_USERNAME);
 356  
     }
 357  
 
 358  
     /**
 359  
      * Returns the proxy password.
 360  
      *
 361  
      * @return the proxy password
 362  
      */
 363  
     public String getProxyPassword() {
 364  0
         return line.getOptionValue(ArgumentName.PROXY_PASSWORD);
 365  
     }
 366  
 
 367  
     /**
 368  
      * Get the value of dataDirectory.
 369  
      *
 370  
      * @return the value of dataDirectory
 371  
      */
 372  
     public String getDataDirectory() {
 373  0
         return line.getOptionValue(ArgumentName.DATA_DIRECTORY);
 374  
     }
 375  
 
 376  
     /**
 377  
      * Returns the properties file specified on the command line.
 378  
      *
 379  
      * @return the properties file specified on the command line
 380  
      */
 381  
     public File getPropertiesFile() {
 382  0
         final String path = line.getOptionValue(ArgumentName.PROP);
 383  0
         if (path != null) {
 384  0
             return new File(path);
 385  
         }
 386  0
         return null;
 387  
     }
 388  
 
 389  
     /**
 390  
      * Returns the path to the verbose log file.
 391  
      *
 392  
      * @return the path to the verbose log file
 393  
      */
 394  
     public String getVerboseLog() {
 395  0
         return line.getOptionValue(ArgumentName.VERBOSE_LOG);
 396  
     }
 397  
 
 398  
     /**
 399  
      * Returns the path to the suppression file.
 400  
      *
 401  
      * @return the path to the suppression file
 402  
      */
 403  
     public String getSuppressionFile() {
 404  0
         return line.getOptionValue(ArgumentName.SUPPRESION_FILE);
 405  
     }
 406  
 
 407  
     /**
 408  
      * <p>Prints the manifest information to standard output.</p>
 409  
      * <ul><li>Implementation-Title: ${pom.name}</li>
 410  
      * <li>Implementation-Version: ${pom.version}</li></ul>
 411  
      */
 412  
     public void printVersionInfo() {
 413  1
         final String version = String.format("%s version %s",
 414  
                 Settings.getString("application.name", "DependencyCheck"),
 415  
                 Settings.getString("application.version", "Unknown"));
 416  1
         System.out.println(version);
 417  1
     }
 418  
 
 419  
     /**
 420  
      * Checks if the auto update feature has been disabled. If it has been
 421  
      * disabled via the command line this will return false.
 422  
      *
 423  
      * @return if auto-update is allowed.
 424  
      */
 425  
     public boolean isAutoUpdate() {
 426  0
         return (line == null) || !line.hasOption(ArgumentName.DISABLE_AUTO_UPDATE);
 427  
     }
 428  
 
 429  
     /**
 430  
      * A collection of static final strings that represent the possible command
 431  
      * line arguments.
 432  
      */
 433  9
     public static class ArgumentName {
 434  
 
 435  
         /**
 436  
          * The long CLI argument name specifying the directory/file to scan.
 437  
          */
 438  
         public static final String SCAN = "scan";
 439  
         /**
 440  
          * The short CLI argument name specifying the directory/file to scan.
 441  
          */
 442  
         public static final String SCAN_SHORT = "s";
 443  
         /**
 444  
          * The long CLI argument name specifying that the CPE/CVE/etc. data
 445  
          * should not be automatically updated.
 446  
          */
 447  
         public static final String DISABLE_AUTO_UPDATE = "noupdate";
 448  
         /**
 449  
          * The short CLI argument name specifying that the CPE/CVE/etc. data
 450  
          * should not be automatically updated.
 451  
          */
 452  
         public static final String DISABLE_AUTO_UPDATE_SHORT = "n";
 453  
         /**
 454  
          * The long CLI argument name specifying the directory to write the
 455  
          * reports to.
 456  
          */
 457  
         public static final String OUT = "out";
 458  
         /**
 459  
          * The short CLI argument name specifying the directory to write the
 460  
          * reports to.
 461  
          */
 462  
         public static final String OUT_SHORT = "o";
 463  
         /**
 464  
          * The long CLI argument name specifying the output format to write the
 465  
          * reports to.
 466  
          */
 467  
         public static final String OUTPUT_FORMAT = "format";
 468  
         /**
 469  
          * The short CLI argument name specifying the output format to write the
 470  
          * reports to.
 471  
          */
 472  
         public static final String OUTPUT_FORMAT_SHORT = "f";
 473  
         /**
 474  
          * The long CLI argument name specifying the name of the application to
 475  
          * be scanned.
 476  
          */
 477  
         public static final String APP_NAME = "app";
 478  
         /**
 479  
          * The short CLI argument name specifying the name of the application to
 480  
          * be scanned.
 481  
          */
 482  
         public static final String APP_NAME_SHORT = "a";
 483  
         /**
 484  
          * The long CLI argument name asking for help.
 485  
          */
 486  
         public static final String HELP = "help";
 487  
         /**
 488  
          * The short CLI argument name asking for help.
 489  
          */
 490  
         public static final String HELP_SHORT = "h";
 491  
         /**
 492  
          * The long CLI argument name asking for the version.
 493  
          */
 494  
         public static final String VERSION_SHORT = "v";
 495  
         /**
 496  
          * The short CLI argument name asking for the version.
 497  
          */
 498  
         public static final String VERSION = "version";
 499  
         /**
 500  
          * The short CLI argument name indicating the proxy port.
 501  
          */
 502  
         public static final String PROXY_PORT_SHORT = "p";
 503  
         /**
 504  
          * The CLI argument name indicating the proxy port.
 505  
          */
 506  
         public static final String PROXY_PORT = "proxyport";
 507  
         /**
 508  
          * The short CLI argument name indicating the proxy url.
 509  
          */
 510  
         public static final String PROXY_URL_SHORT = "u";
 511  
         /**
 512  
          * The CLI argument name indicating the proxy url.
 513  
          */
 514  
         public static final String PROXY_URL = "proxyurl";
 515  
         /**
 516  
          * The short CLI argument name indicating the proxy username.
 517  
          */
 518  
         public static final String PROXY_USERNAME_SHORT = "pu";
 519  
         /**
 520  
          * The CLI argument name indicating the proxy username.
 521  
          */
 522  
         public static final String PROXY_USERNAME = "proxyuser";
 523  
         /**
 524  
          * The short CLI argument name indicating the proxy password.
 525  
          */
 526  
         public static final String PROXY_PASSWORD_SHORT = "pp";
 527  
         /**
 528  
          * The CLI argument name indicating the proxy password.
 529  
          */
 530  
         public static final String PROXY_PASSWORD = "proxypass";
 531  
         /**
 532  
          * The short CLI argument name indicating the connection timeout.
 533  
          */
 534  
         public static final String CONNECTION_TIMEOUT_SHORT = "c";
 535  
         /**
 536  
          * The CLI argument name indicating the connection timeout.
 537  
          */
 538  
         public static final String CONNECTION_TIMEOUT = "connectiontimeout";
 539  
         /**
 540  
          * The short CLI argument name for setting the location of an additional
 541  
          * properties file.
 542  
          */
 543  
         public static final String PROP_SHORT = "p";
 544  
         /**
 545  
          * The CLI argument name for setting the location of an additional
 546  
          * properties file.
 547  
          */
 548  
         public static final String PROP = "propertyfile";
 549  
         /**
 550  
          * The CLI argument name for setting the location of the data directory.
 551  
          */
 552  
         public static final String DATA_DIRECTORY = "data";
 553  
         /**
 554  
          * The short CLI argument name for setting the location of the data
 555  
          * directory.
 556  
          */
 557  
         public static final String DATA_DIRECTORY_SHORT = "d";
 558  
         /**
 559  
          * The CLI argument name for setting the location of the data directory.
 560  
          */
 561  
         public static final String VERBOSE_LOG = "log";
 562  
         /**
 563  
          * The short CLI argument name for setting the location of the data
 564  
          * directory.
 565  
          */
 566  
         public static final String VERBOSE_LOG_SHORT = "l";
 567  
         /**
 568  
          * The CLI argument name for setting the location of the suppression
 569  
          * file.
 570  
          */
 571  
         public static final String SUPPRESION_FILE = "suppression";
 572  
         /**
 573  
          * The short CLI argument name for setting the location of the
 574  
          * suppression file.
 575  
          */
 576  
         public static final String SUPPRESION_FILE_SHORT = "sf";
 577  
     }
 578  
 }