View Javadoc
1   /*
2    * This file is part of dependency-check-cli.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck;
19  
20  import ch.qos.logback.classic.Level;
21  import ch.qos.logback.classic.LoggerContext;
22  import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
23  import ch.qos.logback.classic.filter.ThresholdFilter;
24  import ch.qos.logback.classic.spi.ILoggingEvent;
25  import ch.qos.logback.core.FileAppender;
26  import org.apache.commons.cli.ParseException;
27  import org.apache.tools.ant.DirectoryScanner;
28  import org.apache.tools.ant.types.LogLevel;
29  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
30  import org.owasp.dependencycheck.data.update.exception.UpdateException;
31  import org.owasp.dependencycheck.dependency.Dependency;
32  import org.owasp.dependencycheck.dependency.Vulnerability;
33  import org.owasp.dependencycheck.dependency.naming.Identifier;
34  import org.owasp.dependencycheck.exception.ExceptionCollection;
35  import org.owasp.dependencycheck.exception.ReportException;
36  import org.owasp.dependencycheck.utils.Downloader;
37  import org.owasp.dependencycheck.utils.InvalidSettingException;
38  import org.owasp.dependencycheck.utils.Settings;
39  import org.owasp.dependencycheck.utils.SeverityUtil;
40  import org.owasp.dependencycheck.utils.scarf.TelemetryCollector;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import java.io.File;
45  import java.io.FileNotFoundException;
46  import java.io.IOException;
47  import java.util.ArrayList;
48  import java.util.List;
49  import java.util.Set;
50  import java.util.TreeSet;
51  import java.util.stream.Collectors;
52  import java.util.stream.Stream;
53  
54  /**
55   * The command line interface for the DependencyCheck application.
56   *
57   * @author Jeremy Long
58   */
59  @SuppressWarnings("squid:S106")
60  public class App {
61  
62      /**
63       * The logger.
64       */
65      private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
66      /**
67       * Properties file error message.
68       */
69      private static final String ERROR_LOADING_PROPERTIES_FILE = "Error loading properties file";
70      /**
71       * System specific new line character.
72       */
73      private static final String NEW_LINE = System.getProperty("line.separator", "\n");
74      /**
75       * The configured settings.
76       */
77      private final Settings settings;
78  
79      /**
80       * The main method for the application.
81       *
82       * @param args the command line arguments
83       */
84      @SuppressWarnings("squid:S4823")
85      public static void main(String[] args) {
86          final int exitCode;
87          final App app = new App();
88          exitCode = app.run(args);
89          LOGGER.debug("Exit code: {}", exitCode);
90          System.exit(exitCode);
91      }
92  
93      /**
94       * Builds the App object.
95       */
96      public App() {
97          settings = new Settings();
98      }
99  
100     /**
101      * Builds the App object; this method is used for testing.
102      *
103      * @param settings the configured settings
104      */
105     protected App(Settings settings) {
106         this.settings = settings;
107     }
108 
109     /**
110      * Main CLI entry-point into the application.
111      *
112      * @param args the command line arguments
113      * @return the exit code to return
114      */
115     public int run(String[] args) {
116         int exitCode = 0;
117         final CliParser cli = new CliParser(settings);
118 
119         try {
120             cli.parse(args);
121         } catch (FileNotFoundException ex) {
122             System.err.println(ex.getMessage());
123             cli.printHelp();
124             return 1;
125         } catch (ParseException ex) {
126             System.err.println(ex.getMessage());
127             cli.printHelp();
128             return 2;
129         }
130         final String verboseLog = cli.getStringArgument(CliParser.ARGUMENT.VERBOSE_LOG);
131         if (verboseLog != null) {
132             prepareLogger(verboseLog);
133         }
134 
135         if (cli.isPurge()) {
136             final String connStr = cli.getStringArgument(CliParser.ARGUMENT.CONNECTION_STRING);
137             if (connStr != null) {
138                 LOGGER.error("Unable to purge the database when using a non-default connection string");
139                 exitCode = 3;
140             } else {
141                 try {
142                     populateSettings(cli);
143                     Downloader.getInstance().configure(settings);
144                 } catch (InvalidSettingException ex) {
145                     LOGGER.error(ex.getMessage());
146                     LOGGER.debug(ERROR_LOADING_PROPERTIES_FILE, ex);
147                     exitCode = 4;
148                     return exitCode;
149                 }
150                 try (Engine engine = new Engine(Engine.Mode.EVIDENCE_PROCESSING, settings)) {
151                     if (!engine.purge()) {
152                         exitCode = 7;
153                         return exitCode;
154                     }
155                 } finally {
156                     settings.cleanup();
157                 }
158             }
159         } else if (cli.isGetVersion()) {
160             cli.printVersionInfo();
161         } else if (cli.isUpdateOnly()) {
162             try {
163                 populateSettings(cli);
164                 settings.setBoolean(Settings.KEYS.AUTO_UPDATE, true);
165                 Downloader.getInstance().configure(settings);
166             } catch (InvalidSettingException ex) {
167                 LOGGER.error(ex.getMessage());
168                 LOGGER.debug(ERROR_LOADING_PROPERTIES_FILE, ex);
169                 exitCode = 4;
170                 return exitCode;
171             }
172             try {
173                 runUpdateOnly();
174             } catch (UpdateException ex) {
175                 LOGGER.error(ex.getMessage(), ex);
176                 exitCode = 8;
177             } catch (DatabaseException ex) {
178                 LOGGER.error(ex.getMessage(), ex);
179                 exitCode = 9;
180             } finally {
181                 settings.cleanup();
182             }
183         } else if (cli.isRunScan()) {
184             try {
185                 populateSettings(cli);
186                 Downloader.getInstance().configure(settings);
187                 TelemetryCollector.send(settings);
188             } catch (InvalidSettingException ex) {
189                 LOGGER.error(ex.getMessage(), ex);
190                 LOGGER.debug(ERROR_LOADING_PROPERTIES_FILE, ex);
191                 exitCode = 4;
192                 return exitCode;
193             }
194             try {
195                 final String[] scanFiles = cli.getScanFiles();
196                 if (scanFiles != null) {
197                     exitCode = runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), scanFiles,
198                             cli.getExcludeList(), cli.getSymLinkDepth(), cli.getFailOnCVSS());
199                 } else {
200                     LOGGER.error("No scan files configured");
201                 }
202             } catch (DatabaseException ex) {
203                 LOGGER.error(ex.getMessage());
204                 LOGGER.debug("database exception", ex);
205                 exitCode = 11;
206             } catch (ReportException ex) {
207                 LOGGER.error(ex.getMessage());
208                 LOGGER.debug("report exception", ex);
209                 exitCode = 12;
210             } catch (ExceptionCollection ex) {
211                 if (ex.isFatal()) {
212                     exitCode = 13;
213                     LOGGER.error("One or more fatal errors occurred");
214                 } else {
215                     exitCode = 14;
216                 }
217                 for (Throwable e : ex.getExceptions()) {
218                     if (e.getMessage() != null) {
219                         LOGGER.error(e.getMessage());
220                         LOGGER.debug("unexpected error", e);
221                     }
222                 }
223             } finally {
224                 settings.cleanup();
225             }
226         } else {
227             cli.printHelp();
228         }
229         return exitCode;
230     }
231 
232     /**
233      * Scans the specified directories and writes the dependency reports to the
234      * reportDirectory.
235      *
236      * @param reportDirectory the path to the directory where the reports will
237      * be written
238      * @param outputFormats String[] of output formats of the report
239      * @param applicationName the application name for the report
240      * @param files the files/directories to scan
241      * @param excludes the patterns for files/directories to exclude
242      * @param symLinkDepth the depth that symbolic links will be followed
243      * @param cvssFailScore the score to fail on if a vulnerability is found
244      * @return the exit code if there was an error
245      * @throws ReportException thrown when the report cannot be generated
246      * @throws DatabaseException thrown when there is an error connecting to the
247      * database
248      * @throws ExceptionCollection thrown when an exception occurs during
249      * analysis; there may be multiple exceptions contained within the
250      * collection.
251      */
252     private int runScan(String reportDirectory, String[] outputFormats, String applicationName, String[] files,
253                         String[] excludes, int symLinkDepth, float cvssFailScore) throws DatabaseException,
254             ExceptionCollection, ReportException {
255         Engine engine = null;
256         try {
257             final List<String> antStylePaths = getPaths(files);
258             final Set<File> paths = scanAntStylePaths(antStylePaths, symLinkDepth, excludes);
259 
260             engine = new Engine(settings);
261             engine.scan(paths);
262 
263             ExceptionCollection exCol = null;
264             try {
265                 engine.analyzeDependencies();
266             } catch (ExceptionCollection ex) {
267                 if (ex.isFatal()) {
268                     throw ex;
269                 }
270                 exCol = ex;
271             }
272 
273             try {
274                 for (String outputFormat : outputFormats) {
275                     engine.writeReports(applicationName, new File(reportDirectory), outputFormat, exCol);
276                 }
277             } catch (ReportException ex) {
278                 if (exCol != null) {
279                     exCol.addException(ex);
280                     throw exCol;
281                 } else {
282                     throw ex;
283                 }
284             }
285             if (exCol != null && !exCol.getExceptions().isEmpty()) {
286                 throw exCol;
287             }
288             return determineReturnCode(engine, cvssFailScore);
289         } finally {
290             if (engine != null) {
291                 engine.close();
292             }
293         }
294     }
295 
296     /**
297      * Determines the return code based on if one of the dependencies scanned
298      * has a vulnerability with a CVSS score above the cvssFailScore.
299      *
300      * @param engine the engine used during analysis
301      * @param cvssFailScore the max allowed CVSS score
302      * @return returns <code>1</code> if a severe enough vulnerability is
303      * identified; otherwise <code>0</code>
304      */
305     private int determineReturnCode(Engine engine, float cvssFailScore) {
306         int retCode = 0;
307         //Set the exit code based on whether we found a high enough vulnerability
308         final StringBuilder ids = new StringBuilder();
309         for (Dependency d : engine.getDependencies()) {
310             boolean addName = true;
311             for (Vulnerability v : d.getVulnerabilities()) {
312                 final double cvssV2 = v.getCvssV2() != null && v.getCvssV2().getCvssData() != null
313                         && v.getCvssV2().getCvssData().getBaseScore() != null ? v.getCvssV2().getCvssData().getBaseScore() : -1;
314                 final double cvssV3 = v.getCvssV3() != null && v.getCvssV3().getCvssData() != null
315                         && v.getCvssV3().getCvssData().getBaseScore() != null ? v.getCvssV3().getCvssData().getBaseScore() : -1;
316                 final double cvssV4 = v.getCvssV4() != null && v.getCvssV4().getCvssData() != null
317                         && v.getCvssV4().getCvssData().getBaseScore() != null ? v.getCvssV4().getCvssData().getBaseScore() : -1;
318                 final boolean useUnscored = cvssV2 == -1 && cvssV3 == -1 && cvssV4 == -1;
319                 final double unscoredCvss = (useUnscored && v.getUnscoredSeverity() != null) ? SeverityUtil.estimateCvssV2(v.getUnscoredSeverity()) : -1;
320 
321                 if (cvssV2 >= cvssFailScore
322                         || cvssV3 >= cvssFailScore
323                         || cvssV4 >= cvssFailScore
324                         || unscoredCvss >= cvssFailScore
325                         //safety net to fail on any if for some reason the above misses on 0
326                         || (cvssFailScore <= 0.0f)) {
327                     double score = 0.0;
328                     if (cvssV4 >= 0.0) {
329                         score = cvssV4;
330                     } else if (cvssV3 >= 0.0) {
331                         score = cvssV3;
332                     } else if (cvssV2 >= 0.0) {
333                         score = cvssV2;
334                     } else if (unscoredCvss >= 0.0) {
335                         score = unscoredCvss;
336                     }
337                     if (addName) {
338                         addName = false;
339                         ids.append(NEW_LINE).append(d.getFileName()).append(" (")
340                                 .append(Stream.concat(d.getSoftwareIdentifiers().stream(), d.getVulnerableSoftwareIdentifiers().stream())
341                                         .map(Identifier::getValue)
342                                         .collect(Collectors.joining(", ")))
343                                 .append("): ");
344                         ids.append(v.getName()).append('(').append(score).append(')');
345                     } else {
346                         ids.append(", ").append(v.getName()).append('(').append(score).append(')');
347                     }
348                 }
349             }
350         }
351         if (ids.length() > 0) {
352             LOGGER.error(
353                     String.format("%n%nOne or more dependencies were identified with vulnerabilities that have a CVSS score greater than or "
354                             + "equal to '%.1f': %n%s%n%nSee the dependency-check report for more details.%n%n", cvssFailScore, ids)
355             );
356 
357             retCode = 15;
358         }
359 
360         return retCode;
361     }
362 
363     /**
364      * Scans the give Ant Style paths and collects the actual files.
365      *
366      * @param antStylePaths a list of ant style paths to scan for actual files
367      * @param symLinkDepth the depth to traverse symbolic links
368      * @param excludes an array of ant style excludes
369      * @return returns the set of identified files
370      */
371     private Set<File> scanAntStylePaths(List<String> antStylePaths, int symLinkDepth, String[] excludes) {
372         final Set<File> paths = new TreeSet<>();
373         for (String file : antStylePaths) {
374             LOGGER.debug("Scanning {}", file);
375             final DirectoryScanner scanner = new DirectoryScanner();
376             String include = file.replace('\\', '/');
377             final File baseDir;
378             final int pos = getLastFileSeparator(include);
379             String tmpBase = include.substring(0, pos);
380             //fix for windows style paths scanning c:/temp.
381             if (tmpBase.endsWith(":")) {
382                 tmpBase += "/";
383             }
384             final String tmpInclude = include.substring(pos + 1);
385             if (tmpInclude.indexOf('*') >= 0 || tmpInclude.indexOf('?') >= 0
386                     || new File(include).isFile()) {
387                 baseDir = new File(tmpBase);
388                 include = tmpInclude;
389             } else {
390                 baseDir = new File(tmpBase, tmpInclude);
391                 include = "**/*";
392             }
393             LOGGER.debug("BaseDir: " + baseDir);
394             LOGGER.debug("Include: " + include);
395             scanner.setBasedir(baseDir);
396             final String[] includes = {include};
397             scanner.setIncludes(includes);
398             scanner.setMaxLevelsOfSymlinks(symLinkDepth);
399             if (symLinkDepth <= 0) {
400                 scanner.setFollowSymlinks(false);
401             }
402             if (excludes != null && excludes.length > 0) {
403                 for (String e : excludes) {
404                     LOGGER.debug("Exclude: " + e);
405                 }
406                 scanner.addExcludes(excludes);
407             }
408             scanner.scan();
409             if (scanner.getIncludedFilesCount() > 0) {
410                 for (String s : scanner.getIncludedFiles()) {
411                     final File f = new File(baseDir, s);
412                     LOGGER.debug("Found file {}", f);
413                     paths.add(f);
414                 }
415             }
416         }
417         return paths;
418     }
419 
420     /**
421      * Determines the ant style paths from the given array of files.
422      *
423      * @param files an array of file paths
424      * @return a list containing ant style paths
425      */
426     private List<String> getPaths(String[] files) {
427         final List<String> antStylePaths = new ArrayList<>();
428         for (String file : files) {
429             final String antPath = ensureCanonicalPath(file);
430             antStylePaths.add(antPath);
431         }
432         return antStylePaths;
433     }
434 
435     /**
436      * Only executes the update phase of dependency-check.
437      *
438      * @throws UpdateException thrown if there is an error updating
439      * @throws DatabaseException thrown if a fatal error occurred and a
440      * connection to the database could not be established
441      */
442     private void runUpdateOnly() throws UpdateException, DatabaseException {
443         try (Engine engine = new Engine(settings)) {
444             engine.doUpdates();
445         }
446     }
447 
448     //CSOFF: MethodLength
449 
450     /**
451      * Updates the global Settings.
452      *
453      * @param cli a reference to the CLI Parser that contains the command line
454      * arguments used to set the corresponding settings in the core engine.
455      * @throws InvalidSettingException thrown when a user defined properties
456      * file is unable to be loaded.
457      */
458     protected void populateSettings(CliParser cli) throws InvalidSettingException {
459         String name = System.getenv("ODC_NAME") != null ? System.getenv("ODC_NAME") : "dependency-check-cli";
460         if (name.isBlank()) {
461             name = "dependency-check-cli";
462         }
463         name = name.replace("/", "-").replace(" ", "_");
464         settings.setString(Settings.KEYS.APPLICATION_NAME, name);
465         final File propertiesFile = cli.getFileArgument(CliParser.ARGUMENT.PROP);
466         if (propertiesFile != null) {
467             try {
468                 settings.mergeProperties(propertiesFile);
469             } catch (FileNotFoundException ex) {
470                 throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
471             } catch (IOException ex) {
472                 throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
473             }
474         }
475         final String dataDirectory = cli.getStringArgument(CliParser.ARGUMENT.DATA_DIRECTORY);
476         if (dataDirectory != null) {
477             settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
478         } else if (System.getProperty("basedir") != null) {
479             final File dataDir = new File(System.getProperty("basedir"), "data");
480             settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
481         } else {
482             final File jarPath = new File(App.class
483                     .getProtectionDomain().getCodeSource().getLocation().getPath());
484             final File base = jarPath.getParentFile();
485             final String sub = settings.getString(Settings.KEYS.DATA_DIRECTORY);
486             final File dataDir = new File(base, sub);
487             settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
488         }
489         final Boolean autoUpdate = cli.hasOption(CliParser.ARGUMENT.DISABLE_AUTO_UPDATE) != null ? false : null;
490         settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
491         settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER,
492                 cli.getStringArgument(CliParser.ARGUMENT.PROXY_SERVER));
493         settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT,
494                 cli.getStringArgument(CliParser.ARGUMENT.PROXY_PORT));
495         settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME,
496                 cli.getStringArgument(CliParser.ARGUMENT.PROXY_USERNAME));
497         settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD,
498                 cli.getStringArgument(CliParser.ARGUMENT.PROXY_PASSWORD, Settings.KEYS.PROXY_PASSWORD));
499         settings.setStringIfNotEmpty(Settings.KEYS.PROXY_NON_PROXY_HOSTS,
500                 cli.getStringArgument(CliParser.ARGUMENT.NON_PROXY_HOSTS));
501         settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT,
502                 cli.getStringArgument(CliParser.ARGUMENT.CONNECTION_TIMEOUT));
503         settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_READ_TIMEOUT,
504                 cli.getStringArgument(CliParser.ARGUMENT.CONNECTION_READ_TIMEOUT));
505         settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE,
506                 cli.getStringArgument(CliParser.ARGUMENT.HINTS_FILE));
507         settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE,
508                 cli.getStringArguments(CliParser.ARGUMENT.SUPPRESSION_FILES));
509         settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE_USER,
510                 cli.getStringArgument(CliParser.ARGUMENT.SUPPRESSION_FILE_USER));
511         settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE_PASSWORD,
512                 cli.getStringArgument(CliParser.ARGUMENT.SUPPRESSION_FILE_PASSWORD));
513         settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE_BEARER_TOKEN,
514                 cli.getStringArgument(CliParser.ARGUMENT.SUPPRESSION_FILE_BEARER_TOKEN));
515         //File Type Analyzer Settings
516         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED,
517                 cli.hasOption(CliParser.ARGUMENT.EXPERIMENTAL));
518         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RETIRED_ENABLED,
519                 cli.hasOption(CliParser.ARGUMENT.RETIRED));
520         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_GOLANG_PATH,
521                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_GO));
522         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_YARN_PATH,
523                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_YARN));
524         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_PNPM_PATH,
525                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_PNPM));
526         settings.setBooleanIfNotNull(Settings.KEYS.PRETTY_PRINT,
527                 cli.hasOption(CliParser.ARGUMENT.PRETTY_PRINT));
528         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL,
529                 cli.getStringArgument(CliParser.ARGUMENT.RETIREJS_URL));
530         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_USER,
531                 cli.getStringArgument(CliParser.ARGUMENT.RETIREJS_URL_USER));
532         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_PASSWORD,
533                 cli.getStringArgument(CliParser.ARGUMENT.RETIREJS_URL_PASSWORD));
534         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_BEARER_TOKEN,
535                 cli.getStringArgument(CliParser.ARGUMENT.RETIREJS_URL_BEARER_TOKEN));
536         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE,
537                 cli.hasOption(CliParser.ARGUMENT.RETIRE_JS_FORCEUPDATE));
538         String retireJsFilters = cli.getStringArgument(CliParser.ARGUMENT.RETIRE_JS_FILTERS);
539         if (retireJsFilters == null) {
540             retireJsFilters = cli.getStringArgument(CliParser.ARGUMENT.RETIREJS_FILTERS_DEPRECATED);
541             if (retireJsFilters != null) {
542                 LOGGER.warn("'--{}' is deprecated and may be removed in the next major release, please migrate to '--{}'",
543                         CliParser.ARGUMENT.RETIREJS_FILTERS_DEPRECATED, CliParser.ARGUMENT.RETIRE_JS_FILTERS);
544             }
545         }
546         settings.setStringIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_FILTERS, retireJsFilters);
547         Boolean retireJsFilterNonVuln = cli.hasOption(CliParser.ARGUMENT.RETIRE_JS_FILTER_NON_VULNERABLE);
548         if (retireJsFilterNonVuln == null) {
549             retireJsFilterNonVuln = cli.hasOption(CliParser.ARGUMENT.RETIREJS_FILTER_NON_VULNERABLE_DEPRECATED);
550             if (retireJsFilterNonVuln != null) {
551                 LOGGER.warn("'--{}' is deprecated and may be removed in the next major release, please migrate to '--{}'",
552                         CliParser.ARGUMENT.RETIREJS_FILTER_NON_VULNERABLE_DEPRECATED, CliParser.ARGUMENT.RETIRE_JS_FILTER_NON_VULNERABLE);
553             }
554         }
555         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RETIREJS_FILTER_NON_VULNERABLE, retireJsFilterNonVuln);
556         settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED,
557                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_JAR, Settings.KEYS.ANALYZER_JAR_ENABLED));
558         settings.setBoolean(Settings.KEYS.UPDATE_VERSION_CHECK_ENABLED,
559                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_VERSION_CHECK, Settings.KEYS.UPDATE_VERSION_CHECK_ENABLED));
560         settings.setBoolean(Settings.KEYS.ANALYZER_MSBUILD_PROJECT_ENABLED,
561                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_MSBUILD, Settings.KEYS.ANALYZER_MSBUILD_PROJECT_ENABLED));
562         settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED,
563                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_ARCHIVE, Settings.KEYS.ANALYZER_ARCHIVE_ENABLED));
564         settings.setBoolean(Settings.KEYS.ANALYZER_KNOWN_EXPLOITED_ENABLED,
565                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_KEV, Settings.KEYS.ANALYZER_KNOWN_EXPLOITED_ENABLED));
566         settings.setStringIfNotNull(Settings.KEYS.KEV_URL,
567                 cli.getStringArgument(CliParser.ARGUMENT.KEV_URL));
568         settings.setStringIfNotNull(Settings.KEYS.KEV_USER,
569                 cli.getStringArgument(CliParser.ARGUMENT.KEV_USER));
570         settings.setStringIfNotNull(Settings.KEYS.KEV_PASSWORD,
571                 cli.getStringArgument(CliParser.ARGUMENT.KEV_PASSWORD));
572         settings.setStringIfNotNull(Settings.KEYS.KEV_BEARER_TOKEN,
573                 cli.getStringArgument(CliParser.ARGUMENT.KEV_BEARER_TOKEN));
574         settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED,
575                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_PY_DIST, Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED));
576         settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED,
577                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_PY_PKG, Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED));
578         settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED,
579                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_AUTOCONF, Settings.KEYS.ANALYZER_AUTOCONF_ENABLED));
580         settings.setBoolean(Settings.KEYS.ANALYZER_MAVEN_INSTALL_ENABLED,
581                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_MAVEN_INSTALL, Settings.KEYS.ANALYZER_MAVEN_INSTALL_ENABLED));
582         settings.setBoolean(Settings.KEYS.ANALYZER_PE_ENABLED,
583                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_PE, Settings.KEYS.ANALYZER_PE_ENABLED));
584         settings.setBoolean(Settings.KEYS.ANALYZER_PIP_ENABLED,
585                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_PIP, Settings.KEYS.ANALYZER_PIP_ENABLED));
586         settings.setBoolean(Settings.KEYS.ANALYZER_PIPFILE_ENABLED,
587                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_PIPFILE, Settings.KEYS.ANALYZER_PIPFILE_ENABLED));
588         settings.setBoolean(Settings.KEYS.ANALYZER_POETRY_ENABLED,
589                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_POETRY, Settings.KEYS.ANALYZER_POETRY_ENABLED));
590         settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED,
591                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_CMAKE, Settings.KEYS.ANALYZER_CMAKE_ENABLED));
592         settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED,
593                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_NUSPEC, Settings.KEYS.ANALYZER_NUSPEC_ENABLED));
594         settings.setBoolean(Settings.KEYS.ANALYZER_NUGETCONF_ENABLED,
595                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_NUGETCONF, Settings.KEYS.ANALYZER_NUGETCONF_ENABLED));
596         settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED,
597                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_ASSEMBLY, Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED));
598         settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED,
599                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_BUNDLE_AUDIT, Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED));
600         settings.setBoolean(Settings.KEYS.ANALYZER_FILE_NAME_ENABLED,
601                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_FILENAME, Settings.KEYS.ANALYZER_FILE_NAME_ENABLED));
602         settings.setBoolean(Settings.KEYS.ANALYZER_MIX_AUDIT_ENABLED,
603                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_MIX_AUDIT, Settings.KEYS.ANALYZER_MIX_AUDIT_ENABLED));
604         settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED,
605                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_OPENSSL, Settings.KEYS.ANALYZER_OPENSSL_ENABLED));
606         settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED,
607                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_COMPOSER, Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED));
608         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV,
609                 cli.hasOption(CliParser.ARGUMENT.COMPOSER_LOCK_SKIP_DEV));
610         settings.setBoolean(Settings.KEYS.ANALYZER_CPANFILE_ENABLED,
611                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_CPAN, Settings.KEYS.ANALYZER_CPANFILE_ENABLED));
612         settings.setBoolean(Settings.KEYS.ANALYZER_GOLANG_DEP_ENABLED,
613                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_GO_DEP, Settings.KEYS.ANALYZER_GOLANG_DEP_ENABLED));
614         settings.setBoolean(Settings.KEYS.ANALYZER_GOLANG_MOD_ENABLED,
615                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_GOLANG_MOD, Settings.KEYS.ANALYZER_GOLANG_MOD_ENABLED));
616         settings.setBoolean(Settings.KEYS.ANALYZER_DART_ENABLED,
617                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_DART, Settings.KEYS.ANALYZER_DART_ENABLED));
618         settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED,
619                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_NODE_JS, Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED));
620         settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED,
621                 !cli.isNodeAuditDisabled());
622         settings.setBoolean(Settings.KEYS.ANALYZER_YARN_AUDIT_ENABLED,
623                 !cli.isYarnAuditDisabled());
624         settings.setBoolean(Settings.KEYS.ANALYZER_PNPM_AUDIT_ENABLED,
625                 !cli.isPnpmAuditDisabled());
626         settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE,
627                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_NODE_AUDIT_CACHE, Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE));
628         boolean retireJsDisabled = cli.isDisabled(CliParser.ARGUMENT.DISABLE_RETIRE_JS, Settings.KEYS.ANALYZER_RETIREJS_ENABLED);
629         if (!retireJsDisabled) {
630             retireJsDisabled = cli.isDisabled(CliParser.ARGUMENT.DISABLE_RETIREJS_DEPRECATED, Settings.KEYS.ANALYZER_RETIREJS_ENABLED);
631             if (retireJsDisabled) {
632                 LOGGER.warn("'--{}' is deprecated and may be removed in the next major release, please migrate to '--{}'",
633                         CliParser.ARGUMENT.DISABLE_RETIREJS_DEPRECATED, CliParser.ARGUMENT.DISABLE_RETIRE_JS);
634             }
635         }
636         settings.setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, !retireJsDisabled);
637         settings.setBoolean(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED,
638                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_SWIFT, Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED));
639         settings.setBoolean(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_RESOLVED_ENABLED,
640                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_SWIFT_RESOLVED, Settings.KEYS.ANALYZER_SWIFT_PACKAGE_RESOLVED_ENABLED));
641         settings.setBoolean(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED,
642                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_COCOAPODS, Settings.KEYS.ANALYZER_COCOAPODS_ENABLED));
643         settings.setBoolean(Settings.KEYS.ANALYZER_CARTHAGE_ENABLED,
644                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_CARTHAGE, Settings.KEYS.ANALYZER_CARTHAGE_ENABLED));
645         settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED,
646                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_RUBYGEMS, Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED));
647         settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED,
648                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_CENTRAL, Settings.KEYS.ANALYZER_CENTRAL_ENABLED));
649         settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_USE_CACHE,
650                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_CENTRAL_CACHE, Settings.KEYS.ANALYZER_CENTRAL_USE_CACHE));
651         settings.setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_ENABLED,
652                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_OSSINDEX, Settings.KEYS.ANALYZER_OSSINDEX_ENABLED));
653         settings.setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_USE_CACHE,
654                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_OSSINDEX_CACHE, Settings.KEYS.ANALYZER_OSSINDEX_USE_CACHE));
655 
656         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_SKIPDEV,
657                 cli.hasOption(CliParser.ARGUMENT.NODE_PACKAGE_SKIP_DEV_DEPENDENCIES));
658         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_AUDIT_SKIPDEV,
659                 cli.hasOption(CliParser.ARGUMENT.DISABLE_NODE_AUDIT_SKIPDEV));
660         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED,
661                 cli.hasOption(CliParser.ARGUMENT.ENABLE_NEXUS));
662         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_URL,
663                 cli.getStringArgument(CliParser.ARGUMENT.CENTRAL_URL));
664         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_USER,
665                 cli.getStringArgument(CliParser.ARGUMENT.CENTRAL_USERNAME));
666         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_PASSWORD,
667                 cli.getStringArgument(CliParser.ARGUMENT.CENTRAL_PASSWORD));
668         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_BEARER_TOKEN,
669                 cli.getStringArgument(CliParser.ARGUMENT.CENTRAL_BEARER_TOKEN));
670         settings.setIntIfNotNull(Settings.KEYS.ANALYZER_OSSINDEX_CACHE_VALID_FOR_HOURS,
671                 cli.getIntegerValue(CliParser.ARGUMENT.OSSINDEX_CACHE_VALID_FOR_HOURS));
672         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_OSSINDEX_URL,
673                 cli.getStringArgument(CliParser.ARGUMENT.OSSINDEX_URL));
674         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_OSSINDEX_USER,
675                 cli.getStringArgument(CliParser.ARGUMENT.OSSINDEX_USERNAME));
676         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_OSSINDEX_PASSWORD,
677                 cli.getStringArgument(CliParser.ARGUMENT.OSSINDEX_PASSWORD, Settings.KEYS.ANALYZER_OSSINDEX_PASSWORD));
678         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS,
679                 cli.getStringArgument(CliParser.ARGUMENT.OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS,
680                         Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS));
681         settings.setFloat(Settings.KEYS.JUNIT_FAIL_ON_CVSS,
682                 cli.getFloatArgument(CliParser.ARGUMENT.FAIL_JUNIT_ON_CVSS, 0));
683         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARTIFACTORY_ENABLED,
684                 cli.hasOption(CliParser.ARGUMENT.ARTIFACTORY_ENABLED));
685         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARTIFACTORY_PARALLEL_ANALYSIS,
686                 cli.getBooleanArgument(CliParser.ARGUMENT.ARTIFACTORY_PARALLEL_ANALYSIS));
687         settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARTIFACTORY_USES_PROXY,
688                 cli.getBooleanArgument(CliParser.ARGUMENT.ARTIFACTORY_USES_PROXY));
689         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ARTIFACTORY_URL,
690                 cli.getStringArgument(CliParser.ARGUMENT.ARTIFACTORY_URL));
691         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ARTIFACTORY_API_USERNAME,
692                 cli.getStringArgument(CliParser.ARGUMENT.ARTIFACTORY_USERNAME));
693         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ARTIFACTORY_API_TOKEN,
694                 cli.getStringArgument(CliParser.ARGUMENT.ARTIFACTORY_API_TOKEN));
695         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ARTIFACTORY_BEARER_TOKEN,
696                 cli.getStringArgument(CliParser.ARGUMENT.ARTIFACTORY_BEARER_TOKEN));
697         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_MIX_AUDIT_PATH,
698                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_MIX_AUDIT));
699         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH,
700                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_BUNDLE_AUDIT));
701         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_WORKING_DIRECTORY,
702                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_BUNDLE_AUDIT_WORKING_DIRECTORY));
703         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL,
704                 cli.getStringArgument(CliParser.ARGUMENT.NEXUS_URL));
705         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_USER,
706                 cli.getStringArgument(CliParser.ARGUMENT.NEXUS_USERNAME));
707         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_PASSWORD,
708                 cli.getStringArgument(CliParser.ARGUMENT.NEXUS_PASSWORD, Settings.KEYS.ANALYZER_NEXUS_PASSWORD));
709         //TODO deprecate this in favor of non-proxy host
710         final boolean nexusUsesProxy = cli.isNexusUsesProxy();
711         settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
712         settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME,
713                 cli.getStringArgument(CliParser.ARGUMENT.DB_DRIVER));
714         settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH,
715                 cli.getStringArgument(CliParser.ARGUMENT.DB_DRIVER_PATH));
716         settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING,
717                 cli.getStringArgument(CliParser.ARGUMENT.CONNECTION_STRING));
718         settings.setStringIfNotEmpty(Settings.KEYS.DB_USER,
719                 cli.getStringArgument(CliParser.ARGUMENT.DB_NAME));
720         settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD,
721                 cli.getStringArgument(CliParser.ARGUMENT.DB_PASSWORD, Settings.KEYS.DB_PASSWORD));
722         settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS,
723                 cli.getStringArgument(CliParser.ARGUMENT.ADDITIONAL_ZIP_EXTENSIONS));
724         settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_DOTNET_PATH,
725                 cli.getStringArgument(CliParser.ARGUMENT.PATH_TO_CORE));
726 
727         String key = cli.getStringArgument(CliParser.ARGUMENT.NVD_API_KEY);
728         if (key != null) {
729             if ((key.startsWith("\"") && key.endsWith("\"") || (key.startsWith("'") && key.endsWith("'")))) {
730                 key = key.substring(1, key.length() - 1);
731             }
732             settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_KEY, key);
733         }
734         settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_ENDPOINT,
735                 cli.getStringArgument(CliParser.ARGUMENT.NVD_API_ENDPOINT));
736         settings.setIntIfNotNull(Settings.KEYS.NVD_API_DELAY, cli.getIntegerValue(CliParser.ARGUMENT.NVD_API_DELAY));
737         settings.setIntIfNotNull(Settings.KEYS.NVD_API_RESULTS_PER_PAGE, cli.getIntegerValue(CliParser.ARGUMENT.NVD_API_RESULTS_PER_PAGE));
738         settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_DATAFEED_URL, cli.getStringArgument(CliParser.ARGUMENT.NVD_API_DATAFEED_URL));
739         settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_DATAFEED_USER, cli.getStringArgument(CliParser.ARGUMENT.NVD_API_DATAFEED_USER));
740         settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_DATAFEED_PASSWORD, cli.getStringArgument(CliParser.ARGUMENT.NVD_API_DATAFEED_PASSWORD));
741         settings.setStringIfNotEmpty(Settings.KEYS.NVD_API_DATAFEED_BEARER_TOKEN, cli.getStringArgument(CliParser.ARGUMENT.NVD_API_DATAFEED_BEARER_TOKEN));
742         settings.setIntIfNotNull(Settings.KEYS.NVD_API_MAX_RETRY_COUNT, cli.getIntegerValue(CliParser.ARGUMENT.NVD_API_MAX_RETRY_COUNT));
743         settings.setIntIfNotNull(Settings.KEYS.NVD_API_VALID_FOR_HOURS, cli.getIntegerValue(CliParser.ARGUMENT.NVD_API_VALID_FOR_HOURS));
744 
745         settings.setStringIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_URL,
746                 cli.getStringArgument(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_URL));
747         settings.setStringIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_USER,
748                 cli.getStringArgument(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_USER));
749         settings.setStringIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_PASSWORD,
750                 cli.getStringArgument(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_PASSWORD));
751         settings.setStringIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_BEARER_TOKEN,
752                 cli.getStringArgument(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_BEARER_TOKEN));
753         settings.setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED,
754                 !cli.isDisabled(CliParser.ARGUMENT.DISABLE_HOSTED_SUPPRESSIONS, Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED));
755         settings.setBooleanIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE,
756                 cli.hasOption(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_FORCEUPDATE));
757         settings.setIntIfNotNull(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS,
758                 cli.getIntegerValue(CliParser.ARGUMENT.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS));
759     }
760 
761     //CSON: MethodLength
762 
763     /**
764      * Creates a file appender and adds it to logback.
765      *
766      * @param verboseLog the path to the verbose log file
767      */
768     private void prepareLogger(String verboseLog) {
769         final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
770         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
771         encoder.setPattern("%d %C:%L%n%-5level - %msg%n");
772         encoder.setContext(context);
773         encoder.start();
774         final FileAppender<ILoggingEvent> fa = new FileAppender<>();
775         fa.setAppend(true);
776         fa.setEncoder(encoder);
777         fa.setContext(context);
778         fa.setFile(verboseLog);
779         final File f = new File(verboseLog);
780         String name = f.getName();
781         final int i = name.lastIndexOf('.');
782         if (i > 1) {
783             name = name.substring(0, i);
784         }
785         fa.setName(name);
786         fa.start();
787         final ch.qos.logback.classic.Logger rootLogger = context.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
788         rootLogger.setLevel(Level.DEBUG);
789         final ThresholdFilter filter = new ThresholdFilter();
790         filter.setLevel(LogLevel.INFO.getValue());
791         filter.setContext(context);
792         filter.start();
793         rootLogger.iteratorForAppenders().forEachRemaining(action -> action.addFilter(filter));
794         rootLogger.addAppender(fa);
795     }
796 
797     /**
798      * Takes a path and resolves it to be a canonical &amp; absolute path. The
799      * caveats are that this method will take an Ant style file selector path
800      * (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at
801      * least to the left of the first * or ?).
802      *
803      * @param path the path to canonicalize
804      * @return the canonical path
805      */
806     protected String ensureCanonicalPath(String path) {
807         final String basePath;
808         String wildCards = null;
809         final String file = path.replace('\\', '/');
810         if (file.contains("*") || file.contains("?")) {
811 
812             int pos = getLastFileSeparator(file);
813             if (pos < 0) {
814                 return file;
815             }
816             pos += 1;
817             basePath = file.substring(0, pos);
818             wildCards = file.substring(pos);
819         } else {
820             basePath = file;
821         }
822 
823         File f = new File(basePath);
824         try {
825             f = f.getCanonicalFile();
826             if (wildCards != null) {
827                 f = new File(f, wildCards);
828             }
829         } catch (IOException ex) {
830             LOGGER.warn("Invalid path '{}' was provided.", path);
831             LOGGER.debug("Invalid path provided", ex);
832         }
833         return f.getAbsolutePath().replace('\\', '/');
834     }
835 
836     /**
837      * Returns the position of the last file separator.
838      *
839      * @param file a file path
840      * @return the position of the last file separator
841      */
842     @SuppressWarnings("ManualMinMaxCalculation")
843     private int getLastFileSeparator(String file) {
844         if (file.contains("*") || file.contains("?")) {
845             int p1 = file.indexOf('*');
846             int p2 = file.indexOf('?');
847             p1 = p1 > 0 ? p1 : file.length();
848             p2 = p2 > 0 ? p2 : file.length();
849             int pos = p1 < p2 ? p1 : p2;
850             pos = file.lastIndexOf('/', pos);
851             return pos;
852         } else {
853             return file.lastIndexOf('/');
854         }
855     }
856 }