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