View Javadoc
1   /*
2    * This file is part of dependency-check-utils.
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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  import com.fasterxml.jackson.core.JsonProcessingException;
25  import com.fasterxml.jackson.databind.ObjectMapper;
26  
27  import org.jetbrains.annotations.NotNull;
28  import org.jetbrains.annotations.Nullable;
29  
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.FileNotFoundException;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.PrintWriter;
36  import java.io.StringWriter;
37  import java.io.UnsupportedEncodingException;
38  import java.net.URLDecoder;
39  import java.nio.charset.StandardCharsets;
40  import java.security.ProtectionDomain;
41  import java.util.ArrayList;
42  import java.util.Arrays;
43  import java.util.Enumeration;
44  import java.util.List;
45  import java.util.Properties;
46  import java.util.UUID;
47  import java.util.function.Predicate;
48  import java.util.regex.Pattern;
49  import java.util.stream.Collectors;
50  
51  /**
52   * A simple settings container that wraps the dependencycheck.properties file.
53   *
54   * @author Jeremy Long
55   * @version $Id: $Id
56   */
57  public final class Settings {
58  
59      /**
60       * The logger.
61       */
62      private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);
63      /**
64       * The properties file location.
65       */
66      private static final String PROPERTIES_FILE = "dependencycheck.properties";
67      /**
68       * Array separator.
69       */
70      private static final String ARRAY_SEP = ",";
71      /**
72       * The properties.
73       */
74      private Properties props = null;
75      /**
76       * The collection of properties that should be masked when logged.
77       */
78      private List<Predicate<String>> maskedKeys;
79      /**
80       * A reference to the temporary directory; used in case it needs to be
81       * deleted during cleanup.
82       */
83      private File tempDirectory = null;
84  
85      /**
86       * Reference to a utility class used to convert objects to json.
87       */
88      private final ObjectMapper objectMapper = new ObjectMapper();
89  
90      //<editor-fold defaultstate="collapsed" desc="KEYS used to access settings">
91      /**
92       * The collection of keys used within the properties file.
93       */
94      //suppress hard-coded password rule
95      @SuppressWarnings("squid:S2068")
96      public static final class KEYS {
97  
98          /**
99           * The key to obtain the application name.
100          */
101         public static final String APPLICATION_NAME = "odc.application.name";
102         /**
103          * The key to obtain the application version.
104          */
105         public static final String APPLICATION_VERSION = "odc.application.version";
106         /**
107          * The key to obtain the URL to retrieve the current release version
108          * from.
109          */
110         public static final String ENGINE_VERSION_CHECK_URL = "engine.version.url";
111         /**
112          * The properties key indicating whether or not the cached data sources
113          * should be updated.
114          */
115         public static final String AUTO_UPDATE = "odc.autoupdate";
116         /**
117          * The database driver class name. If this is not in the properties file
118          * the embedded database is used.
119          */
120         public static final String DB_DRIVER_NAME = "data.driver_name";
121         /**
122          * The database driver class name. If this is not in the properties file
123          * the embedded database is used.
124          */
125         public static final String DB_DRIVER_PATH = "data.driver_path";
126         /**
127          * The database connection string. If this is not in the properties file
128          * the embedded database is used.
129          */
130         public static final String DB_CONNECTION_STRING = "data.connection_string";
131         /**
132          * The username to use when connecting to the database.
133          */
134         public static final String DB_USER = "data.user";
135         /**
136          * The password to authenticate to the database.
137          */
138         public static final String DB_PASSWORD = "data.password";
139         /**
140          * The base path to use for the data directory (for embedded db and
141          * other cached resources from the Internet).
142          */
143         public static final String DATA_DIRECTORY = "data.directory";
144         /**
145          * The base path to use for the H2 data directory (for embedded db).
146          */
147         public static final String H2_DATA_DIRECTORY = "data.h2.directory";
148         /**
149          * The database file name.
150          */
151         public static final String DB_FILE_NAME = "data.file_name";
152         /**
153          * The database schema version.
154          */
155         public static final String DB_VERSION = "data.version";
156         /**
157          * The starts with filter used to exclude CVE entries from the database.
158          * By default this is set to 'cpe:2.3:a:' which limits the CVEs imported
159          * to just those that are related to applications. If this were set to
160          * just 'cpe:2.3:' the OS, hardware, and application related CVEs would
161          * be imported.
162          */
163         public static final String CVE_CPE_STARTS_WITH_FILTER = "cve.cpe.startswith.filter";
164         /**
165          * The NVD API Endpoint.
166          */
167         public static final String NVD_API_ENDPOINT = "nvd.api.endpoint";
168         /**
169          * API Key for the NVD API.
170          */
171         public static final String NVD_API_KEY = "nvd.api.key";
172         /**
173          * The delay between requests for the NVD API.
174          */
175         public static final String NVD_API_DELAY = "nvd.api.delay";
176         /**
177          * The maximum number of retry requests for a single call to the NVD
178          * API.
179          */
180         public static final String NVD_API_MAX_RETRY_COUNT = "nvd.api.max.retry.count";
181         /**
182          * The properties key to control the skipping of the check for NVD
183          * updates.
184          */
185         public static final String NVD_API_VALID_FOR_HOURS = "nvd.api.check.validforhours";
186         /**
187          * The properties key to control the results per page lower than NVD's default of 2000
188          * See #6863 for the rationale on allowing lower configurations.
189          */
190         public static final String NVD_API_RESULTS_PER_PAGE = "nvd.api.results.per.page";
191         /**
192          * The properties key that indicates how often the NVD API data feed
193          * needs to be updated before a full refresh is evaluated.
194          */
195         public static final String NVD_API_DATAFEED_VALID_FOR_DAYS = "nvd.api.datafeed.validfordays";
196         /**
197          * The URL for the NVD API Data Feed.
198          */
199         public static final String NVD_API_DATAFEED_URL = "nvd.api.datafeed.url";
200         /**
201          * The username to use when connecting to the NVD Data feed.
202          */
203         public static final String NVD_API_DATAFEED_USER = "nvd.api.datafeed.user";
204         /**
205          * The password to authenticate to the NVD Data feed.
206          */
207         public static final String NVD_API_DATAFEED_PASSWORD = "nvd.api.datafeed.password";
208         /**
209          * The starting year for the NVD CVE Data feed cache.
210          */
211         public static final String NVD_API_DATAFEED_START_YEAR = "nvd.api.datafeed.startyear";
212         //END NEW
213         /**
214          * The key to determine if the NVD CVE analyzer is enabled.
215          */
216         public static final String ANALYZER_NVD_CVE_ENABLED = "analyzer.nvdcve.enabled";
217         /**
218          * The properties key that indicates how often the CPE data needs to be
219          * updated.
220          */
221         public static final String CPE_MODIFIED_VALID_FOR_DAYS = "cpe.validfordays";
222         /**
223          * The properties key for the URL to retrieve the CPE.
224          */
225         public static final String CPE_URL = "cpe.url";
226         /**
227          * The properties key for the URL to retrieve the Known Exploited
228          * Vulnerabilities..
229          */
230         public static final String KEV_URL = "kev.url";
231 
232         /**
233          * The properties key for the hosted suppressions username.
234          * For use when hosted suppressions are mirrored locally on a site requiring authentication
235          */
236         public static final String KEV_USER = "kev.user";
237 
238         /**
239          * The properties key for the hosted suppressions password.
240          * For use when hosted suppressions are mirrored locally on a site requiring authentication
241          */
242         public static final String KEV_PASSWORD = "kev.password";
243 
244         /**
245          * The properties key to control the skipping of the check for Known
246          * Exploited Vulnerabilities updates.
247          */
248         public static final String KEV_CHECK_VALID_FOR_HOURS = "kev.check.validforhours";
249         /**
250          * Whether or not if using basic auth with a proxy the system setting
251          * 'jdk.http.auth.tunneling.disabledSchemes' should be set to an empty
252          * string.
253          */
254         public static final String PROXY_DISABLE_SCHEMAS = "proxy.disableSchemas";
255         /**
256          * The properties key for the proxy server.
257          */
258         public static final String PROXY_SERVER = "proxy.server";
259         /**
260          * The properties key for the proxy port - this must be an integer
261          * value.
262          */
263         public static final String PROXY_PORT = "proxy.port";
264         /**
265          * The properties key for the proxy username.
266          */
267         public static final String PROXY_USERNAME = "proxy.username";
268         /**
269          * The properties key for the proxy password.
270          */
271         public static final String PROXY_PASSWORD = "proxy.password";
272         /**
273          * The properties key for the non proxy hosts.
274          */
275         public static final String PROXY_NON_PROXY_HOSTS = "proxy.nonproxyhosts";
276         /**
277          * The properties key for the connection timeout.
278          */
279         public static final String CONNECTION_TIMEOUT = "connection.timeout";
280         /**
281          * The properties key for the connection read timeout.
282          */
283         public static final String CONNECTION_READ_TIMEOUT = "connection.read.timeout";
284         /**
285          * The location of the temporary directory.
286          */
287         public static final String TEMP_DIRECTORY = "temp.directory";
288         /**
289          * The maximum number of threads to allocate when downloading files.
290          */
291         public static final String MAX_DOWNLOAD_THREAD_POOL_SIZE = "max.download.threads";
292         /**
293          * The properties key for the analysis timeout.
294          */
295         public static final String ANALYSIS_TIMEOUT = "odc.analysis.timeout";
296         /**
297          * The key for the suppression file.
298          */
299         public static final String SUPPRESSION_FILE = "suppression.file";
300         /**
301          * The username used when connecting to the suppressionFiles.
302          */
303         public static final String SUPPRESSION_FILE_USER = "suppression.file.user";
304         /**
305          * The password used when connecting to the suppressionFiles.
306          */
307         public static final String SUPPRESSION_FILE_PASSWORD = "suppression.file.password";
308         /**
309          * The key for the whether the hosted suppressions file datasource is
310          * enabled.
311          */
312         public static final String HOSTED_SUPPRESSIONS_ENABLED = "hosted.suppressions.enabled";
313         /**
314          * The key for the hosted suppressions file URL.
315          */
316         public static final String HOSTED_SUPPRESSIONS_URL = "hosted.suppressions.url";
317 
318         /**
319          * The properties key for the hosted suppressions username.
320          * For use when hosted suppressions are mirrored locally on a site requiring authentication
321          */
322         public static final String HOSTED_SUPPRESSIONS_USER = "hosted.suppressions.user";
323 
324         /**
325          * The properties key for the hosted suppressions password.
326          * For use when hosted suppressions are mirrored locally on a site requiring authentication
327          */
328         public static final String HOSTED_SUPPRESSIONS_PASSWORD = "hosted.suppressions.password";
329 
330         /**
331          * The properties key for defining whether the hosted suppressions file
332          * will be updated regardless of the autoupdate settings.
333          */
334         public static final String HOSTED_SUPPRESSIONS_FORCEUPDATE = "hosted.suppressions.forceupdate";
335 
336         /**
337          * The properties key to control the skipping of the check for hosted
338          * suppressions file updates.
339          */
340         public static final String HOSTED_SUPPRESSIONS_VALID_FOR_HOURS = "hosted.suppressions.validforhours";
341 
342         /**
343          * The key for the hint file.
344          */
345         public static final String HINTS_FILE = "hints.file";
346         /**
347          * The key for the property that controls what CVSS scores are
348          * considered failing test cases for the JUNIT repor.
349          */
350         public static final String JUNIT_FAIL_ON_CVSS = "junit.fail.on.cvss";
351 
352         /**
353          * The properties key for whether the Jar Analyzer is enabled.
354          */
355         public static final String ANALYZER_JAR_ENABLED = "analyzer.jar.enabled";
356 
357         /**
358          * The properties key for whether the Known Exploited Vulnerability
359          * Analyzer is enabled.
360          */
361         public static final String ANALYZER_KNOWN_EXPLOITED_ENABLED = "analyzer.knownexploited.enabled";
362 
363         /**
364          * The properties key for whether experimental analyzers are loaded.
365          */
366         public static final String ANALYZER_EXPERIMENTAL_ENABLED = "analyzer.experimental.enabled";
367         /**
368          * The properties key for whether experimental analyzers are loaded.
369          */
370         public static final String ANALYZER_RETIRED_ENABLED = "analyzer.retired.enabled";
371         /**
372          * The properties key for whether the Archive analyzer is enabled.
373          */
374         public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled";
375         /**
376          * The properties key for whether the node.js package analyzer is
377          * enabled.
378          */
379         public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled";
380         /**
381          * The properties key for configure whether the Node Package analyzer
382          * should skip devDependencies.
383          */
384         public static final String ANALYZER_NODE_PACKAGE_SKIPDEV = "analyzer.node.package.skipdev";
385         /**
386          * The properties key for whether the Node Audit analyzer is enabled.
387          */
388         public static final String ANALYZER_NODE_AUDIT_ENABLED = "analyzer.node.audit.enabled";
389         /**
390          * The properties key for whether the Yarn Audit analyzer is enabled.
391          */
392         public static final String ANALYZER_YARN_AUDIT_ENABLED = "analyzer.yarn.audit.enabled";
393         /**
394          * The properties key for whether the Pnpm Audit analyzer is enabled.
395          */
396         public static final String ANALYZER_PNPM_AUDIT_ENABLED = "analyzer.pnpm.audit.enabled";
397         /**
398          * The properties key for supplying the URL to the Node Audit API.
399          */
400         public static final String ANALYZER_NODE_AUDIT_URL = "analyzer.node.audit.url";
401         /**
402          * The properties key for configure whether the Node Audit analyzer
403          * should skip devDependencies.
404          */
405         public static final String ANALYZER_NODE_AUDIT_SKIPDEV = "analyzer.node.audit.skipdev";
406         /**
407          * The properties key for whether node audit analyzer results will be
408          * cached.
409          */
410         public static final String ANALYZER_NODE_AUDIT_USE_CACHE = "analyzer.node.audit.use.cache";
411         /**
412          * The properties key for whether the RetireJS analyzer is enabled.
413          */
414         public static final String ANALYZER_RETIREJS_ENABLED = "analyzer.retirejs.enabled";
415         /**
416          * The properties key for whether the RetireJS analyzer file content
417          * filters.
418          */
419         public static final String ANALYZER_RETIREJS_FILTERS = "analyzer.retirejs.filters";
420         /**
421          * The properties key for whether the RetireJS analyzer should filter
422          * out non-vulnerable dependencies.
423          */
424         public static final String ANALYZER_RETIREJS_FILTER_NON_VULNERABLE = "analyzer.retirejs.filternonvulnerable";
425         /**
426          * The properties key for defining the URL to the RetireJS repository.
427          */
428         public static final String ANALYZER_RETIREJS_REPO_JS_URL = "analyzer.retirejs.repo.js.url";
429         /**
430          * The properties key for the Nexus search credentials username.
431          */
432         public static final String ANALYZER_RETIREJS_REPO_JS_USER = "analyzer.retirejs.repo.js.username";
433         /**
434          * The properties key for the Nexus search credentials password.
435          */
436         public static final String ANALYZER_RETIREJS_REPO_JS_PASSWORD = "analyzer.retirejs.repo.js.password";
437         /**
438          * The properties key for defining whether the RetireJS repository will
439          * be updated regardless of the autoupdate settings.
440          */
441         public static final String ANALYZER_RETIREJS_FORCEUPDATE = "analyzer.retirejs.forceupdate";
442         /**
443          * The properties key to control the skipping of the check for CVE
444          * updates.
445          */
446         public static final String ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS = "analyzer.retirejs.repo.validforhours";
447         /**
448          * The properties key for whether the PHP composer lock file analyzer is
449          * enabled.
450          */
451         public static final String ANALYZER_COMPOSER_LOCK_ENABLED = "analyzer.composer.lock.enabled";
452         /**
453          * The properties key for whether the PHP composer lock file analyzer 
454          * should skip dev packages.
455          */
456         public static final String ANALYZER_COMPOSER_LOCK_SKIP_DEV = "analyzer.composer.lock.skipdev";
457         /**
458          * The properties key for whether the Perl CPAN file file analyzer is
459          * enabled.
460          */
461         public static final String ANALYZER_CPANFILE_ENABLED = "analyzer.cpanfile.enabled";
462         /**
463          * The properties key for whether the Python Distribution analyzer is
464          * enabled.
465          */
466         public static final String ANALYZER_PYTHON_DISTRIBUTION_ENABLED = "analyzer.python.distribution.enabled";
467         /**
468          * The properties key for whether the Python Package analyzer is
469          * enabled.
470          */
471         public static final String ANALYZER_PYTHON_PACKAGE_ENABLED = "analyzer.python.package.enabled";
472         /**
473          * The properties key for whether the Elixir mix audit analyzer is
474          * enabled.
475          */
476         public static final String ANALYZER_MIX_AUDIT_ENABLED = "analyzer.mix.audit.enabled";
477         /**
478          * The path to mix_audit, if available.
479          */
480         public static final String ANALYZER_MIX_AUDIT_PATH = "analyzer.mix.audit.path";
481         /**
482          * The properties key for whether the Golang Mod analyzer is enabled.
483          */
484         public static final String ANALYZER_GOLANG_MOD_ENABLED = "analyzer.golang.mod.enabled";
485         /**
486          * The path to go, if available.
487          */
488         public static final String ANALYZER_GOLANG_PATH = "analyzer.golang.path";
489         /**
490          * The path to go, if available.
491          */
492         public static final String ANALYZER_YARN_PATH = "analyzer.yarn.path";
493         /**
494          * The path to pnpm, if available.
495          */
496         public static final String ANALYZER_PNPM_PATH = "analyzer.pnpm.path";
497         /**
498          * The properties key for whether the Golang Dep analyzer is enabled.
499          */
500         public static final String ANALYZER_GOLANG_DEP_ENABLED = "analyzer.golang.dep.enabled";
501         /**
502          * The properties key for whether the Ruby Gemspec Analyzer is enabled.
503          */
504         public static final String ANALYZER_RUBY_GEMSPEC_ENABLED = "analyzer.ruby.gemspec.enabled";
505         /**
506          * The properties key for whether the Autoconf analyzer is enabled.
507          */
508         public static final String ANALYZER_AUTOCONF_ENABLED = "analyzer.autoconf.enabled";
509         /**
510          * The properties key for whether the maven_install.json analyzer is
511          * enabled.
512          */
513         public static final String ANALYZER_MAVEN_INSTALL_ENABLED = "analyzer.maveninstall.enabled";
514         /**
515          * The properties key for whether the pip analyzer is enabled.
516          */
517         public static final String ANALYZER_PIP_ENABLED = "analyzer.pip.enabled";
518         /**
519          * The properties key for whether the pipfile analyzer is enabled.
520          */
521         public static final String ANALYZER_PIPFILE_ENABLED = "analyzer.pipfile.enabled";
522         /**
523          * The properties key for whether the Poetry analyzer is enabled.
524          */
525         public static final String ANALYZER_POETRY_ENABLED = "analyzer.poetry.enabled";
526         /**
527          * The properties key for whether the CMake analyzer is enabled.
528          */
529         public static final String ANALYZER_CMAKE_ENABLED = "analyzer.cmake.enabled";
530         /**
531          * The properties key for whether the Ruby Bundler Audit analyzer is
532          * enabled.
533          */
534         public static final String ANALYZER_BUNDLE_AUDIT_ENABLED = "analyzer.bundle.audit.enabled";
535         /**
536          * The properties key for whether the .NET Assembly analyzer is enabled.
537          */
538         public static final String ANALYZER_ASSEMBLY_ENABLED = "analyzer.assembly.enabled";
539         /**
540          * The properties key for whether the .NET Nuspec analyzer is enabled.
541          */
542         public static final String ANALYZER_NUSPEC_ENABLED = "analyzer.nuspec.enabled";
543         /**
544          * The properties key for whether the .NET Nuget packages.config
545          * analyzer is enabled.
546          */
547         public static final String ANALYZER_NUGETCONF_ENABLED = "analyzer.nugetconf.enabled";
548         /**
549          * The properties key for whether the Libman analyzer is enabled.
550          */
551         public static final String ANALYZER_LIBMAN_ENABLED = "analyzer.libman.enabled";
552         /**
553          * The properties key for whether the .NET MSBuild Project analyzer is
554          * enabled.
555          */
556         public static final String ANALYZER_MSBUILD_PROJECT_ENABLED = "analyzer.msbuildproject.enabled";
557         /**
558          * The properties key for whether the Nexus analyzer is enabled.
559          */
560         public static final String ANALYZER_NEXUS_ENABLED = "analyzer.nexus.enabled";
561         /**
562          * The properties key for the Nexus search URL.
563          */
564         public static final String ANALYZER_NEXUS_URL = "analyzer.nexus.url";
565         /**
566          * The properties key for the Nexus search credentials username.
567          */
568         public static final String ANALYZER_NEXUS_USER = "analyzer.nexus.username";
569         /**
570          * The properties key for the Nexus search credentials password.
571          */
572         public static final String ANALYZER_NEXUS_PASSWORD = "analyzer.nexus.password";
573         /**
574          * The properties key for using the proxy to reach Nexus.
575          */
576         public static final String ANALYZER_NEXUS_USES_PROXY = "analyzer.nexus.proxy";
577         /**
578          * The properties key for whether the Artifactory analyzer is enabled.
579          */
580         public static final String ANALYZER_ARTIFACTORY_ENABLED = "analyzer.artifactory.enabled";
581         /**
582          * The properties key for the Artifactory search URL.
583          */
584         public static final String ANALYZER_ARTIFACTORY_URL = "analyzer.artifactory.url";
585         /**
586          * The properties key for the Artifactory username.
587          */
588         public static final String ANALYZER_ARTIFACTORY_API_USERNAME = "analyzer.artifactory.api.username";
589         /**
590          * The properties key for the Artifactory API token.
591          */
592         public static final String ANALYZER_ARTIFACTORY_API_TOKEN = "analyzer.artifactory.api.token";
593         /**
594          * The properties key for the Artifactory bearer token
595          * (https://www.jfrog.com/confluence/display/RTF/Access+Tokens). It can
596          * be generated using:
597          * <pre>curl -u yourUserName -X POST \
598          *    "https://artifactory.techno.ingenico.com/artifactory/api/security/token" \
599          *    -d "username=yourUserName"</pre>.
600          */
601         public static final String ANALYZER_ARTIFACTORY_BEARER_TOKEN = "analyzer.artifactory.bearer.token";
602         /**
603          * The properties key for using the proxy to reach Artifactory.
604          */
605         public static final String ANALYZER_ARTIFACTORY_USES_PROXY = "analyzer.artifactory.proxy";
606         /**
607          * The properties key for whether the Artifactory analyzer should use
608          * parallel processing.
609          */
610         public static final String ANALYZER_ARTIFACTORY_PARALLEL_ANALYSIS = "analyzer.artifactory.parallel.analysis";
611         /**
612          * The properties key for whether the Central analyzer is enabled.
613          */
614         public static final String ANALYZER_CENTRAL_ENABLED = "analyzer.central.enabled";
615         /**
616          * Key for the path to the local Maven repository.
617          */
618         public static final String MAVEN_LOCAL_REPO = "odc.maven.local.repo";
619         /**
620          * Key for the URL to obtain content from Maven Central.
621          */
622         public static final String CENTRAL_CONTENT_URL = "central.content.url";
623         /**
624          * Key for the Username to obtain content from Maven Central.
625          */
626         public static final String CENTRAL_CONTENT_USER = "central.content.username";
627         /**
628          * Key for the Password to obtain content from Maven Central.
629          */
630         public static final String CENTRAL_CONTENT_PASSWORD = "central.content.password";
631         /**
632          * The properties key for whether the Central analyzer should use
633          * parallel processing.
634          */
635         public static final String ANALYZER_CENTRAL_PARALLEL_ANALYSIS = "analyzer.central.parallel.analysis";
636         /**
637          * The properties key for whether the Central analyzer should use
638          * parallel processing.
639          */
640         public static final String ANALYZER_CENTRAL_RETRY_COUNT = "analyzer.central.retry.count";
641         /**
642          * The properties key for whether the OpenSSL analyzer is enabled.
643          */
644         public static final String ANALYZER_OPENSSL_ENABLED = "analyzer.openssl.enabled";
645         /**
646          * The properties key for whether the cocoapods analyzer is enabled.
647          */
648         public static final String ANALYZER_COCOAPODS_ENABLED = "analyzer.cocoapods.enabled";
649         /**
650          * The properties key for whether the carthage analyzer is enabled.
651          */
652         public static final String ANALYZER_CARTHAGE_ENABLED = "analyzer.carthage.enabled";
653         /**
654          * The properties key for whether the SWIFT package manager analyzer is
655          * enabled.
656          */
657         public static final String ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED = "analyzer.swift.package.manager.enabled";
658         /**
659          * The properties key for whether the SWIFT package resolved analyzer is
660          * enabled.
661          */
662         public static final String ANALYZER_SWIFT_PACKAGE_RESOLVED_ENABLED = "analyzer.swift.package.resolved.enabled";
663         /**
664          * The properties key for the Central search URL.
665          */
666         public static final String ANALYZER_CENTRAL_URL = "analyzer.central.url";
667         /**
668          * The properties key for the Central search username.
669          */
670         public static final String ANALYZER_CENTRAL_USER = "analyzer.central.username";
671         /**
672          * The properties key for the Central search password.
673          */
674         public static final String ANALYZER_CENTRAL_PASSWORD = "analyzer.central.password";
675         /**
676          * The properties key for the Central search query.
677          */
678         public static final String ANALYZER_CENTRAL_QUERY = "analyzer.central.query";
679         /**
680          * The properties key for whether Central search results will be cached.
681          */
682         public static final String ANALYZER_CENTRAL_USE_CACHE = "analyzer.central.use.cache";
683         /**
684          * The path to dotnet core, if available.
685          */
686         public static final String ANALYZER_ASSEMBLY_DOTNET_PATH = "analyzer.assembly.dotnet.path";
687         /**
688          * The path to bundle-audit, if available.
689          */
690         public static final String ANALYZER_BUNDLE_AUDIT_PATH = "analyzer.bundle.audit.path";
691         /**
692          * The path to bundle-audit, if available.
693          */
694         public static final String ANALYZER_BUNDLE_AUDIT_WORKING_DIRECTORY = "analyzer.bundle.audit.working.directory";
695         /**
696          * The additional configured zip file extensions, if available.
697          */
698         public static final String ADDITIONAL_ZIP_EXTENSIONS = "extensions.zip";
699         /**
700          * The key to obtain the path to the VFEED data file.
701          */
702         public static final String VFEED_DATA_FILE = "vfeed.data_file";
703         /**
704          * The key to obtain the VFEED connection string.
705          */
706         public static final String VFEED_CONNECTION_STRING = "vfeed.connection_string";
707         /**
708          * The key to obtain the base download URL for the VFeed data file.
709          */
710         public static final String VFEED_DOWNLOAD_URL = "vfeed.download_url";
711         /**
712          * The key to obtain the download file name for the VFeed data.
713          */
714         public static final String VFEED_DOWNLOAD_FILE = "vfeed.download_file";
715         /**
716          * The key to obtain the VFeed update status.
717          */
718         public static final String VFEED_UPDATE_STATUS = "vfeed.update_status";
719         /**
720          * The key to the HTTP request method for query last modified date.
721          */
722         public static final String DOWNLOADER_QUICK_QUERY_TIMESTAMP = "downloader.quick.query.timestamp";
723         /**
724          * The key to HTTP protocol list to use.
725          */
726         public static final String DOWNLOADER_TLS_PROTOCOL_LIST = "downloader.tls.protocols";
727         /**
728          * The key to determine if the CPE analyzer is enabled.
729          */
730         public static final String ANALYZER_CPE_ENABLED = "analyzer.cpe.enabled";
731         /**
732          * The key to determine if the NPM CPE analyzer is enabled.
733          */
734         public static final String ANALYZER_NPM_CPE_ENABLED = "analyzer.npm.cpe.enabled";
735         /**
736          * The key to determine if the CPE Suppression analyzer is enabled.
737          */
738         public static final String ANALYZER_CPE_SUPPRESSION_ENABLED = "analyzer.cpesuppression.enabled";
739         /**
740          * The key to determine if the Dependency Bundling analyzer is enabled.
741          */
742         public static final String ANALYZER_DEPENDENCY_BUNDLING_ENABLED = "analyzer.dependencybundling.enabled";
743         /**
744          * The key to determine if the Dependency Merging analyzer is enabled.
745          */
746         public static final String ANALYZER_DEPENDENCY_MERGING_ENABLED = "analyzer.dependencymerging.enabled";
747         /**
748          * The key to determine if the False Positive analyzer is enabled.
749          */
750         public static final String ANALYZER_FALSE_POSITIVE_ENABLED = "analyzer.falsepositive.enabled";
751         /**
752          * The key to determine if the File Name analyzer is enabled.
753          */
754         public static final String ANALYZER_FILE_NAME_ENABLED = "analyzer.filename.enabled";
755         /**
756          * The key to determine if the File Version analyzer is enabled.
757          */
758         public static final String ANALYZER_PE_ENABLED = "analyzer.pe.enabled";
759         /**
760          * The key to determine if the Hint analyzer is enabled.
761          */
762         public static final String ANALYZER_HINT_ENABLED = "analyzer.hint.enabled";
763         /**
764          * The key to determine if the Version Filter analyzer is enabled.
765          */
766         public static final String ANALYZER_VERSION_FILTER_ENABLED = "analyzer.versionfilter.enabled";
767         /**
768          * The key to determine if the Vulnerability Suppression analyzer is
769          * enabled.
770          */
771         public static final String ANALYZER_VULNERABILITY_SUPPRESSION_ENABLED = "analyzer.vulnerabilitysuppression.enabled";
772         /**
773          * The key to determine if the NVD CVE updater should be enabled.
774          */
775         public static final String UPDATE_NVDCVE_ENABLED = "updater.nvdcve.enabled";
776         /**
777          * The key to determine if dependency-check should check if there is a
778          * new version available.
779          */
780         public static final String UPDATE_VERSION_CHECK_ENABLED = "updater.versioncheck.enabled";
781         /**
782          * The key to determine which ecosystems should skip the CPE analysis.
783          */
784         public static final String ECOSYSTEM_SKIP_CPEANALYZER = "ecosystem.skip.cpeanalyzer";
785         /**
786          * Adds capabilities to batch insert. Tested on PostgreSQL and H2.
787          */
788         public static final String ENABLE_BATCH_UPDATES = "database.batchinsert.enabled";
789         /**
790          * Size of database batch inserts.
791          */
792         public static final String MAX_BATCH_SIZE = "database.batchinsert.maxsize";
793         /**
794          * The key that specifies the class name of the Write Lock shutdown
795          * hook.
796          */
797         public static final String WRITELOCK_SHUTDOWN_HOOK = "data.writelock.shutdownhook";
798         /**
799          * The properties key for whether the Sonatype OSS Index analyzer is
800          * enabled.
801          */
802         public static final String ANALYZER_OSSINDEX_ENABLED = "analyzer.ossindex.enabled";
803         /**
804          * The properties key for whether the Sonatype OSS Index should use a
805          * local cache.
806          */
807         public static final String ANALYZER_OSSINDEX_USE_CACHE = "analyzer.ossindex.use.cache";
808         /**
809          * The properties key for the Sonatype OSS Index URL.
810          */
811         public static final String ANALYZER_OSSINDEX_URL = "analyzer.ossindex.url";
812         /**
813          * The properties key for the Sonatype OSS Index user.
814          */
815         public static final String ANALYZER_OSSINDEX_USER = "analyzer.ossindex.user";
816         /**
817          * The properties key for the Sonatype OSS Index password.
818          */
819         public static final String ANALYZER_OSSINDEX_PASSWORD = "analyzer.ossindex.password";
820         /**
821          * The properties key for the Sonatype OSS batch-size.
822          */
823         public static final String ANALYZER_OSSINDEX_BATCH_SIZE = "analyzer.ossindex.batch.size";
824         /**
825          * The properties key for the Sonatype OSS Request Delay. Amount of time
826          * in seconds to wait before executing a request against the Sonatype
827          * OSS Rest API
828          */
829         public static final String ANALYZER_OSSINDEX_REQUEST_DELAY = "analyzer.ossindex.request.delay";
830         /**
831          * The properties key for only warning about Sonatype OSS Index remote
832          * errors instead of failing the request.
833          */
834         public static final String ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS = "analyzer.ossindex.remote-error.warn-only";
835         /**
836          * The properties key setting whether or not the JSON and XML reports
837          * will be pretty printed.
838          */
839 
840         /**
841          * The properties key for whether the Dart analyzer is enabled.
842          */
843         public static final String ANALYZER_DART_ENABLED = "analyzer.dart.enabled";
844 
845         /**
846          * The properties key for whether to pretty print the XML/JSON reports.
847          */
848         public static final String PRETTY_PRINT = "odc.reports.pretty.print";
849         /**
850          * The properties key setting which other keys should be considered
851          * sensitive and subsequently masked when logged.
852          */
853         public static final String MASKED_PROPERTIES = "odc.settings.mask";
854         /**
855          * The properties key for the default max query size for Lucene query
856          * results.
857          */
858         public static final String MAX_QUERY_SIZE_DEFAULT = "odc.ecosystem.maxquerylimit.default";
859         /**
860          * The properties key prefix for the default max query size for Lucene
861          * query results; append the ecosystem to obtain the default query size.
862          */
863         public static final String MAX_QUERY_SIZE_PREFIX = "odc.ecosystem.maxquerylimit.";
864 
865         /**
866          * private constructor because this is a "utility" class containing
867          * constants
868          */
869         private KEYS() {
870             //do nothing
871         }
872     }
873     //</editor-fold>
874 
875     /**
876      * Initialize the settings object.
877      */
878     public Settings() {
879         initialize(PROPERTIES_FILE);
880     }
881 
882     /**
883      * Initialize the settings object using the given properties.
884      *
885      * @param properties the properties to be used with this Settings instance
886      * @since 4.0.3
887      */
888     public Settings(final Properties properties) {
889         props = properties;
890         logProperties("Properties loaded", props);
891     }
892 
893     /**
894      * Initialize the settings object using the given properties file.
895      *
896      * @param propertiesFilePath the path to the base properties file to load
897      */
898     public Settings(@NotNull final String propertiesFilePath) {
899         initialize(propertiesFilePath);
900     }
901 
902     /**
903      * Initializes the settings object from the given file.
904      *
905      * @param propertiesFilePath the path to the settings property file
906      */
907     private void initialize(@NotNull final String propertiesFilePath) {
908         props = new Properties();
909         try (InputStream in = FileUtils.getResourceAsStream(propertiesFilePath)) {
910             props.load(in);
911         } catch (NullPointerException ex) {
912             LOGGER.error("Did not find settings file '{}'.", propertiesFilePath);
913             LOGGER.debug("", ex);
914         } catch (IOException ex) {
915             LOGGER.error("Unable to load settings from '{}'.", propertiesFilePath);
916             LOGGER.debug("", ex);
917         }
918         logProperties("Properties loaded", props);
919     }
920 
921     /**
922      * Cleans up resources to prevent memory leaks.
923      */
924     public void cleanup() {
925         cleanup(true);
926     }
927 
928     /**
929      * Cleans up resources to prevent memory leaks.
930      *
931      * @param deleteTemporary flag indicating whether any temporary directories
932      * generated should be removed
933      */
934     public synchronized void cleanup(boolean deleteTemporary) {
935         if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) {
936             LOGGER.debug("Deleting ALL temporary files from `{}`", tempDirectory.toString());
937             FileUtils.delete(tempDirectory);
938             tempDirectory = null;
939         }
940     }
941 
942     /**
943      * Check if a given key is considered to have a value with sensitive data.
944      *
945      * @param key the key to determine if the property should be masked
946      * @return <code>true</code> if the key is for a sensitive property value;
947      * otherwise <code>false</code>
948      */
949     private boolean isKeyMasked(@NotNull String key) {
950         if (maskedKeys == null || maskedKeys.isEmpty()) {
951             initMaskedKeys();
952         }
953         return maskedKeys.stream().anyMatch(maskExp -> maskExp.test(key));
954     }
955 
956     /**
957      * Obtains the printable/loggable value for a given key/value pair. This
958      * will mask some values so as to not leak sensitive information.
959      *
960      * @param key the property key
961      * @param value the property value
962      * @return the printable value
963      */
964     String getPrintableValue(@NotNull String key, String value) {
965         String printableValue = null;
966         if (value != null) {
967             printableValue = isKeyMasked(key) ? "********" : value;
968         }
969         return printableValue;
970     }
971 
972     /**
973      * Initializes the masked keys collection. This is done outside of the
974      * {@link #initialize(java.lang.String)} method because a caller may use the
975      * {@link #mergeProperties(java.io.File)} to add additional properties after
976      * the call to initialize.
977      */
978     void initMaskedKeys() {
979         final String[] masked = getArray(Settings.KEYS.MASKED_PROPERTIES);
980         if (masked == null) {
981             maskedKeys = new ArrayList<>();
982         } else {
983             maskedKeys = Arrays.stream(masked)
984                     .map(v -> Pattern.compile(v).asPredicate())
985                     .collect(Collectors.toList());
986         }
987     }
988 
989     /**
990      * Logs the properties. This will not log any properties that contain
991      * 'password' in the key.
992      *
993      * @param header the header to print with the log message
994      * @param properties the properties to log
995      */
996     private void logProperties(@NotNull final String header, @NotNull final Properties properties) {
997         if (LOGGER.isDebugEnabled()) {
998             initMaskedKeys();
999             final StringWriter sw = new StringWriter();
1000             try (PrintWriter pw = new PrintWriter(sw)) {
1001                 pw.format("%s:%n%n", header);
1002                 final Enumeration<?> e = properties.propertyNames();
1003                 while (e.hasMoreElements()) {
1004                     final String key = (String) e.nextElement();
1005                     final String value = getPrintableValue(key, properties.getProperty(key));
1006                     if (value != null) {
1007                         pw.format("%s='%s'%n", key, value);
1008                     }
1009                 }
1010                 pw.flush();
1011                 LOGGER.debug(sw.toString());
1012             }
1013         }
1014     }
1015 
1016     /**
1017      * Sets a property value.
1018      *
1019      * @param key the key for the property
1020      * @param value the value for the property
1021      */
1022     public void setString(@NotNull final String key, @NotNull final String value) {
1023         props.setProperty(key, value);
1024         LOGGER.debug("Setting: {}='{}'", key, getPrintableValue(key, value));
1025     }
1026 
1027     /**
1028      * Sets a property value only if the value is not null.
1029      *
1030      * @param key the key for the property
1031      * @param value the value for the property
1032      */
1033     public void setStringIfNotNull(@NotNull final String key, @Nullable final String value) {
1034         if (null != value) {
1035             setString(key, value);
1036         }
1037     }
1038 
1039     /**
1040      * Sets a property value only if the value is not null and not empty.
1041      *
1042      * @param key the key for the property
1043      * @param value the value for the property
1044      */
1045     public void setStringIfNotEmpty(@NotNull final String key, @Nullable final String value) {
1046         if (null != value && !value.isEmpty()) {
1047             setString(key, value);
1048         }
1049     }
1050 
1051     /**
1052      * Sets a property value only if the array value is not null and not empty.
1053      *
1054      * @param key the key for the property
1055      * @param value the value for the property
1056      */
1057     public void setArrayIfNotEmpty(@NotNull final String key, @Nullable final String[] value) {
1058         if (null != value && value.length > 0) {
1059             try {
1060                 setString(key, objectMapper.writeValueAsString(value));
1061             } catch (JsonProcessingException e) {
1062                 throw new IllegalArgumentException();
1063             }
1064         }
1065     }
1066 
1067     /**
1068      * Sets a property value only if the array value is not null and not empty.
1069      *
1070      * @param key the key for the property
1071      * @param value the value for the property
1072      */
1073     public void setArrayIfNotEmpty(@NotNull final String key, @Nullable final List<String> value) {
1074         if (null != value && !value.isEmpty()) {
1075             try {
1076                 setString(key, objectMapper.writeValueAsString(value));
1077             } catch (JsonProcessingException e) {
1078                 throw new IllegalArgumentException();
1079             }
1080         }
1081     }
1082 
1083     /**
1084      * Sets a property value.
1085      *
1086      * @param key the key for the property
1087      * @param value the value for the property
1088      */
1089     public void setBoolean(@NotNull final String key, boolean value) {
1090         setString(key, Boolean.toString(value));
1091     }
1092 
1093     /**
1094      * Sets a property value only if the value is not null.
1095      *
1096      * @param key the key for the property
1097      * @param value the value for the property
1098      */
1099     public void setBooleanIfNotNull(@NotNull final String key, @Nullable final Boolean value) {
1100         if (null != value) {
1101             setBoolean(key, value);
1102         }
1103     }
1104 
1105     /**
1106      * Sets a float property value.
1107      *
1108      * @param key the key for the property
1109      * @param value the value for the property
1110      */
1111     public void setFloat(@NotNull final String key, final float value) {
1112         setString(key, Float.toString(value));
1113     }
1114 
1115     /**
1116      * Sets a property value.
1117      *
1118      * @param key the key for the property
1119      * @param value the value for the property
1120      */
1121     public void setInt(@NotNull final String key, final int value) {
1122         props.setProperty(key, String.valueOf(value));
1123         LOGGER.debug("Setting: {}='{}'", key, value);
1124     }
1125 
1126     /**
1127      * Sets a property value only if the value is not null.
1128      *
1129      * @param key the key for the property
1130      * @param value the value for the property
1131      */
1132     public void setIntIfNotNull(@NotNull final String key, @Nullable final Integer value) {
1133         if (null != value) {
1134             setInt(key, value);
1135         }
1136     }
1137 
1138     /**
1139      * Merges a new properties file into the current properties. This method
1140      * allows for the loading of a user provided properties file.<br><br>
1141      * <b>Note</b>: even if using this method - system properties will be loaded
1142      * before properties loaded from files.
1143      *
1144      * @param filePath the path to the properties file to merge.
1145      * @throws java.io.FileNotFoundException is thrown when the filePath points
1146      * to a non-existent file
1147      * @throws java.io.IOException is thrown when there is an exception
1148      * loading/merging the properties
1149      */
1150     @SuppressFBWarnings(justification = "try with resource will clenaup the resources", value = {"OBL_UNSATISFIED_OBLIGATION"})
1151     public void mergeProperties(@NotNull final File filePath) throws FileNotFoundException, IOException {
1152         try (FileInputStream fis = new FileInputStream(filePath)) {
1153             mergeProperties(fis);
1154         }
1155     }
1156 
1157     /**
1158      * Merges a new properties file into the current properties. This method
1159      * allows for the loading of a user provided properties file.<br><br>
1160      * Note: even if using this method - system properties will be loaded before
1161      * properties loaded from files.
1162      *
1163      * @param filePath the path to the properties file to merge.
1164      * @throws java.io.FileNotFoundException is thrown when the filePath points
1165      * to a non-existent file
1166      * @throws java.io.IOException is thrown when there is an exception
1167      * loading/merging the properties
1168      */
1169     @SuppressFBWarnings(justification = "try with resource will clenaup the resources", value = {"OBL_UNSATISFIED_OBLIGATION"})
1170     public void mergeProperties(@NotNull final String filePath) throws FileNotFoundException, IOException {
1171         try (FileInputStream fis = new FileInputStream(filePath)) {
1172             mergeProperties(fis);
1173         }
1174     }
1175 
1176     /**
1177      * Merges a new properties file into the current properties. This method
1178      * allows for the loading of a user provided properties file.<br><br>
1179      * <b>Note</b>: even if using this method - system properties will be loaded
1180      * before properties loaded from files.
1181      *
1182      * @param stream an Input Stream pointing at a properties file to merge
1183      * @throws java.io.IOException is thrown when there is an exception
1184      * loading/merging the properties
1185      */
1186     public void mergeProperties(@NotNull final InputStream stream) throws IOException {
1187         props.load(stream);
1188         logProperties("Properties updated via merge", props);
1189     }
1190 
1191     /**
1192      * Returns a value from the properties file as a File object. If the value
1193      * was specified as a system property or passed in via the -Dprop=value
1194      * argument - this method will return the value from the system properties
1195      * before the values in the contained configuration file.
1196      *
1197      * @param key the key to lookup within the properties file
1198      * @return the property from the properties file converted to a File object
1199      */
1200     @Nullable
1201     public File getFile(@NotNull final String key) {
1202         final String file = getString(key);
1203         if (file == null) {
1204             return null;
1205         }
1206         return new File(file);
1207     }
1208 
1209     /**
1210      * Returns a value from the properties file as a File object. If the value
1211      * was specified as a system property or passed in via the -Dprop=value
1212      * argument - this method will return the value from the system properties
1213      * before the values in the contained configuration file.
1214      * <p>
1215      * This method will check the configured base directory and will use this as
1216      * the base of the file path. Additionally, if the base directory begins
1217      * with a leading "[JAR]\" sequence with the path to the folder containing
1218      * the JAR file containing this class.
1219      *
1220      * @param key the key to lookup within the properties file
1221      * @return the property from the properties file converted to a File object
1222      */
1223     File getDataFile(@NotNull final String key) {
1224         final String file = getString(key);
1225         LOGGER.debug("Settings.getDataFile() - file: '{}'", file);
1226         if (file == null) {
1227             return null;
1228         }
1229         if (file.startsWith("[JAR]")) {
1230             LOGGER.debug("Settings.getDataFile() - transforming filename");
1231             final File jarPath = getJarPath();
1232             LOGGER.debug("Settings.getDataFile() - jar file: '{}'", jarPath.toString());
1233             final File retVal = new File(jarPath, file.substring(6));
1234             LOGGER.debug("Settings.getDataFile() - returning: '{}'", retVal);
1235             return retVal;
1236         }
1237         return new File(file);
1238     }
1239 
1240     /**
1241      * Attempts to retrieve the folder containing the Jar file containing the
1242      * Settings class.
1243      *
1244      * @return a File object
1245      */
1246     private File getJarPath() {
1247         String decodedPath = ".";
1248         String jarPath = "";
1249         final ProtectionDomain domain = Settings.class.getProtectionDomain();
1250         if (domain != null && domain.getCodeSource() != null && domain.getCodeSource().getLocation() != null) {
1251             jarPath = Settings.class.getProtectionDomain().getCodeSource().getLocation().getPath();
1252         }
1253         try {
1254             decodedPath = URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name());
1255         } catch (UnsupportedEncodingException ex) {
1256             LOGGER.trace("", ex);
1257         }
1258 
1259         final File path = new File(decodedPath);
1260         if (path.getName().toLowerCase().endsWith(".jar")) {
1261             return path.getParentFile();
1262         } else {
1263             return new File(".");
1264         }
1265     }
1266 
1267     /**
1268      * Returns a value from the properties file. If the value was specified as a
1269      * system property or passed in via the -Dprop=value argument - this method
1270      * will return the value from the system properties before the values in the
1271      * contained configuration file.
1272      *
1273      * @param key the key to lookup within the properties file
1274      * @param defaultValue the default value for the requested property
1275      * @return the property from the properties file
1276      */
1277     public String getString(@NotNull final String key, @Nullable final String defaultValue) {
1278         return System.getProperty(key, props.getProperty(key, defaultValue));
1279     }
1280 
1281     /**
1282      * Returns the temporary directory.
1283      *
1284      * @return the temporary directory
1285      * @throws java.io.IOException if any.
1286      */
1287     public synchronized File getTempDirectory() throws IOException {
1288         if (tempDirectory == null) {
1289             final File baseTemp = new File(getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")));
1290             tempDirectory = FileUtils.createTempDirectory(baseTemp);
1291         }
1292         return tempDirectory;
1293     }
1294 
1295     /**
1296      * Returns a value from the properties file. If the value was specified as a
1297      * system property or passed in via the -Dprop=value argument - this method
1298      * will return the value from the system properties before the values in the
1299      * contained configuration file.
1300      *
1301      * @param key the key to lookup within the properties file
1302      * @return the property from the properties file
1303      */
1304     public String getString(@NotNull final String key) {
1305         return System.getProperty(key, props.getProperty(key));
1306     }
1307 
1308     /**
1309      * Returns a list with the given key.
1310      * <p>
1311      * If the property is not set then {@code null} will be returned.
1312      *
1313      * @param key the key to get from this
1314      * {@link org.owasp.dependencycheck.utils.Settings}.
1315      * @return the list or {@code null} if the key wasn't present.
1316      */
1317     public String[] getArray(@NotNull final String key) {
1318         final String string = getString(key);
1319         if (string != null) {
1320             if (string.charAt(0) == '{' || string.charAt(0) == '[') {
1321                 try {
1322                     return objectMapper.readValue(string, String[].class);
1323                 } catch (JsonProcessingException e) {
1324                     throw new IllegalStateException("Unable to read value '" + string + "' as an array");
1325                 }
1326             } else {
1327                 return string.split(ARRAY_SEP);
1328             }
1329         }
1330         return null;
1331     }
1332 
1333     /**
1334      * Removes a property from the local properties collection. This is mainly
1335      * used in test cases.
1336      *
1337      * @param key the property key to remove
1338      */
1339     public void removeProperty(@NotNull final String key) {
1340         props.remove(key);
1341     }
1342 
1343     /**
1344      * Returns an int value from the properties file. If the value was specified
1345      * as a system property or passed in via the -Dprop=value argument - this
1346      * method will return the value from the system properties before the values
1347      * in the contained configuration file.
1348      *
1349      * @param key the key to lookup within the properties file
1350      * @return the property from the properties file
1351      * @throws org.owasp.dependencycheck.utils.InvalidSettingException is thrown
1352      * if there is an error retrieving the setting
1353      */
1354     public int getInt(@NotNull final String key) throws InvalidSettingException {
1355         try {
1356             return Integer.parseInt(getString(key));
1357         } catch (NumberFormatException ex) {
1358             throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
1359         }
1360     }
1361 
1362     /**
1363      * Returns an int value from the properties file. If the value was specified
1364      * as a system property or passed in via the -Dprop=value argument - this
1365      * method will return the value from the system properties before the values
1366      * in the contained configuration file.
1367      *
1368      * @param key the key to lookup within the properties file
1369      * @param defaultValue the default value to return
1370      * @return the property from the properties file or the defaultValue if the
1371      * property does not exist or cannot be converted to an integer
1372      */
1373     public int getInt(@NotNull final String key, int defaultValue) {
1374         int value;
1375         try {
1376             value = Integer.parseInt(getString(key));
1377         } catch (NumberFormatException ex) {
1378             if (!getString(key, "").isEmpty()) {
1379                 LOGGER.debug("Could not convert property '{}={}' to an int; using {} instead.",
1380                         key, getPrintableValue(key, getString(key)), defaultValue);
1381             }
1382             value = defaultValue;
1383         }
1384         return value;
1385     }
1386 
1387     /**
1388      * Returns a long value from the properties file. If the value was specified
1389      * as a system property or passed in via the -Dprop=value argument - this
1390      * method will return the value from the system properties before the values
1391      * in the contained configuration file.
1392      *
1393      * @param key the key to lookup within the properties file
1394      * @return the property from the properties file
1395      * @throws org.owasp.dependencycheck.utils.InvalidSettingException is thrown
1396      * if there is an error retrieving the setting
1397      */
1398     public long getLong(@NotNull final String key) throws InvalidSettingException {
1399         try {
1400             return Long.parseLong(getString(key));
1401         } catch (NumberFormatException ex) {
1402             throw new InvalidSettingException("Could not convert property '" + key + "' to a long.", ex);
1403         }
1404     }
1405 
1406     /**
1407      * Returns a boolean value from the properties file. If the value was
1408      * specified as a system property or passed in via the
1409      * <code>-Dprop=value</code> argument this method will return the value from
1410      * the system properties before the values in the contained configuration
1411      * file.
1412      *
1413      * @param key the key to lookup within the properties file
1414      * @return the property from the properties file
1415      * @throws org.owasp.dependencycheck.utils.InvalidSettingException is thrown
1416      * if there is an error retrieving the setting
1417      */
1418     public boolean getBoolean(@NotNull final String key) throws InvalidSettingException {
1419         return Boolean.parseBoolean(getString(key));
1420     }
1421 
1422     /**
1423      * Returns a boolean value from the properties file. If the value was
1424      * specified as a system property or passed in via the
1425      * <code>-Dprop=value</code> argument this method will return the value from
1426      * the system properties before the values in the contained configuration
1427      * file.
1428      *
1429      * @param key the key to lookup within the properties file
1430      * @param defaultValue the default value to return if the setting does not
1431      * exist
1432      * @return the property from the properties file
1433      */
1434     public boolean getBoolean(@NotNull final String key, boolean defaultValue) {
1435         return Boolean.parseBoolean(getString(key, Boolean.toString(defaultValue)));
1436     }
1437 
1438     /**
1439      * Returns a float value from the properties file. If the value was
1440      * specified as a system property or passed in via the
1441      * <code>-Dprop=value</code> argument this method will return the value from
1442      * the system properties before the values in the contained configuration
1443      * file.
1444      *
1445      * @param key the key to lookup within the properties file
1446      * @param defaultValue the default value to return if the setting does not
1447      * exist
1448      * @return the property from the properties file
1449      */
1450     public float getFloat(@NotNull final String key, float defaultValue) {
1451         float retValue = defaultValue;
1452         try {
1453             retValue = Float.parseFloat(getString(key));
1454         } catch (Throwable ex) {
1455             LOGGER.trace("ignore", ex);
1456         }
1457         return retValue;
1458     }
1459 
1460     /**
1461      * Returns a connection string from the configured properties. If the
1462      * connection string contains a %s, this method will determine the 'data'
1463      * directory and replace the %s with the path to the data directory. If the
1464      * data directory does not exist it will be created.
1465      *
1466      * @param connectionStringKey the property file key for the connection
1467      * string
1468      * @param dbFileNameKey the settings key for the db filename
1469      * @return the connection string
1470      * @throws IOException thrown the data directory cannot be created
1471      * @throws InvalidSettingException thrown if there is an invalid setting
1472      */
1473     public String getConnectionString(String connectionStringKey, String dbFileNameKey)
1474             throws IOException, InvalidSettingException {
1475         final String connStr = getString(connectionStringKey);
1476         if (connStr == null) {
1477             final String msg = String.format("Invalid properties file; %s is missing.", connectionStringKey);
1478             throw new InvalidSettingException(msg);
1479         }
1480         if (connStr.contains("%s")) {
1481             final File directory = getH2DataDirectory();
1482             LOGGER.debug("Data directory: {}", directory);
1483             String fileName = null;
1484             if (dbFileNameKey != null) {
1485                 fileName = getString(dbFileNameKey);
1486             }
1487             if (fileName == null) {
1488                 final String msg = String.format("Invalid properties file to get a file based connection string; '%s' must be defined.",
1489                         dbFileNameKey);
1490                 throw new InvalidSettingException(msg);
1491             }
1492             if (connStr.startsWith("jdbc:h2:file:") && fileName.endsWith(".mv.db")) {
1493                 fileName = fileName.substring(0, fileName.length() - 6);
1494             }
1495             // yes, for H2 this path won't actually exists - but this is sufficient to get the value needed
1496             final File dbFile = new File(directory, fileName);
1497             final String cString = String.format(connStr, dbFile.getCanonicalPath());
1498             LOGGER.debug("Connection String: '{}'", cString);
1499             return cString;
1500         }
1501         return connStr;
1502     }
1503 
1504     /**
1505      * Retrieves the primary data directory that is used for caching web
1506      * content.
1507      *
1508      * @return the data directory to store data files
1509      * @throws java.io.IOException is thrown if an java.io.IOException occurs of
1510      * course...
1511      */
1512     public File getDataDirectory() throws IOException {
1513         final File path = getDataFile(Settings.KEYS.DATA_DIRECTORY);
1514         if (path != null && (path.exists() || path.mkdirs())) {
1515             return path;
1516         }
1517         throw new IOException(String.format("Unable to create the data directory '%s'",
1518                 (path == null) ? "unknown" : path.getAbsolutePath()));
1519     }
1520 
1521     /**
1522      * Retrieves the H2 data directory - if the database has been moved to the
1523      * temp directory this method will return the temp directory.
1524      *
1525      * @return the data directory to store data files
1526      * @throws java.io.IOException is thrown if an java.io.IOException occurs of
1527      * course...
1528      */
1529     public File getH2DataDirectory() throws IOException {
1530         final String h2Test = getString(Settings.KEYS.H2_DATA_DIRECTORY);
1531         final File path;
1532         if (h2Test != null && !h2Test.isEmpty()) {
1533             path = getDataFile(Settings.KEYS.H2_DATA_DIRECTORY);
1534         } else {
1535             path = getDataFile(Settings.KEYS.DATA_DIRECTORY);
1536         }
1537         if (path != null && (path.exists() || path.mkdirs())) {
1538             return path;
1539         }
1540         throw new IOException(String.format("Unable to create the h2 data directory '%s'",
1541                 (path == null) ? "unknown" : path.getAbsolutePath()));
1542     }
1543 
1544     /**
1545      * Generates a new temporary file name that is guaranteed to be unique.
1546      *
1547      * @param prefix the prefix for the file name to generate
1548      * @param extension the extension of the generated file name
1549      * @return a temporary File
1550      * @throws java.io.IOException if any.
1551      */
1552     public File getTempFile(@NotNull final String prefix, @NotNull final String extension) throws IOException {
1553         final File dir = getTempDirectory();
1554         final String tempFileName = String.format("%s%s.%s", prefix, UUID.randomUUID(), extension);
1555         final File tempFile = new File(dir, tempFileName);
1556         if (tempFile.exists()) {
1557             return getTempFile(prefix, extension);
1558         }
1559         return tempFile;
1560     }
1561 }