1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck;
19
20 import ch.qos.logback.classic.LoggerContext;
21 import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29 import org.apache.commons.cli.ParseException;
30 import org.owasp.dependencycheck.data.nvdcve.CveDB;
31 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
32 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
33 import org.owasp.dependencycheck.dependency.Dependency;
34 import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner;
35 import org.owasp.dependencycheck.reporting.ReportGenerator;
36 import org.owasp.dependencycheck.utils.Settings;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import ch.qos.logback.core.FileAppender;
40 import org.slf4j.impl.StaticLoggerBinder;
41
42
43
44
45
46
47 public class App {
48
49
50
51
52 private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
53
54
55
56
57
58
59 public static void main(String[] args) {
60 try {
61 Settings.initialize();
62 final App app = new App();
63 app.run(args);
64 } finally {
65 Settings.cleanup(true);
66 }
67 }
68
69
70
71
72
73
74 public void run(String[] args) {
75 final CliParser cli = new CliParser();
76
77 try {
78 cli.parse(args);
79 } catch (FileNotFoundException ex) {
80 System.err.println(ex.getMessage());
81 cli.printHelp();
82 return;
83 } catch (ParseException ex) {
84 System.err.println(ex.getMessage());
85 cli.printHelp();
86 return;
87 }
88
89 if (cli.getVerboseLog() != null) {
90 prepareLogger(cli.getVerboseLog());
91 }
92
93 if (cli.isGetVersion()) {
94 cli.printVersionInfo();
95 } else if (cli.isUpdateOnly()) {
96 populateSettings(cli);
97 runUpdateOnly();
98 } else if (cli.isRunScan()) {
99 populateSettings(cli);
100 try {
101 runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles(),
102 cli.getExcludeList(), cli.getSymLinkDepth());
103 } catch (InvalidScanPathException ex) {
104 LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths");
105 }
106 } else {
107 cli.printHelp();
108 }
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123 private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files,
124 String[] excludes, int symLinkDepth) throws InvalidScanPathException {
125 Engine engine = null;
126 try {
127 engine = new Engine();
128 final List<String> antStylePaths = new ArrayList<String>();
129 for (String file : files) {
130 final String antPath = ensureCanonicalPath(file);
131 antStylePaths.add(antPath);
132 }
133
134 final Set<File> paths = new HashSet<File>();
135 for (String file : antStylePaths) {
136 LOGGER.debug("Scanning {}", file);
137 final DirectoryScanner scanner = new DirectoryScanner();
138 String include = file.replace('\\', '/');
139 File baseDir;
140
141 if (include.startsWith("//")) {
142 throw new InvalidScanPathException("Unable to scan paths specified by //");
143 } else {
144 final int pos = getLastFileSeparator(include);
145 final String tmpBase = include.substring(0, pos);
146 final String tmpInclude = include.substring(pos + 1);
147 if (tmpInclude.indexOf('*') >= 0 || tmpInclude.indexOf('?') >= 0
148 || (new File(include)).isFile()) {
149 baseDir = new File(tmpBase);
150 include = tmpInclude;
151 } else {
152 baseDir = new File(tmpBase, tmpInclude);
153 include = "**/*";
154 }
155 }
156
157
158 scanner.setBasedir(baseDir);
159 scanner.setIncludes(include);
160 scanner.setMaxLevelsOfSymlinks(symLinkDepth);
161 if (symLinkDepth <= 0) {
162 scanner.setFollowSymlinks(false);
163 }
164 if (excludes != null && excludes.length > 0) {
165 scanner.addExcludes(excludes);
166 }
167 scanner.scan();
168 if (scanner.getIncludedFilesCount() > 0) {
169 for (String s : scanner.getIncludedFiles()) {
170 final File f = new File(baseDir, s);
171 LOGGER.debug("Found file {}", f.toString());
172 paths.add(f);
173 }
174 }
175 }
176 engine.scan(paths);
177
178 engine.analyzeDependencies();
179 final List<Dependency> dependencies = engine.getDependencies();
180 DatabaseProperties prop = null;
181 CveDB cve = null;
182 try {
183 cve = new CveDB();
184 cve.open();
185 prop = cve.getDatabaseProperties();
186 } catch (DatabaseException ex) {
187 LOGGER.debug("Unable to retrieve DB Properties", ex);
188 } finally {
189 if (cve != null) {
190 cve.close();
191 }
192 }
193 final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
194 try {
195 report.generateReports(reportDirectory, outputFormat);
196 } catch (IOException ex) {
197 LOGGER.error("There was an IO error while attempting to generate the report.");
198 LOGGER.debug("", ex);
199 } catch (Throwable ex) {
200 LOGGER.error("There was an error while attempting to generate the report.");
201 LOGGER.debug("", ex);
202 }
203 } catch (DatabaseException ex) {
204 LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
205 LOGGER.debug("", ex);
206 } finally {
207 if (engine != null) {
208 engine.cleanup();
209 }
210 }
211 }
212
213
214
215
216 private void runUpdateOnly() {
217 Engine engine = null;
218 try {
219 engine = new Engine();
220 engine.doUpdates();
221 } catch (DatabaseException ex) {
222 LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
223 LOGGER.debug("", ex);
224 } finally {
225 if (engine != null) {
226 engine.cleanup();
227 }
228 }
229 }
230
231
232
233
234
235
236
237 private void populateSettings(CliParser cli) {
238
239 final boolean autoUpdate = cli.isAutoUpdate();
240 final String connectionTimeout = cli.getConnectionTimeout();
241 final String proxyServer = cli.getProxyServer();
242 final String proxyPort = cli.getProxyPort();
243 final String proxyUser = cli.getProxyUsername();
244 final String proxyPass = cli.getProxyPassword();
245 final String dataDirectory = cli.getDataDirectory();
246 final File propertiesFile = cli.getPropertiesFile();
247 final String suppressionFile = cli.getSuppressionFile();
248 final boolean jarDisabled = cli.isJarDisabled();
249 final boolean archiveDisabled = cli.isArchiveDisabled();
250 final boolean pyDistDisabled = cli.isPythonDistributionDisabled();
251 final boolean cMakeDisabled = cli.isCmakeDisabled();
252 final boolean pyPkgDisabled = cli.isPythonPackageDisabled();
253 final boolean autoconfDisabled = cli.isAutoconfDisabled();
254 final boolean assemblyDisabled = cli.isAssemblyDisabled();
255 final boolean nuspecDisabled = cli.isNuspecDisabled();
256 final boolean centralDisabled = cli.isCentralDisabled();
257 final boolean nexusDisabled = cli.isNexusDisabled();
258 final String nexusUrl = cli.getNexusUrl();
259 final String databaseDriverName = cli.getDatabaseDriverName();
260 final String databaseDriverPath = cli.getDatabaseDriverPath();
261 final String connectionString = cli.getConnectionString();
262 final String databaseUser = cli.getDatabaseUser();
263 final String databasePassword = cli.getDatabasePassword();
264 final String additionalZipExtensions = cli.getAdditionalZipExtensions();
265 final String pathToMono = cli.getPathToMono();
266 final String cveMod12 = cli.getModifiedCve12Url();
267 final String cveMod20 = cli.getModifiedCve20Url();
268 final String cveBase12 = cli.getBaseCve12Url();
269 final String cveBase20 = cli.getBaseCve20Url();
270
271 if (propertiesFile != null) {
272 try {
273 Settings.mergeProperties(propertiesFile);
274 } catch (FileNotFoundException ex) {
275 LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath());
276 LOGGER.debug("", ex);
277 } catch (IOException ex) {
278 LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath());
279 LOGGER.debug("", ex);
280 }
281 }
282
283
284
285 final boolean nexusUsesProxy = cli.isNexusUsesProxy();
286 if (dataDirectory != null) {
287 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
288 } else if (System.getProperty("basedir") != null) {
289 final File dataDir = new File(System.getProperty("basedir"), "data");
290 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
291 } else {
292 final File jarPath = new File(App.class.getProtectionDomain().getCodeSource().getLocation().getPath());
293 final File base = jarPath.getParentFile();
294 final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
295 final File dataDir = new File(base, sub);
296 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
297 }
298 Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
299 if (proxyServer != null && !proxyServer.isEmpty()) {
300 Settings.setString(Settings.KEYS.PROXY_SERVER, proxyServer);
301 }
302 if (proxyPort != null && !proxyPort.isEmpty()) {
303 Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
304 }
305 if (proxyUser != null && !proxyUser.isEmpty()) {
306 Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUser);
307 }
308 if (proxyPass != null && !proxyPass.isEmpty()) {
309 Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPass);
310 }
311 if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
312 Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
313 }
314 if (suppressionFile != null && !suppressionFile.isEmpty()) {
315 Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
316 }
317
318
319 Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
320 Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled);
321 Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !pyDistDisabled);
322 Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !pyPkgDisabled);
323 Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !autoconfDisabled);
324 Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cMakeDisabled);
325 Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled);
326 Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
327 Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled());
328
329 Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !centralDisabled);
330 Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !nexusDisabled);
331 if (nexusUrl != null && !nexusUrl.isEmpty()) {
332 Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
333 }
334 Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
335 if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
336 Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
337 }
338 if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
339 Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
340 }
341 if (connectionString != null && !connectionString.isEmpty()) {
342 Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
343 }
344 if (databaseUser != null && !databaseUser.isEmpty()) {
345 Settings.setString(Settings.KEYS.DB_USER, databaseUser);
346 }
347 if (databasePassword != null && !databasePassword.isEmpty()) {
348 Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
349 }
350 if (additionalZipExtensions != null && !additionalZipExtensions.isEmpty()) {
351 Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions);
352 }
353 if (pathToMono != null && !pathToMono.isEmpty()) {
354 Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
355 }
356 if (cveBase12 != null && !cveBase12.isEmpty()) {
357 Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12);
358 Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20);
359 Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveMod12);
360 Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveMod20);
361 }
362 }
363
364
365
366
367
368
369 private void prepareLogger(String verboseLog) {
370 final StaticLoggerBinder loggerBinder = StaticLoggerBinder.getSingleton();
371 final LoggerContext context = (LoggerContext) loggerBinder.getLoggerFactory();
372
373 final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
374 encoder.setPattern("%d %C:%L%n%-5level - %msg%n");
375 encoder.setContext(context);
376 encoder.start();
377 final FileAppender fa = new FileAppender();
378 fa.setAppend(true);
379 fa.setEncoder(encoder);
380 fa.setContext(context);
381 fa.setFile(verboseLog);
382 final File f = new File(verboseLog);
383 String name = f.getName();
384 final int i = name.lastIndexOf('.');
385 if (i > 1) {
386 name = name.substring(0, i);
387 }
388 fa.setName(name);
389 fa.start();
390 final ch.qos.logback.classic.Logger rootLogger = context.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
391 rootLogger.addAppender(fa);
392 }
393
394
395
396
397
398
399
400
401
402 protected String ensureCanonicalPath(String path) {
403 String basePath = null;
404 String wildCards = null;
405 final String file = path.replace('\\', '/');
406 if (file.contains("*") || file.contains("?")) {
407
408 int pos = getLastFileSeparator(file);
409 if (pos < 0) {
410 return file;
411 }
412 pos += 1;
413 basePath = file.substring(0, pos);
414 wildCards = file.substring(pos);
415 } else {
416 basePath = file;
417 }
418
419 File f = new File(basePath);
420 try {
421 f = f.getCanonicalFile();
422 if (wildCards != null) {
423 f = new File(f, wildCards);
424 }
425 } catch (IOException ex) {
426 LOGGER.warn("Invalid path '{}' was provided.", path);
427 LOGGER.debug("Invalid path provided", ex);
428 }
429 return f.getAbsolutePath().replace('\\', '/');
430 }
431
432
433
434
435
436
437
438 private int getLastFileSeparator(String file) {
439 if (file.contains("*") || file.contains("?")) {
440 int p1 = file.indexOf('*');
441 int p2 = file.indexOf('?');
442 p1 = p1 > 0 ? p1 : file.length();
443 p2 = p2 > 0 ? p2 : file.length();
444 int pos = p1 < p2 ? p1 : p2;
445 pos = file.lastIndexOf('/', pos);
446 return pos;
447 } else {
448 return file.lastIndexOf('/');
449 }
450 }
451 }