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