View Javadoc
1   /*
2    * This file is part of dependency-check-cli.
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) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck;
19  
20  import java.io.File;
21  import java.io.FileNotFoundException;
22  
23  import org.apache.commons.cli.CommandLine;
24  import org.apache.commons.cli.CommandLineParser;
25  import org.apache.commons.cli.DefaultParser;
26  import org.apache.commons.cli.HelpFormatter;
27  import org.apache.commons.cli.Option;
28  import org.apache.commons.cli.OptionGroup;
29  import org.apache.commons.cli.Options;
30  import org.apache.commons.cli.ParseException;
31  import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
32  import org.owasp.dependencycheck.utils.InvalidSettingException;
33  import org.owasp.dependencycheck.utils.Settings;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * A utility to parse command line arguments for the DependencyCheck.
39   *
40   * @author Jeremy Long
41   */
42  public final class CliParser {
43  
44      /**
45       * The logger.
46       */
47      private static final Logger LOGGER = LoggerFactory.getLogger(CliParser.class);
48      /**
49       * The command line.
50       */
51      private CommandLine line;
52      /**
53       * Indicates whether the arguments are valid.
54       */
55      private boolean isValid = true;
56  
57      /**
58       * Parses the arguments passed in and captures the results for later use.
59       *
60       * @param args the command line arguments
61       * @throws FileNotFoundException is thrown when a 'file' argument does not point to a file that exists.
62       * @throws ParseException is thrown when a Parse Exception occurs.
63       */
64      public void parse(String[] args) throws FileNotFoundException, ParseException {
65          line = parseArgs(args);
66  
67          if (line != null) {
68              validateArgs();
69          }
70      }
71  
72      /**
73       * Parses the command line arguments.
74       *
75       * @param args the command line arguments
76       * @return the results of parsing the command line arguments
77       * @throws ParseException if the arguments are invalid
78       */
79      private CommandLine parseArgs(String[] args) throws ParseException {
80          final CommandLineParser parser = new DefaultParser();
81          final Options options = createCommandLineOptions();
82          return parser.parse(options, args);
83      }
84  
85      /**
86       * Validates that the command line arguments are valid.
87       *
88       * @throws FileNotFoundException if there is a file specified by either the SCAN or CPE command line arguments that does not
89       * exist.
90       * @throws ParseException is thrown if there is an exception parsing the command line.
91       */
92      private void validateArgs() throws FileNotFoundException, ParseException {
93          if (isRunScan()) {
94              validatePathExists(getScanFiles(), ARGUMENT.SCAN);
95              validatePathExists(getReportDirectory(), ARGUMENT.OUT);
96              if (getPathToMono() != null) {
97                  validatePathExists(getPathToMono(), ARGUMENT.PATH_TO_MONO);
98              }
99              if (!line.hasOption(ARGUMENT.APP_NAME) && !line.hasOption(ARGUMENT.PROJECT)) {
100                 throw new ParseException("Missing '" + ARGUMENT.PROJECT + "' argument; the scan cannot be run without the an project name.");
101             }
102             if (line.hasOption(ARGUMENT.OUTPUT_FORMAT)) {
103                 final String format = line.getOptionValue(ARGUMENT.OUTPUT_FORMAT);
104                 try {
105                     Format.valueOf(format);
106                 } catch (IllegalArgumentException ex) {
107                     final String msg = String.format("An invalid 'format' of '%s' was specified. "
108                             + "Supported output formats are XML, HTML, VULN, or ALL", format);
109                     throw new ParseException(msg);
110                 }
111             }
112             if ((getBaseCve12Url() != null || getBaseCve20Url() != null || getModifiedCve12Url() != null || getModifiedCve20Url() != null)
113                     && (getBaseCve12Url() == null || getBaseCve20Url() == null || getModifiedCve12Url() == null || getModifiedCve20Url() == null)) {
114                 final String msg = "If one of the CVE URLs is specified they must all be specified; please add the missing CVE URL.";
115                 throw new ParseException(msg);
116             }
117             if (line.hasOption((ARGUMENT.SYM_LINK_DEPTH))) {
118                 try {
119                     final int i = Integer.parseInt(line.getOptionValue(ARGUMENT.SYM_LINK_DEPTH));
120                     if (i < 0) {
121                         throw new ParseException("Symbolic Link Depth (symLink) must be greater than zero.");
122                     }
123                 } catch (NumberFormatException ex) {
124                     throw new ParseException("Symbolic Link Depth (symLink) is not a number.");
125                 }
126             }
127         }
128     }
129 
130     /**
131      * Validates whether or not the path(s) points at a file that exists; if the path(s) does not point to an existing file a
132      * FileNotFoundException is thrown.
133      *
134      * @param paths the paths to validate if they exists
135      * @param optType the option being validated (e.g. scan, out, etc.)
136      * @throws FileNotFoundException is thrown if one of the paths being validated does not exist.
137      */
138     private void validatePathExists(String[] paths, String optType) throws FileNotFoundException {
139         for (String path : paths) {
140             validatePathExists(path, optType);
141         }
142     }
143 
144     /**
145      * Validates whether or not the path points at a file that exists; if the path does not point to an existing file a
146      * FileNotFoundException is thrown.
147      *
148      * @param path the paths to validate if they exists
149      * @param argumentName the argument being validated (e.g. scan, out, etc.)
150      * @throws FileNotFoundException is thrown if the path being validated does not exist.
151      */
152     private void validatePathExists(String path, String argumentName) throws FileNotFoundException {
153         if (path == null) {
154             isValid = false;
155             final String msg = String.format("Invalid '%s' argument: null", argumentName);
156             throw new FileNotFoundException(msg);
157         } else if (!path.contains("*") && !path.contains("?")) {
158             File f = new File(path);
159             if ("o".equalsIgnoreCase(argumentName.substring(0, 1)) && !"ALL".equalsIgnoreCase(this.getReportFormat())) {
160                 final String checkPath = path.toLowerCase();
161                 if (checkPath.endsWith(".html") || checkPath.endsWith(".xml") || checkPath.endsWith(".htm")) {
162                     if (f.getParentFile() == null) {
163                         f = new File(".", path);
164                     }
165                     if (!f.getParentFile().isDirectory()) {
166                         isValid = false;
167                         final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path);
168                         throw new FileNotFoundException(msg);
169                     }
170                 }
171             } else {
172                 if (!f.exists()) {
173                     isValid = false;
174                     final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path);
175                     throw new FileNotFoundException(msg);
176                 }
177             }
178         } else if (path.startsWith("//") || path.startsWith("\\\\")) {
179             isValid = false;
180             final String msg = String.format("Invalid '%s' argument: '%s'%nUnable to scan paths that start with '//'.", argumentName, path);
181             throw new FileNotFoundException(msg);
182         }
183     }
184 
185     /**
186      * Generates an Options collection that is used to parse the command line and to display the help message.
187      *
188      * @return the command line options used for parsing the command line
189      */
190     @SuppressWarnings("static-access")
191     private Options createCommandLineOptions() {
192         final Options options = new Options();
193         addStandardOptions(options);
194         addAdvancedOptions(options);
195         addDeprecatedOptions(options);
196         return options;
197     }
198 
199     /**
200      * Adds the standard command line options to the given options collection.
201      *
202      * @param options a collection of command line arguments
203      * @throws IllegalArgumentException thrown if there is an exception
204      */
205     @SuppressWarnings("static-access")
206     private void addStandardOptions(final Options options) throws IllegalArgumentException {
207         final Option help = new Option(ARGUMENT.HELP_SHORT, ARGUMENT.HELP, false,
208                 "Print this message.");
209 
210         final Option advancedHelp = Option.builder().longOpt(ARGUMENT.ADVANCED_HELP)
211                 .desc("Print the advanced help message.").build();
212 
213         final Option version = new Option(ARGUMENT.VERSION_SHORT, ARGUMENT.VERSION,
214                 false, "Print the version information.");
215 
216         final Option noUpdate = new Option(ARGUMENT.DISABLE_AUTO_UPDATE_SHORT, ARGUMENT.DISABLE_AUTO_UPDATE,
217                 false, "Disables the automatic updating of the CPE data.");
218 
219         final Option projectName = Option.builder().hasArg().argName("name").longOpt(ARGUMENT.PROJECT)
220                 .desc("The name of the project being scanned. This is a required argument.")
221                 .build();
222 
223         final Option path = Option.builder(ARGUMENT.SCAN_SHORT).argName("path").hasArg().longOpt(ARGUMENT.SCAN)
224                 .desc("The path to scan - this option can be specified multiple times. Ant style"
225                         + " paths are supported (e.g. path/**/*.jar).")
226                 .build();
227 
228         final Option excludes = Option.builder().argName("pattern").hasArg().longOpt(ARGUMENT.EXCLUDE)
229                 .desc("Specify and exclusion pattern. This option can be specified multiple times"
230                         + " and it accepts Ant style excludsions.")
231                 .build();
232 
233         final Option props = Option.builder(ARGUMENT.PROP_SHORT).argName("file").hasArg().longOpt(ARGUMENT.PROP)
234                 .desc("A property file to load.")
235                 .build();
236 
237         final Option out = Option.builder(ARGUMENT.OUT_SHORT).argName("path").hasArg().longOpt(ARGUMENT.OUT)
238                 .desc("The folder to write reports to. This defaults to the current directory. "
239                         + "It is possible to set this to a specific file name if the format argument is not set to ALL.")
240                 .build();
241 
242         final Option outputFormat = Option.builder(ARGUMENT.OUTPUT_FORMAT_SHORT).argName("format").hasArg().longOpt(ARGUMENT.OUTPUT_FORMAT)
243                 .desc("The output format to write to (XML, HTML, VULN, ALL). The default is HTML.")
244                 .build();
245 
246         final Option verboseLog = Option.builder(ARGUMENT.VERBOSE_LOG_SHORT).argName("file").hasArg().longOpt(ARGUMENT.VERBOSE_LOG)
247                 .desc("The file path to write verbose logging information.")
248                 .build();
249 
250         final Option symLinkDepth = Option.builder().argName("depth").hasArg().longOpt(ARGUMENT.SYM_LINK_DEPTH)
251                 .desc("Sets how deep nested symbolic links will be followed; 0 indicates symbolic links will not be followed.")
252                 .build();
253 
254         final Option suppressionFile = Option.builder().argName("file").hasArg().longOpt(ARGUMENT.SUPPRESSION_FILE)
255                 .desc("The file path to the suppression XML file.")
256                 .build();
257 
258         //This is an option group because it can be specified more then once.
259         final OptionGroup og = new OptionGroup();
260         og.addOption(path);
261 
262         final OptionGroup exog = new OptionGroup();
263         exog.addOption(excludes);
264 
265         options.addOptionGroup(og)
266                 .addOptionGroup(exog)
267                 .addOption(projectName)
268                 .addOption(out)
269                 .addOption(outputFormat)
270                 .addOption(version)
271                 .addOption(help)
272                 .addOption(advancedHelp)
273                 .addOption(noUpdate)
274                 .addOption(symLinkDepth)
275                 .addOption(props)
276                 .addOption(verboseLog)
277                 .addOption(suppressionFile);
278     }
279 
280     /**
281      * Adds the advanced command line options to the given options collection. These are split out for purposes of being able to
282      * display two different help messages.
283      *
284      * @param options a collection of command line arguments
285      * @throws IllegalArgumentException thrown if there is an exception
286      */
287     @SuppressWarnings("static-access")
288     private void addAdvancedOptions(final Options options) throws IllegalArgumentException {
289 
290         final Option cve12Base = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.CVE_BASE_12)
291                 .desc("Base URL for each year’s CVE 1.2, the %d will be replaced with the year. ")
292                 .build();
293 
294         final Option cve20Base = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.CVE_BASE_20)
295                 .desc("Base URL for each year’s CVE 2.0, the %d will be replaced with the year.")
296                 .build();
297 
298         final Option cve12Modified = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.CVE_MOD_12)
299                 .desc("URL for the modified CVE 1.2.")
300                 .build();
301 
302         final Option cve20Modified = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.CVE_MOD_20)
303                 .desc("URL for the modified CVE 2.0.")
304                 .build();
305 
306         final Option updateOnly = Option.builder().longOpt(ARGUMENT.UPDATE_ONLY)
307                 .desc("Only update the local NVD data cache; no scan will be executed.").build();
308 
309         final Option data = Option.builder(ARGUMENT.DATA_DIRECTORY_SHORT).argName("path").hasArg().longOpt(ARGUMENT.DATA_DIRECTORY)
310                 .desc("The location of the H2 Database file. This option should generally not be set.")
311                 .build();
312 
313         final Option nexusUrl = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.NEXUS_URL)
314                 .desc("The url to the Nexus Server's REST API Endpoint (http://domain/nexus/service/local). "
315                         + "If not set the Nexus Analyzer will be disabled.").build();
316 
317         final Option nexusUsesProxy = Option.builder().argName("true/false").hasArg().longOpt(ARGUMENT.NEXUS_USES_PROXY)
318                 .desc("Whether or not the configured proxy should be used when connecting to Nexus.")
319                 .build();
320 
321         final Option additionalZipExtensions = Option.builder().argName("extensions").hasArg()
322                 .longOpt(ARGUMENT.ADDITIONAL_ZIP_EXTENSIONS)
323                 .desc("A comma separated list of additional extensions to be scanned as ZIP files "
324                         + "(ZIP, EAR, WAR are already treated as zip files)").build();
325 
326         final Option pathToMono = Option.builder().argName("path").hasArg().longOpt(ARGUMENT.PATH_TO_MONO)
327                 .desc("The path to Mono for .NET Assembly analysis on non-windows systems.")
328                 .build();
329 
330         final Option connectionTimeout = Option.builder(ARGUMENT.CONNECTION_TIMEOUT_SHORT).argName("timeout").hasArg()
331                 .longOpt(ARGUMENT.CONNECTION_TIMEOUT).desc("The connection timeout (in milliseconds) to use when downloading resources.")
332                 .build();
333 
334         final Option proxyServer = Option.builder().argName("server").hasArg().longOpt(ARGUMENT.PROXY_SERVER)
335                 .desc("The proxy server to use when downloading resources.").build();
336 
337         final Option proxyPort = Option.builder().argName("port").hasArg().longOpt(ARGUMENT.PROXY_PORT)
338                 .desc("The proxy port to use when downloading resources.").build();
339 
340         final Option proxyUsername = Option.builder().argName("user").hasArg().longOpt(ARGUMENT.PROXY_USERNAME)
341                 .desc("The proxy username to use when downloading resources.").build();
342 
343         final Option proxyPassword = Option.builder().argName("pass").hasArg().longOpt(ARGUMENT.PROXY_PASSWORD)
344                 .desc("The proxy password to use when downloading resources.").build();
345 
346         final Option connectionString = Option.builder().argName("connStr").hasArg().longOpt(ARGUMENT.CONNECTION_STRING)
347                 .desc("The connection string to the database.").build();
348 
349         final Option dbUser = Option.builder().argName("user").hasArg().longOpt(ARGUMENT.DB_NAME)
350                 .desc("The username used to connect to the database.").build();
351 
352         final Option dbPassword = Option.builder().argName("password").hasArg().longOpt(ARGUMENT.DB_PASSWORD)
353                 .desc("The password for connecting to the database.").build();
354 
355         final Option dbDriver = Option.builder().argName("driver").hasArg().longOpt(ARGUMENT.DB_DRIVER)
356                 .desc("The database driver name.").build();
357 
358         final Option dbDriverPath = Option.builder().argName("path").hasArg().longOpt(ARGUMENT.DB_DRIVER_PATH)
359                 .desc("The path to the database driver; note, this does not need to be set unless the JAR is outside of the classpath.")
360                 .build();
361 
362         final Option disableJarAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_JAR)
363                 .desc("Disable the Jar Analyzer.").build();
364 
365         final Option disableArchiveAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_ARCHIVE)
366                 .desc("Disable the Archive Analyzer.").build();
367 
368         final Option disableNuspecAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_NUSPEC)
369                 .desc("Disable the Nuspec Analyzer.").build();
370 
371         final Option disableAssemblyAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_ASSEMBLY)
372                 .desc("Disable the .NET Assembly Analyzer.").build();
373 
374         final Option disablePythonDistributionAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_PY_DIST)
375                 .desc("Disable the Python Distribution Analyzer.").build();
376 
377         final Option disablePythonPackageAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_PY_PKG)
378                 .desc("Disable the Python Package Analyzer.").build();
379 
380         final Option disableComposerAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_COMPOSER)
381                 .desc("Disable the PHP Composer Analyzer.").build();
382 
383         final Option disableAutoconfAnalyzer = Option.builder()
384                 .longOpt(ARGUMENT.DISABLE_AUTOCONF)
385                 .desc("Disable the Autoconf Analyzer.").build();
386 
387         final Option disableOpenSSLAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_OPENSSL)
388                 .desc("Disable the OpenSSL Analyzer.").build();
389         final Option disableCmakeAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_CMAKE)
390                 .desc("Disable the Cmake Analyzer.").build();
391 
392         final Option disableCentralAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_CENTRAL)
393                 .desc("Disable the Central Analyzer. If this analyzer is disabled it is likely you also want to disable "
394                         + "the Nexus Analyzer.").build();
395 
396         final Option disableNexusAnalyzer = Option.builder().longOpt(ARGUMENT.DISABLE_NEXUS)
397                 .desc("Disable the Nexus Analyzer.").build();
398 
399         final Option purge = Option.builder().longOpt(ARGUMENT.PURGE_NVD)
400                 .desc("Purges the local NVD data cache")
401                 .build();
402 
403         options.addOption(updateOnly)
404                 .addOption(cve12Base)
405                 .addOption(cve20Base)
406                 .addOption(cve12Modified)
407                 .addOption(cve20Modified)
408                 .addOption(proxyPort)
409                 .addOption(proxyServer)
410                 .addOption(proxyUsername)
411                 .addOption(proxyPassword)
412                 .addOption(connectionTimeout)
413                 .addOption(connectionString)
414                 .addOption(dbUser)
415                 .addOption(data)
416                 .addOption(dbPassword)
417                 .addOption(dbDriver)
418                 .addOption(dbDriverPath)
419                 .addOption(disableJarAnalyzer)
420                 .addOption(disableArchiveAnalyzer)
421                 .addOption(disableAssemblyAnalyzer)
422                 .addOption(disablePythonDistributionAnalyzer)
423                 .addOption(disableCmakeAnalyzer)
424                 .addOption(disablePythonPackageAnalyzer)
425                 .addOption(Option.builder().longOpt(ARGUMENT.DISABLE_RUBYGEMS)
426                         .desc("Disable the Ruby Gemspec Analyzer.").build())
427                 .addOption(disableAutoconfAnalyzer)
428                 .addOption(disableComposerAnalyzer)
429                 .addOption(disableOpenSSLAnalyzer)
430                 .addOption(disableNuspecAnalyzer)
431                 .addOption(disableCentralAnalyzer)
432                 .addOption(disableNexusAnalyzer)
433                 .addOption(Option.builder().longOpt(ARGUMENT.DISABLE_NODE_JS)
434                         .desc("Disable the Node.js Package Analyzer.").build())
435                 .addOption(nexusUrl)
436                 .addOption(nexusUsesProxy)
437                 .addOption(additionalZipExtensions)
438                 .addOption(pathToMono)
439                 .addOption(purge);
440     }
441 
442     /**
443      * Adds the deprecated command line options to the given options collection. These are split out for purposes of not including
444      * them in the help message. We need to add the deprecated options so as not to break existing scripts.
445      *
446      * @param options a collection of command line arguments
447      * @throws IllegalArgumentException thrown if there is an exception
448      */
449     @SuppressWarnings({"static-access", "deprecation"})
450     private void addDeprecatedOptions(final Options options) throws IllegalArgumentException {
451 
452         final Option proxyServer = Option.builder().argName("url").hasArg().longOpt(ARGUMENT.PROXY_URL)
453                 .desc("The proxy url argument is deprecated, use proxyserver instead.")
454                 .build();
455         final Option appName = Option.builder(ARGUMENT.APP_NAME_SHORT).argName("name").hasArg().longOpt(ARGUMENT.APP_NAME)
456                 .desc("The name of the project being scanned.")
457                 .build();
458 
459         options.addOption(proxyServer);
460         options.addOption(appName);
461     }
462 
463     /**
464      * Determines if the 'version' command line argument was passed in.
465      *
466      * @return whether or not the 'version' command line argument was passed in
467      */
468     public boolean isGetVersion() {
469         return (line != null) && line.hasOption(ARGUMENT.VERSION);
470     }
471 
472     /**
473      * Determines if the 'help' command line argument was passed in.
474      *
475      * @return whether or not the 'help' command line argument was passed in
476      */
477     public boolean isGetHelp() {
478         return (line != null) && line.hasOption(ARGUMENT.HELP);
479     }
480 
481     /**
482      * Determines if the 'scan' command line argument was passed in.
483      *
484      * @return whether or not the 'scan' command line argument was passed in
485      */
486     public boolean isRunScan() {
487         return (line != null) && isValid && line.hasOption(ARGUMENT.SCAN);
488     }
489 
490     /**
491      * Returns the symbolic link depth (how deeply symbolic links will be followed).
492      *
493      * @return the symbolic link depth
494      */
495     public int getSymLinkDepth() {
496         int value = 0;
497         try {
498             value = Integer.parseInt(line.getOptionValue(ARGUMENT.SYM_LINK_DEPTH, "0"));
499             if (value < 0) {
500                 value = 0;
501             }
502         } catch (NumberFormatException ex) {
503             LOGGER.debug("Symbolic link was not a number");
504         }
505         return value;
506     }
507 
508     /**
509      * Returns true if the disableJar command line argument was specified.
510      *
511      * @return true if the disableJar command line argument was specified; otherwise false
512      */
513     public boolean isJarDisabled() {
514         return (line != null) && line.hasOption(ARGUMENT.DISABLE_JAR);
515     }
516 
517     /**
518      * Returns true if the disableArchive command line argument was specified.
519      *
520      * @return true if the disableArchive command line argument was specified; otherwise false
521      */
522     public boolean isArchiveDisabled() {
523         return (line != null) && line.hasOption(ARGUMENT.DISABLE_ARCHIVE);
524     }
525 
526     /**
527      * Returns true if the disableNuspec command line argument was specified.
528      *
529      * @return true if the disableNuspec command line argument was specified; otherwise false
530      */
531     public boolean isNuspecDisabled() {
532         return (line != null) && line.hasOption(ARGUMENT.DISABLE_NUSPEC);
533     }
534 
535     /**
536      * Returns true if the disableAssembly command line argument was specified.
537      *
538      * @return true if the disableAssembly command line argument was specified; otherwise false
539      */
540     public boolean isAssemblyDisabled() {
541         return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY);
542     }
543 
544     /**
545      * Returns true if the disablePyDist command line argument was specified.
546      *
547      * @return true if the disablePyDist command line argument was specified; otherwise false
548      */
549     public boolean isPythonDistributionDisabled() {
550         return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST);
551     }
552 
553     /**
554      * Returns true if the disablePyPkg command line argument was specified.
555      *
556      * @return true if the disablePyPkg command line argument was specified; otherwise false
557      */
558     public boolean isPythonPackageDisabled() {
559         return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG);
560     }
561 
562     /**
563      * Returns whether the Ruby gemspec analyzer is disabled.
564      *
565      * @return true if the {@link ARGUMENT#DISABLE_RUBYGEMS} command line argument was specified; otherwise false
566      */
567     public boolean isRubyGemspecDisabled() {
568         return (null != line) && line.hasOption(ARGUMENT.DISABLE_RUBYGEMS);
569     }
570 
571     /**
572      * Returns true if the disableCmake command line argument was specified.
573      *
574      * @return true if the disableCmake command line argument was specified; otherwise false
575      */
576     public boolean isCmakeDisabled() {
577         return (line != null) && line.hasOption(ARGUMENT.DISABLE_CMAKE);
578     }
579 
580     /**
581      * Returns true if the disableAutoconf command line argument was specified.
582      *
583      * @return true if the disableAutoconf command line argument was specified; otherwise false
584      */
585     public boolean isAutoconfDisabled() {
586         return (line != null) && line.hasOption(ARGUMENT.DISABLE_AUTOCONF);
587     }
588 
589     /**
590      * Returns true if the disableComposer command line argument was specified.
591      *
592      * @return true if the disableComposer command line argument was specified; otherwise false
593      */
594     public boolean isComposerDisabled() {
595         return (line != null) && line.hasOption(ARGUMENT.DISABLE_COMPOSER);
596     }
597 
598     /**
599      * Returns true if the disableNexus command line argument was specified.
600      *
601      * @return true if the disableNexus command line argument was specified; otherwise false
602      */
603     public boolean isNexusDisabled() {
604         return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS);
605     }
606 
607     /**
608      * Returns true if the disableOpenSSL command line argument was specified.
609      *
610      * @return true if the disableOpenSSL command line argument was specified; otherwise false
611      */
612     public boolean isOpenSSLDisabled() {
613         return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL);
614     }
615 
616     /**
617      * Returns true if the disableNodeJS command line argument was specified.
618      *
619      * @return true if the disableNodeJS command line argument was specified; otherwise false
620      */
621     public boolean isNodeJsDisabled() {
622         return (line != null) && line.hasOption(ARGUMENT.DISABLE_NODE_JS);
623     }
624 
625     /**
626      * Returns true if the disableCentral command line argument was specified.
627      *
628      * @return true if the disableCentral command line argument was specified; otherwise false
629      */
630     public boolean isCentralDisabled() {
631         return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL);
632     }
633 
634     /**
635      * Returns the url to the nexus server if one was specified.
636      *
637      * @return the url to the nexus server; if none was specified this will return null;
638      */
639     public String getNexusUrl() {
640         if (line == null || !line.hasOption(ARGUMENT.NEXUS_URL)) {
641             return null;
642         } else {
643             return line.getOptionValue(ARGUMENT.NEXUS_URL);
644         }
645     }
646 
647     /**
648      * Returns true if the Nexus Analyzer should use the configured proxy to connect to Nexus; otherwise false is returned.
649      *
650      * @return true if the Nexus Analyzer should use the configured proxy to connect to Nexus; otherwise false
651      */
652     public boolean isNexusUsesProxy() {
653         // If they didn't specify whether Nexus needs to use the proxy, we should
654         // still honor the property if it's set.
655         if (line == null || !line.hasOption(ARGUMENT.NEXUS_USES_PROXY)) {
656             try {
657                 return Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY);
658             } catch (InvalidSettingException ise) {
659                 return true;
660             }
661         } else {
662             return Boolean.parseBoolean(line.getOptionValue(ARGUMENT.NEXUS_USES_PROXY));
663         }
664     }
665 
666     /**
667      * Displays the command line help message to the standard output.
668      */
669     public void printHelp() {
670         final HelpFormatter formatter = new HelpFormatter();
671         final Options options = new Options();
672         addStandardOptions(options);
673         if (line != null && line.hasOption(ARGUMENT.ADVANCED_HELP)) {
674             addAdvancedOptions(options);
675         }
676         final String helpMsg = String.format("%n%s"
677                 + " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. "
678                 + "%s will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.%n%n",
679                 Settings.getString("application.name", "DependencyCheck"),
680                 Settings.getString("application.name", "DependencyCheck"));
681 
682         formatter.printHelp(Settings.getString("application.name", "DependencyCheck"),
683                 helpMsg,
684                 options,
685                 "",
686                 true);
687     }
688 
689     /**
690      * Retrieves the file command line parameter(s) specified for the 'scan' argument.
691      *
692      * @return the file paths specified on the command line for scan
693      */
694     public String[] getScanFiles() {
695         return line.getOptionValues(ARGUMENT.SCAN);
696     }
697 
698     /**
699      * Retrieves the list of excluded file patterns specified by the 'exclude' argument.
700      *
701      * @return the excluded file patterns
702      */
703     public String[] getExcludeList() {
704         return line.getOptionValues(ARGUMENT.EXCLUDE);
705     }
706 
707     /**
708      * Returns the directory to write the reports to specified on the command line.
709      *
710      * @return the path to the reports directory.
711      */
712     public String getReportDirectory() {
713         return line.getOptionValue(ARGUMENT.OUT, ".");
714     }
715 
716     /**
717      * Returns the path to Mono for .NET Assembly analysis on non-windows systems.
718      *
719      * @return the path to Mono
720      */
721     public String getPathToMono() {
722         return line.getOptionValue(ARGUMENT.PATH_TO_MONO);
723     }
724 
725     /**
726      * Returns the output format specified on the command line. Defaults to HTML if no format was specified.
727      *
728      * @return the output format name.
729      */
730     public String getReportFormat() {
731         return line.getOptionValue(ARGUMENT.OUTPUT_FORMAT, "HTML");
732     }
733 
734     /**
735      * Returns the application name specified on the command line.
736      *
737      * @return the application name.
738      */
739     public String getProjectName() {
740         final String appName = line.getOptionValue(ARGUMENT.APP_NAME);
741         String name = line.getOptionValue(ARGUMENT.PROJECT);
742         if (name == null && appName != null) {
743             name = appName;
744             LOGGER.warn("The '" + ARGUMENT.APP_NAME + "' argument should no longer be used; use '" + ARGUMENT.PROJECT + "' instead.");
745         }
746         return name;
747     }
748 
749     /**
750      * Returns the base URL for the CVE 1.2 XMl file.
751      *
752      * @return the URL to the CVE 1.2 XML file.
753      */
754     public String getBaseCve12Url() {
755         return line.getOptionValue(ARGUMENT.CVE_BASE_12);
756     }
757 
758     /**
759      * Returns the base URL for the CVE 2.0 XMl file.
760      *
761      * @return the URL to the CVE 2.0 XML file.
762      */
763     public String getBaseCve20Url() {
764         return line.getOptionValue(ARGUMENT.CVE_BASE_20);
765     }
766 
767     /**
768      * Returns the URL for the modified CVE 1.2 XMl file.
769      *
770      * @return the URL to the modified CVE 1.2 XML file.
771      */
772     public String getModifiedCve12Url() {
773         return line.getOptionValue(ARGUMENT.CVE_MOD_12);
774     }
775 
776     /**
777      * Returns the URL for the modified CVE 2.0 XMl file.
778      *
779      * @return the URL to the modified CVE 2.0 XML file.
780      */
781     public String getModifiedCve20Url() {
782         return line.getOptionValue(ARGUMENT.CVE_MOD_20);
783     }
784 
785     /**
786      * Returns the connection timeout.
787      *
788      * @return the connection timeout
789      */
790     public String getConnectionTimeout() {
791         return line.getOptionValue(ARGUMENT.CONNECTION_TIMEOUT);
792     }
793 
794     /**
795      * Returns the proxy server.
796      *
797      * @return the proxy server
798      */
799     @SuppressWarnings("deprecation")
800     public String getProxyServer() {
801 
802         String server = line.getOptionValue(ARGUMENT.PROXY_SERVER);
803         if (server == null) {
804             server = line.getOptionValue(ARGUMENT.PROXY_URL);
805             if (server != null) {
806                 LOGGER.warn("An old command line argument 'proxyurl' was detected; use proxyserver instead");
807             }
808         }
809         return server;
810     }
811 
812     /**
813      * Returns the proxy port.
814      *
815      * @return the proxy port
816      */
817     public String getProxyPort() {
818         return line.getOptionValue(ARGUMENT.PROXY_PORT);
819     }
820 
821     /**
822      * Returns the proxy username.
823      *
824      * @return the proxy username
825      */
826     public String getProxyUsername() {
827         return line.getOptionValue(ARGUMENT.PROXY_USERNAME);
828     }
829 
830     /**
831      * Returns the proxy password.
832      *
833      * @return the proxy password
834      */
835     public String getProxyPassword() {
836         return line.getOptionValue(ARGUMENT.PROXY_PASSWORD);
837     }
838 
839     /**
840      * Get the value of dataDirectory.
841      *
842      * @return the value of dataDirectory
843      */
844     public String getDataDirectory() {
845         return line.getOptionValue(ARGUMENT.DATA_DIRECTORY);
846     }
847 
848     /**
849      * Returns the properties file specified on the command line.
850      *
851      * @return the properties file specified on the command line
852      */
853     public File getPropertiesFile() {
854         final String path = line.getOptionValue(ARGUMENT.PROP);
855         if (path != null) {
856             return new File(path);
857         }
858         return null;
859     }
860 
861     /**
862      * Returns the path to the verbose log file.
863      *
864      * @return the path to the verbose log file
865      */
866     public String getVerboseLog() {
867         return line.getOptionValue(ARGUMENT.VERBOSE_LOG);
868     }
869 
870     /**
871      * Returns the path to the suppression file.
872      *
873      * @return the path to the suppression file
874      */
875     public String getSuppressionFile() {
876         return line.getOptionValue(ARGUMENT.SUPPRESSION_FILE);
877     }
878 
879     /**
880      * <p>
881      * Prints the manifest information to standard output.</p>
882      * <ul><li>Implementation-Title: ${pom.name}</li>
883      * <li>Implementation-Version: ${pom.version}</li></ul>
884      */
885     public void printVersionInfo() {
886         final String version = String.format("%s version %s",
887                 Settings.getString(Settings.KEYS.APPLICATION_VAME, "dependency-check"),
888                 Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
889         System.out.println(version);
890     }
891 
892     /**
893      * Checks if the auto update feature has been disabled. If it has been disabled via the command line this will return false.
894      *
895      * @return <code>true</code> if auto-update is allowed; otherwise <code>false</code>
896      */
897     public boolean isAutoUpdate() {
898         return line != null && !line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE);
899     }
900 
901     /**
902      * Checks if the update only flag has been set.
903      *
904      * @return <code>true</code> if the update only flag has been set; otherwise <code>false</code>.
905      */
906     public boolean isUpdateOnly() {
907         return line != null && line.hasOption(ARGUMENT.UPDATE_ONLY);
908     }
909 
910     /**
911      * Checks if the purge NVD flag has been set.
912      *
913      * @return <code>true</code> if the purge nvd flag has been set; otherwise <code>false</code>.
914      */
915     public boolean isPurge() {
916         return line != null && line.hasOption(ARGUMENT.PURGE_NVD);
917     }
918 
919     /**
920      * Returns the database driver name if specified; otherwise null is returned.
921      *
922      * @return the database driver name if specified; otherwise null is returned
923      */
924     public String getDatabaseDriverName() {
925         return line.getOptionValue(ARGUMENT.DB_DRIVER);
926     }
927 
928     /**
929      * Returns the database driver path if specified; otherwise null is returned.
930      *
931      * @return the database driver name if specified; otherwise null is returned
932      */
933     public String getDatabaseDriverPath() {
934         return line.getOptionValue(ARGUMENT.DB_DRIVER_PATH);
935     }
936 
937     /**
938      * Returns the database connection string if specified; otherwise null is returned.
939      *
940      * @return the database connection string if specified; otherwise null is returned
941      */
942     public String getConnectionString() {
943         return line.getOptionValue(ARGUMENT.CONNECTION_STRING);
944     }
945 
946     /**
947      * Returns the database database user name if specified; otherwise null is returned.
948      *
949      * @return the database database user name if specified; otherwise null is returned
950      */
951     public String getDatabaseUser() {
952         return line.getOptionValue(ARGUMENT.DB_NAME);
953     }
954 
955     /**
956      * Returns the database database password if specified; otherwise null is returned.
957      *
958      * @return the database database password if specified; otherwise null is returned
959      */
960     public String getDatabasePassword() {
961         return line.getOptionValue(ARGUMENT.DB_PASSWORD);
962     }
963 
964     /**
965      * Returns the additional Extensions if specified; otherwise null is returned.
966      *
967      * @return the additional Extensions; otherwise null is returned
968      */
969     public String getAdditionalZipExtensions() {
970         return line.getOptionValue(ARGUMENT.ADDITIONAL_ZIP_EXTENSIONS);
971     }
972 
973     /**
974      * A collection of static final strings that represent the possible command line arguments.
975      */
976     public static class ARGUMENT {
977 
978         /**
979          * The long CLI argument name specifying the directory/file to scan.
980          */
981         public static final String SCAN = "scan";
982         /**
983          * The short CLI argument name specifying the directory/file to scan.
984          */
985         public static final String SCAN_SHORT = "s";
986         /**
987          * The long CLI argument name specifying that the CPE/CVE/etc. data should not be automatically updated.
988          */
989         public static final String DISABLE_AUTO_UPDATE = "noupdate";
990         /**
991          * The short CLI argument name specifying that the CPE/CVE/etc. data should not be automatically updated.
992          */
993         public static final String DISABLE_AUTO_UPDATE_SHORT = "n";
994         /**
995          * The long CLI argument name specifying that only the update phase should be executed; no scan should be run.
996          */
997         public static final String UPDATE_ONLY = "updateonly";
998         /**
999          * The long CLI argument name specifying that only the update phase should be executed; no scan should be run.
1000          */
1001         public static final String PURGE_NVD = "purge";
1002         /**
1003          * The long CLI argument name specifying the directory to write the reports to.
1004          */
1005         public static final String OUT = "out";
1006         /**
1007          * The short CLI argument name specifying the directory to write the reports to.
1008          */
1009         public static final String OUT_SHORT = "o";
1010         /**
1011          * The long CLI argument name specifying the output format to write the reports to.
1012          */
1013         public static final String OUTPUT_FORMAT = "format";
1014         /**
1015          * The short CLI argument name specifying the output format to write the reports to.
1016          */
1017         public static final String OUTPUT_FORMAT_SHORT = "f";
1018         /**
1019          * The long CLI argument name specifying the name of the project to be scanned.
1020          */
1021         public static final String PROJECT = "project";
1022         /**
1023          * The long CLI argument name specifying the name of the application to be scanned.
1024          *
1025          * @deprecated project should be used instead
1026          */
1027         @Deprecated
1028         public static final String APP_NAME = "app";
1029         /**
1030          * The short CLI argument name specifying the name of the application to be scanned.
1031          *
1032          * @deprecated project should be used instead
1033          */
1034         @Deprecated
1035         public static final String APP_NAME_SHORT = "a";
1036         /**
1037          * The long CLI argument name asking for help.
1038          */
1039         public static final String HELP = "help";
1040         /**
1041          * The long CLI argument name asking for advanced help.
1042          */
1043         public static final String ADVANCED_HELP = "advancedHelp";
1044         /**
1045          * The short CLI argument name asking for help.
1046          */
1047         public static final String HELP_SHORT = "h";
1048         /**
1049          * The long CLI argument name asking for the version.
1050          */
1051         public static final String VERSION_SHORT = "v";
1052         /**
1053          * The short CLI argument name asking for the version.
1054          */
1055         public static final String VERSION = "version";
1056         /**
1057          * The CLI argument name indicating the proxy port.
1058          */
1059         public static final String PROXY_PORT = "proxyport";
1060         /**
1061          * The CLI argument name indicating the proxy server.
1062          */
1063         public static final String PROXY_SERVER = "proxyserver";
1064         /**
1065          * The CLI argument name indicating the proxy url.
1066          *
1067          * @deprecated use {@link #PROXY_SERVER} instead
1068          */
1069         @Deprecated
1070         public static final String PROXY_URL = "proxyurl";
1071         /**
1072          * The CLI argument name indicating the proxy username.
1073          */
1074         public static final String PROXY_USERNAME = "proxyuser";
1075         /**
1076          * The CLI argument name indicating the proxy password.
1077          */
1078         public static final String PROXY_PASSWORD = "proxypass";
1079         /**
1080          * The short CLI argument name indicating the connection timeout.
1081          */
1082         public static final String CONNECTION_TIMEOUT_SHORT = "c";
1083         /**
1084          * The CLI argument name indicating the connection timeout.
1085          */
1086         public static final String CONNECTION_TIMEOUT = "connectiontimeout";
1087         /**
1088          * The short CLI argument name for setting the location of an additional properties file.
1089          */
1090         public static final String PROP_SHORT = "P";
1091         /**
1092          * The CLI argument name for setting the location of an additional properties file.
1093          */
1094         public static final String PROP = "propertyfile";
1095         /**
1096          * The CLI argument name for setting the location of the data directory.
1097          */
1098         public static final String DATA_DIRECTORY = "data";
1099         /**
1100          * The CLI argument name for setting the URL for the CVE Data Files.
1101          */
1102         public static final String CVE_MOD_12 = "cveUrl12Modified";
1103         /**
1104          * The CLI argument name for setting the URL for the CVE Data Files.
1105          */
1106         public static final String CVE_MOD_20 = "cveUrl20Modified";
1107         /**
1108          * The CLI argument name for setting the URL for the CVE Data Files.
1109          */
1110         public static final String CVE_BASE_12 = "cveUrl12Base";
1111         /**
1112          * The CLI argument name for setting the URL for the CVE Data Files.
1113          */
1114         public static final String CVE_BASE_20 = "cveUrl20Base";
1115         /**
1116          * The short CLI argument name for setting the location of the data directory.
1117          */
1118         public static final String DATA_DIRECTORY_SHORT = "d";
1119         /**
1120          * The CLI argument name for setting the location of the data directory.
1121          */
1122         public static final String VERBOSE_LOG = "log";
1123         /**
1124          * The short CLI argument name for setting the location of the data directory.
1125          */
1126         public static final String VERBOSE_LOG_SHORT = "l";
1127 
1128         /**
1129          * The CLI argument name for setting the depth of symbolic links that will be followed.
1130          */
1131         public static final String SYM_LINK_DEPTH = "symLink";
1132         /**
1133          * The CLI argument name for setting the location of the suppression file.
1134          */
1135         public static final String SUPPRESSION_FILE = "suppression";
1136         /**
1137          * Disables the Jar Analyzer.
1138          */
1139         public static final String DISABLE_JAR = "disableJar";
1140         /**
1141          * Disables the Archive Analyzer.
1142          */
1143         public static final String DISABLE_ARCHIVE = "disableArchive";
1144         /**
1145          * Disables the Python Distribution Analyzer.
1146          */
1147         public static final String DISABLE_PY_DIST = "disablePyDist";
1148         /**
1149          * Disables the Python Package Analyzer.
1150          */
1151         public static final String DISABLE_PY_PKG = "disablePyPkg";
1152         /**
1153          * Disables the Python Package Analyzer.
1154          */
1155         public static final String DISABLE_COMPOSER = "disableComposer";
1156         /**
1157          * Disables the Ruby Gemspec Analyzer.
1158          */
1159         public static final String DISABLE_RUBYGEMS = "disableRubygems";
1160         /**
1161          * Disables the Autoconf Analyzer.
1162          */
1163         public static final String DISABLE_AUTOCONF = "disableAutoconf";
1164         /**
1165          * Disables the Cmake Analyzer.
1166          */
1167         public static final String DISABLE_CMAKE = "disableCmake";
1168         /**
1169          * Disables the Assembly Analyzer.
1170          */
1171         public static final String DISABLE_ASSEMBLY = "disableAssembly";
1172         /**
1173          * Disables the Nuspec Analyzer.
1174          */
1175         public static final String DISABLE_NUSPEC = "disableNuspec";
1176         /**
1177          * Disables the Central Analyzer.
1178          */
1179         public static final String DISABLE_CENTRAL = "disableCentral";
1180         /**
1181          * Disables the Nexus Analyzer.
1182          */
1183         public static final String DISABLE_NEXUS = "disableNexus";
1184         /**
1185          * Disables the OpenSSL Analyzer.
1186          */
1187         public static final String DISABLE_OPENSSL = "disableOpenSSL";
1188         /**
1189          * Disables the Node.js Package Analyzer.
1190          */
1191         public static final String DISABLE_NODE_JS = "disableNodeJS";
1192         /**
1193          * The URL of the nexus server.
1194          */
1195         public static final String NEXUS_URL = "nexus";
1196         /**
1197          * Whether or not the defined proxy should be used when connecting to Nexus.
1198          */
1199         public static final String NEXUS_USES_PROXY = "nexusUsesProxy";
1200         /**
1201          * The CLI argument name for setting the connection string.
1202          */
1203         public static final String CONNECTION_STRING = "connectionString";
1204         /**
1205          * The CLI argument name for setting the database user name.
1206          */
1207         public static final String DB_NAME = "dbUser";
1208         /**
1209          * The CLI argument name for setting the database password.
1210          */
1211         public static final String DB_PASSWORD = "dbPassword";
1212         /**
1213          * The CLI argument name for setting the database driver name.
1214          */
1215         public static final String DB_DRIVER = "dbDriverName";
1216         /**
1217          * The CLI argument name for setting the path to the database driver; in case it is not on the class path.
1218          */
1219         public static final String DB_DRIVER_PATH = "dbDriverPath";
1220         /**
1221          * The CLI argument name for setting the path to mono for .NET Assembly analysis on non-windows systems.
1222          */
1223         public static final String PATH_TO_MONO = "mono";
1224         /**
1225          * The CLI argument name for setting extra extensions.
1226          */
1227         public static final String ADDITIONAL_ZIP_EXTENSIONS = "zipExtensions";
1228         /**
1229          * Exclude path argument.
1230          */
1231         public static final String EXCLUDE = "exclude";
1232     }
1233 }