Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions cli/src/main/java/org/owasp/dependencycheck/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.LogLevel;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
Expand All @@ -46,6 +47,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -95,6 +97,14 @@ public static void main(String[] args) {
*/
public App() {
settings = new Settings();
settings.setString(Settings.KEYS.APPLICATION_NAME, determineName());
}

private static String determineName() {
return Optional.ofNullable(System.getenv("ODC_NAME"))
.filter(StringUtils::isNotBlank)
.map(n -> n.replace('/', '-').replace(' ', '_'))
.orElse("dependency-check-cli");
}

/**
Expand All @@ -120,11 +130,11 @@ public int run(String[] args) {
cli.parse(args);
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
cli.printHelp();
cli.printHelp(System.out);
return 1;
} catch (ParseException ex) {
System.err.println(ex.getMessage());
cli.printHelp();
cli.printHelp(System.out);
return 2;
}
final String verboseLog = cli.getStringArgument(CliParser.ARGUMENT.VERBOSE_LOG);
Expand Down Expand Up @@ -224,7 +234,7 @@ public int run(String[] args) {
settings.cleanup();
}
} else {
cli.printHelp();
cli.printHelp(System.out);
}
return exitCode;
}
Expand Down Expand Up @@ -456,12 +466,6 @@ private void runUpdateOnly() throws UpdateException, DatabaseException {
* file is unable to be loaded.
*/
protected void populateSettings(CliParser cli) throws InvalidSettingException {
String name = System.getenv("ODC_NAME") != null ? System.getenv("ODC_NAME") : "dependency-check-cli";
if (name.isBlank()) {
name = "dependency-check-cli";
}
name = name.replace("/", "-").replace(" ", "_");
settings.setString(Settings.KEYS.APPLICATION_NAME, name);
final File propertiesFile = cli.getFileArgument(CliParser.ARGUMENT.PROP);
if (propertiesFile != null) {
try {
Expand Down
69 changes: 38 additions & 31 deletions cli/src/main/java/org/owasp/dependencycheck/CliParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import org.apache.commons.cli.help.TextHelpAppendable;
import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings;
Expand All @@ -34,6 +35,9 @@

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Comparator;

/**
* A utility to parse command line arguments for the DependencyCheck.
Expand Down Expand Up @@ -65,6 +69,12 @@ public final class CliParser {
*/
private static final String SUPPORTED_FORMATS = "HTML, XML, CSV, JSON, JUNIT, SARIF, JENKINS, GITLAB or ALL";

private static final String HELP_MSG = String.format(
"Dependency-Check can be used to identify if there are any known CVE vulnerabilities in libraries " +
"utilized by an application. Dependency-Check will automatically update required data from the " +
"Internet, such as the CVE and CPE data files from nvd.nist.gov.%n"
);

/**
* Constructs a new CLI Parser object with the configured settings.
*
Expand All @@ -82,7 +92,7 @@ public CliParser(Settings settings) {
* point to a file that exists.
* @throws ParseException is thrown when a Parse Exception occurs.
*/
public void parse(String[] args) throws FileNotFoundException, ParseException {
public void parse(String... args) throws FileNotFoundException, ParseException {
line = parseArgs(args);

if (line != null) {
Expand All @@ -97,7 +107,7 @@ public void parse(String[] args) throws FileNotFoundException, ParseException {
* @return the results of parsing the command line arguments
* @throws ParseException if the arguments are invalid
*/
private CommandLine parseArgs(String[] args) throws ParseException {
private CommandLine parseArgs(String... args) throws ParseException {
final CommandLineParser parser = new DefaultParser();
final Options options = createCommandLineOptions();
return parser.parse(options, args);
Expand Down Expand Up @@ -213,7 +223,7 @@ private boolean isValidFormat(String format) {
* @param argumentName the argument being validated (e.g. scan, out, etc.)
* @return true, if path exists; false otherwise
*/
private boolean isValidFilePath(String path, String argumentName) {
private boolean isValidFilePath(String path, @SuppressWarnings("SameParameterValue") String argumentName) {
try {
validatePathExists(path, argumentName);
return true;
Expand All @@ -232,7 +242,7 @@ private boolean isValidFilePath(String path, String argumentName) {
* @throws FileNotFoundException is thrown if one of the paths being
* validated does not exist.
*/
private void validatePathExists(String[] paths, String optType) throws FileNotFoundException {
private void validatePathExists(String[] paths, @SuppressWarnings("SameParameterValue") String optType) throws FileNotFoundException {
for (String path : paths) {
validatePathExists(path, optType);
}
Expand Down Expand Up @@ -301,7 +311,6 @@ private void validatePathExists(String path, String argumentName) throws FileNot
*
* @return the command line options used for parsing the command line
*/
@SuppressWarnings("static-access")
private Options createCommandLineOptions() {
final Options options = new Options();
addStandardOptions(options);
Expand All @@ -315,10 +324,8 @@ private Options createCommandLineOptions() {
*
* @param options a collection of command line arguments
*/
@SuppressWarnings("static-access")
private void addStandardOptions(final Options options) {
//This is an option group because it can be specified more then once.

//This is an option group because it can be specified more than once.
options.addOptionGroup(newOptionGroup(newOptionWithArg(ARGUMENT.SCAN_SHORT, ARGUMENT.SCAN, "path",
"The path to scan - this option can be specified multiple times. Ant style paths are supported (e.g. 'path/**/*.jar'); "
+ "if using Ant style paths it is highly recommended to quote the argument value.")))
Expand Down Expand Up @@ -357,7 +364,6 @@ private void addStandardOptions(final Options options) {
*
* @param options a collection of command line arguments
*/
@SuppressWarnings("static-access")
private void addAdvancedOptions(final Options options) {
options
.addOption(newOption(ARGUMENT.UPDATE_ONLY,
Expand Down Expand Up @@ -445,11 +451,11 @@ private void addAdvancedOptions(final Options options) {
.addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL, "url",
"The Retire JS Repository URL"))
.addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_USER, "username",
"The password to authenticate to Retire JS Repository URL"))
"Credentials for basic auth towards the Retire JS Repository URL"))
.addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_PASSWORD, "password",
"The password to authenticate to Retire JS Repository URL"))
"Credentials for basic auth towards the Retire JS Repository URL"))
Comment thread
chadlwilson marked this conversation as resolved.
.addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_BEARER_TOKEN, "token",
"The password to authenticate to Retire JS Repository URL"))
"Token for bearer auth towards the Retire JS Repository URL"))
.addOption(newOption(ARGUMENT.RETIRE_JS_FILTER_NON_VULNERABLE, "Specifies that the Retire JS "
+ "Analyzer should filter out non-vulnerable JS files from the report."))
.addOption(newOptionWithArg(ARGUMENT.ARTIFACTORY_PARALLEL_ANALYSIS, "true/false",
Expand Down Expand Up @@ -571,7 +577,6 @@ private void addAdvancedOptions(final Options options) {
*
* @param options a collection of command line arguments
*/
@SuppressWarnings({"static-access", "deprecation"})
private void addDeprecatedOptions(final Options options) {
//not a real option - but enables java debugging via the shell script
options.addOption(newOption("debug",
Expand Down Expand Up @@ -784,26 +789,28 @@ public File getFileArgument(String option) {
}

/**
* Displays the command line help message to the standard output.
* Appends the command line help message to the passed appendable
*/
public void printHelp() {
final HelpFormatter formatter = new HelpFormatter();
void printHelp(Appendable appendable) {
TextHelpAppendable helpAppendable = new TextHelpAppendable(appendable);
helpAppendable.setMaxWidth(100);
Comment thread
chadlwilson marked this conversation as resolved.
HelpFormatter formatter = HelpFormatter.builder()
.setShowSince(false)
.setComparator(Comparator.comparing(Option::getKey, String::compareToIgnoreCase))
.setHelpAppendable(helpAppendable)
.get();

final Options options = new Options();
addStandardOptions(options);
if (line != null && line.hasOption(ARGUMENT.ADVANCED_HELP)) {
addAdvancedOptions(options);
}
final String helpMsg = String.format("%n%s"
+ " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. "
+ "%s will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.%n%n",
settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck"),
settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck"));

formatter.printHelp(settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck"),
helpMsg,
options,
"",
true);
try {
formatter.printHelp("dependency-check", HELP_MSG, formatter.sort(options), "", true);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
Expand Down Expand Up @@ -1005,7 +1012,7 @@ public float getFloatArgument(String option, float defaultValue) {
* @return a new option
*/
private Option newOption(String name, String description) {
return Option.builder().longOpt(name).desc(description).build();
return Option.builder().longOpt(name).desc(description).get();
}

/**
Expand All @@ -1017,7 +1024,7 @@ private Option newOption(String name, String description) {
* @return a new option
*/
private Option newOption(String shortName, String name, String description) {
return Option.builder(shortName).longOpt(name).desc(description).build();
return Option.builder(shortName).longOpt(name).desc(description).get();
}

/**
Expand All @@ -1029,7 +1036,7 @@ private Option newOption(String shortName, String name, String description) {
* @return a new option
*/
private Option newOptionWithArg(String name, String arg, String description) {
return Option.builder().longOpt(name).argName(arg).hasArg().desc(description).build();
return Option.builder().longOpt(name).argName(arg).hasArg().desc(description).get();
}

/**
Expand All @@ -1042,7 +1049,7 @@ private Option newOptionWithArg(String name, String arg, String description) {
* @return a new option
*/
private Option newOptionWithArg(String shortName, String name, String arg, String description) {
return Option.builder(shortName).longOpt(name).argName(arg).hasArg().desc(description).build();
return Option.builder(shortName).longOpt(name).argName(arg).hasArg().desc(description).get();
}

/**
Expand Down
72 changes: 33 additions & 39 deletions cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.matchesPattern;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

/**
*
Expand All @@ -47,13 +54,8 @@ class CliParserTest extends BaseTest {
@Test
void testParse() throws Exception {

String[] args = {};

ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos, true, UTF_8));

CliParser instance = new CliParser(getSettings());
instance.parse(args);
instance.parse();

assertFalse(instance.isGetVersion());
assertFalse(instance.isGetHelp());
Expand Down Expand Up @@ -160,16 +162,9 @@ void testParse_failOnCVSSValidArgument() throws Exception {
@Test
void testParse_unknown() {

String[] args = {"-unknown"};

ByteArrayOutputStream baos_out = new ByteArrayOutputStream();
ByteArrayOutputStream baos_err = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos_out, true, UTF_8));
System.setErr(new PrintStream(baos_err, true, UTF_8));

CliParser instance = new CliParser(getSettings());

ParseException ex = assertThrows(ParseException.class, () -> instance.parse(args) ,
ParseException ex = assertThrows(ParseException.class, () -> instance.parse("-unknown"),
"Unrecognized option should have caused an exception");
assertTrue(ex.getMessage().contains("Unrecognized option"));

Expand Down Expand Up @@ -243,7 +238,6 @@ void testParse_scan_withFileExists() throws Exception {
*
*/
@Test
@SuppressWarnings("StringSplitter")
void testParse_printVersionInfo() {

PrintStream out = System.out;
Expand All @@ -253,11 +247,7 @@ void testParse_printVersionInfo() {
CliParser instance = new CliParser(getSettings());
instance.printVersionInfo();
try {
String text = baos.toString(UTF_8).toLowerCase();
String[] lines = text.split(System.lineSeparator());
assertTrue(lines.length >= 1);
assertTrue(text.contains("version"));
assertFalse(text.contains("unknown"));
assertThat(baos.toString(UTF_8), matchesPattern("(?mi)dependency-check.*version [0-9]+.*\n"));
} finally {
System.setOut(out);
}
Expand All @@ -269,28 +259,32 @@ void testParse_printVersionInfo() {
* @throws Exception thrown when an exception occurs.
*/
@Test
@SuppressWarnings("StringSplitter")
void testParse_printHelp() throws Exception {
CliParser instance = new CliParser(getSettings());
instance.parse("-h");
StringWriter text = new StringWriter();
instance.printHelp(text);

PrintStream out = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos, true, UTF_8));
List<String> lines = text.toString().lines().collect(toList());
assertThat(lines, containsInRelativeOrder(startsWith(" usage: dependency-check"), startsWith(" -v, --version")));
assertThat(lines.size(), greaterThan(10));
}

/**
* Test of printHelp, of class CliParser.
*
* @throws Exception thrown when an exception occurs.
*/
@Test
void testParse_printHelpAdvanced() throws Exception {
CliParser instance = new CliParser(getSettings());
String[] args = {"-h"};
instance.parse(args);
instance.printHelp();
args[0] = "--advancedHelp";
instance.parse(args);
instance.printHelp();
try {
String text = (baos.toString(UTF_8));
String[] lines = text.split(System.lineSeparator());
assertTrue(lines[0].startsWith("usage: "));
assertTrue((lines.length > 2));
} finally {
System.setOut(out);
}
instance.parse("--advancedHelp");
StringWriter text = new StringWriter();
instance.printHelp(text);

List<String> lines = text.toString().lines().collect(toList());
assertThat(lines, containsInRelativeOrder(startsWith(" usage: dependency-check"), startsWith(" -c, --connectiontimeout"), startsWith(" -v, --version")));
assertThat(lines.size(), greaterThan(60));
}

/**
Expand Down
Loading