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 java.io.File;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  import org.apache.commons.cli.ParseException;
32  import org.owasp.dependencycheck.data.nvdcve.CveDB;
33  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
34  import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
35  import org.owasp.dependencycheck.dependency.Dependency;
36  import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner;
37  import org.owasp.dependencycheck.reporting.ReportGenerator;
38  import org.owasp.dependencycheck.utils.LogUtils;
39  import org.owasp.dependencycheck.utils.Settings;
40  
41  /**
42   * The command line interface for the DependencyCheck application.
43   *
44   * @author Jeremy Long <jeremy.long@owasp.org>
45   */
46  public class App {
47  
48      /**
49       * The location of the log properties configuration file.
50       */
51      private static final String LOG_PROPERTIES_FILE = "log.properties";
52  
53      /**
54       * The logger.
55       */
56      private static final Logger LOGGER = Logger.getLogger(App.class.getName());
57  
58      /**
59       * The main method for the application.
60       *
61       * @param args the command line arguments
62       */
63      public static void main(String[] args) {
64          try {
65              Settings.initialize();
66              final App app = new App();
67              app.run(args);
68          } finally {
69              Settings.cleanup(true);
70          }
71      }
72  
73      /**
74       * Main CLI entry-point into the application.
75       *
76       * @param args the command line arguments
77       */
78      public void run(String[] args) {
79          final CliParser cli = new CliParser();
80  
81          try {
82              cli.parse(args);
83          } catch (FileNotFoundException ex) {
84              System.err.println(ex.getMessage());
85              cli.printHelp();
86              return;
87          } catch (ParseException ex) {
88              System.err.println(ex.getMessage());
89              cli.printHelp();
90              return;
91          }
92  
93          final InputStream in = App.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
94          LogUtils.prepareLogger(in, cli.getVerboseLog());
95  
96          if (cli.isGetVersion()) {
97              cli.printVersionInfo();
98          } else if (cli.isRunScan()) {
99              populateSettings(cli);
100             try {
101                 runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles(), cli.getExcludeList());
102             } catch (InvalidScanPathException ex) {
103                 Logger.getLogger(App.class.getName()).log(Level.SEVERE, "An invalid scan path was detected; unable to scan '//*' paths");
104             }
105         } else {
106             cli.printHelp();
107         }
108     }
109 
110     /**
111      * Scans the specified directories and writes the dependency reports to the reportDirectory.
112      *
113      * @param reportDirectory the path to the directory where the reports will be written
114      * @param outputFormat the output format of the report
115      * @param applicationName the application name for the report
116      * @param files the files/directories to scan
117      * @param excludes the patterns for files/directories to exclude
118      *
119      * @throws InvalidScanPathException thrown if the path to scan starts with "//"
120      */
121     private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files,
122             String[] excludes) throws InvalidScanPathException {
123         Engine engine = null;
124         try {
125             engine = new Engine();
126             List<String> antStylePaths = new ArrayList<String>();
127             if (excludes == null || excludes.length == 0) {
128                 for (String file : files) {
129                     if (file.contains("*") || file.contains("?")) {
130                         antStylePaths.add(file);
131                     } else {
132                         engine.scan(file);
133                     }
134                 }
135             } else {
136                 antStylePaths = Arrays.asList(files);
137             }
138 
139             final Set<File> paths = new HashSet<File>();
140             for (String file : antStylePaths) {
141                 final DirectoryScanner scanner = new DirectoryScanner();
142                 String include = file.replace('\\', '/');
143                 File baseDir;
144 
145                 if (include.startsWith("//")) {
146                     throw new InvalidScanPathException("Unable to scan paths specified by //");
147                 } else if (include.startsWith("./")) {
148                     baseDir = new File(".");
149                     include = include.substring(2);
150                 } else if (include.startsWith("/")) {
151                     baseDir = new File("/");
152                     include = include.substring(1);
153                 } else if (include.contains("/")) {
154                     final int pos = include.indexOf('/');
155                     final String tmp = include.substring(0, pos);
156                     if (tmp.contains("*") || tmp.contains("?")) {
157                         baseDir = new File(".");
158                     } else {
159                         baseDir = new File(tmp);
160                         include = include.substring(pos + 1);
161                     }
162                 } else { //no path info - must just be a file in the working directory
163                     baseDir = new File(".");
164                 }
165                 scanner.setBasedir(baseDir);
166                 scanner.setIncludes(include);
167                 if (excludes != null && excludes.length > 0) {
168                     scanner.addExcludes(excludes);
169                 }
170                 scanner.scan();
171                 if (scanner.getIncludedFilesCount() > 0) {
172                     for (String s : scanner.getIncludedFiles()) {
173                         final File f = new File(baseDir, s);
174                         paths.add(f);
175                     }
176                 }
177             }
178             engine.scan(paths);
179 
180             engine.analyzeDependencies();
181             final List<Dependency> dependencies = engine.getDependencies();
182             DatabaseProperties prop = null;
183             CveDB cve = null;
184             try {
185                 cve = new CveDB();
186                 cve.open();
187                 prop = cve.getDatabaseProperties();
188             } catch (DatabaseException ex) {
189                 LOGGER.log(Level.FINE, "Unable to retrieve DB Properties", ex);
190             } finally {
191                 if (cve != null) {
192                     cve.close();
193                 }
194             }
195             final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
196             try {
197                 report.generateReports(reportDirectory, outputFormat);
198             } catch (IOException ex) {
199                 LOGGER.log(Level.SEVERE, "There was an IO error while attempting to generate the report.");
200                 LOGGER.log(Level.FINE, null, ex);
201             } catch (Throwable ex) {
202                 LOGGER.log(Level.SEVERE, "There was an error while attempting to generate the report.");
203                 LOGGER.log(Level.FINE, null, ex);
204             }
205         } catch (DatabaseException ex) {
206             LOGGER.log(Level.SEVERE, "Unable to connect to the dependency-check database; analysis has stopped");
207             LOGGER.log(Level.FINE, "", ex);
208         } finally {
209             if (engine != null) {
210                 engine.cleanup();
211             }
212         }
213     }
214 
215     /**
216      * Updates the global Settings.
217      *
218      * @param cli a reference to the CLI Parser that contains the command line arguments used to set the corresponding
219      * settings in the core engine.
220      */
221     private void populateSettings(CliParser cli) {
222 
223         final boolean autoUpdate = cli.isAutoUpdate();
224         final String connectionTimeout = cli.getConnectionTimeout();
225         final String proxyServer = cli.getProxyServer();
226         final String proxyPort = cli.getProxyPort();
227         final String proxyUser = cli.getProxyUsername();
228         final String proxyPass = cli.getProxyPassword();
229         final String dataDirectory = cli.getDataDirectory();
230         final File propertiesFile = cli.getPropertiesFile();
231         final String suppressionFile = cli.getSuppressionFile();
232         final boolean jarDisabled = cli.isJarDisabled();
233         final boolean archiveDisabled = cli.isArchiveDisabled();
234         final boolean assemblyDisabled = cli.isAssemblyDisabled();
235         final boolean nuspecDisabled = cli.isNuspecDisabled();
236         final boolean nexusDisabled = cli.isNexusDisabled();
237         final String nexusUrl = cli.getNexusUrl();
238         final String databaseDriverName = cli.getDatabaseDriverName();
239         final String databaseDriverPath = cli.getDatabaseDriverPath();
240         final String connectionString = cli.getConnectionString();
241         final String databaseUser = cli.getDatabaseUser();
242         final String databasePassword = cli.getDatabasePassword();
243         final String additionalZipExtensions = cli.getAdditionalZipExtensions();
244         final String pathToMono = cli.getPathToMono();
245 
246         if (propertiesFile != null) {
247             try {
248                 Settings.mergeProperties(propertiesFile);
249             } catch (FileNotFoundException ex) {
250                 final String msg = String.format("Unable to load properties file '%s'", propertiesFile.getPath());
251                 LOGGER.log(Level.SEVERE, msg);
252                 LOGGER.log(Level.FINE, null, ex);
253             } catch (IOException ex) {
254                 final String msg = String.format("Unable to find properties file '%s'", propertiesFile.getPath());
255                 LOGGER.log(Level.SEVERE, msg);
256                 LOGGER.log(Level.FINE, null, ex);
257             }
258         }
259         // We have to wait until we've merged the properties before attempting to set whether we use
260         // the proxy for Nexus since it could be disabled in the properties, but not explicitly stated
261         // on the command line
262         final boolean nexusUsesProxy = cli.isNexusUsesProxy();
263         if (dataDirectory != null) {
264             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
265         } else if (System.getProperty("basedir") != null) {
266             final File dataDir = new File(System.getProperty("basedir"), "data");
267             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
268         } else {
269             final File jarPath = new File(App.class.getProtectionDomain().getCodeSource().getLocation().getPath());
270             final File base = jarPath.getParentFile();
271             final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
272             final File dataDir = new File(base, sub);
273             Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
274         }
275         Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
276         if (proxyServer != null && !proxyServer.isEmpty()) {
277             Settings.setString(Settings.KEYS.PROXY_SERVER, proxyServer);
278         }
279         if (proxyPort != null && !proxyPort.isEmpty()) {
280             Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
281         }
282         if (proxyUser != null && !proxyUser.isEmpty()) {
283             Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUser);
284         }
285         if (proxyPass != null && !proxyPass.isEmpty()) {
286             Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPass);
287         }
288         if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
289             Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
290         }
291         if (suppressionFile != null && !suppressionFile.isEmpty()) {
292             Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
293         }
294 
295         //File Type Analyzer Settings
296         Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
297         Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled);
298         Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled);
299         Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
300 
301         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !nexusDisabled);
302         if (nexusUrl != null && !nexusUrl.isEmpty()) {
303             Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
304         }
305         Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
306         if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
307             Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
308         }
309         if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
310             Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
311         }
312         if (connectionString != null && !connectionString.isEmpty()) {
313             Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
314         }
315         if (databaseUser != null && !databaseUser.isEmpty()) {
316             Settings.setString(Settings.KEYS.DB_USER, databaseUser);
317         }
318         if (databasePassword != null && !databasePassword.isEmpty()) {
319             Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
320         }
321         if (additionalZipExtensions != null && !additionalZipExtensions.isEmpty()) {
322             Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions);
323         }
324         if (pathToMono != null && !pathToMono.isEmpty()) {
325             Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
326         }
327     }
328 }