Coverage Report - org.owasp.dependencycheck.App
 
Classes in this File Line Coverage Branch Coverage Complexity
App
8%
26/313
7%
10/130
10.625
 
 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.LoggerContext;
 21  
 import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
 22  
 import ch.qos.logback.classic.spi.ILoggingEvent;
 23  
 import java.io.File;
 24  
 import java.io.FileNotFoundException;
 25  
 import java.io.IOException;
 26  
 import java.util.ArrayList;
 27  
 import java.util.HashSet;
 28  
 import java.util.List;
 29  
 import java.util.Set;
 30  
 import org.apache.commons.cli.ParseException;
 31  
 import org.owasp.dependencycheck.data.nvdcve.CveDB;
 32  
 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
 33  
 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
 34  
 import org.owasp.dependencycheck.dependency.Dependency;
 35  
 import org.apache.tools.ant.DirectoryScanner;
 36  
 import org.owasp.dependencycheck.dependency.Vulnerability;
 37  
 import org.owasp.dependencycheck.reporting.ReportGenerator;
 38  
 import org.owasp.dependencycheck.utils.Settings;
 39  
 import org.slf4j.Logger;
 40  
 import org.slf4j.LoggerFactory;
 41  
 import ch.qos.logback.core.FileAppender;
 42  
 import org.owasp.dependencycheck.data.update.exception.UpdateException;
 43  
 import org.owasp.dependencycheck.exception.ExceptionCollection;
 44  
 import org.owasp.dependencycheck.exception.ReportException;
 45  
 import org.owasp.dependencycheck.utils.InvalidSettingException;
 46  
 import org.slf4j.impl.StaticLoggerBinder;
 47  
 
 48  
 /**
 49  
  * The command line interface for the DependencyCheck application.
 50  
  *
 51  
  * @author Jeremy Long
 52  
  */
 53  2
 public class App {
 54  
 
 55  
     /**
 56  
      * The logger.
 57  
      */
 58  1
     private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
 59  
 
 60  
     /**
 61  
      * The main method for the application.
 62  
      *
 63  
      * @param args the command line arguments
 64  
      */
 65  
     public static void main(String[] args) {
 66  0
         int exitCode = 0;
 67  
         try {
 68  0
             Settings.initialize();
 69  0
             final App app = new App();
 70  0
             exitCode = app.run(args);
 71  0
             LOGGER.debug("Exit code: " + exitCode);
 72  
         } finally {
 73  0
             Settings.cleanup(true);
 74  0
         }
 75  0
         System.exit(exitCode);
 76  0
     }
 77  
 
 78  
     /**
 79  
      * Main CLI entry-point into the application.
 80  
      *
 81  
      * @param args the command line arguments
 82  
      * @return the exit code to return
 83  
      */
 84  
     public int run(String[] args) {
 85  0
         int exitCode = 0;
 86  0
         final CliParser cli = new CliParser();
 87  
 
 88  
         try {
 89  0
             cli.parse(args);
 90  0
         } catch (FileNotFoundException ex) {
 91  0
             System.err.println(ex.getMessage());
 92  0
             cli.printHelp();
 93  0
             return -1;
 94  0
         } catch (ParseException ex) {
 95  0
             System.err.println(ex.getMessage());
 96  0
             cli.printHelp();
 97  0
             return -2;
 98  0
         }
 99  
 
 100  0
         if (cli.getVerboseLog() != null) {
 101  0
             prepareLogger(cli.getVerboseLog());
 102  
         }
 103  
 
 104  0
         if (cli.isPurge()) {
 105  0
             if (cli.getConnectionString() != null) {
 106  0
                 LOGGER.error("Unable to purge the database when using a non-default connection string");
 107  0
                 exitCode = -3;
 108  
             } else {
 109  
                 try {
 110  0
                     populateSettings(cli);
 111  0
                 } catch (InvalidSettingException ex) {
 112  0
                     LOGGER.error(ex.getMessage());
 113  0
                     LOGGER.debug("Error loading properties file", ex);
 114  0
                     exitCode = -4;
 115  0
                 }
 116  
                 File db;
 117  
                 try {
 118  0
                     db = new File(Settings.getDataDirectory(), "dc.h2.db");
 119  0
                     if (db.exists()) {
 120  0
                         if (db.delete()) {
 121  0
                             LOGGER.info("Database file purged; local copy of the NVD has been removed");
 122  
                         } else {
 123  0
                             LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath());
 124  0
                             exitCode = -5;
 125  
                         }
 126  
                     } else {
 127  0
                         LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath());
 128  0
                         exitCode = -6;
 129  
                     }
 130  0
                 } catch (IOException ex) {
 131  0
                     LOGGER.error("Unable to delete the database");
 132  0
                     exitCode = -7;
 133  0
                 }
 134  
             }
 135  0
         } else if (cli.isGetVersion()) {
 136  0
             cli.printVersionInfo();
 137  0
         } else if (cli.isUpdateOnly()) {
 138  
             try {
 139  0
                 populateSettings(cli);
 140  0
             } catch (InvalidSettingException ex) {
 141  0
                 LOGGER.error(ex.getMessage());
 142  0
                 LOGGER.debug("Error loading properties file", ex);
 143  0
                 exitCode = -4;
 144  0
             }
 145  
             try {
 146  0
                 runUpdateOnly();
 147  0
             } catch (UpdateException ex) {
 148  0
                 LOGGER.error(ex.getMessage());
 149  0
                 exitCode = -8;
 150  0
             } catch (DatabaseException ex) {
 151  0
                 LOGGER.error(ex.getMessage());
 152  0
                 exitCode = -9;
 153  0
             }
 154  0
         } else if (cli.isRunScan()) {
 155  
             try {
 156  0
                 populateSettings(cli);
 157  0
             } catch (InvalidSettingException ex) {
 158  0
                 LOGGER.error(ex.getMessage());
 159  0
                 LOGGER.debug("Error loading properties file", ex);
 160  0
                 exitCode = -4;
 161  0
             }
 162  
             try {
 163  0
                 final String[] scanFiles = cli.getScanFiles();
 164  0
                 if (scanFiles != null) {
 165  0
                     exitCode = runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), scanFiles,
 166  0
                             cli.getExcludeList(), cli.getSymLinkDepth(), cli.getFailOnCVSS());
 167  
                 } else {
 168  0
                     LOGGER.error("No scan files configured");
 169  
                 }
 170  0
             } catch (InvalidScanPathException ex) {
 171  0
                 LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths");
 172  0
                 exitCode = -10;
 173  0
             } catch (DatabaseException ex) {
 174  0
                 LOGGER.error(ex.getMessage());
 175  0
                 exitCode = -11;
 176  0
             } catch (ReportException ex) {
 177  0
                 LOGGER.error(ex.getMessage());
 178  0
                 exitCode = -12;
 179  0
             } catch (ExceptionCollection ex) {
 180  0
                 if (ex.isFatal()) {
 181  0
                     exitCode = -13;
 182  0
                     LOGGER.error("One or more fatal errors occurred");
 183  
                 } else {
 184  0
                     exitCode = -14;
 185  
                 }
 186  0
                 for (Throwable e : ex.getExceptions()) {
 187  0
                     LOGGER.error(e.getMessage());
 188  0
                 }
 189  0
             }
 190  
         } else {
 191  0
             cli.printHelp();
 192  
         }
 193  0
         return exitCode;
 194  
     }
 195  
 
 196  
     /**
 197  
      * Scans the specified directories and writes the dependency reports to the
 198  
      * reportDirectory.
 199  
      *
 200  
      * @param reportDirectory the path to the directory where the reports will
 201  
      * be written
 202  
      * @param outputFormat the output format of the report
 203  
      * @param applicationName the application name for the report
 204  
      * @param files the files/directories to scan
 205  
      * @param excludes the patterns for files/directories to exclude
 206  
      * @param symLinkDepth the depth that symbolic links will be followed
 207  
      * @param cvssFailScore the score to fail on if a vulnerability is found
 208  
      * @return the exit code if there was an error
 209  
      *
 210  
      * @throws InvalidScanPathException thrown if the path to scan starts with
 211  
      * "//"
 212  
      * @throws ReportException thrown when the report cannot be generated
 213  
      * @throws DatabaseException thrown when there is an error connecting to the
 214  
      * database
 215  
      * @throws ExceptionCollection thrown when an exception occurs during
 216  
      * analysis; there may be multiple exceptions contained within the
 217  
      * collection.
 218  
      */
 219  
     private int runScan(String reportDirectory, String outputFormat, String applicationName, String[] files,
 220  
             String[] excludes, int symLinkDepth, int cvssFailScore) throws InvalidScanPathException, DatabaseException,
 221  
             ExceptionCollection, ReportException {
 222  0
         Engine engine = null;
 223  0
         int retCode = 0;
 224  
         try {
 225  0
             engine = new Engine();
 226  0
             final List<String> antStylePaths = new ArrayList<String>();
 227  0
             for (String file : files) {
 228  0
                 final String antPath = ensureCanonicalPath(file);
 229  0
                 antStylePaths.add(antPath);
 230  
             }
 231  
 
 232  0
             final Set<File> paths = new HashSet<File>();
 233  0
             for (String file : antStylePaths) {
 234  0
                 LOGGER.debug("Scanning {}", file);
 235  0
                 final DirectoryScanner scanner = new DirectoryScanner();
 236  0
                 String include = file.replace('\\', '/');
 237  
                 File baseDir;
 238  
 
 239  0
                 if (include.startsWith("//")) {
 240  0
                     throw new InvalidScanPathException("Unable to scan paths specified by //");
 241  
                 } else {
 242  0
                     final int pos = getLastFileSeparator(include);
 243  0
                     final String tmpBase = include.substring(0, pos);
 244  0
                     final String tmpInclude = include.substring(pos + 1);
 245  0
                     if (tmpInclude.indexOf('*') >= 0 || tmpInclude.indexOf('?') >= 0
 246  0
                             || (new File(include)).isFile()) {
 247  0
                         baseDir = new File(tmpBase);
 248  0
                         include = tmpInclude;
 249  
                     } else {
 250  0
                         baseDir = new File(tmpBase, tmpInclude);
 251  0
                         include = "**/*";
 252  
                     }
 253  
                 }
 254  0
                 scanner.setBasedir(baseDir);
 255  0
                 final String[] includes = {include};
 256  0
                 scanner.setIncludes(includes);
 257  0
                 scanner.setMaxLevelsOfSymlinks(symLinkDepth);
 258  0
                 if (symLinkDepth <= 0) {
 259  0
                     scanner.setFollowSymlinks(false);
 260  
                 }
 261  0
                 if (excludes != null && excludes.length > 0) {
 262  0
                     scanner.addExcludes(excludes);
 263  
                 }
 264  0
                 scanner.scan();
 265  0
                 if (scanner.getIncludedFilesCount() > 0) {
 266  0
                     for (String s : scanner.getIncludedFiles()) {
 267  0
                         final File f = new File(baseDir, s);
 268  0
                         LOGGER.debug("Found file {}", f.toString());
 269  0
                         paths.add(f);
 270  
                     }
 271  
                 }
 272  0
             }
 273  0
             engine.scan(paths);
 274  
 
 275  0
             ExceptionCollection exCol = null;
 276  
             try {
 277  0
                 engine.analyzeDependencies();
 278  0
             } catch (ExceptionCollection ex) {
 279  0
                 if (ex.isFatal()) {
 280  0
                     throw ex;
 281  
                 }
 282  0
                 exCol = ex;
 283  0
             }
 284  0
             final List<Dependency> dependencies = engine.getDependencies();
 285  0
             DatabaseProperties prop = null;
 286  0
             CveDB cve = null;
 287  
             try {
 288  0
                 cve = new CveDB();
 289  0
                 cve.open();
 290  0
                 prop = cve.getDatabaseProperties();
 291  
             } finally {
 292  0
                 if (cve != null) {
 293  0
                     cve.close();
 294  
                 }
 295  
             }
 296  0
             final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
 297  
             try {
 298  0
                 report.generateReports(reportDirectory, outputFormat);
 299  0
             } catch (ReportException ex) {
 300  0
                 if (exCol != null) {
 301  0
                     exCol.addException(ex);
 302  0
                     throw exCol;
 303  
                 } else {
 304  0
                     throw ex;
 305  
                 }
 306  0
             }
 307  0
             if (exCol != null && exCol.getExceptions().size() > 0) {
 308  0
                 throw exCol;
 309  
             }
 310  
 
 311  
             //Set the exit code based on whether we found a high enough vulnerability
 312  0
             for (Dependency dep : dependencies) {
 313  0
                 if (!dep.getVulnerabilities().isEmpty()) {
 314  0
                     for (Vulnerability vuln : dep.getVulnerabilities()) {
 315  0
                         LOGGER.debug("VULNERABILITY FOUND " + dep.getDisplayFileName());
 316  0
                         if (vuln.getCvssScore() > cvssFailScore) {
 317  0
                             retCode = 1;
 318  
                         }
 319  0
                     }
 320  
                 }
 321  0
             }
 322  
 
 323  0
             return retCode;
 324  
         } finally {
 325  0
             if (engine != null) {
 326  0
                 engine.cleanup();
 327  
             }
 328  
         }
 329  
     }
 330  
 
 331  
     /**
 332  
      * Only executes the update phase of dependency-check.
 333  
      *
 334  
      * @throws UpdateException thrown if there is an error updating
 335  
      * @throws DatabaseException thrown if a fatal error occurred and a
 336  
      * connection to the database could not be established
 337  
      */
 338  
     private void runUpdateOnly() throws UpdateException, DatabaseException {
 339  0
         Engine engine = null;
 340  
         try {
 341  0
             engine = new Engine();
 342  0
             engine.doUpdates();
 343  
         } finally {
 344  0
             if (engine != null) {
 345  0
                 engine.cleanup();
 346  
             }
 347  
         }
 348  0
     }
 349  
 
 350  
     /**
 351  
      * Updates the global Settings.
 352  
      *
 353  
      * @param cli a reference to the CLI Parser that contains the command line
 354  
      * arguments used to set the corresponding settings in the core engine.
 355  
      *
 356  
      * @throws InvalidSettingException thrown when a user defined properties
 357  
      * file is unable to be loaded.
 358  
      */
 359  
     private void populateSettings(CliParser cli) throws InvalidSettingException {
 360  0
         final boolean autoUpdate = cli.isAutoUpdate();
 361  0
         final String connectionTimeout = cli.getConnectionTimeout();
 362  0
         final String proxyServer = cli.getProxyServer();
 363  0
         final String proxyPort = cli.getProxyPort();
 364  0
         final String proxyUser = cli.getProxyUsername();
 365  0
         final String proxyPass = cli.getProxyPassword();
 366  0
         final String dataDirectory = cli.getDataDirectory();
 367  0
         final File propertiesFile = cli.getPropertiesFile();
 368  0
         final String suppressionFile = cli.getSuppressionFile();
 369  0
         final String hintsFile = cli.getHintsFile();
 370  0
         final String nexusUrl = cli.getNexusUrl();
 371  0
         final String databaseDriverName = cli.getDatabaseDriverName();
 372  0
         final String databaseDriverPath = cli.getDatabaseDriverPath();
 373  0
         final String connectionString = cli.getConnectionString();
 374  0
         final String databaseUser = cli.getDatabaseUser();
 375  0
         final String databasePassword = cli.getDatabasePassword();
 376  0
         final String additionalZipExtensions = cli.getAdditionalZipExtensions();
 377  0
         final String pathToMono = cli.getPathToMono();
 378  0
         final String cveMod12 = cli.getModifiedCve12Url();
 379  0
         final String cveMod20 = cli.getModifiedCve20Url();
 380  0
         final String cveBase12 = cli.getBaseCve12Url();
 381  0
         final String cveBase20 = cli.getBaseCve20Url();
 382  0
         final Integer cveValidForHours = cli.getCveValidForHours();
 383  0
         final boolean experimentalEnabled = cli.isExperimentalEnabled();
 384  
 
 385  0
         if (propertiesFile != null) {
 386  
             try {
 387  0
                 Settings.mergeProperties(propertiesFile);
 388  0
             } catch (FileNotFoundException ex) {
 389  0
                 throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
 390  0
             } catch (IOException ex) {
 391  0
                 throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
 392  0
             }
 393  
         }
 394  
         // We have to wait until we've merged the properties before attempting to set whether we use
 395  
         // the proxy for Nexus since it could be disabled in the properties, but not explicitly stated
 396  
         // on the command line
 397  0
         final boolean nexusUsesProxy = cli.isNexusUsesProxy();
 398  0
         if (dataDirectory != null) {
 399  0
             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
 400  0
         } else if (System.getProperty("basedir") != null) {
 401  0
             final File dataDir = new File(System.getProperty("basedir"), "data");
 402  0
             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
 403  0
         } else {
 404  0
             final File jarPath = new File(App.class.getProtectionDomain().getCodeSource().getLocation().getPath());
 405  0
             final File base = jarPath.getParentFile();
 406  0
             final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
 407  0
             final File dataDir = new File(base, sub);
 408  0
             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
 409  
         }
 410  0
         Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
 411  0
         Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
 412  0
         Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
 413  0
         Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
 414  0
         Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPass);
 415  0
         Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
 416  0
         Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
 417  0
         Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
 418  0
         Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
 419  
 
 420  
         //File Type Analyzer Settings
 421  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled);
 422  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled());
 423  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled());
 424  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());
 425  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !cli.isPythonPackageDisabled());
 426  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !cli.isAutoconfDisabled());
 427  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cli.isCmakeDisabled());
 428  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !cli.isNuspecDisabled());
 429  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !cli.isAssemblyDisabled());
 430  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, !cli.isBundleAuditDisabled());
 431  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled());
 432  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, !cli.isComposerDisabled());
 433  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, !cli.isNodeJsDisabled());
 434  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, !cli.isSwiftPackageAnalyzerDisabled());
 435  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, !cli.isCocoapodsAnalyzerDisabled());
 436  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, !cli.isRubyGemspecDisabled());
 437  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !cli.isCentralDisabled());
 438  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !cli.isNexusDisabled());
 439  
 
 440  0
         Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, cli.getPathToBundleAudit());
 441  0
         Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
 442  0
         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
 443  0
         Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
 444  0
         Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
 445  0
         Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
 446  0
         Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser);
 447  0
         Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword);
 448  0
         Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions);
 449  0
         Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
 450  0
         if (cveBase12 != null && !cveBase12.isEmpty()) {
 451  0
             Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12);
 452  0
             Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20);
 453  0
             Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveMod12);
 454  0
             Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveMod20);
 455  
         }
 456  0
     }
 457  
 
 458  
     /**
 459  
      * Creates a file appender and adds it to logback.
 460  
      *
 461  
      * @param verboseLog the path to the verbose log file
 462  
      */
 463  
     private void prepareLogger(String verboseLog) {
 464  0
         final StaticLoggerBinder loggerBinder = StaticLoggerBinder.getSingleton();
 465  0
         final LoggerContext context = (LoggerContext) loggerBinder.getLoggerFactory();
 466  
 
 467  0
         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
 468  0
         encoder.setPattern("%d %C:%L%n%-5level - %msg%n");
 469  0
         encoder.setContext(context);
 470  0
         encoder.start();
 471  0
         final FileAppender<ILoggingEvent> fa = new FileAppender<ILoggingEvent>();
 472  0
         fa.setAppend(true);
 473  0
         fa.setEncoder(encoder);
 474  0
         fa.setContext(context);
 475  0
         fa.setFile(verboseLog);
 476  0
         final File f = new File(verboseLog);
 477  0
         String name = f.getName();
 478  0
         final int i = name.lastIndexOf('.');
 479  0
         if (i > 1) {
 480  0
             name = name.substring(0, i);
 481  
         }
 482  0
         fa.setName(name);
 483  0
         fa.start();
 484  0
         final ch.qos.logback.classic.Logger rootLogger = context.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
 485  0
         rootLogger.addAppender(fa);
 486  0
     }
 487  
 
 488  
     /**
 489  
      * Takes a path and resolves it to be a canonical &amp; absolute path. The
 490  
      * caveats are that this method will take an Ant style file selector path
 491  
      * (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at
 492  
      * least to the left of the first * or ?).
 493  
      *
 494  
      * @param path the path to canonicalize
 495  
      * @return the canonical path
 496  
      */
 497  
     protected String ensureCanonicalPath(String path) {
 498  
         String basePath;
 499  2
         String wildCards = null;
 500  2
         final String file = path.replace('\\', '/');
 501  2
         if (file.contains("*") || file.contains("?")) {
 502  
 
 503  1
             int pos = getLastFileSeparator(file);
 504  1
             if (pos < 0) {
 505  0
                 return file;
 506  
             }
 507  1
             pos += 1;
 508  1
             basePath = file.substring(0, pos);
 509  1
             wildCards = file.substring(pos);
 510  1
         } else {
 511  1
             basePath = file;
 512  
         }
 513  
 
 514  2
         File f = new File(basePath);
 515  
         try {
 516  2
             f = f.getCanonicalFile();
 517  2
             if (wildCards != null) {
 518  1
                 f = new File(f, wildCards);
 519  
             }
 520  0
         } catch (IOException ex) {
 521  0
             LOGGER.warn("Invalid path '{}' was provided.", path);
 522  0
             LOGGER.debug("Invalid path provided", ex);
 523  2
         }
 524  2
         return f.getAbsolutePath().replace('\\', '/');
 525  
     }
 526  
 
 527  
     /**
 528  
      * Returns the position of the last file separator.
 529  
      *
 530  
      * @param file a file path
 531  
      * @return the position of the last file separator
 532  
      */
 533  
     private int getLastFileSeparator(String file) {
 534  1
         if (file.contains("*") || file.contains("?")) {
 535  1
             int p1 = file.indexOf('*');
 536  1
             int p2 = file.indexOf('?');
 537  1
             p1 = p1 > 0 ? p1 : file.length();
 538  1
             p2 = p2 > 0 ? p2 : file.length();
 539  1
             int pos = p1 < p2 ? p1 : p2;
 540  1
             pos = file.lastIndexOf('/', pos);
 541  1
             return pos;
 542  
         } else {
 543  0
             return file.lastIndexOf('/');
 544  
         }
 545  
     }
 546  
 }