1 /*
2 * This file is part of dependency-check-core.
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.utils;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.net.URLDecoder;
29 import java.util.Enumeration;
30 import java.util.Properties;
31 import java.util.logging.Level;
32 import java.util.logging.Logger;
33
34 /**
35 * A simple settings container that wraps the dependencycheck.properties file.
36 *
37 * @author Jeremy Long <jeremy.long@owasp.org>
38 */
39 public final class Settings {
40
41 //<editor-fold defaultstate="collapsed" desc="KEYS used to access settings">
42 /**
43 * The collection of keys used within the properties file.
44 */
45 public static final class KEYS {
46
47 /**
48 * private constructor because this is a "utility" class containing constants
49 */
50 private KEYS() {
51 //do nothing
52 }
53 /**
54 * The properties key indicating whether or not the cached data sources should be updated.
55 */
56 public static final String AUTO_UPDATE = "autoupdate";
57 /**
58 * The database driver class name. If this is not in the properties file the embedded database is used.
59 */
60 public static final String DB_DRIVER_NAME = "data.driver_name";
61 /**
62 * The database driver class name. If this is not in the properties file the embedded database is used.
63 */
64 public static final String DB_DRIVER_PATH = "data.driver_path";
65 /**
66 * The database connection string. If this is not in the properties file the embedded database is used.
67 */
68 public static final String DB_CONNECTION_STRING = "data.connection_string";
69 /**
70 * The username to use when connecting to the database.
71 */
72 public static final String DB_USER = "data.user";
73 /**
74 * The password to authenticate to the database.
75 */
76 public static final String DB_PASSWORD = "data.password";
77 /**
78 * The base path to use for the data directory (for embedded db).
79 */
80 public static final String DATA_DIRECTORY = "data.directory";
81 /**
82 * The properties key for the URL to retrieve the "meta" data from about the CVE entries.
83 */
84 public static final String CVE_META_URL = "cve.url.meta";
85 /**
86 * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using
87 * the 2.0 schema.
88 */
89 public static final String CVE_MODIFIED_20_URL = "cve.url-2.0.modified";
90 /**
91 * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using
92 * the 1.2 schema.
93 */
94 public static final String CVE_MODIFIED_12_URL = "cve.url-1.2.modified";
95 /**
96 * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days).
97 */
98 public static final String CVE_MODIFIED_VALID_FOR_DAYS = "cve.url.modified.validfordays";
99 /**
100 * The properties key for the telling us how many cvr.url.* URLs exists. This is used in combination with
101 * CVE_BASE_URL to be able to retrieve the URLs for all of the files that make up the NVD CVE listing.
102 */
103 public static final String CVE_START_YEAR = "cve.startyear";
104 /**
105 * The properties key for the CVE schema version 1.2.
106 */
107 public static final String CVE_SCHEMA_1_2 = "cve.url-1.2.base";
108 /**
109 * The properties key for the CVE schema version 2.0.
110 */
111 public static final String CVE_SCHEMA_2_0 = "cve.url-2.0.base";
112 /**
113 * The properties key for the proxy server.
114 *
115 * @deprecated use {@link org.owasp.dependencycheck.utils.Settings.KEYS#PROXY_SERVER} instead.
116 */
117 @Deprecated
118 public static final String PROXY_URL = "proxy.server";
119 /**
120 * The properties key for the proxy server.
121 */
122 public static final String PROXY_SERVER = "proxy.server";
123 /**
124 * The properties key for the proxy port - this must be an integer value.
125 */
126 public static final String PROXY_PORT = "proxy.port";
127 /**
128 * The properties key for the proxy username.
129 */
130 public static final String PROXY_USERNAME = "proxy.username";
131 /**
132 * The properties key for the proxy password.
133 */
134 public static final String PROXY_PASSWORD = "proxy.password";
135 /**
136 * The properties key for the connection timeout.
137 */
138 public static final String CONNECTION_TIMEOUT = "connection.timeout";
139 /**
140 * The location of the temporary directory.
141 */
142 public static final String TEMP_DIRECTORY = "temp.directory";
143 /**
144 * The maximum number of threads to allocate when downloading files.
145 */
146 public static final String MAX_DOWNLOAD_THREAD_POOL_SIZE = "max.download.threads";
147 /**
148 * The key for a list of suppression files.
149 */
150 public static final String SUPPRESSION_FILE = "suppression.file";
151 /**
152 * The properties key for whether the Jar Analyzer is enabled.
153 */
154 public static final String ANALYZER_JAR_ENABLED = "analyzer.jar.enabled";
155 /**
156 * The properties key for whether the Archive analyzer is enabled.
157 */
158 public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled";
159 /**
160 * The properties key for whether the .NET Assembly analyzer is enabled.
161 */
162 public static final String ANALYZER_ASSEMBLY_ENABLED = "analyzer.assembly.enabled";
163 /**
164 * The properties key for whether the .NET Nuspec analyzer is enabled.
165 */
166 public static final String ANALYZER_NUSPEC_ENABLED = "analyzer.nuspec.enabled";
167 /**
168 * The properties key for whether the JavaScript analyzer is enabled.
169 */
170 public static final String ANALYZER_JAVASCRIPT_ENABLED = "analyzer.javascript.enabled";
171 /**
172 * The properties key for whether the Nexus analyzer is enabled.
173 */
174 public static final String ANALYZER_NEXUS_ENABLED = "analyzer.nexus.enabled";
175 /**
176 * The properties key for the Nexus search URL.
177 */
178 public static final String ANALYZER_NEXUS_URL = "analyzer.nexus.url";
179 /**
180 * The properties key for using the proxy to reach Nexus.
181 */
182 public static final String ANALYZER_NEXUS_PROXY = "analyzer.nexus.proxy";
183 /**
184 * The path to mono, if available.
185 */
186 public static final String ANALYZER_ASSEMBLY_MONO_PATH = "analyzer.assembly.mono.path";
187 /**
188 * The additional configured zip file extensions, if available.
189 */
190 public static final String ADDITIONAL_ZIP_EXTENSIONS = "extensions.zip";
191 /**
192 * The properties key for whether Test Scope dependencies should be skipped.
193 */
194 public static final String SKIP_TEST_SCOPE = "skip.test.scope";
195 /**
196 * The properties key for whether Runtime Scope dependencies should be skipped.
197 */
198 public static final String SKIP_RUNTIME_SCOPE = "skip.runtime.scope";
199 /**
200 * The properties key for whether Provided Scope dependencies should be skipped.
201 */
202 public static final String SKIP_PROVIDED_SCOPE = "skip.provided.scope";
203 }
204 //</editor-fold>
205
206 /**
207 * The logger.
208 */
209 private static final Logger LOGGER = Logger.getLogger(Settings.class.getName());
210 /**
211 * The properties file location.
212 */
213 private static final String PROPERTIES_FILE = "dependencycheck.properties";
214 /**
215 * Thread local settings.
216 */
217 private static ThreadLocal<Settings> localSettings = new ThreadLocal();
218 /**
219 * The properties.
220 */
221 private Properties props = null;
222
223 /**
224 * Private constructor for the Settings class. This class loads the properties files.
225 *
226 * @param propertiesFilePath the path to the base properties file to load
227 */
228 private Settings(String propertiesFilePath) {
229 InputStream in = null;
230 props = new Properties();
231 try {
232 in = this.getClass().getClassLoader().getResourceAsStream(propertiesFilePath);
233 props.load(in);
234 } catch (IOException ex) {
235 LOGGER.log(Level.SEVERE, "Unable to load default settings.");
236 LOGGER.log(Level.FINE, null, ex);
237 } finally {
238 if (in != null) {
239 try {
240 in.close();
241 } catch (IOException ex) {
242 LOGGER.log(Level.FINEST, null, ex);
243 }
244 }
245 }
246 logProperties("Properties loaded", props);
247 }
248
249 /**
250 * Initializes the thread local settings object. Note, to use the settings object you must call this method.
251 * However, you must also call Settings.cleanup() to properly release resources.
252 */
253 public static void initialize() {
254 localSettings.set(new Settings(PROPERTIES_FILE));
255 }
256
257 /**
258 * Initializes the thread local settings object. Note, to use the settings object you must call this method.
259 * However, you must also call Settings.cleanup() to properly release resources.
260 *
261 * @param propertiesFilePath the path to the base properties file to load
262 */
263 public static void initialize(String propertiesFilePath) {
264 localSettings.set(new Settings(propertiesFilePath));
265 }
266
267 /**
268 * Cleans up resources to prevent memory leaks.
269 *
270 */
271 public static void cleanup() {
272 cleanup(true);
273 }
274
275 /**
276 * Cleans up resources to prevent memory leaks.
277 *
278 * @param deleteTemporary flag indicating whether any temporary directories generated should be removed
279 */
280 public static void cleanup(boolean deleteTemporary) {
281 if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) {
282 FileUtils.delete(tempDirectory);
283 }
284 try {
285 localSettings.remove();
286 } catch (Throwable ex) {
287 LOGGER.log(Level.FINE, "Error cleaning up Settings", ex);
288 }
289 }
290
291 /**
292 * Gets the underlying instance of the Settings object.
293 *
294 * @return the Settings object
295 */
296 public static Settings getInstance() {
297 return localSettings.get();
298 }
299
300 /**
301 * Sets the instance of the Settings object to use in this thread.
302 *
303 * @param instance the instance of the settings object to use in this thread
304 */
305 public static void setInstance(Settings instance) {
306 localSettings.set(instance);
307 }
308
309 /**
310 * Logs the properties. This will not log any properties that contain 'password' in the key.
311 *
312 * @param header the header to print with the log message
313 * @param properties the properties to log
314 */
315 private static void logProperties(String header, Properties properties) {
316 if (LOGGER.isLoggable(Level.FINE)) {
317 final StringWriter sw = new StringWriter();
318 PrintWriter pw = null;
319 try {
320 pw = new PrintWriter(sw);
321 pw.format("%s:%n%n", header);
322 final Enumeration e = properties.propertyNames();
323 while (e.hasMoreElements()) {
324 final String key = (String) e.nextElement();
325 if (key.contains("password")) {
326 pw.format("%s='*****'%n", key);
327 } else {
328 final String value = properties.getProperty(key);
329 if (value != null) {
330 pw.format("%s='%s'%n", key, value);
331 }
332 }
333 }
334 pw.flush();
335 LOGGER.fine(sw.toString());
336 } finally {
337 if (pw != null) {
338 pw.close();
339 }
340 }
341
342 }
343 }
344
345 /**
346 * Sets a property value.
347 *
348 * @param key the key for the property
349 * @param value the value for the property
350 */
351 public static void setString(String key, String value) {
352 localSettings.get().props.setProperty(key, value);
353 if (LOGGER.isLoggable(Level.FINE)) {
354 LOGGER.fine(String.format("Setting: %s='%s'", key, value));
355 }
356 }
357
358 /**
359 * Sets a property value.
360 *
361 * @param key the key for the property
362 * @param value the value for the property
363 */
364 public static void setBoolean(String key, boolean value) {
365 if (value) {
366 localSettings.get().props.setProperty(key, Boolean.TRUE.toString());
367 } else {
368 localSettings.get().props.setProperty(key, Boolean.FALSE.toString());
369 }
370 if (LOGGER.isLoggable(Level.FINE)) {
371 LOGGER.fine(String.format("Setting: %s='%b'", key, value));
372 }
373 }
374
375 /**
376 * Merges a new properties file into the current properties. This method allows for the loading of a user provided
377 * properties file.<br/><br/>
378 * Note: even if using this method - system properties will be loaded before properties loaded from files.
379 *
380 * @param filePath the path to the properties file to merge.
381 * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
382 * @throws IOException is thrown when there is an exception loading/merging the properties
383 */
384 public static void mergeProperties(File filePath) throws FileNotFoundException, IOException {
385 final FileInputStream fis = new FileInputStream(filePath);
386 mergeProperties(fis);
387 }
388
389 /**
390 * Merges a new properties file into the current properties. This method allows for the loading of a user provided
391 * properties file.<br/><br/>
392 * Note: even if using this method - system properties will be loaded before properties loaded from files.
393 *
394 * @param filePath the path to the properties file to merge.
395 * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
396 * @throws IOException is thrown when there is an exception loading/merging the properties
397 */
398 public static void mergeProperties(String filePath) throws FileNotFoundException, IOException {
399 final FileInputStream fis = new FileInputStream(filePath);
400 mergeProperties(fis);
401 }
402
403 /**
404 * Merges a new properties file into the current properties. This method allows for the loading of a user provided
405 * properties file.<br/><br/>
406 * Note: even if using this method - system properties will be loaded before properties loaded from files.
407 *
408 * @param stream an Input Stream pointing at a properties file to merge
409 * @throws IOException is thrown when there is an exception loading/merging the properties
410 */
411 public static void mergeProperties(InputStream stream) throws IOException {
412 localSettings.get().props.load(stream);
413 logProperties("Properties updated via merge", localSettings.get().props);
414 }
415
416 /**
417 * Returns a value from the properties file as a File object. If the value was specified as a system property or
418 * passed in via the -Dprop=value argument - this method will return the value from the system properties before the
419 * values in the contained configuration file.
420 *
421 * @param key the key to lookup within the properties file
422 * @return the property from the properties file converted to a File object
423 */
424 public static File getFile(String key) {
425 final String file = getString(key);
426 if (file == null) {
427 return null;
428 }
429 return new File(file);
430 }
431
432 /**
433 * Returns a value from the properties file as a File object. If the value was specified as a system property or
434 * passed in via the -Dprop=value argument - this method will return the value from the system properties before the
435 * values in the contained configuration file.
436 *
437 * This method will check the configured base directory and will use this as the base of the file path.
438 * Additionally, if the base directory begins with a leading "[JAR]\" sequence with the path to the folder
439 * containing the JAR file containing this class.
440 *
441 * @param key the key to lookup within the properties file
442 * @return the property from the properties file converted to a File object
443 */
444 public static File getDataFile(String key) {
445 final String file = getString(key);
446 LOGGER.log(Level.FINE, String.format("Settings.getDataFile() - file: '%s'", file));
447 if (file == null) {
448 return null;
449 }
450 if (file.startsWith("[JAR]")) {
451 LOGGER.log(Level.FINE, "Settings.getDataFile() - transforming filename");
452 final File jarPath = getJarPath();
453 LOGGER.log(Level.FINE, String.format("Settings.getDataFile() - jar file: '%s'", jarPath.toString()));
454 final File retVal = new File(jarPath, file.substring(6));
455 LOGGER.log(Level.FINE, String.format("Settings.getDataFile() - returning: '%s'", retVal.toString()));
456 return retVal;
457 }
458 return new File(file);
459 }
460
461 /**
462 * Attempts to retrieve the folder containing the Jar file containing the Settings class.
463 *
464 * @return a File object
465 */
466 private static File getJarPath() {
467 final String jarPath = Settings.class.getProtectionDomain().getCodeSource().getLocation().getPath();
468 String decodedPath = ".";
469 try {
470 decodedPath = URLDecoder.decode(jarPath, "UTF-8");
471 } catch (UnsupportedEncodingException ex) {
472 LOGGER.log(Level.FINEST, null, ex);
473 }
474
475 final File path = new File(decodedPath);
476 if (path.getName().toLowerCase().endsWith(".jar")) {
477 return path.getParentFile();
478 } else {
479 return new File(".");
480 }
481 }
482
483 /**
484 * Returns a value from the properties file. If the value was specified as a system property or passed in via the
485 * -Dprop=value argument - this method will return the value from the system properties before the values in the
486 * contained configuration file.
487 *
488 * @param key the key to lookup within the properties file
489 * @param defaultValue the default value for the requested property
490 * @return the property from the properties file
491 */
492 public static String getString(String key, String defaultValue) {
493 final String str = System.getProperty(key, localSettings.get().props.getProperty(key, defaultValue));
494 return str;
495 }
496
497 /**
498 * A reference to the temporary directory; used incase it needs to be deleted during cleanup.
499 */
500 private static File tempDirectory = null;
501
502 /**
503 * Returns the temporary directory.
504 *
505 * @return the temporary directory
506 * @throws java.io.IOException thrown if the temporary directory does not exist and cannot be created
507 */
508 public static File getTempDirectory() throws IOException {
509 final File tmpDir = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")));
510 if (!tmpDir.exists()) {
511 if (!tmpDir.mkdirs()) {
512 final String msg = String.format("Unable to make a temporary folder '%s'", tmpDir.getPath());
513 throw new IOException(msg);
514 } else {
515 tempDirectory = tmpDir;
516 }
517 }
518 return tmpDir;
519 }
520
521 /**
522 * Returns a value from the properties file. If the value was specified as a system property or passed in via the
523 * -Dprop=value argument - this method will return the value from the system properties before the values in the
524 * contained configuration file.
525 *
526 * @param key the key to lookup within the properties file
527 * @return the property from the properties file
528 */
529 public static String getString(String key) {
530 return System.getProperty(key, localSettings.get().props.getProperty(key));
531 }
532
533 /**
534 * Removes a property from the local properties collection. This is mainly used in test cases.
535 *
536 * @param key the property key to remove
537 */
538 public static void removeProperty(String key) {
539 localSettings.get().props.remove(key);
540 }
541
542 /**
543 * Returns an int value from the properties file. If the value was specified as a system property or passed in via
544 * the -Dprop=value argument - this method will return the value from the system properties before the values in the
545 * contained configuration file.
546 *
547 * @param key the key to lookup within the properties file
548 * @return the property from the properties file
549 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
550 */
551 public static int getInt(String key) throws InvalidSettingException {
552 int value;
553 try {
554 value = Integer.parseInt(Settings.getString(key));
555 } catch (NumberFormatException ex) {
556 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
557 }
558 return value;
559 }
560
561 /**
562 * Returns an int value from the properties file. If the value was specified as a system property or passed in via
563 * the -Dprop=value argument - this method will return the value from the system properties before the values in the
564 * contained configuration file.
565 *
566 * @param key the key to lookup within the properties file
567 * @param defaultValue the default value to return
568 * @return the property from the properties file or the defaultValue if the property does not exist or cannot be
569 * converted to an integer
570 */
571 public static int getInt(String key, int defaultValue) {
572 int value;
573 try {
574 value = Integer.parseInt(Settings.getString(key));
575 } catch (NumberFormatException ex) {
576 final String msg = String.format("Could not convert property '%s' to an int.", key);
577 LOGGER.log(Level.FINEST, msg, ex);
578 value = defaultValue;
579 }
580 return value;
581 }
582
583 /**
584 * Returns a long value from the properties file. If the value was specified as a system property or passed in via
585 * the -Dprop=value argument - this method will return the value from the system properties before the values in the
586 * contained configuration file.
587 *
588 * @param key the key to lookup within the properties file
589 * @return the property from the properties file
590 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
591 */
592 public static long getLong(String key) throws InvalidSettingException {
593 long value;
594 try {
595 value = Long.parseLong(Settings.getString(key));
596 } catch (NumberFormatException ex) {
597 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
598 }
599 return value;
600 }
601
602 /**
603 * Returns a boolean value from the properties file. If the value was specified as a system property or passed in
604 * via the <code>-Dprop=value</code> argument this method will return the value from the system properties before
605 * the values in the contained configuration file.
606 *
607 * @param key the key to lookup within the properties file
608 * @return the property from the properties file
609 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
610 */
611 public static boolean getBoolean(String key) throws InvalidSettingException {
612 boolean value;
613 try {
614 value = Boolean.parseBoolean(Settings.getString(key));
615 } catch (NumberFormatException ex) {
616 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
617 }
618 return value;
619 }
620
621 /**
622 * Returns a boolean value from the properties file. If the value was specified as a system property or passed in
623 * via the <code>-Dprop=value</code> argument this method will return the value from the system properties before
624 * the values in the contained configuration file.
625 *
626 * @param key the key to lookup within the properties file
627 * @param defaultValue the default value to return if the setting does not exist
628 * @return the property from the properties file
629 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
630 */
631 public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException {
632 boolean value;
633 try {
634 final String strValue = Settings.getString(key);
635 if (strValue == null) {
636 return defaultValue;
637 }
638 value = Boolean.parseBoolean(strValue);
639 } catch (NumberFormatException ex) {
640 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
641 }
642 return value;
643 }
644 }