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