1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.cli;
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.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.InvalidSettingException;
34 import org.owasp.dependencycheck.utils.Settings;
35
36
37
38
39
40
41 public final class CliParser {
42
43
44
45
46 private CommandLine line;
47
48
49
50 private boolean isValid = true;
51
52
53
54
55
56
57
58
59 public void parse(String[] args) throws FileNotFoundException, ParseException {
60 line = parseArgs(args);
61
62 if (line != null) {
63 validateArgs();
64 }
65 }
66
67
68
69
70
71
72
73
74 private CommandLine parseArgs(String[] args) throws ParseException {
75 final CommandLineParser parser = new PosixParser();
76 final Options options = createCommandLineOptions();
77 return parser.parse(options, args);
78 }
79
80
81
82
83
84
85
86
87 private void validateArgs() throws FileNotFoundException, ParseException {
88 if (isRunScan()) {
89 validatePathExists(getScanFiles(), ArgumentName.SCAN);
90 validatePathExists(getReportDirectory(), ArgumentName.OUT);
91 if (getPathToMono() != null) {
92 validatePathExists(getPathToMono(), ArgumentName.PATH_TO_MONO);
93 }
94 if (!line.hasOption(ArgumentName.APP_NAME)) {
95 throw new ParseException("Missing 'app' argument; the scan cannot be run without the an application name.");
96 }
97 if (line.hasOption(ArgumentName.OUTPUT_FORMAT)) {
98 final String format = line.getOptionValue(ArgumentName.OUTPUT_FORMAT);
99 try {
100 Format.valueOf(format);
101 } catch (IllegalArgumentException ex) {
102 final String msg = String.format("An invalid 'format' of '%s' was specified. "
103 + "Supported output formats are XML, HTML, VULN, or ALL", format);
104 throw new ParseException(msg);
105 }
106 }
107 }
108 }
109
110
111
112
113
114
115
116
117
118 private void validatePathExists(String[] paths, String optType) throws FileNotFoundException {
119 for (String path : paths) {
120 validatePathExists(path, optType);
121 }
122 }
123
124
125
126
127
128
129
130
131
132 private void validatePathExists(String path, String argumentName) throws FileNotFoundException {
133 final File f = new File(path);
134 if (!f.exists()) {
135 isValid = false;
136 final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path);
137 throw new FileNotFoundException(msg);
138 }
139 }
140
141
142
143
144
145
146 @SuppressWarnings("static-access")
147 private Options createCommandLineOptions() {
148
149 final Options options = new Options();
150 addStandardOptions(options);
151 addAdvancedOptions(options);
152
153 return options;
154 }
155
156
157
158
159
160
161
162 @SuppressWarnings("static-access")
163 private void addStandardOptions(final Options options) throws IllegalArgumentException {
164 final Option help = new Option(ArgumentName.HELP_SHORT, ArgumentName.HELP, false,
165 "Print this message.");
166
167 final Option advancedHelp = OptionBuilder.withLongOpt(ArgumentName.ADVANCED_HELP)
168 .withDescription("Print the advanced help message.").create();
169
170 final Option version = new Option(ArgumentName.VERSION_SHORT, ArgumentName.VERSION,
171 false, "Print the version information.");
172
173 final Option noUpdate = new Option(ArgumentName.DISABLE_AUTO_UPDATE_SHORT, ArgumentName.DISABLE_AUTO_UPDATE,
174 false, "Disables the automatic updating of the CPE data.");
175
176 final Option appName = OptionBuilder.withArgName("name").hasArg().withLongOpt(ArgumentName.APP_NAME)
177 .withDescription("The name of the application being scanned. This is a required argument.")
178 .create(ArgumentName.APP_NAME_SHORT);
179
180 final Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN)
181 .withDescription("The path to scan - this option can be specified multiple times.")
182 .create(ArgumentName.SCAN_SHORT);
183
184 final Option props = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.PROP)
185 .withDescription("A property file to load.")
186 .create(ArgumentName.PROP_SHORT);
187
188 final Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ArgumentName.OUT)
189 .withDescription("The folder to write reports to. This defaults to the current directory.")
190 .create(ArgumentName.OUT_SHORT);
191
192 final Option outputFormat = OptionBuilder.withArgName("format").hasArg().withLongOpt(ArgumentName.OUTPUT_FORMAT)
193 .withDescription("The output format to write to (XML, HTML, VULN, ALL). The default is HTML.")
194 .create(ArgumentName.OUTPUT_FORMAT_SHORT);
195
196 final Option verboseLog = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.VERBOSE_LOG)
197 .withDescription("The file path to write verbose logging information.")
198 .create(ArgumentName.VERBOSE_LOG_SHORT);
199
200 final Option suppressionFile = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.SUPPRESION_FILE)
201 .withDescription("The file path to the suppression XML file.")
202 .create();
203
204
205 final OptionGroup og = new OptionGroup();
206 og.addOption(path);
207
208 options.addOptionGroup(og)
209 .addOption(out)
210 .addOption(outputFormat)
211 .addOption(appName)
212 .addOption(version)
213 .addOption(help)
214 .addOption(advancedHelp)
215 .addOption(noUpdate)
216 .addOption(props)
217 .addOption(verboseLog)
218 .addOption(suppressionFile);
219 }
220
221
222
223
224
225
226
227
228 @SuppressWarnings("static-access")
229 private void addAdvancedOptions(final Options options) throws IllegalArgumentException {
230
231 final Option data = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.DATA_DIRECTORY)
232 .withDescription("The location of the H2 Database file. This option should generally not be set.")
233 .create(ArgumentName.DATA_DIRECTORY_SHORT);
234
235 final Option connectionTimeout = OptionBuilder.withArgName("timeout").hasArg().withLongOpt(ArgumentName.CONNECTION_TIMEOUT)
236 .withDescription("The connection timeout (in milliseconds) to use when downloading resources.")
237 .create(ArgumentName.CONNECTION_TIMEOUT_SHORT);
238
239 final Option proxyUrl = OptionBuilder.withArgName("url").hasArg().withLongOpt(ArgumentName.PROXY_URL)
240 .withDescription("The proxy url to use when downloading resources.")
241 .create(ArgumentName.PROXY_URL_SHORT);
242
243 final Option proxyPort = OptionBuilder.withArgName("port").hasArg().withLongOpt(ArgumentName.PROXY_PORT)
244 .withDescription("The proxy port to use when downloading resources.")
245 .create(ArgumentName.PROXY_PORT_SHORT);
246
247 final Option proxyUsername = OptionBuilder.withArgName("user").hasArg().withLongOpt(ArgumentName.PROXY_USERNAME)
248 .withDescription("The proxy username to use when downloading resources.")
249 .create();
250
251 final Option proxyPassword = OptionBuilder.withArgName("pass").hasArg().withLongOpt(ArgumentName.PROXY_PASSWORD)
252 .withDescription("The proxy password to use when downloading resources.")
253 .create();
254
255 final Option connectionString = OptionBuilder.withArgName("connStr").hasArg().withLongOpt(ArgumentName.CONNECTION_STRING)
256 .withDescription("The connection string to the database.")
257 .create();
258
259 final Option dbUser = OptionBuilder.withArgName("user").hasArg().withLongOpt(ArgumentName.DB_NAME)
260 .withDescription("The username used to connect to the database.")
261 .create();
262
263 final Option dbPassword = OptionBuilder.withArgName("password").hasArg().withLongOpt(ArgumentName.DB_PASSWORD)
264 .withDescription("The password for connecting to the database.")
265 .create();
266
267 final Option dbDriver = OptionBuilder.withArgName("driver").hasArg().withLongOpt(ArgumentName.DB_DRIVER)
268 .withDescription("The database driver name.")
269 .create();
270
271 final Option dbDriverPath = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.DB_DRIVER_PATH)
272 .withDescription("The path to the database driver; note, this does not need to be set unless the JAR is outside of the classpath.")
273 .create();
274
275 final Option disableNexusAnalyzer = OptionBuilder.withLongOpt(ArgumentName.DISABLE_NEXUS)
276 .withDescription("Disable the Nexus Analyzer.")
277 .create();
278
279 final Option nexusUrl = OptionBuilder.withArgName("url").hasArg().withLongOpt(ArgumentName.NEXUS_URL)
280 .withDescription("The url to the Nexus Server.")
281 .create();
282
283 final Option nexusUsesProxy = OptionBuilder.withArgName("true/false").hasArg().withLongOpt(ArgumentName.NEXUS_USES_PROXY)
284 .withDescription("Whether or not the configured proxy should be used when connecting to Nexus.")
285 .create();
286
287 final Option additionalZipExtensions = OptionBuilder.withArgName("extensions").hasArg()
288 .withLongOpt(ArgumentName.ADDITIONAL_ZIP_EXTENSIONS)
289 .withDescription("A comma seperated list of additional extensions to be scanned as ZIP files "
290 + "(ZIP, EAR, WAR are already treated as zip files)")
291 .create();
292
293 final Option pathToMono = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.PATH_TO_MONO)
294 .withDescription("The path to Mono for .NET Assembly analysis on non-windows systems.")
295 .create();
296
297 options.addOption(proxyPort)
298 .addOption(proxyUrl)
299 .addOption(proxyUsername)
300 .addOption(proxyPassword)
301 .addOption(connectionTimeout)
302 .addOption(connectionString)
303 .addOption(dbUser)
304 .addOption(data)
305 .addOption(dbPassword)
306 .addOption(dbDriver)
307 .addOption(dbDriverPath)
308 .addOption(disableNexusAnalyzer)
309 .addOption(nexusUrl)
310 .addOption(nexusUsesProxy)
311 .addOption(additionalZipExtensions)
312 .addOption(pathToMono);
313 }
314
315
316
317
318
319
320 public boolean isGetVersion() {
321 return (line != null) && line.hasOption(ArgumentName.VERSION);
322 }
323
324
325
326
327
328
329 public boolean isGetHelp() {
330 return (line != null) && line.hasOption(ArgumentName.HELP);
331 }
332
333
334
335
336
337
338 public boolean isRunScan() {
339 return (line != null) && isValid && line.hasOption(ArgumentName.SCAN);
340 }
341
342
343
344
345
346
347 public boolean isNexusDisabled() {
348 return (line != null) && line.hasOption(ArgumentName.DISABLE_NEXUS);
349 }
350
351
352
353
354
355
356 public String getNexusUrl() {
357 if (line == null || !line.hasOption(ArgumentName.NEXUS_URL)) {
358 return null;
359 } else {
360 return line.getOptionValue(ArgumentName.NEXUS_URL);
361 }
362 }
363
364
365
366
367
368
369
370 public boolean isNexusUsesProxy() {
371
372
373 if (line == null || !line.hasOption(ArgumentName.NEXUS_USES_PROXY)) {
374 try {
375 return Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY);
376 } catch (InvalidSettingException ise) {
377 return true;
378 }
379 } else {
380 return Boolean.parseBoolean(line.getOptionValue(ArgumentName.NEXUS_USES_PROXY));
381 }
382 }
383
384
385
386
387 public void printHelp() {
388 final HelpFormatter formatter = new HelpFormatter();
389 final Options options = new Options();
390 addStandardOptions(options);
391 if (line != null && line.hasOption(ArgumentName.ADVANCED_HELP)) {
392 addAdvancedOptions(options);
393 }
394 final String helpMsg = String.format("%n%s"
395 + " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. "
396 + "%s will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.%n%n",
397 Settings.getString("application.name", "DependencyCheck"),
398 Settings.getString("application.name", "DependencyCheck"));
399
400 formatter.printHelp(Settings.getString("application.name", "DependencyCheck"),
401 helpMsg,
402 options,
403 "",
404 true);
405
406 }
407
408
409
410
411
412
413 public String[] getScanFiles() {
414 return line.getOptionValues(ArgumentName.SCAN);
415 }
416
417
418
419
420
421
422 public String getReportDirectory() {
423 return line.getOptionValue(ArgumentName.OUT, ".");
424 }
425
426
427
428
429
430
431 public String getPathToMono() {
432 return line.getOptionValue(ArgumentName.PATH_TO_MONO);
433 }
434
435
436
437
438
439
440 public String getReportFormat() {
441 return line.getOptionValue(ArgumentName.OUTPUT_FORMAT, "HTML");
442 }
443
444
445
446
447
448
449 public String getApplicationName() {
450 return line.getOptionValue(ArgumentName.APP_NAME);
451 }
452
453
454
455
456
457
458 public String getConnectionTimeout() {
459 return line.getOptionValue(ArgumentName.CONNECTION_TIMEOUT);
460 }
461
462
463
464
465
466
467 public String getProxyUrl() {
468 return line.getOptionValue(ArgumentName.PROXY_URL);
469 }
470
471
472
473
474
475
476 public String getProxyPort() {
477 return line.getOptionValue(ArgumentName.PROXY_PORT);
478 }
479
480
481
482
483
484
485 public String getProxyUsername() {
486 return line.getOptionValue(ArgumentName.PROXY_USERNAME);
487 }
488
489
490
491
492
493
494 public String getProxyPassword() {
495 return line.getOptionValue(ArgumentName.PROXY_PASSWORD);
496 }
497
498
499
500
501
502
503 public String getDataDirectory() {
504 return line.getOptionValue(ArgumentName.DATA_DIRECTORY);
505 }
506
507
508
509
510
511
512 public File getPropertiesFile() {
513 final String path = line.getOptionValue(ArgumentName.PROP);
514 if (path != null) {
515 return new File(path);
516 }
517 return null;
518 }
519
520
521
522
523
524
525 public String getVerboseLog() {
526 return line.getOptionValue(ArgumentName.VERBOSE_LOG);
527 }
528
529
530
531
532
533
534 public String getSuppressionFile() {
535 return line.getOptionValue(ArgumentName.SUPPRESION_FILE);
536 }
537
538
539
540
541
542
543
544 public void printVersionInfo() {
545 final String version = String.format("%s version %s",
546 Settings.getString("application.name", "DependencyCheck"),
547 Settings.getString("application.version", "Unknown"));
548 System.out.println(version);
549 }
550
551
552
553
554
555
556
557 public boolean isAutoUpdate() {
558 return (line == null) || !line.hasOption(ArgumentName.DISABLE_AUTO_UPDATE);
559 }
560
561
562
563
564
565
566 public String getDatabaseDriverName() {
567 return line.getOptionValue(ArgumentName.DB_DRIVER);
568 }
569
570
571
572
573
574
575 public String getDatabaseDriverPath() {
576 return line.getOptionValue(ArgumentName.DB_DRIVER_PATH);
577 }
578
579
580
581
582
583
584 public String getConnectionString() {
585 return line.getOptionValue(ArgumentName.CONNECTION_STRING);
586 }
587
588
589
590
591
592
593 public String getDatabaseUser() {
594 return line.getOptionValue(ArgumentName.DB_NAME);
595 }
596
597
598
599
600
601
602 public String getDatabasePassword() {
603 return line.getOptionValue(ArgumentName.DB_PASSWORD);
604 }
605
606
607
608
609
610
611 public String getAdditionalZipExtensions() {
612 return line.getOptionValue(ArgumentName.ADDITIONAL_ZIP_EXTENSIONS);
613 }
614
615
616
617
618 public static class ArgumentName {
619
620
621
622
623 public static final String SCAN = "scan";
624
625
626
627 public static final String SCAN_SHORT = "s";
628
629
630
631 public static final String DISABLE_AUTO_UPDATE = "noupdate";
632
633
634
635 public static final String DISABLE_AUTO_UPDATE_SHORT = "n";
636
637
638
639 public static final String OUT = "out";
640
641
642
643 public static final String OUT_SHORT = "o";
644
645
646
647 public static final String OUTPUT_FORMAT = "format";
648
649
650
651 public static final String OUTPUT_FORMAT_SHORT = "f";
652
653
654
655 public static final String APP_NAME = "app";
656
657
658
659 public static final String APP_NAME_SHORT = "a";
660
661
662
663 public static final String HELP = "help";
664
665
666
667 public static final String ADVANCED_HELP = "advancedHelp";
668
669
670
671 public static final String HELP_SHORT = "h";
672
673
674
675 public static final String VERSION_SHORT = "v";
676
677
678
679 public static final String VERSION = "version";
680
681
682
683 public static final String PROXY_PORT_SHORT = "p";
684
685
686
687 public static final String PROXY_PORT = "proxyport";
688
689
690
691 public static final String PROXY_URL_SHORT = "u";
692
693
694
695 public static final String PROXY_URL = "proxyurl";
696
697
698
699 public static final String PROXY_USERNAME = "proxyuser";
700
701
702
703 public static final String PROXY_PASSWORD = "proxypass";
704
705
706
707 public static final String CONNECTION_TIMEOUT_SHORT = "c";
708
709
710
711 public static final String CONNECTION_TIMEOUT = "connectiontimeout";
712
713
714
715 public static final String PROP_SHORT = "P";
716
717
718
719 public static final String PROP = "propertyfile";
720
721
722
723 public static final String DATA_DIRECTORY = "data";
724
725
726
727 public static final String DATA_DIRECTORY_SHORT = "d";
728
729
730
731 public static final String VERBOSE_LOG = "log";
732
733
734
735 public static final String VERBOSE_LOG_SHORT = "l";
736
737
738
739 public static final String SUPPRESION_FILE = "suppression";
740
741
742
743 public static final String DISABLE_NEXUS = "disableNexus";
744
745
746
747 public static final String NEXUS_URL = "nexus";
748
749
750
751 public static final String NEXUS_USES_PROXY = "nexusUsesProxy";
752
753
754
755 public static final String CONNECTION_STRING = "connectionString";
756
757
758
759 public static final String DB_NAME = "dbUser";
760
761
762
763 public static final String DB_PASSWORD = "dbPassword";
764
765
766
767 public static final String DB_DRIVER = "dbDriverName";
768
769
770
771 public static final String DB_DRIVER_PATH = "dbDriverPath";
772
773
774
775 public static final String PATH_TO_MONO = "mono";
776
777
778
779 public static final String ADDITIONAL_ZIP_EXTENSIONS = "zipExtensions";
780 }
781 }