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