From 140b90ab726c9622a6f68d35ff2ede28fc29efea Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Tue, 19 May 2026 18:31:05 +0800 Subject: [PATCH 1/9] fix: ensure working hostedSuppressions/RetireJS forceUpdate & correct cache validForHours checks (#8494) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .../dependencycheck/taskdefs/Update.java | 4 +- ant/src/site/markdown/config-update.md | 2 +- ant/src/site/markdown/configuration.md | 2 +- .../org/owasp/dependencycheck/CliParser.java | 2 +- cli/src/site/markdown/arguments.md | 2 +- .../analyzer/AbstractSuppressionAnalyzer.java | 209 ++++++-------- .../analyzer/LibmanAnalyzer.java | 4 +- .../analyzer/NodeAuditAnalyzer.java | 4 +- .../analyzer/NodePackageAnalyzer.java | 4 +- .../analyzer/PinnedMavenInstallAnalyzer.java | 8 +- .../dependencycheck/analyzer/PipAnalyzer.java | 4 +- .../analyzer/PipfileAnalyzer.java | 4 +- .../analyzer/PipfilelockAnalyzer.java | 4 +- .../analyzer/PnpmAuditAnalyzer.java | 4 +- .../analyzer/RetireJsAnalyzer.java | 57 ++-- .../analyzer/YarnAuditAnalyzer.java | 7 +- .../update/HostedSuppressionsDataSource.java | 129 +++++---- .../data/update/LocalDataSource.java | 49 +++- .../data/update/RetireJSDataSource.java | 104 ++++--- .../owasp/dependencycheck/BaseDBTestCase.java | 2 +- .../org/owasp/dependencycheck/BaseTest.java | 58 +++- .../AbstractSuppressionAnalyzerTest.java | 272 +++++++++++------- .../analyzer/RetireJsAnalyzerTest.java | 143 +++++++++ .../HostedSuppressionsDataSourceTest.java | 223 ++++++++++++++ .../data/update/RetireJSDataSourceTest.java | 233 +++++++++++++++ .../retirejs/jsrepository-invalid.json | 3 + .../test/resources/retirejs/jsrepository.json | 37 +++ .../test/resources/suppressions-invalid.xml | 7 + .../maven/BaseDependencyCheckMojo.java | 2 +- maven/src/site/markdown/configuration.md | 2 +- .../configuration-aggregate.md | 2 +- .../configuration-update.md | 2 +- .../dependency-check-gradle/configuration.md | 2 +- .../dependencycheck/utils/FileUtils.java | 24 +- .../dependencycheck/utils/FileUtilsTest.java | 21 ++ 35 files changed, 1218 insertions(+), 418 deletions(-) create mode 100644 core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerTest.java create mode 100644 core/src/test/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSourceTest.java create mode 100644 core/src/test/java/org/owasp/dependencycheck/data/update/RetireJSDataSourceTest.java create mode 100644 core/src/test/resources/retirejs/jsrepository-invalid.json create mode 100644 core/src/test/resources/retirejs/jsrepository.json create mode 100644 core/src/test/resources/suppressions-invalid.xml diff --git a/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java b/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java index 9736dfc277e..6019a193964 100644 --- a/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java +++ b/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java @@ -20,12 +20,12 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.ant.logging.AntTaskHolder; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; -import org.owasp.dependencycheck.ant.logging.AntTaskHolder; /** * An Ant task definition to execute dependency-check update. This will download @@ -203,7 +203,7 @@ public class Update extends Purge { */ private Boolean hostedSuppressionsForceUpdate; /** - * Whether the hosted suppressions file will be used. Defaults to true. + * Whether the hosted suppressions will be updated from the configured URL. Defaults to true. */ private Boolean hostedSuppressionsEnabled; /** diff --git a/ant/src/site/markdown/config-update.md b/ant/src/site/markdown/config-update.md index b310bc48788..aff1d4d2519 100644 --- a/ant/src/site/markdown/config-update.md +++ b/ant/src/site/markdown/config-update.md @@ -50,7 +50,7 @@ The following properties can be configured in the plugin. However, they are less | connectionString | The connection string used to connect to the database. See using a [database server](../data/database.html). |   | | databaseUser | The username used when connecting to the database. |   | | databasePassword | The password used when connecting to the database. |   | -| hostedSuppressionsEnabled | Whether the hosted suppression file will be used. | true | +| hostedSuppressionsEnabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressionsUrl | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressionsUser | The user for a Basic-auth-protected mirrored copy of the hosted suppressions file for internet-constrained environments |   | | hostedSuppressionsPassword | The password/token for a Basic-auth-protected mirrored copy of the hosted suppressions file for internet-constrained environments |   | diff --git a/ant/src/site/markdown/configuration.md b/ant/src/site/markdown/configuration.md index 0556f752ad6..874214f4ec0 100644 --- a/ant/src/site/markdown/configuration.md +++ b/ant/src/site/markdown/configuration.md @@ -166,7 +166,7 @@ The following properties can be configured in the plugin. However, they are less | connectionString | The connection string used to connect to the database. See using a [database server](../data/database.html). |   | | databaseUser | The username used when connecting to the database. |   | | databasePassword | The password used when connecting to the database. |   | -| hostedSuppressionsEnabled | Whether the hosted suppression file will be used. | true | +| hostedSuppressionsEnabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressionsUrl | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressionsUser | The user for a Basic-auth-protected mirrored copy of the hosted suppressions file for internet-constrained environments |   | | hostedSuppressionsPassword | The password/token for a Basic-auth-protected mirrored copy of the hosted suppressions file for internet-constrained environments |   | diff --git a/cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 2db9e05aa88..ce69d34f68e 100644 --- a/cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -547,7 +547,7 @@ private void addAdvancedOptions(final Options options) { .addOption(newOption(ARGUMENT.ENABLE_NEXUS, "Enable the Nexus Analyzer.")) .addOption(newOption(ARGUMENT.ARTIFACTORY_ENABLED, "Whether the Artifactory Analyzer should be enabled.")) .addOption(newOption(ARGUMENT.PURGE_NVD, "Purges the local NVD data cache")) - .addOption(newOption(ARGUMENT.DISABLE_HOSTED_SUPPRESSIONS, "Disable the usage of the hosted suppressions file")) + .addOption(newOption(ARGUMENT.DISABLE_HOSTED_SUPPRESSIONS, "Disable retrieval of the hosted suppressions from the configured URL.")) .addOption(newOption(ARGUMENT.HOSTED_SUPPRESSIONS_FORCEUPDATE, "Force the hosted suppressions file to update even" + " if autoupdate is disabled")) .addOption(newOptionWithArg(ARGUMENT.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, "hours", diff --git a/cli/src/site/markdown/arguments.md b/cli/src/site/markdown/arguments.md index 74340343e3d..e081b4109d5 100644 --- a/cli/src/site/markdown/arguments.md +++ b/cli/src/site/markdown/arguments.md @@ -134,7 +134,7 @@ Advanced Options | | \-\-dbUser | \ | The username used to connect to the database. |   | | \-d | \-\-data | \ | The location of the data directory used to store persistent data. | /usr/local/var/dependencycheck if installed through brew (→ [formula](https://github.com/Homebrew/homebrew-core/blob/master/Formula/d/dependency-check.rb#L29)). Otherwise, the data directory is created inside the install directory i.e. as a sibling to the `/bin`, `/lib` directories. | | | \-\-purge | | Delete the local copy of the NVD. This is used to force a refresh of the data. |   | -| | \-\-disableHostedSuppressions | | Whether the usage of the hosted suppressions file will be disabled. | false | +| | \-\-disableHostedSuppressions | | Disable retrieval of the hosted suppressions from the configured URL. | false | | | \-\-hostedSuppressionsForceUpdate | | Whether the hosted suppressions file will update regardless of the `noupdate` argument. | false | | | \-\-hostedSuppressionsValidForHours | \ | The number of hours to wait before checking for new updates of the hosted suppressions file | 2 | | | \-\-hostedSuppressionsUrl | \ | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java index a8635751162..a9069c9eb27 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java @@ -17,41 +17,45 @@ */ package org.owasp.dependencycheck.analyzer; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; -import javax.annotation.concurrent.ThreadSafe; - +import com.google.common.annotations.VisibleForTesting; import org.jspecify.annotations.NonNull; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.update.HostedSuppressionsDataSource; +import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.exception.WriteLockException; -import org.owasp.dependencycheck.utils.WriteLock; -import org.owasp.dependencycheck.xml.suppression.SuppressionParseException; -import org.owasp.dependencycheck.xml.suppression.SuppressionParser; -import org.owasp.dependencycheck.xml.suppression.SuppressionRule; import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.ResourceNotFoundException; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.TooManyRequestsException; +import org.owasp.dependencycheck.utils.WriteLock; +import org.owasp.dependencycheck.xml.suppression.SuppressionParseException; +import org.owasp.dependencycheck.xml.suppression.SuppressionParser; +import org.owasp.dependencycheck.xml.suppression.SuppressionRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; +import javax.annotation.concurrent.ThreadSafe; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.owasp.dependencycheck.data.update.HostedSuppressionsDataSource.falsePositivesDueTo; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Abstract base suppression analyzer that contains methods for parsing the * suppression XML file. @@ -78,16 +82,6 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { */ public static final String SUPPRESSION_OBJECT_KEY = "suppression.rules"; - /** - * Returns a list of file EXTENSIONS supported by this analyzer. - * - * @return a list of file EXTENSIONS supported by this analyzer. - */ - @SuppressWarnings("SameReturnValue") - public Set getSupportedExtensions() { - return null; - } - /** * The prepare method loads the suppression XML file. * @@ -102,13 +96,13 @@ public synchronized void prepareAnalyzer(Engine engine) throws InitializationExc try { loadSuppressionBaseData(engine); } catch (SuppressionParseException ex) { - throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex, true); + throw new InitializationException("Error initializing the suppression analyzer base data: " + ex, ex, true); } try { - loadSuppressionData(engine); + loadSuppressionUserData(engine); } catch (SuppressionParseException ex) { - throw new InitializationException("Warn initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex, false); + throw new InitializationException("Warn initializing the suppression analyzer user data: " + ex, ex, false); } } @@ -145,12 +139,12 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An * @param engine a reference to the ODC engine. * @throws SuppressionParseException thrown if the XML cannot be parsed. */ - private void loadSuppressionData(Engine engine) throws SuppressionParseException { - final List ruleList = new ArrayList<>(); + private void loadSuppressionUserData(Engine engine) throws SuppressionParseException { final SuppressionParser parser = new SuppressionParser(); final String[] suppressionFilePaths = getSettings().getArray(Settings.KEYS.SUPPRESSION_FILE); final List failedLoadingFiles = new ArrayList<>(); if (suppressionFilePaths != null && suppressionFilePaths.length > 0) { + final List ruleList = new ArrayList<>(); // Load all the suppression file paths for (final String suppressionFilePath : suppressionFilePaths) { try { @@ -160,20 +154,12 @@ private void loadSuppressionData(Engine engine) throws SuppressionParseException failedLoadingFiles.add(msg); } } + LOGGER.debug("{} user suppression rules were loaded from {} sources.", ruleList.size(), suppressionFilePaths.length - failedLoadingFiles.size()); + appendRules(engine, ruleList); } - LOGGER.debug("{} suppression rules were loaded.", ruleList.size()); - if (!ruleList.isEmpty()) { - if (engine.hasObject(SUPPRESSION_OBJECT_KEY)) { - @SuppressWarnings("unchecked") - final List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); - rules.addAll(ruleList); - } else { - engine.putObject(SUPPRESSION_OBJECT_KEY, ruleList); - } - } if (!failedLoadingFiles.isEmpty()) { - LOGGER.debug("{} suppression files failed to load.", failedLoadingFiles.size()); + LOGGER.debug("{} user suppression files failed to load.", failedLoadingFiles.size()); final StringBuilder sb = new StringBuilder(); failedLoadingFiles.forEach(sb::append); throw new SuppressionParseException(sb.toString()); @@ -187,35 +173,27 @@ private void loadSuppressionData(Engine engine) throws SuppressionParseException * @throws SuppressionParseException thrown if the XML cannot be parsed. */ private void loadSuppressionBaseData(final Engine engine) throws SuppressionParseException { - final SuppressionParser parser = new SuppressionParser(); - loadPackagedSuppressionBaseData(parser, engine); - loadHostedSuppressionBaseData(parser, engine); + loadPackagedBaseSuppressionData(engine); + loadHostedSuppressionBaseData(engine); } /** * Loads the suppression rules packaged with the application. * - * @param parser The suppression parser to use * @param engine a reference the dependency-check engine * @throws SuppressionParseException thrown if the XML cannot be parsed. */ - private void loadPackagedSuppressionBaseData(final SuppressionParser parser, final Engine engine) throws SuppressionParseException { - List ruleList = null; + @VisibleForTesting + void loadPackagedBaseSuppressionData(final Engine engine) throws SuppressionParseException { + List ruleList; URL baseSuppressionURL = getPackagedFile(BASE_SUPPRESSION_FILE); try (InputStream in = baseSuppressionURL.openStream()) { - ruleList = parser.parseSuppressionRules(in); + ruleList = new SuppressionParser().parseSuppressionRules(in); + LOGGER.debug("{} base suppression rules were loaded.", ruleList.size()); + appendRules(engine, ruleList); } catch (SAXException | IOException ex) { throw new SuppressionParseException("Unable to parse the base suppression data file", ex); } - if (ruleList != null && !ruleList.isEmpty()) { - if (engine.hasObject(SUPPRESSION_OBJECT_KEY)) { - @SuppressWarnings("unchecked") - final List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); - rules.addAll(ruleList); - } else { - engine.putObject(SUPPRESSION_OBJECT_KEY, ruleList); - } - } } private static @NonNull URL getPackagedFile(String packagedFileName) throws SuppressionParseException { @@ -230,13 +208,11 @@ private void loadPackagedSuppressionBaseData(final SuppressionParser parser, fin } else { suppressionFileLocation = "file:" + suppressionFileLocation + packagedFileName; } - URL baseSuppressionURL = null; try { - baseSuppressionURL = new URL(suppressionFileLocation); + return new URL(suppressionFileLocation); } catch (MalformedURLException e) { throw new SuppressionParseException("Unable to load the packaged file: " + packagedFileName, e); } - return baseSuppressionURL; } /** @@ -249,62 +225,62 @@ private void loadPackagedSuppressionBaseData(final SuppressionParser parser, fin * already been resolved by the dependency-check project. * * @param engine a reference the dependency-check engine - * @param parser The suppression parser to use */ - private void loadHostedSuppressionBaseData(final SuppressionParser parser, final Engine engine) { - final boolean enabled = getSettings().getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); - if (!enabled) { - return; - } - + @VisibleForTesting + void loadHostedSuppressionBaseData(final Engine engine) { try { - final String configuredUrl = getSettings().getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, - HostedSuppressionsDataSource.DEFAULT_SUPPRESSIONS_URL); - final URL url = new URL(configuredUrl); - final String fileName = new File(url.getPath()).getName(); - if (fileName.isBlank()) { - throw new IOException("Hosted Suppression URL must imply a filename"); - } - final File repoFile = new File(getSettings().getDataDirectory(), fileName); - boolean repoEmpty = !repoFile.isFile() || repoFile.length() <= 1L; - if (repoEmpty) { - // utilize the snapshot hosted suppression file + // Try remote update if enabled and stale or forced by user + File repoFile = tryRemoteHostedSuppressionsFetchIfConfigured(engine); + + // If still empty after update attempt; utilize the snapshot hosted suppression file + // + // Note that this local fallback will run regardless of whether hosted suppressions are "enabled" or the + // value of autoUpdate, forceupdate etc since this is an offline operation similar to regular "base" suppressions. + if (!existsWithContent(repoFile)) { + LOGGER.debug("Hosted suppressions not found locally; attempting fallback to store packaged snapshot from this Dependency-Check release at {}...", repoFile.toPath()); URL hostedSuppressionSnapshotURL = getPackagedFile(HOSTED_SUPPRESSION_SNAPSHOT_FILE); try (InputStream in = hostedSuppressionSnapshotURL.openStream()) { Files.copy(in, repoFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - repoEmpty = false; - LOGGER.debug("Copied hosted suppression snapshot file to {}", repoFile.toPath()); - } catch (IOException ex) { - LOGGER.warn("Unable to copy the hosted suppression snapshot file to {}, results may contain false positives " - + "already resolved by the DependencyCheck project", repoFile.toPath(), ex); } + LOGGER.info(falsePositivesDueTo("Hosted suppressions using snapshot as of this Dependency-Check release")); } - if (!repoEmpty) { - loadCachedHostedSuppressionsRules(parser, repoFile, engine); - } else { - LOGGER.warn("Empty Hosted Suppression file after update, results may contain false positives " - + "already resolved by the DependencyCheck project due to failed download of the hosted suppression file"); - } + + loadCachedHostedSuppressionsRules(repoFile, engine); + } catch (IOException | InitializationException ex) { - LOGGER.warn("Unable to load hosted suppressions", ex); + LOGGER.warn(falsePositivesDueTo("Unable to load hosted suppressions from either remote source or packaged snapshot"), ex); } } + /** + * If configured to do so, try fetching hosted suppressions from the configured remote source. + * @return The local cached repoFile the suppressions are to be loaded from. Note that on return this may still not be created. + * @throws IOException only if settings are invalid to handle hosted suppressions either remotely or locally + */ + private File tryRemoteHostedSuppressionsFetchIfConfigured(Engine engine) throws IOException { + HostedSuppressionsDataSource ds = new HostedSuppressionsDataSource(); + try { + ds.updateUnhandled(engine); + } catch (UpdateException ex) { + LOGGER.warn(falsePositivesDueTo("Failed to update hosted suppressions file from remote source"), ex); + } + return ds.validatedRepoFile(); + } + /** * Load the hosted suppression file from the web resource * - * @param parser The suppressionParser to use for loading * @param repoFile The cached web resource * @param engine a reference the dependency-check engine * * @throws InitializationException When errors occur trying to create a * defensive copy of the web resource before loading */ - private void loadCachedHostedSuppressionsRules(final SuppressionParser parser, final File repoFile, final Engine engine) + private void loadCachedHostedSuppressionsRules(final File repoFile, final Engine engine) throws InitializationException { // take a defensive copy to avoid a risk of corrupted file by a competing parallel new download. final Path defensiveCopy; - try (WriteLock lock = new WriteLock(getSettings(), true, repoFile.getName() + ".lock")) { + try (WriteLock ignored = new WriteLock(getSettings(), true, repoFile.getName() + ".lock")) { defensiveCopy = Files.createTempFile("dc-basesuppressions", ".xml"); LOGGER.debug("copying hosted suppressions file {} to {}", repoFile.toPath(), defensiveCopy); Files.copy(repoFile.toPath(), defensiveCopy, StandardCopyOption.REPLACE_EXISTING); @@ -314,19 +290,12 @@ private void loadCachedHostedSuppressionsRules(final SuppressionParser parser, f try (InputStream in = Files.newInputStream(defensiveCopy)) { final List ruleList; - ruleList = parser.parseSuppressionRules(in); - if (!ruleList.isEmpty()) { - if (engine.hasObject(SUPPRESSION_OBJECT_KEY)) { - @SuppressWarnings("unchecked") - final List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); - rules.addAll(ruleList); - } else { - engine.putObject(SUPPRESSION_OBJECT_KEY, ruleList); - } - } + ruleList = new SuppressionParser().parseSuppressionRules(in); + LOGGER.debug("{} hosted suppression rules were loaded.", ruleList.size()); + appendRules(engine, ruleList); + } catch (SAXException | IOException ex) { - LOGGER.warn("Unable to parse the hosted suppressions data file, results may contain false positives already resolved " - + "by the DependencyCheck project", ex); + LOGGER.warn(falsePositivesDueTo("Unable to parse the hosted suppressions data file at {}"), repoFile.getPath(), ex); } try { Files.delete(defensiveCopy); @@ -335,6 +304,18 @@ private void loadCachedHostedSuppressionsRules(final SuppressionParser parser, f } } + private void appendRules(Engine engine, List ruleList) { + if (!ruleList.isEmpty()) { + if (engine.hasObject(SUPPRESSION_OBJECT_KEY)) { + @SuppressWarnings("unchecked") + final List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); + rules.addAll(ruleList); + } else { + engine.putObject(SUPPRESSION_OBJECT_KEY, ruleList); + } + } + } + /** * Load a single suppression rules file from the path provided using the * parser provided. @@ -367,20 +348,20 @@ private List loadSuppressionFile(final SuppressionParser parser Downloader.getInstance().fetchFile(url, file, true, Settings.KEYS.SUPPRESSION_FILE_USER, Settings.KEYS.SUPPRESSION_FILE_PASSWORD, Settings.KEYS.SUPPRESSION_FILE_BEARER_TOKEN); } catch (TooManyRequestsException ex1) { - throw new SuppressionParseException("Unable to download supression file `" + file + throw new SuppressionParseException("Unable to download suppression file `" + file + "`; received 429 - too many requests", ex1); } catch (ResourceNotFoundException ex1) { - throw new SuppressionParseException("Unable to download supression file `" + file + throw new SuppressionParseException("Unable to download suppression file `" + file + "`; received 404 - resource not found", ex1); } catch (InterruptedException ex1) { Thread.currentThread().interrupt(); - throw new SuppressionParseException("Unable to download supression file `" + file + "`", ex1); + throw new SuppressionParseException("Unable to download suppression file `" + file + "`", ex1); } } catch (TooManyRequestsException ex) { - throw new SuppressionParseException("Unable to download supression file `" + file + throw new SuppressionParseException("Unable to download suppression file `" + file + "`; received 429 - too many requests", ex); } catch (ResourceNotFoundException ex) { - throw new SuppressionParseException("Unable to download supression file `" + file + "`; received 404 - resource not found", ex); + throw new SuppressionParseException("Unable to download suppression file `" + file + "`; received 404 - resource not found", ex); } } else { file = new File(suppressionFilePath); @@ -435,7 +416,7 @@ private List loadSuppressionFile(final SuppressionParser parser * SuppressionParseException */ private void throwSuppressionParseException(String message, Exception exception, String suppressionFilePath) throws SuppressionParseException { - LOGGER.warn(String.format(message + " '%s'", suppressionFilePath)); + LOGGER.warn("{} [{}]", message, suppressionFilePath); LOGGER.debug("", exception); throw new SuppressionParseException(message, exception); } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/LibmanAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/LibmanAnalyzer.java index 8013e704239..1c946194494 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/LibmanAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/LibmanAnalyzer.java @@ -47,6 +47,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Analyzer which parses a libman.json file to gather module information. * @@ -161,7 +163,7 @@ public void analyzeDependency(Dependency dependency, Engine engine) throws Analy final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0) { + if (!existsWithContent(dependencyFile)) { return; } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java index c6de926ed56..51307bbf5ac 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java @@ -45,6 +45,8 @@ import java.nio.file.Files; import java.util.List; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Used to analyze Node Package Manager (npm) package-lock.json and * npm-shrinkwrap.json files via NPM Audit API. @@ -135,7 +137,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An LOGGER.debug("Skipping {} because shrinkwrap lock file exists", dependency.getFilePath()); return; } - if (!packageLock.isFile() || packageLock.length() == 0 || !shouldProcess(packageLock)) { + if (!existsWithContent(packageLock) || !shouldProcess(packageLock)) { return; } final File packageJson = new File(packageLock.getParentFile(), "package.json"); diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java index 5b29f43e5e3..7c5da572d00 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java @@ -54,6 +54,8 @@ import java.util.Map; import java.util.Objects; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Used to analyze Node Package Manager (npm) package.json files, and collect * information that can be used to determine the associated CPE. @@ -223,7 +225,7 @@ private boolean noLockFileExists(File dependencyFile) { @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0 || !shouldProcess(dependencyFile)) { + if (!existsWithContent(dependencyFile) || !shouldProcess(dependencyFile)) { return; } if (isNodeAuditEnabled(engine) diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PinnedMavenInstallAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PinnedMavenInstallAnalyzer.java index 0be4239329f..cc156eba679 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PinnedMavenInstallAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PinnedMavenInstallAnalyzer.java @@ -25,8 +25,6 @@ import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; import com.github.packageurl.PackageURLBuilder; -import java.util.Map; -import java.util.stream.Collectors; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem; @@ -45,8 +43,12 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; /** * Used to analyze Maven pinned dependency files named {@code *install*.json}, a @@ -116,7 +118,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An LOGGER.debug("Checking file {}", dependency.getActualFilePath()); final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0) { + if (!existsWithContent(dependencyFile)) { return; } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipAnalyzer.java index e4ceb68bf25..bf7b88e8f7f 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipAnalyzer.java @@ -44,6 +44,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Used to analyze pip dependency files named requirements.txt. * @@ -131,7 +133,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An engine.removeDependency(dependency); } final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0) { + if (!existsWithContent(dependencyFile)) { return; } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfileAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfileAnalyzer.java index bb5c66fc75e..318bc003ebe 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfileAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfileAnalyzer.java @@ -43,6 +43,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Used to analyze dependencies defined in Pipfile. This analyzer works in * tandem with the `PipfilelockAnalyzer` - and both analyzers use the same key @@ -141,7 +143,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An } final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0) { + if (!existsWithContent(dependencyFile)) { return; } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfilelockAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfilelockAnalyzer.java index d2a3f8f4719..993c2b302d9 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfilelockAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PipfilelockAnalyzer.java @@ -50,6 +50,8 @@ import java.nio.file.Files; import java.util.Set; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Used to analyze dependencies defined in Pipfile.lock. This analyzer works in * tandem with the `PipfileAnalyzer` - and both analyzers use the same key to @@ -134,7 +136,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An engine.removeDependency(dependency); final File dependencyFile = dependency.getActualFile(); - if (!dependencyFile.isFile() || dependencyFile.length() == 0) { + if (!existsWithContent(dependencyFile)) { return; } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java index a9a88c78835..ac42f445006 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PnpmAuditAnalyzer.java @@ -47,6 +47,8 @@ import java.util.ArrayList; import java.util.List; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + @ThreadSafe public class PnpmAuditAnalyzer extends AbstractNpmAnalyzer { @@ -90,7 +92,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An engine.removeDependency(dependency); } final File packageLock = dependency.getActualFile(); - if (!packageLock.isFile() || packageLock.length() == 0 || !shouldProcess(packageLock)) { + if (!existsWithContent(packageLock) || !shouldProcess(packageLock)) { return; } final List advisories; diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzer.java index af9319a4c4d..d73fd3e7201 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzer.java @@ -20,10 +20,12 @@ import com.esotericsoftware.minlog.Log; import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURLBuilder; +import com.google.common.annotations.VisibleForTesting; import com.h3xstream.retirejs.repo.JsLibraryResult; import com.h3xstream.retirejs.repo.ScannerFacade; import com.h3xstream.retirejs.repo.VulnerabilitiesRepository; import com.h3xstream.retirejs.repo.VulnerabilitiesRepositoryLoader; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.validator.routines.UrlValidator; import org.json.JSONException; @@ -58,19 +60,18 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.io.IOUtils; - import static org.owasp.dependencycheck.analyzer.RetireJsLibrary.KnownIdentifierTypes.CVE; import static org.owasp.dependencycheck.analyzer.RetireJsLibrary.KnownIdentifierTypes.GITHUB_SECURITY_ADVISORY; import static org.owasp.dependencycheck.analyzer.RetireJsLibrary.KnownIdentifierTypes.SECONDARY_NAME_TYPES; @@ -193,40 +194,13 @@ protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationExcep // it aligns with other analyzers that don't log such information. Log.set(Log.LEVEL_WARN); - File repoFile = null; - boolean repoEmpty = false; - try { - final String configuredUrl = getSettings().getString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, RetireJSDataSource.DEFAULT_JS_URL); - final URL url = new URL(configuredUrl); - final File filepath = new File(url.getPath()); - repoFile = new File(getSettings().getDataDirectory(), filepath.getName()); - if (!repoFile.isFile() || repoFile.length() <= 1L) { - LOGGER.warn("Retire JS repository is empty or missing - attempting to force the update"); - repoEmpty = true; - getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, true); - } - } catch (IOException ex) { - this.setEnabled(false); - throw new InitializationException("Failed to initialize the RetireJS", ex); - } + File repoFile = tryRemoteFetchIfConfigured(engine); - final boolean autoupdate = getSettings().getBoolean(Settings.KEYS.AUTO_UPDATE, true); - final boolean forceupdate = getSettings().getBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); - if ((!autoupdate && forceupdate) || (autoupdate && repoEmpty)) { - final RetireJSDataSource ds = new RetireJSDataSource(); - try { - ds.update(engine); - } catch (UpdateException ex) { - throw new InitializationException("Unable to initialize the Retire JS repository", ex); - } - } - - //several users are reporting that the retire js repository is getting corrupted. try (WriteLock ignored = new WriteLock(getSettings(), true, repoFile.getName() + ".lock")) { final File temp = getSettings().getTempDirectory(); final File tempRepo = new File(temp, repoFile.getName()); - LOGGER.debug("copying retireJs repo {} to {}", repoFile.toPath(), tempRepo.toPath()); - Files.copy(repoFile.toPath(), tempRepo.toPath()); + LOGGER.debug("copying RetireJS repo {} to {}", repoFile.toPath(), tempRepo.toPath()); + Files.copy(repoFile.toPath(), tempRepo.toPath(), StandardCopyOption.REPLACE_EXISTING); repoFile = tempRepo; } catch (WriteLockException | IOException ex) { this.setEnabled(false); @@ -245,6 +219,17 @@ protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationExcep } } + private File tryRemoteFetchIfConfigured(Engine engine) throws InitializationException { + RetireJSDataSource ds = new RetireJSDataSource(); + try { + ds.update(engine); + return ds.validatedRepoFile(); + } catch (UpdateException ex) { + this.setEnabled(false); + throw new InitializationException("Failed to initialize the RetireJS repo", ex); + } + } + /** * Returns the name of the analyzer. * @@ -324,6 +309,12 @@ public void analyzeDependency(Dependency dependency, Engine engine) throws Analy protected void closeAnalyzer() throws Exception { Log.set(Log.LEVEL_INFO); } + + @SuppressWarnings("SameParameterValue") + @VisibleForTesting + OptionalInt knownLibraryCountFor(String fileName) { + return jsRepository == null ? OptionalInt.empty() : OptionalInt.of(jsRepository.findByFilename(fileName).size()); + } } class RetireJsLibrary { diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java index 8a17a1077b4..a8a9f8d94fe 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java @@ -41,12 +41,11 @@ import org.slf4j.LoggerFactory; import us.springett.parsers.cpe.exceptions.CpeValidationException; +import javax.annotation.concurrent.ThreadSafe; import jakarta.json.Json; import jakarta.json.JsonException; import jakarta.json.JsonObject; import jakarta.json.JsonReader; - -import javax.annotation.concurrent.ThreadSafe; import java.io.File; import java.io.FileFilter; import java.io.IOException; @@ -57,6 +56,8 @@ import java.util.List; import java.util.stream.Stream; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + @ThreadSafe public class YarnAuditAnalyzer extends AbstractNpmAnalyzer { @@ -233,7 +234,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An engine.removeDependency(dependency); } final File packageLock = dependency.getActualFile(); - if (!packageLock.isFile() || packageLock.length() == 0 || !shouldProcess(packageLock)) { + if (!existsWithContent(packageLock) || !shouldProcess(packageLock)) { return; } File dependencyDirectory = getDependencyDirectory(packageLock); diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java index 09d7627b7b2..70c9d94f8a9 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java @@ -17,10 +17,12 @@ */ package org.owasp.dependencycheck.data.update; +import org.jspecify.annotations.NonNull; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.exception.WriteLockException; import org.owasp.dependencycheck.utils.Downloader; +import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.ResourceNotFoundException; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.TooManyRequestsException; @@ -33,8 +35,13 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; +import java.time.Duration; public class HostedSuppressionsDataSource extends LocalDataSource { + /** + * The default URL to the Hosted Suppressions file. + */ + public static final String DEFAULT_SUPPRESSIONS_URL = "https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml"; /** * Static logger. @@ -45,81 +52,89 @@ public class HostedSuppressionsDataSource extends LocalDataSource { * The configured settings. */ private Settings settings; - /** - * The default URL to the Hosted Suppressions file. - */ - public static final String DEFAULT_SUPPRESSIONS_URL = "https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml"; /** - * Downloads the current Hosted suppressions file. + * Makes a best effort to download the current Hosted suppressions file if configured to do so. * * @param engine a reference to the ODC Engine * @return returns false as no updates are made to the database, just web * resources cached locally - * @throws UpdateException thrown if the update encountered fatal errors + * @throws UpdateException thrown only if the update encountered fatal configuration errors */ @Override public boolean update(Engine engine) throws UpdateException { - this.settings = engine.getSettings(); - final String configuredUrl = settings.getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, DEFAULT_SUPPRESSIONS_URL); - final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); - final boolean forceupdate = settings.getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, false); - final boolean cpeSuppressionEnabled = settings.getBoolean(Settings.KEYS.ANALYZER_CPE_SUPPRESSION_ENABLED, true); - final boolean vulnSuppressionEnabled = settings.getBoolean(Settings.KEYS.ANALYZER_VULNERABILITY_SUPPRESSION_ENABLED, true); - boolean enabled = settings.getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); - enabled = enabled && (cpeSuppressionEnabled || vulnSuppressionEnabled); try { - final URL url = new URL(configuredUrl); - final File filepath = new File(url.getPath()); - final File repoFile = new File(settings.getDataDirectory(), filepath.getName()); - final boolean proceed = enabled && (forceupdate || (autoupdate && shouldUpdate(repoFile))); - if (proceed) { - LOGGER.debug("Begin Hosted Suppressions file update"); - fetchHostedSuppressions(settings, url, repoFile); - saveLastUpdated(repoFile, System.currentTimeMillis() / 1000); - } + updateUnhandled(engine); } catch (UpdateException ex) { // only emit a warning, DependencyCheck will continue without taking the latest hosted suppressions into account. - LOGGER.warn("Failed to update hosted suppressions file, results may contain false positives already resolved by the " - + "DependencyCheck project", ex); - } catch (MalformedURLException ex) { - throw new UpdateException(String.format("Invalid URL for Hosted Suppressions file (%s)", configuredUrl), ex); + LOGGER.warn(falsePositivesDueTo("Failed to update hosted suppressions file from remote source"), ex); } catch (IOException ex) { - throw new UpdateException("Unable to get the data directory", ex); + // Unhandled IOExceptions are fatal configuration errors of a sort + throw new UpdateException("Unable to determine the local location to cache hosted suppressions", ex); } return false; } + public static @NonNull String falsePositivesDueTo(String reason) { + return reason + ", results may contain false positives already resolved by the DependencyCheck project"; + } + /** - * Determines if the we should update the Hosted Suppressions file. + * Updates the current Hosted Suppressions file if configured to do so; failing if it cannot be done * - * @param repo the Hosted Suppressions file. - * @return true if an update to the Hosted Suppressions file - * should be performed; otherwise false - * @throws NumberFormatException thrown if an invalid value is contained in - * the database properties + * @param engine a reference to the ODC Engine + * @throws IOException if there is an error determining the local location to cache hosted suppressions + * @throws UpdateException if the remote update failed for any reason */ - protected boolean shouldUpdate(File repo) throws NumberFormatException { - boolean proceed = true; - if (repo != null && repo.isFile()) { - final int validForHours = settings.getInt(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, 2); - final long lastUpdatedOn = getLastUpdated(repo); - final long now = System.currentTimeMillis(); - LOGGER.debug("Last updated: {}", lastUpdatedOn); - LOGGER.debug("Now: {}", now); - final long msValid = validForHours * 60L * 60L * 1000L; - proceed = (now - lastUpdatedOn) > msValid; - if (!proceed) { - LOGGER.info("Skipping Hosted Suppressions file update since last update was within {} hours.", validForHours); - } + public void updateUnhandled(Engine engine) throws IOException, UpdateException { + this.settings = engine.getSettings(); + final URL url = validatedUrl(); + final File repoFile = validatedRepoFileFrom(url); + + if (isEnabled() && shouldUpdateFromRemote(repoFile)) { + LOGGER.debug("Begin Hosted Suppressions file update from remote source"); + fetchHostedSuppressions(url, repoFile); + saveLastUpdated(repoFile); } - return proceed; + } + + private @NonNull URL validatedUrl() throws InvalidSettingException { + final String configuredUrl = settings.getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, DEFAULT_SUPPRESSIONS_URL); + try { + return new URL(configuredUrl); + } catch (MalformedURLException ex) { + throw new InvalidSettingException(String.format("Invalid URL for Hosted Suppressions file (%s)", configuredUrl), ex); + } + } + + public @NonNull File validatedRepoFile() throws IOException { + return validatedRepoFileFrom(validatedUrl()); + } + + private @NonNull File validatedRepoFileFrom(URL url) throws IOException { + String fileName = new File(url.getPath()).getName(); + if (fileName.isBlank()) { + throw new InvalidSettingException("Hosted Suppression URL must imply a filename; even if disabled."); + } + return new File(settings.getDataDirectory(), fileName); + } + + private boolean isEnabled() { + return settings.getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true) && ( + settings.getBoolean(Settings.KEYS.ANALYZER_CPE_SUPPRESSION_ENABLED, true) || + settings.getBoolean(Settings.KEYS.ANALYZER_VULNERABILITY_SUPPRESSION_ENABLED, true)); + } + + private boolean shouldUpdateFromRemote(File repoFile) { + boolean forceupdate = settings.getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, false); + boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); + Duration validFor = Duration.ofHours(settings.getInt(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, 2)); + return forceupdate || (autoupdate && isStale(repoFile, validFor)); } /** * Fetches the hosted suppressions file * - * @param settings a reference to the dependency-check settings * @param repoUrl the URL to the hosted suppressions file to use * @param repoFile the local file where the hosted suppressions file is to * be placed @@ -127,7 +142,7 @@ protected boolean shouldUpdate(File repo) throws NumberFormatException { * initialization */ @SuppressWarnings("try") - private void fetchHostedSuppressions(Settings settings, URL repoUrl, File repoFile) throws UpdateException { + private void fetchHostedSuppressions(URL repoUrl, File repoFile) throws UpdateException { try (WriteLock ignored = new WriteLock(settings, true, repoFile.getName() + ".lock")) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Hosted Suppressions URL: {}", repoUrl.toExternalForm()); @@ -144,17 +159,14 @@ public boolean purge(Engine engine) { this.settings = engine.getSettings(); boolean result = true; try { - final URL repoUrl = new URL(settings.getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, - DEFAULT_SUPPRESSIONS_URL)); - final String filename = new File(repoUrl.getPath()).getName(); - final File repo = new File(settings.getDataDirectory(), filename); + final File repo = validatedRepoFile(); if (repo.exists()) { - try (WriteLock ignored = new WriteLock(settings, true, filename + ".lock")) { + try (WriteLock ignored = new WriteLock(settings, true, repo.getName() + ".lock")) { result = deleteCachedFile(repo); } } } catch (WriteLockException | IOException ex) { - LOGGER.error("Unable to delete the Hosted suppression file - invalid configuration"); + LOGGER.error("Unable to delete the Hosted suppression file - invalid configuration: {}", ex.toString()); result = false; } return result; @@ -163,8 +175,9 @@ public boolean purge(Engine engine) { private boolean deleteCachedFile(final File repo) { boolean deleted = true; try { - Files.delete(repo.toPath()); - LOGGER.info("Hosted suppression file removed successfully"); + if (Files.deleteIfExists(repo.toPath())) { + LOGGER.info("Hosted suppression file removed successfully"); + } } catch (IOException ex) { LOGGER.error("Unable to delete '{}'; please delete the file manually", repo.getAbsolutePath(), ex); deleted = false; diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/LocalDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/LocalDataSource.java index 88d51840668..78c8a24dc83 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/LocalDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/LocalDataSource.java @@ -17,15 +17,21 @@ */ package org.owasp.dependencycheck.data.update; +import org.jspecify.annotations.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.time.Duration; +import java.time.Instant; import java.util.Properties; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; /** * @@ -39,16 +45,15 @@ public abstract class LocalDataSource implements CachedWebDataSource { private static final Logger LOGGER = LoggerFactory.getLogger(LocalDataSource.class); /** - * Saves the timestamp in a properties file next to the provided repo file + * Saves the timestamp in a properties file adjacent to the provided repo file * * @param repo the local file data source - * @param timestamp the epoch timestamp to store */ - protected void saveLastUpdated(File repo, long timestamp) { + protected void saveLastUpdated(@NonNull File repo) { final File timestampFile = new File(repo + ".properties"); try (OutputStream out = new FileOutputStream(timestampFile)) { final Properties prop = new Properties(); - prop.setProperty("LAST_UPDATED", String.valueOf(timestamp)); + prop.setProperty("LAST_UPDATED", String.valueOf(System.currentTimeMillis())); prop.store(out, null); } catch (IOException ex) { throw new RuntimeException(ex); @@ -60,24 +65,46 @@ protected void saveLastUpdated(File repo, long timestamp) { * next to the repo file). * * @param repo the local file data source - * @return the epoch timestamp of the last updated date/time + * @return the instant of the last updated date/time */ - protected long getLastUpdated(File repo) { + protected Instant getLastUpdated(@NonNull File repo) { long lastUpdatedOn = 0; final File timestampFile = new File(repo + ".properties"); if (timestampFile.isFile()) { try (InputStream is = new FileInputStream(timestampFile)) { final Properties props = new Properties(); props.load(is); - lastUpdatedOn = Integer.parseInt(props.getProperty("LAST_UPDATED", "0")); + lastUpdatedOn = Long.parseLong(props.getProperty("LAST_UPDATED", "0")); } catch (IOException | NumberFormatException ex) { LOGGER.debug("error reading timestamp file", ex); } if (lastUpdatedOn <= 0) { - //fall back on conversion from file last modified to storing in the db. + //fall back on conversion from file last modified lastUpdatedOn = repo.lastModified(); } } - return lastUpdatedOn; + return Instant.ofEpochMilli(lastUpdatedOn); + } + + /** + * Determines if we should update the local data source. + * + * @param repo the local file data source + * @param validFor the duration for which the local data source should be considered valid + * @return true if an update to the data source should be performed; otherwise false. + * If the repo does not exist, or is an empty file, it is considered stale. + */ + protected boolean isStale(@NonNull File repo, @NonNull Duration validFor) { + boolean stale = true; + if (existsWithContent(repo)) { + final Instant lastUpdatedOn = getLastUpdated(repo); + final Instant now = Instant.now(); + LOGGER.debug("{} last updated: {}, now: {}", getClass().getSimpleName(), lastUpdatedOn, now); + stale = lastUpdatedOn.plus(validFor).isBefore(now); + if (!stale) { + LOGGER.info("Should skip {} update since last update was within period {}.", getClass().getSimpleName(), validFor); + } + } + return stale; } } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java index 7dcbf994b0d..74280d08224 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java @@ -17,16 +17,12 @@ */ package org.owasp.dependencycheck.data.update; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import javax.annotation.concurrent.ThreadSafe; - +import org.jspecify.annotations.NonNull; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.exception.WriteLockException; import org.owasp.dependencycheck.utils.Downloader; +import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.ResourceNotFoundException; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.TooManyRequestsException; @@ -34,6 +30,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.concurrent.ThreadSafe; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; + +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; + /** * Downloads a local copy of the RetireJS repository. * @@ -41,23 +46,18 @@ */ @ThreadSafe public class RetireJSDataSource extends LocalDataSource { - /** - * Static logger. + * The default URL to the RetireJS JavaScript repository. */ - private static final Logger LOGGER = LoggerFactory.getLogger(RetireJSDataSource.class); + private static final String DEFAULT_JS_URL = "https://raw.githubusercontent.com/Retirejs/retire.js/master/repository/jsrepository.json"; /** - * The property key indicating when the last update occurred. + * Static logger. */ - public static final String RETIREJS_UPDATED_ON = "RetireJSUpdatedOn"; + private static final Logger LOGGER = LoggerFactory.getLogger(RetireJSDataSource.class); /** * The configured settings. */ private Settings settings; - /** - * The default URL to the RetireJS JavaScript repository. - */ - public static final String DEFAULT_JS_URL = "https://raw.githubusercontent.com/Retirejs/retire.js/master/repository/jsrepository.json"; /** * Constructs a new engine version check utility. @@ -75,54 +75,51 @@ public RetireJSDataSource() { @Override public boolean update(Engine engine) throws UpdateException { this.settings = engine.getSettings(); + final URL url = validatedUrl(); + final File repoFile = validatedRepoFileFrom(url); + if (isEnabled() && shouldUpdateFromRemote(repoFile)) { + LOGGER.debug("Begin RetireJS Update"); + initializeRetireJsRepo(settings, url, repoFile); + saveLastUpdated(repoFile); + } + return false; + } + + private @NonNull URL validatedUrl() throws UpdateException { final String configuredUrl = settings.getString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, DEFAULT_JS_URL); - final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); - final boolean forceupdate = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); - final boolean enabled = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); try { - final URL url = new URL(configuredUrl); - final File filepath = new File(url.getPath()); - final File repoFile = new File(settings.getDataDirectory(), filepath.getName()); - final boolean proceed = enabled && (forceupdate || (autoupdate && shouldUpdate(repoFile))); - if (proceed) { - LOGGER.debug("Begin RetireJS Update"); - initializeRetireJsRepo(settings, url, repoFile); - saveLastUpdated(repoFile, System.currentTimeMillis() / 1000); - } + return new URL(configuredUrl); } catch (MalformedURLException ex) { throw new UpdateException(String.format("Invalid URL for RetireJS repository (%s)", configuredUrl), ex); - } catch (IOException ex) { - throw new UpdateException("Unable to get the data directory", ex); } - return false; } - /** - * Determines if the we should update the RetireJS database. - * - * @param repo the retire JS repository. - * @return true if an updated to the RetireJS database should - * be performed; otherwise false - * @throws NumberFormatException thrown if an invalid value is contained in - * the database properties - */ - protected boolean shouldUpdate(File repo) throws NumberFormatException { - boolean proceed = true; - if (repo != null && repo.isFile()) { - final int validForHours = settings.getInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 0); - final long lastUpdatedOn = getLastUpdated(repo); - final long now = System.currentTimeMillis(); - LOGGER.debug("Last updated: {}", lastUpdatedOn); - LOGGER.debug("Now: {}", now); - final long msValid = validForHours * 60L * 60L * 1000L; - proceed = (now - lastUpdatedOn) > msValid; - if (!proceed) { - LOGGER.info("Skipping RetireJS update since last update was within {} hours.", validForHours); + public @NonNull File validatedRepoFile() throws UpdateException { + return validatedRepoFileFrom(validatedUrl()); + } + + private @NonNull File validatedRepoFileFrom(URL url) throws UpdateException { + try { + String fileName = new File(url.getPath()).getName(); + if (fileName.isBlank()) { + throw new InvalidSettingException("RetireJS URL must imply a filename."); } + return new File(settings.getDataDirectory(), fileName); + } catch (IOException ex) { + throw new UpdateException("Unable to determine the local location to cache RetireJS repo", ex); } - return proceed; } + private boolean isEnabled() { + return settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + } + + private boolean shouldUpdateFromRemote(File repoFile) { + boolean forceupdate = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); + boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); + Duration validFor = Duration.ofHours(settings.getInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 24)); + return forceupdate || !existsWithContent(repoFile) || (autoupdate && isStale(repoFile, validFor)); + } /** * Initializes the local RetireJS repository @@ -130,8 +127,7 @@ protected boolean shouldUpdate(File repo) throws NumberFormatException { * @param settings a reference to the dependency-check settings * @param repoUrl the URL to the RetireJS repository to use * @param repoFile the filename to use for the RetireJS repository - * @throws UpdateException thrown if there is an exception during - * initialization + * @throws UpdateException thrown if there is an exception during initialization */ @SuppressWarnings("try") private void initializeRetireJsRepo(Settings settings, URL repoUrl, File repoFile) throws UpdateException { diff --git a/core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java b/core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java index 4c0b7b10ebb..6a29dd07627 100644 --- a/core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java +++ b/core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java @@ -68,7 +68,7 @@ public void ensureDBExists() throws Exception { if (!dataPath.exists() || !dataFile.exists()) { LOGGER.trace("Extracting database to {}", dataPath); dataPath.mkdirs(); - File path = new File(BaseDBTestCase.class.getClassLoader().getResource("data.zip").toURI().getPath()); + File path = BaseTest.getResourceAsFile(this, "data.zip"); try (FileInputStream fis = new FileInputStream(path); ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis))) { ZipEntry entry; diff --git a/core/src/test/java/org/owasp/dependencycheck/BaseTest.java b/core/src/test/java/org/owasp/dependencycheck/BaseTest.java index daffc98c6da..a7b283a11dc 100644 --- a/core/src/test/java/org/owasp/dependencycheck/BaseTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/BaseTest.java @@ -15,14 +15,19 @@ */ package org.owasp.dependencycheck; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.owasp.dependencycheck.utils.Settings; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.util.Objects; /** @@ -67,29 +72,70 @@ public static void tearDownClass() { /** * Returns the given resource as an InputStream using the object's class loader. * - * @param o the object used to obtain a reference to the class loader + * @param o the object used to obtain a reference to the class loader * @param resource the name of the resource to load * @return the resource as an InputStream */ - public static InputStream getResourceAsStream(Object o, String resource) { + public static @NonNull InputStream getResourceAsStream(Object o, String resource) { return Objects.requireNonNull(o.getClass().getClassLoader().getResourceAsStream(resource), resource + " not found on classpath"); } /** * Returns the given resource as a File using the object's class loader. * - * @param o the object used to obtain a reference to the class loader + * @param o the object used to obtain a reference to the class loader * @param resource the name of the resource to load - * @return the resource as an File + * @return the resource as a File */ - public static File getResourceAsFile(Object o, String resource) { + public static @NonNull File getResourceAsFile(Object o, String resource) { + return new File(getResourceAsURI(o, resource).getPath()); + } + + /** + * Returns the given resource as a URI using the object's class loader. + * + * @param o the object used to obtain a reference to the class loader + * @param resource the name of the resource to load + * @return the resource as a URI + */ + public static @NonNull URI getResourceAsURI(Object o, String resource) { try { - return new File(Objects.requireNonNull(o.getClass().getClassLoader().getResource(resource), resource + " not found on classpath").toURI().getPath()); + return Objects.requireNonNull(o.getClass().getClassLoader().getResource(resource), resource + " not found on classpath").toURI(); } catch (URISyntaxException e) { throw new UnsupportedOperationException(e); } } + /** + * Returns the given resource as a URL string using the object's class loader. + * + * @param o the object used to obtain a reference to the class loader + * @param resource the name of the resource to load + * @return the resource as a URL string + */ + public static @NonNull String getResourceAsUrlString(Object o, String resource) { + try { + return getResourceAsURI(o, resource).toURL().toString(); + } catch (MalformedURLException e) { + throw new UnsupportedOperationException(e); + } + } + + /** + * Returns the given resource content using the object's class loader. + * + * @param o the object used to obtain a reference to the class loader + * @param resource the name of the resource to load + * @return the resource as a String + */ + public static @NonNull String getResourceAsContentString(Object o, String resource) { + try (InputStream is = getResourceAsStream(o, resource)) { + return new String(is.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UnsupportedOperationException(e); + } + } + /** * @return the settings for the test cases. */ diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java index 619a54cbe7b..88677f9cd9d 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java @@ -18,30 +18,33 @@ package org.owasp.dependencycheck.analyzer; import org.jspecify.annotations.NonNull; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine.Mode; +import org.owasp.dependencycheck.data.update.HostedSuppressionsDataSource; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings.KEYS; +import org.owasp.dependencycheck.xml.suppression.SuppressionParseException; import org.owasp.dependencycheck.xml.suppression.SuppressionRule; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.owasp.dependencycheck.analyzer.AbstractSuppressionAnalyzer.SUPPRESSION_OBJECT_KEY; @@ -53,84 +56,154 @@ class AbstractSuppressionAnalyzerTest extends BaseTest { /** * A second suppression file to test with. */ - private static final String OTHER_SUPPRESSIONS_FILE = "other-suppressions.xml"; + private static final String OTHER_TEST_SUPPRESSIONS_FILE = "other-suppressions.xml"; /** * Suppression file to test with. */ - private static final String SUPPRESSIONS_FILE = "suppressions.xml"; + private static final String TEST_SUPPRESSIONS_FILE = "suppressions.xml"; + private static final int TEST_SUPPRESSIONS_EXPECTED_COUNT = 5; - private AbstractSuppressionAnalyzer instance; + private Engine engine; - @BeforeEach - void createObjectUnderTest() { - instance = new AbstractSuppressionAnalyzerImpl(); + @AfterEach + void cleanUp() { + if (engine != null) { + new HostedSuppressionsDataSource().purge(engine); + engine.close(); + } } - /** - * Test of getSupportedExtensions method, of class - * AbstractSuppressionAnalyzer. - */ - @Test - void testGetSupportedExtensions() { - Set result = instance.getSupportedExtensions(); - assertNull(result); + private String testSuppressionsFileUrl() { + return BaseTest.getResourceAsUrlString(this, TEST_SUPPRESSIONS_FILE); } - /** - * Test of getRules method, of class AbstractSuppressionAnalyzer for - * suppression file declared as URL. - */ - @Test - void testGetRulesFromSuppressionFileFromURL() throws Exception { - final String fileUrl = getClass().getClassLoader().getResource(SUPPRESSIONS_FILE).toURI().toURL().toString(); - final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(fileUrl) - getNumberOfRulesLoadedInCoreFile(); - assertEquals(5, numberOfExtraLoadedRules, "Expected 5 extra rules in the given path"); + @Nested + class BasePackagedSuppressionsLoading { + @Test + void packagedBaseSuppressions() throws Exception { + prepareBaseSuppressionsOnly(); + @SuppressWarnings("unchecked") List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); + assertThat(rules, not(empty())); + assertThat("Expected all suppressions in base file to be marked as base", allRulesNotMarkedAsBase(rules), empty()); + } } - /** - * Test of getRules method, of class AbstractSuppressionAnalyzer for - * suppression file on the class path. - */ - @Test - void testGetRulesFromSuppressionFileInClasspath() throws Exception { - final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(SUPPRESSIONS_FILE) - getNumberOfRulesLoadedInCoreFile(); - assertEquals(5, numberOfExtraLoadedRules, "Expected 5 extra rules in the given file"); - } + @Nested + class HostedSuppressionsLoading { + @Test + void packagedSnapshotHostedSuppressionsLoadedEvenIfRemoteHostedSuppressionsDisabled() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, false); + prepareHostedSuppressionsOnly(); + @SuppressWarnings("unchecked") List rules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); + assertThat(rules, not(empty())); + assertThat("Expected all suppressions in hosted suppressions snapshot file to be marked as base", allRulesNotMarkedAsBase(rules), empty()); + } - /** - * Assert that rules are loaded from multiple files if multiple files are - * defined in the {@link Settings}. - */ - @Test - void testGetRulesFromMultipleSuppressionFiles() throws Exception { - final int rulesInCoreFile = getNumberOfRulesLoadedInCoreFile(); - - // GIVEN suppression rules from one file - final int rulesInFirstFile = getNumberOfRulesLoadedFromPath(SUPPRESSIONS_FILE) - rulesInCoreFile; - - // AND suppression rules from another file - final int rulesInSecondFile = getNumberOfRulesLoadedFromPath(OTHER_SUPPRESSIONS_FILE) - rulesInCoreFile; - - // WHEN initializing with both suppression files - final String[] suppressionFiles = {SUPPRESSIONS_FILE, OTHER_SUPPRESSIONS_FILE}; - getSettings().setArrayIfNotEmpty(KEYS.SUPPRESSION_FILE, suppressionFiles); - instance.initialize(getSettings()); - Engine engine = new Engine(getSettings()); - instance.prepare(engine); - - // THEN rules from both files were loaded - final int expectedSize = rulesInFirstFile + rulesInSecondFile + rulesInCoreFile; - assertThat("Expected suppressions from both files", instance.getRuleCount(engine), is(expectedSize)); + @Test + void packagedSnapshotHostedSuppressionsLoadedIfAutoUpdateDisabled() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(KEYS.AUTO_UPDATE, false); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), greaterThan(0)); + } + @Test + void packagedSnapshotHostedSuppressionsLoadedIfRemoteHostedSuppressionsFail() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(KEYS.AUTO_UPDATE, true); + getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, "file:///does-not-exist.xml"); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), greaterThan(0)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void ignoresHostedSuppressionsIfUrlDoesntIncludeAFileNameRegardlessOfEnabledState(boolean hostedSuppressionsEnabled) throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, hostedSuppressionsEnabled); + getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, "https://valid.url.but.no.file/"); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), is(0)); + } + + @Test + void ignoresHostedSuppressionsIfCannotBeParsedFromRemote() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, BaseTest.getResourceAsUrlString(this, "suppressions-invalid.xml")); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), is(0)); + } + + @Test + void prefersRemoteHostedSuppressionsIfEnabled() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(KEYS.AUTO_UPDATE, true); + getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), is(TEST_SUPPRESSIONS_EXPECTED_COUNT)); + } + + @Test + void prefersRemoteHostedSuppressionsIfEnabledAndForced() throws Exception { + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, true); + getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + prepareHostedSuppressionsOnly(); + assertThat(AbstractSuppressionAnalyzer.getRuleCount(engine), is(TEST_SUPPRESSIONS_EXPECTED_COUNT)); + } } - @Test - void testFailureToLocateSuppressionFileAnywhere() { - getSettings().setString(Settings.KEYS.SUPPRESSION_FILE, "doesnotexist.xml"); - instance.initialize(getSettings()); - Engine engine = new Engine(Mode.EVIDENCE_COLLECTION, getSettings()); - assertThrows(InitializationException.class, () -> - instance.prepare(engine)); + @Nested + class UserSuppressionsLoading { + /** + * Test of getRules method, of class AbstractSuppressionAnalyzer for + * suppression file declared as URL. + */ + @Test + void testGetRulesFromSuppressionFileFromURL() throws Exception { + final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(testSuppressionsFileUrl()) - getNumberOfRulesLoadedInCoreFile(); + assertEquals(TEST_SUPPRESSIONS_EXPECTED_COUNT, numberOfExtraLoadedRules, "Wrong # of expected extra user suppression rules"); + } + + /** + * Test of getRules method, of class AbstractSuppressionAnalyzer for + * suppression file on the class path. + */ + @Test + void testGetRulesFromSuppressionFileInClasspath() throws Exception { + final int numberOfExtraLoadedRules = getNumberOfRulesLoadedFromPath(TEST_SUPPRESSIONS_FILE) - getNumberOfRulesLoadedInCoreFile(); + assertEquals(TEST_SUPPRESSIONS_EXPECTED_COUNT, numberOfExtraLoadedRules, "Wrong # of expected extra user suppression rules"); + } + + /** + * Assert that rules are loaded from multiple files if multiple files are + * defined in the {@link Settings}. + */ + @Test + void testGetRulesFromMultipleSuppressionFiles() throws Exception { + final int rulesInCoreFile = getNumberOfRulesLoadedInCoreFile(); + + // GIVEN suppression rules from one file + final int rulesInFirstFile = getNumberOfRulesLoadedFromPath(TEST_SUPPRESSIONS_FILE) - rulesInCoreFile; + + // AND suppression rules from another file + final int rulesInSecondFile = getNumberOfRulesLoadedFromPath(OTHER_TEST_SUPPRESSIONS_FILE) - rulesInCoreFile; + + // WHEN initializing with both suppression files + final String[] suppressionFiles = {TEST_SUPPRESSIONS_FILE, OTHER_TEST_SUPPRESSIONS_FILE}; + getSettings().setArrayIfNotEmpty(KEYS.SUPPRESSION_FILE, suppressionFiles); + prepareSuppressions(); + + // THEN rules from both files were loaded + final int expectedSize = rulesInFirstFile + rulesInSecondFile + rulesInCoreFile; + assertThat("Expected suppressions from both files", AbstractSuppressionAnalyzer.getRuleCount(engine), is(expectedSize)); + } + + @Test + void testFailureToLocateSuppressionFileAnywhere() { + getSettings().setString(Settings.KEYS.SUPPRESSION_FILE, "doesnotexist.xml"); + assertThrows(InitializationException.class, AbstractSuppressionAnalyzerTest.this::prepareSuppressions); + } } /** @@ -142,7 +215,7 @@ void testFailureToLocateSuppressionFileAnywhere() { */ private int getNumberOfRulesLoadedInCoreFile() throws Exception { getSettings().removeProperty(KEYS.SUPPRESSION_FILE); - Engine engine = prepareSuppressions(); + prepareSuppressions(); return AbstractSuppressionAnalyzer.getRuleCount(engine); } @@ -156,70 +229,51 @@ private int getNumberOfRulesLoadedInCoreFile() throws Exception { */ private int getNumberOfRulesLoadedFromPath(final String path) throws Exception { getSettings().setString(KEYS.SUPPRESSION_FILE, path); - Engine engine = prepareSuppressions(); + prepareSuppressions(); return AbstractSuppressionAnalyzer.getRuleCount(engine); } - private @NonNull Engine prepareSuppressions() throws InvalidSettingException, InitializationException { - final AbstractSuppressionAnalyzerImpl fileAnalyzer = new AbstractSuppressionAnalyzerImpl(); - fileAnalyzer.initialize(getSettings()); - Downloader.getInstance().configure(getSettings()); - Engine engine = new Engine(Mode.EVIDENCE_COLLECTION, getSettings()); - fileAnalyzer.prepare(engine); - return engine; + private void prepareSuppressions() throws InvalidSettingException, InitializationException { + engine = new Engine(Mode.EVIDENCE_COLLECTION, getSettings()); + newAnalyzer().prepare(engine); } - @Nested - class CoreSuppressionsLoading { - @Test - void testLoadCorePackagedSuppressions() throws Exception { - List baseRules = assertAllBaseSuppressionRulesAreMarkedCorrectly(); - - assertAllHostedSnapshotSuppressionsAreMarkedAsBase(baseRules); - } - - private @NonNull List assertAllBaseSuppressionRulesAreMarkedCorrectly() throws InvalidSettingException, InitializationException { - getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, false); - Engine engine = prepareSuppressions(); - - @SuppressWarnings("unchecked") List baseRules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); - assertThat(baseRules, not(empty())); - assertThat("Expected all suppressions in base file to be marked as base", allRulesNotMarkedAsBase(baseRules), empty()); - return baseRules; - } - - private void assertAllHostedSnapshotSuppressionsAreMarkedAsBase(List baseRules) throws InvalidSettingException, InitializationException { - getSettings().setBoolean(KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); - getSettings().setString(KEYS.HOSTED_SUPPRESSIONS_URL, "https://intentionally-bad-url/hosted-suppressions.xml"); - Engine engine = prepareSuppressions(); + private void prepareBaseSuppressionsOnly() throws InvalidSettingException, SuppressionParseException { + engine = new Engine(Mode.EVIDENCE_COLLECTION, getSettings()); + newAnalyzer().loadPackagedBaseSuppressionData(engine); + } - @SuppressWarnings("unchecked") List allRules = (List) engine.getObject(SUPPRESSION_OBJECT_KEY); + private void prepareHostedSuppressionsOnly() throws InvalidSettingException { + engine = new Engine(Mode.EVIDENCE_COLLECTION, getSettings()); + newAnalyzer().loadHostedSuppressionBaseData(engine); + } - List hostedSnapshotRules = allRules.stream().filter(r -> !baseRules.contains(r)).collect(Collectors.toList()); - assertThat(hostedSnapshotRules, not(empty())); - assertThat("Expected all suppressions in hosted suppressions snapshot file to be marked as base", allRulesNotMarkedAsBase(hostedSnapshotRules), empty()); - } + private @NonNull AbstractSuppressionAnalyzerImpl newAnalyzer() throws InvalidSettingException { + final AbstractSuppressionAnalyzerImpl fileAnalyzer = new AbstractSuppressionAnalyzerImpl(); + fileAnalyzer.initialize(getSettings()); + Downloader.getInstance().configure(getSettings()); + return fileAnalyzer; + } - private @NonNull List allRulesNotMarkedAsBase(List baseRules) { - return baseRules.stream().filter(r -> !r.isBase()).collect(Collectors.toList()); - } + private @NonNull List allRulesNotMarkedAsBase(List baseRules) { + return baseRules.stream().filter(r -> !r.isBase()).collect(Collectors.toList()); } public static class AbstractSuppressionAnalyzerImpl extends AbstractSuppressionAnalyzer { @Override public void analyzeDependency(Dependency dependency, Engine engine) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); } @Override public String getName() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); } @Override public AnalysisPhase getAnalysisPhase() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); } @Override diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerTest.java new file mode 100644 index 00000000000..ca3bc5e3388 --- /dev/null +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerTest.java @@ -0,0 +1,143 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2026 Chad Wilson. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.json.JSONException; +import org.jspecify.annotations.NonNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.data.update.RetireJSDataSource; +import org.owasp.dependencycheck.exception.InitializationException; +import org.owasp.dependencycheck.utils.Downloader; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; + +import java.nio.file.NoSuchFileException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class RetireJsAnalyzerTest extends BaseTest { + private static final String TEST_RETIRE_JS_REPOSITORY_FILE = "retirejs/jsrepository.json"; + + private RetireJsAnalyzer analyzer; + private Engine engine; + + @BeforeEach + void settings() { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + } + + @AfterEach + void cleanUp() { + if (engine != null) { + new RetireJSDataSource().purge(engine); + engine.close(); + } + } + + private String testRepositoryFileUrl() { + return BaseTest.getResourceAsUrlString(this, TEST_RETIRE_JS_REPOSITORY_FILE); + } + + @Nested + class RepositoryLoading { + + @Test + void loadsRemoteIfEmptyWithAutoUpdateDisabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + prepareRetireJs(); + assertThat(analyzer.knownLibraryCountFor("retire-example-0.0.1.js").orElseThrow(), is(1)); + } + + @Test + void loadsRemoteIfPresentAndForced() throws Exception { + prepareRetireJs(); + + // try again with repo present; but with a URL that will fail when invoked so we know it tried + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl().replace(TEST_RETIRE_JS_REPOSITORY_FILE, "doesnt-exist/" + TEST_RETIRE_JS_REPOSITORY_FILE)); + + var ex = assertThrows(InitializationException.class, RetireJsAnalyzerTest.this::prepareRetireJs); + assertThat(ex.getMessage(), containsString("Failed to initialize the RetireJS repo")); + assertThat(ExceptionUtils.getRootCause(ex), instanceOf(NoSuchFileException.class)); + } + + @Test + void doesNothingIfPresentAndAutoUpdateDisabled() throws Exception { + prepareRetireJs(); + + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); + + // try again with repo present; but with a URL that would fail if invoked + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl().replace(TEST_RETIRE_JS_REPOSITORY_FILE, "doesnt-exist/" + TEST_RETIRE_JS_REPOSITORY_FILE)); + prepareRetireJs(); + } + + @Test + void failsIfRemoteRetireJsRepoNotFound() { + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, "file:///does-not-exist.xml"); + var ex = assertThrows(InitializationException.class, RetireJsAnalyzerTest.this::prepareRetireJs); + assertThat(ex.getMessage(), containsString("Failed to initialize the RetireJS repo")); + assertThat(ExceptionUtils.getRootCause(ex), instanceOf(NoSuchFileException.class)); + assertThat(analyzer.isEnabled(), is(false)); + } + + @Test + void failsIfRemoteRetireJsCannotBeParsed() { + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, BaseTest.getResourceAsUrlString(this, "retirejs/jsrepository-invalid.json")); + var ex = assertThrows(InitializationException.class, RetireJsAnalyzerTest.this::prepareRetireJs); + assertThat(ex.getMessage(), containsString("Failed to initialize the RetireJS repo")); + assertThat(ExceptionUtils.getRootCause(ex), instanceOf(JSONException.class)); + assertThat(analyzer.isEnabled(), is(false)); + } + + @Test + void prefersRemoteRetireJsIfEnabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + prepareRetireJs(); + assertThat(analyzer.knownLibraryCountFor("retire-example-0.0.1.js").orElseThrow(), is(1)); + } + } + + private void prepareRetireJs() throws InvalidSettingException, InitializationException { + engine = new Engine(Engine.Mode.EVIDENCE_COLLECTION, getSettings()); + analyzer = newAnalyzer(); + analyzer.prepareFileTypeAnalyzer(engine); + } + + private @NonNull RetireJsAnalyzer newAnalyzer() throws InvalidSettingException { + final RetireJsAnalyzer fileAnalyzer = new RetireJsAnalyzer(); + fileAnalyzer.initialize(getSettings()); + Downloader.getInstance().configure(getSettings()); + return fileAnalyzer; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSourceTest.java b/core/src/test/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSourceTest.java new file mode 100644 index 00000000000..530e112df46 --- /dev/null +++ b/core/src/test/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSourceTest.java @@ -0,0 +1,223 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2026 Chad Wilson. All Rights Reserved. + */ +package org.owasp.dependencycheck.data.update; + +import org.jspecify.annotations.NonNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +class HostedSuppressionsDataSourceTest extends BaseTest { + + private static final String TEST_SUPPRESSIONS_FILE = "suppressions.xml"; + + private HostedSuppressionsDataSource dataSource; + private Engine engine; + + @BeforeEach + public void createEngine() { + dataSource = new HostedSuppressionsDataSource(); + engine = new Engine(Engine.Mode.EVIDENCE_COLLECTION, getSettings()); + } + + @AfterEach + void closeEngine() { + if (engine != null) { + dataSource.purge(engine); + engine.close(); + } + } + + @Nested + class Update { + @Test + void doesNothingIfRemoteHostedSuppressionsDisabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, false); + dataSource.update(engine); + assertNoCachedHostedSuppressions(); + } + + @Test + void doesNothingIfNoSuppressionAnalyzersEnabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.ANALYZER_VULNERABILITY_SUPPRESSION_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CPE_SUPPRESSION_ENABLED, false); + dataSource.update(engine); + assertNoCachedHostedSuppressions(); + } + + @Test + void doesNothingIfAutoUpdateDisabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, false); + dataSource.update(engine); + assertNoCachedHostedSuppressions(); + } + + @Test + void ignoresHostedSuppressionsIfRemoteHostedSuppressionsFail() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, "file:///does-not-exist.xml"); + dataSource.update(engine); + assertNoCachedHostedSuppressions(); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void failsIfSuppressionsUrlDoesntIncludeAFileNameRegardlessOfEnabledState(boolean hostedSuppressionsEnabled) throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, hostedSuppressionsEnabled); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, "https://valid.url.but.no.file/"); + var ex = assertThrowsExactly(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ex.getMessage(), containsString("Unable to determine the local location to cache hosted suppressions")); + assertThat(ex.getCause(), instanceOf(InvalidSettingException.class)); + assertThat(ex.getCause().getMessage(), containsString("Hosted Suppression URL must imply a filename")); + } + + @Test + void failsIfSuppressionsUrlIsMalformed() { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, "bad-url"); + var ex = assertThrowsExactly(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ex.getMessage(), containsString("Unable to determine the local location to cache hosted suppressions")); + assertThat(ex.getCause(), instanceOf(InvalidSettingException.class)); + assertThat(ex.getCause().getMessage(), containsString("Invalid URL for Hosted Suppressions")); + } + + @Test + void loadsRemoteHostedSuppressionsIfEnabledAndForced() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + dataSource.update(engine); + assertThat(Files.readString(cachedRepoFile()), is(testSuppressionsFileContent())); + assertThat(Files.exists(cachedRepoFileProperties()), is(true)); + } + + @Test + void loadsRemoteHostedSuppressionsIfEnabledWithAutoUpdate() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + dataSource.update(engine); + assertThat(Files.readString(cachedRepoFile()), is(testSuppressionsFileContent())); + assertThat(Files.exists(cachedRepoFileProperties()), is(true)); + } + + @Test + void doesNothingIfRemoteHostedSuppressionsIsNotStale() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + getSettings().setInt(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, 1); + dataSource.update(engine); + + // Update again immediately + String firstUpdateProperties = Files.readString(cachedRepoFileProperties()); + FileTime firstUpdatePropertiesModified = Files.getLastModifiedTime(cachedRepoFileProperties()); + dataSource.update(engine); + + assertThat(Files.readString(cachedRepoFileProperties()), is(firstUpdateProperties)); + assertThat(Files.getLastModifiedTime(cachedRepoFileProperties()), is(firstUpdatePropertiesModified)); + } + + @Test + void reloadsRemoteHostedSuppressionsIfStale() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + getSettings().setInt(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, 0); + dataSource.update(engine); + + // Reset to force an update + Files.writeString(cachedRepoFile(), "stale content"); + + dataSource.update(engine); + assertThat(Files.readString(cachedRepoFile()), not("stale content")); + } + } + + @Nested + class Purge { + @Test + void purgeRemovesCachedFiles() throws Exception { + getSettings().setBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, testSuppressionsFileUrl()); + dataSource.update(engine); + + assertThat(Files.exists(cachedRepoFile()), is(true)); + dataSource.purge(engine); + assertThat(Files.exists(cachedRepoFile()), is(false)); + } + + @Test + void doesNothingIfNoCachedFile() throws Exception { + dataSource.purge(engine); + assertThat(Files.exists(cachedRepoFile()), is(false)); + } + + @Test + void doesNothingIfSuppressionsUrlIsMalformed() { + getSettings().setString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, "bad-url"); + dataSource.purge(engine); + } + } + + private @NonNull Path cachedRepoFile() throws IOException { + return dataSource.validatedRepoFile().toPath(); + } + + private @NonNull Path cachedRepoFileProperties() throws IOException { + return Path.of(cachedRepoFile() + ".properties"); + } + + private @NonNull String testSuppressionsFileUrl() { + return BaseTest.getResourceAsUrlString(this, TEST_SUPPRESSIONS_FILE); + } + + private @NonNull String testSuppressionsFileContent() { + return BaseTest.getResourceAsContentString(this, TEST_SUPPRESSIONS_FILE); + } + + private void assertNoCachedHostedSuppressions() throws IOException { + assertThat("hosted suppression repo file should not exist", dataSource.validatedRepoFile().exists(), is(false)); + } +} diff --git a/core/src/test/java/org/owasp/dependencycheck/data/update/RetireJSDataSourceTest.java b/core/src/test/java/org/owasp/dependencycheck/data/update/RetireJSDataSourceTest.java new file mode 100644 index 00000000000..e9f07856f8f --- /dev/null +++ b/core/src/test/java/org/owasp/dependencycheck/data/update/RetireJSDataSourceTest.java @@ -0,0 +1,233 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2026 Chad Wilson. All Rights Reserved. + */ +package org.owasp.dependencycheck.data.update; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.jspecify.annotations.NonNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; + +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +class RetireJSDataSourceTest extends BaseTest { + private static final String TEST_RETIRE_JS_REPOSITORY_FILE = "retirejs/jsrepository.json"; + + private RetireJSDataSource dataSource; + private Engine engine; + + @BeforeEach + public void createEngine() { + dataSource = new RetireJSDataSource(); + engine = new Engine(Engine.Mode.EVIDENCE_COLLECTION, getSettings()); + } + + @AfterEach + void closeEngine() { + if (engine != null) { + dataSource.purge(engine); + engine.close(); + } + } + + @Nested + class Update { + @Test + void doesNothingIfRetireJsDisabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, false); + dataSource.update(engine); + assertNoCachedRetireJs(); + } + + @Test + void loadsRemoteIfEmptyWithAutoUpdateDisabled() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + dataSource.update(engine); + assertCachedRetireJs(); + } + + @Test + void doesNothingIfPresentAndAutoUpdateDisabled() throws Exception { + dataSource.update(engine); + + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); + + // try again with repo present; but with a URL that would fail if invoked + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl().replace(TEST_RETIRE_JS_REPOSITORY_FILE, "doesnt-exist/" + TEST_RETIRE_JS_REPOSITORY_FILE)); + dataSource.update(engine); + } + + @Test + void failsIfRemoteRetireJsRepoNotFound() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, "file:///does-not-exist.xml"); + var ex = assertThrows(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ex.getMessage(), containsString("Failed to initialize the RetireJS repo")); + assertThat(ExceptionUtils.getRootCause(ex), instanceOf(NoSuchFileException.class)); + assertNoCachedRetireJs(); + } + + @Test + void failsIfUrlDoesntIncludeAFileName() { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, "https://valid.url.but.no.file/"); + var ex = assertThrowsExactly(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ex.getMessage(), containsString("Unable to determine the local location to cache")); + assertThat(ex.getCause(), instanceOf(InvalidSettingException.class)); + assertThat(ex.getCause().getMessage(), containsString("RetireJS URL must imply a filename")); + } + + @Test + void failsIfUrlIsMalformed() { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, "bad-url"); + var ex = assertThrowsExactly(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ex.getMessage(), containsString("Invalid URL for RetireJS repository (bad-url)")); + assertThat(ex.getCause(), instanceOf(MalformedURLException.class)); + } + + @Test + void loadsRemoteRetireJsIfEnabledAndForced() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + dataSource.update(engine); + + // try again with repo present; but with a URL that fails when invoked + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl().replace(TEST_RETIRE_JS_REPOSITORY_FILE, "doesnt-exist/" + TEST_RETIRE_JS_REPOSITORY_FILE)); + var ex = assertThrowsExactly(UpdateException.class, () -> dataSource.update(engine)); + assertThat(ExceptionUtils.getRootCause(ex), instanceOf(NoSuchFileException.class)); + } + + @Test + void loadsRemoteRetireJsIfEnabledWithAutoUpdate() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + dataSource.update(engine); + assertThat(Files.readString(cachedRepoFile()), is(testRepositoryFileContent())); + assertThat(Files.exists(cachedRepoFileProperties()), is(true)); + } + + @Test + void doesNothingIfRemoteRetireJsIsNotStale() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + getSettings().setInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 1); + dataSource.update(engine); + + // Update again immediately + String firstUpdateProperties = Files.readString(cachedRepoFileProperties()); + FileTime firstUpdatePropertiesModified = Files.getLastModifiedTime(cachedRepoFileProperties()); + dataSource.update(engine); + + assertThat(Files.readString(cachedRepoFileProperties()), is(firstUpdateProperties)); + assertThat(Files.getLastModifiedTime(cachedRepoFileProperties()), is(firstUpdatePropertiesModified)); + } + + @Test + void reloadsRemoteRetireJsIfStale() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + getSettings().setInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 0); + dataSource.update(engine); + + // Reset to force an update + Files.writeString(cachedRepoFile(), "stale content"); + + dataSource.update(engine); + assertThat(Files.readString(cachedRepoFile()), not("stale content")); + } + } + + @Nested + class Purge { + @Test + void purgeRemovesCachedFiles() throws Exception { + getSettings().setBoolean(Settings.KEYS.ANALYZER_RETIREJS_ENABLED, true); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, true); + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, testRepositoryFileUrl()); + dataSource.update(engine); + + assertThat(Files.exists(cachedRepoFile()), is(true)); + dataSource.purge(engine); + assertThat(Files.exists(cachedRepoFile()), is(false)); + } + + @Test + void doesNothingIfNoCachedFile() throws Exception { + dataSource.purge(engine); + assertThat(Files.exists(cachedRepoFile()), is(false)); + } + + @Test + void doesNothingIfUrlIsMalformed() { + getSettings().setString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, "bad-url"); + dataSource.purge(engine); + } + } + + private @NonNull Path cachedRepoFile() throws UpdateException { + return dataSource.validatedRepoFile().toPath(); + } + + private @NonNull Path cachedRepoFileProperties() throws UpdateException { + return Path.of(cachedRepoFile() + ".properties"); + } + + private @NonNull String testRepositoryFileUrl() { + return BaseTest.getResourceAsUrlString(this, TEST_RETIRE_JS_REPOSITORY_FILE); + } + + private @NonNull String testRepositoryFileContent() { + return BaseTest.getResourceAsContentString(this, TEST_RETIRE_JS_REPOSITORY_FILE); + } + + private void assertNoCachedRetireJs() throws UpdateException { + assertThat("RetireJS repo file should not exist", dataSource.validatedRepoFile().exists(), is(false)); + } + + private void assertCachedRetireJs() throws UpdateException { + assertThat("RetireJS repo file should exist", dataSource.validatedRepoFile().exists(), is(true)); + } +} diff --git a/core/src/test/resources/retirejs/jsrepository-invalid.json b/core/src/test/resources/retirejs/jsrepository-invalid.json new file mode 100644 index 00000000000..e076eea9296 --- /dev/null +++ b/core/src/test/resources/retirejs/jsrepository-invalid.json @@ -0,0 +1,3 @@ +{ + "bad": "syntax" +} diff --git a/core/src/test/resources/retirejs/jsrepository.json b/core/src/test/resources/retirejs/jsrepository.json new file mode 100644 index 00000000000..1dcaf60ee7f --- /dev/null +++ b/core/src/test/resources/retirejs/jsrepository.json @@ -0,0 +1,37 @@ +{ + "retire-example": { + "vulnerabilities": [ + { + "below": "0.0.2", + "severity": "low", + "cwe": [ + "CWE-477" + ], + "identifiers": { + "summary": "bug summary", + "CVE": [ + "CVE-XXXX-XXXX" + ], + "bug": "1234" + }, + "info": [ + "http://github.com/eoftedal/retire.js/" + ] + } + ], + "extractors": { + "func": [ + "retire.VERSION" + ], + "filename": [ + "retire-example-(§§version§§)(.min)?\\.js" + ], + "filecontent": [ + "/\\*!? Retire-example v(§§version§§)" + ], + "hashes": { + "07f8b94c8d601a24a1914a1a92bec0e4fafda964": "0.0.1" + } + } + } +} diff --git a/core/src/test/resources/suppressions-invalid.xml b/core/src/test/resources/suppressions-invalid.xml new file mode 100644 index 00000000000..efd7b9ead1e --- /dev/null +++ b/core/src/test/resources/suppressions-invalid.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java b/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java index dc9d19085b2..f45828eb5fd 100644 --- a/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java +++ b/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java @@ -1158,7 +1158,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma @Parameter(property = "hostedSuppressionsForceUpdate") private Boolean hostedSuppressionsForceUpdate; /** - * Whether the hosted suppressions file will be used. + * Whether the hosted suppressions will be updated from the configured URL. */ @SuppressWarnings("CanBeFinal") @Parameter(property = "hostedSuppressionsEnabled") diff --git a/maven/src/site/markdown/configuration.md b/maven/src/site/markdown/configuration.md index e750b170b93..68a79397c11 100644 --- a/maven/src/site/markdown/configuration.md +++ b/maven/src/site/markdown/configuration.md @@ -175,7 +175,7 @@ Note that any passwords in the below configuration could be exposed if you use ` | serverId | The id of a server defined in the settings.xml; this can be used to encrypt the database password. See [password encryption](http://maven.apache.org/guides/mini/guide-encryption.html) for more information. |   | | databaseUser | The username used when connecting to the database. |   | | databasePassword | The password used when connecting to the database. |   | -| hostedSuppressionsEnabled | Whether the hosted suppressions file will be used. | true | +| hostedSuppressionsEnabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressionsForceUpdate | Whether the hosted suppressions file will update regardless of the `autoupdate` setting. | false | | hostedSuppressionsUrl | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments. | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressionsValidForHours | Sets the number of hours to wait before checking for new updates of the hosted suppressions file. | 2 | diff --git a/src/site/markdown/dependency-check-gradle/configuration-aggregate.md b/src/site/markdown/dependency-check-gradle/configuration-aggregate.md index 9776a6003a5..a93d5ec9592 100644 --- a/src/site/markdown/dependency-check-gradle/configuration-aggregate.md +++ b/src/site/markdown/dependency-check-gradle/configuration-aggregate.md @@ -90,7 +90,7 @@ The following properties can be configured in the dependencyCheck task. However, | data | password | The password used when connecting to the database. |   | | slack | enabled | Whether or not slack notifications are enabled. | false | | slack | webhookUrl | The custom incoming webhook URL to receive notifications. Note that the current implementation only notifies about build failures so this should be used in combination with failBuildOnCVSS. |   | -| hostedSuppressions | enabled | Whether the hosted suppressions file will be used. | true | +| hostedSuppressions | enabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressions | forceupdate | Sets whether hosted suppressions file will update regardless of the `autoupdate` setting. | false | | hostedSuppressions | url | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments. | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressions | user | Credentials used for basic authentication for the hosted suppressions file. |   | diff --git a/src/site/markdown/dependency-check-gradle/configuration-update.md b/src/site/markdown/dependency-check-gradle/configuration-update.md index 17d6fc9ca42..2406e3123c7 100644 --- a/src/site/markdown/dependency-check-gradle/configuration-update.md +++ b/src/site/markdown/dependency-check-gradle/configuration-update.md @@ -62,7 +62,7 @@ The following properties can be configured in the dependencyCheck task. However, | data | connectionString | The connection string used to connect to the database. See using a [database server](../data/database.html). |   | | data | username | The username used when connecting to the database. |   | | data | password | The password used when connecting to the database. |   | -| hostedSuppressions | enabled | Whether the hosted suppressions file will be used. | true | +| hostedSuppressions | enabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressions | forceupdate | Sets whether hosted suppressions file will update regardless of the `autoupdate` setting. | false | | hostedSuppressions | url | The URL to (a mirror of) the hosted suppressions file. | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressions | user | Credentials used for basic authentication for the hosted suppressions file. |   | diff --git a/src/site/markdown/dependency-check-gradle/configuration.md b/src/site/markdown/dependency-check-gradle/configuration.md index dbc8495dc1a..8e0d4bdd781 100644 --- a/src/site/markdown/dependency-check-gradle/configuration.md +++ b/src/site/markdown/dependency-check-gradle/configuration.md @@ -90,7 +90,7 @@ The following properties can be configured in the dependencyCheck task. However, | data | password | The password used when connecting to the database. |   | | slack | enabled | Whether or not slack notifications are enabled. | false | | slack | webhookUrl | The custom incoming webhook URL to receive notifications. Note that the current implementation only notifies about build failures so this should be used in combination with failBuildOnCVSS. |   | -| hostedSuppressions | enabled | Whether the hosted suppressions file will be used. | true | +| hostedSuppressions | enabled | Whether the hosted suppressions will be updated from the configured URL. | true | | hostedSuppressions | forceupdate | Sets whether hosted suppressions file will update regardless of the `autoupdate` setting. | false | | hostedSuppressions | url | The URL to a mirrored copy of the hosted suppressions file for internet-constrained environments. | https://dependency-check.github.io/DependencyCheck/suppressions/publishedSuppressions.xml | | hostedSuppressions | user | Credentials used for basic authentication for the hosted suppressions file. |   | diff --git a/utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java b/utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java index 88428dee453..5a55f16ad72 100644 --- a/utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java +++ b/utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java @@ -17,6 +17,14 @@ */ package org.owasp.dependencycheck.utils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -30,14 +38,6 @@ import java.util.UUID; import java.util.stream.Stream; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.SystemUtils; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * A collection of utilities for processing information about files. * @@ -195,4 +195,12 @@ public static File getResourceAsFile(final String resource) { } return new File(path); } + + /** + * @param file the file to check + * @return true if the passed file is a file with more than 1 byte of content + */ + public static boolean existsWithContent(@NonNull File file) { + return file.isFile() && file.length() > 1; + } } diff --git a/utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java b/utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java index 5c63b668099..35918b7fe56 100644 --- a/utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java +++ b/utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java @@ -18,12 +18,17 @@ package org.owasp.dependencycheck.utils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.owasp.dependencycheck.utils.FileUtils.existsWithContent; /** * @@ -31,6 +36,9 @@ */ class FileUtilsTest extends BaseTest { + @TempDir + Path tempDir; + /** * Test of getFileExtension method, of class FileUtils. */ @@ -74,4 +82,17 @@ void testDeleteWithSubDirectories() throws Exception { assertTrue(status, "delete returned a failed status"); assertFalse(file.exists(), "Temporary file exists after attempting deletion"); } + + @Test + void testExistsWithContent() throws IOException { + assertFalse(existsWithContent(new File("doesnt-exist"))); + assertFalse(existsWithContent(new File(".")), "directory shouldn't be considered as existing with content"); + + Path tempFile = Files.createTempFile(tempDir, "", ""); + assertFalse(existsWithContent(tempFile.toFile()), "empty file shouldn't be considered as existing with content"); + Files.writeString(tempFile, " "); + assertFalse(existsWithContent(tempFile.toFile()), "1 byte file shouldn't be considered as existing with content"); + Files.writeString(tempFile, " "); + assertTrue(existsWithContent(tempFile.toFile())); + } } From 6e568bf837b92c1b466d330fdcb3918e5f4ae01e Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Tue, 19 May 2026 18:32:09 +0800 Subject: [PATCH 2/9] fix(cli): correct displayed application name and deprecations in help text (#8496) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .../java/org/owasp/dependencycheck/App.java | 22 +++--- .../org/owasp/dependencycheck/CliParser.java | 69 ++++++++++-------- .../owasp/dependencycheck/CliParserTest.java | 72 +++++++++---------- 3 files changed, 84 insertions(+), 79 deletions(-) diff --git a/cli/src/main/java/org/owasp/dependencycheck/App.java b/cli/src/main/java/org/owasp/dependencycheck/App.java index 5bccba7218c..672e74dbb24 100644 --- a/cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/cli/src/main/java/org/owasp/dependencycheck/App.java @@ -24,6 +24,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.types.LogLevel; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; @@ -46,6 +47,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; @@ -95,6 +97,14 @@ public static void main(String[] args) { */ public App() { settings = new Settings(); + settings.setString(Settings.KEYS.APPLICATION_NAME, determineName()); + } + + private static String determineName() { + return Optional.ofNullable(System.getenv("ODC_NAME")) + .filter(StringUtils::isNotBlank) + .map(n -> n.replace('/', '-').replace(' ', '_')) + .orElse("dependency-check-cli"); } /** @@ -120,11 +130,11 @@ public int run(String[] args) { cli.parse(args); } catch (FileNotFoundException ex) { System.err.println(ex.getMessage()); - cli.printHelp(); + cli.printHelp(System.out); return 1; } catch (ParseException ex) { System.err.println(ex.getMessage()); - cli.printHelp(); + cli.printHelp(System.out); return 2; } final String verboseLog = cli.getStringArgument(CliParser.ARGUMENT.VERBOSE_LOG); @@ -224,7 +234,7 @@ public int run(String[] args) { settings.cleanup(); } } else { - cli.printHelp(); + cli.printHelp(System.out); } return exitCode; } @@ -456,12 +466,6 @@ private void runUpdateOnly() throws UpdateException, DatabaseException { * file is unable to be loaded. */ protected void populateSettings(CliParser cli) throws InvalidSettingException { - String name = System.getenv("ODC_NAME") != null ? System.getenv("ODC_NAME") : "dependency-check-cli"; - if (name.isBlank()) { - name = "dependency-check-cli"; - } - name = name.replace("/", "-").replace(" ", "_"); - settings.setString(Settings.KEYS.APPLICATION_NAME, name); final File propertiesFile = cli.getFileArgument(CliParser.ARGUMENT.PROP); if (propertiesFile != null) { try { diff --git a/cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/cli/src/main/java/org/owasp/dependencycheck/CliParser.java index ce69d34f68e..6260c10e040 100644 --- a/cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -21,11 +21,12 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.help.HelpFormatter; +import org.apache.commons.cli.help.TextHelpAppendable; import org.owasp.dependencycheck.reporting.ReportGenerator.Format; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; @@ -34,6 +35,9 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Comparator; /** * A utility to parse command line arguments for the DependencyCheck. @@ -65,6 +69,12 @@ public final class CliParser { */ private static final String SUPPORTED_FORMATS = "HTML, XML, CSV, JSON, JUNIT, SARIF, JENKINS, GITLAB or ALL"; + private static final String HELP_MSG = String.format( + "Dependency-Check can be used to identify if there are any known CVE vulnerabilities in libraries " + + "utilized by an application. Dependency-Check will automatically update required data from the " + + "Internet, such as the CVE and CPE data files from nvd.nist.gov.%n" + ); + /** * Constructs a new CLI Parser object with the configured settings. * @@ -82,7 +92,7 @@ public CliParser(Settings settings) { * point to a file that exists. * @throws ParseException is thrown when a Parse Exception occurs. */ - public void parse(String[] args) throws FileNotFoundException, ParseException { + public void parse(String... args) throws FileNotFoundException, ParseException { line = parseArgs(args); if (line != null) { @@ -97,7 +107,7 @@ public void parse(String[] args) throws FileNotFoundException, ParseException { * @return the results of parsing the command line arguments * @throws ParseException if the arguments are invalid */ - private CommandLine parseArgs(String[] args) throws ParseException { + private CommandLine parseArgs(String... args) throws ParseException { final CommandLineParser parser = new DefaultParser(); final Options options = createCommandLineOptions(); return parser.parse(options, args); @@ -213,7 +223,7 @@ private boolean isValidFormat(String format) { * @param argumentName the argument being validated (e.g. scan, out, etc.) * @return true, if path exists; false otherwise */ - private boolean isValidFilePath(String path, String argumentName) { + private boolean isValidFilePath(String path, @SuppressWarnings("SameParameterValue") String argumentName) { try { validatePathExists(path, argumentName); return true; @@ -232,7 +242,7 @@ private boolean isValidFilePath(String path, String argumentName) { * @throws FileNotFoundException is thrown if one of the paths being * validated does not exist. */ - private void validatePathExists(String[] paths, String optType) throws FileNotFoundException { + private void validatePathExists(String[] paths, @SuppressWarnings("SameParameterValue") String optType) throws FileNotFoundException { for (String path : paths) { validatePathExists(path, optType); } @@ -301,7 +311,6 @@ private void validatePathExists(String path, String argumentName) throws FileNot * * @return the command line options used for parsing the command line */ - @SuppressWarnings("static-access") private Options createCommandLineOptions() { final Options options = new Options(); addStandardOptions(options); @@ -315,10 +324,8 @@ private Options createCommandLineOptions() { * * @param options a collection of command line arguments */ - @SuppressWarnings("static-access") private void addStandardOptions(final Options options) { - //This is an option group because it can be specified more then once. - + //This is an option group because it can be specified more than once. options.addOptionGroup(newOptionGroup(newOptionWithArg(ARGUMENT.SCAN_SHORT, ARGUMENT.SCAN, "path", "The path to scan - this option can be specified multiple times. Ant style paths are supported (e.g. 'path/**/*.jar'); " + "if using Ant style paths it is highly recommended to quote the argument value."))) @@ -357,7 +364,6 @@ private void addStandardOptions(final Options options) { * * @param options a collection of command line arguments */ - @SuppressWarnings("static-access") private void addAdvancedOptions(final Options options) { options .addOption(newOption(ARGUMENT.UPDATE_ONLY, @@ -445,11 +451,11 @@ private void addAdvancedOptions(final Options options) { .addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL, "url", "The Retire JS Repository URL")) .addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_USER, "username", - "The password to authenticate to Retire JS Repository URL")) + "Credentials for basic auth towards the Retire JS Repository URL")) .addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_PASSWORD, "password", - "The password to authenticate to Retire JS Repository URL")) + "Credentials for basic auth towards the Retire JS Repository URL")) .addOption(newOptionWithArg(ARGUMENT.RETIREJS_URL_BEARER_TOKEN, "token", - "The password to authenticate to Retire JS Repository URL")) + "Token for bearer auth towards the Retire JS Repository URL")) .addOption(newOption(ARGUMENT.RETIRE_JS_FILTER_NON_VULNERABLE, "Specifies that the Retire JS " + "Analyzer should filter out non-vulnerable JS files from the report.")) .addOption(newOptionWithArg(ARGUMENT.ARTIFACTORY_PARALLEL_ANALYSIS, "true/false", @@ -571,7 +577,6 @@ private void addAdvancedOptions(final Options options) { * * @param options a collection of command line arguments */ - @SuppressWarnings({"static-access", "deprecation"}) private void addDeprecatedOptions(final Options options) { //not a real option - but enables java debugging via the shell script options.addOption(newOption("debug", @@ -784,26 +789,28 @@ public File getFileArgument(String option) { } /** - * Displays the command line help message to the standard output. + * Appends the command line help message to the passed appendable */ - public void printHelp() { - final HelpFormatter formatter = new HelpFormatter(); + void printHelp(Appendable appendable) { + TextHelpAppendable helpAppendable = new TextHelpAppendable(appendable); + helpAppendable.setMaxWidth(100); + HelpFormatter formatter = HelpFormatter.builder() + .setShowSince(false) + .setComparator(Comparator.comparing(Option::getKey, String::compareToIgnoreCase)) + .setHelpAppendable(helpAppendable) + .get(); + final Options options = new Options(); addStandardOptions(options); if (line != null && line.hasOption(ARGUMENT.ADVANCED_HELP)) { addAdvancedOptions(options); } - final String helpMsg = String.format("%n%s" - + " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. " - + "%s will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.%n%n", - settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck"), - settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck")); - formatter.printHelp(settings.getString(Settings.KEYS.APPLICATION_NAME, "DependencyCheck"), - helpMsg, - options, - "", - true); + try { + formatter.printHelp("dependency-check", HELP_MSG, formatter.sort(options), "", true); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } /** @@ -1005,7 +1012,7 @@ public float getFloatArgument(String option, float defaultValue) { * @return a new option */ private Option newOption(String name, String description) { - return Option.builder().longOpt(name).desc(description).build(); + return Option.builder().longOpt(name).desc(description).get(); } /** @@ -1017,7 +1024,7 @@ private Option newOption(String name, String description) { * @return a new option */ private Option newOption(String shortName, String name, String description) { - return Option.builder(shortName).longOpt(name).desc(description).build(); + return Option.builder(shortName).longOpt(name).desc(description).get(); } /** @@ -1029,7 +1036,7 @@ private Option newOption(String shortName, String name, String description) { * @return a new option */ private Option newOptionWithArg(String name, String arg, String description) { - return Option.builder().longOpt(name).argName(arg).hasArg().desc(description).build(); + return Option.builder().longOpt(name).argName(arg).hasArg().desc(description).get(); } /** @@ -1042,7 +1049,7 @@ private Option newOptionWithArg(String name, String arg, String description) { * @return a new option */ private Option newOptionWithArg(String shortName, String name, String arg, String description) { - return Option.builder(shortName).longOpt(name).argName(arg).hasArg().desc(description).build(); + return Option.builder(shortName).longOpt(name).argName(arg).hasArg().desc(description).get(); } /** diff --git a/cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java b/cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java index aba771547d2..d63337316dc 100644 --- a/cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java +++ b/cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java @@ -24,14 +24,21 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.PrintStream; +import java.io.StringWriter; +import java.util.List; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.toList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInRelativeOrder; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; /** * @@ -47,13 +54,8 @@ class CliParserTest extends BaseTest { @Test void testParse() throws Exception { - String[] args = {}; - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - System.setOut(new PrintStream(baos, true, UTF_8)); - CliParser instance = new CliParser(getSettings()); - instance.parse(args); + instance.parse(); assertFalse(instance.isGetVersion()); assertFalse(instance.isGetHelp()); @@ -160,16 +162,9 @@ void testParse_failOnCVSSValidArgument() throws Exception { @Test void testParse_unknown() { - String[] args = {"-unknown"}; - - ByteArrayOutputStream baos_out = new ByteArrayOutputStream(); - ByteArrayOutputStream baos_err = new ByteArrayOutputStream(); - System.setOut(new PrintStream(baos_out, true, UTF_8)); - System.setErr(new PrintStream(baos_err, true, UTF_8)); - CliParser instance = new CliParser(getSettings()); - ParseException ex = assertThrows(ParseException.class, () -> instance.parse(args) , + ParseException ex = assertThrows(ParseException.class, () -> instance.parse("-unknown"), "Unrecognized option should have caused an exception"); assertTrue(ex.getMessage().contains("Unrecognized option")); @@ -243,7 +238,6 @@ void testParse_scan_withFileExists() throws Exception { * */ @Test - @SuppressWarnings("StringSplitter") void testParse_printVersionInfo() { PrintStream out = System.out; @@ -253,11 +247,7 @@ void testParse_printVersionInfo() { CliParser instance = new CliParser(getSettings()); instance.printVersionInfo(); try { - String text = baos.toString(UTF_8).toLowerCase(); - String[] lines = text.split(System.lineSeparator()); - assertTrue(lines.length >= 1); - assertTrue(text.contains("version")); - assertFalse(text.contains("unknown")); + assertThat(baos.toString(UTF_8), matchesPattern("(?mi)dependency-check.*version [0-9]+.*\n")); } finally { System.setOut(out); } @@ -269,28 +259,32 @@ void testParse_printVersionInfo() { * @throws Exception thrown when an exception occurs. */ @Test - @SuppressWarnings("StringSplitter") void testParse_printHelp() throws Exception { + CliParser instance = new CliParser(getSettings()); + instance.parse("-h"); + StringWriter text = new StringWriter(); + instance.printHelp(text); - PrintStream out = System.out; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - System.setOut(new PrintStream(baos, true, UTF_8)); + List lines = text.toString().lines().collect(toList()); + assertThat(lines, containsInRelativeOrder(startsWith(" usage: dependency-check"), startsWith(" -v, --version"))); + assertThat(lines.size(), greaterThan(10)); + } + /** + * Test of printHelp, of class CliParser. + * + * @throws Exception thrown when an exception occurs. + */ + @Test + void testParse_printHelpAdvanced() throws Exception { CliParser instance = new CliParser(getSettings()); - String[] args = {"-h"}; - instance.parse(args); - instance.printHelp(); - args[0] = "--advancedHelp"; - instance.parse(args); - instance.printHelp(); - try { - String text = (baos.toString(UTF_8)); - String[] lines = text.split(System.lineSeparator()); - assertTrue(lines[0].startsWith("usage: ")); - assertTrue((lines.length > 2)); - } finally { - System.setOut(out); - } + instance.parse("--advancedHelp"); + StringWriter text = new StringWriter(); + instance.printHelp(text); + + List lines = text.toString().lines().collect(toList()); + assertThat(lines, containsInRelativeOrder(startsWith(" usage: dependency-check"), startsWith(" -c, --connectiontimeout"), startsWith(" -v, --version"))); + assertThat(lines.size(), greaterThan(60)); } /** From 4c116672f99cc5d5a923c73db192cccecf20ff41 Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Tue, 19 May 2026 18:32:47 +0800 Subject: [PATCH 3/9] chore(fp): remove unnecessary prometheus suppressions (#8500) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .../dependencycheck-base-suppression.xml | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/core/src/main/resources/dependencycheck-base-suppression.xml b/core/src/main/resources/dependencycheck-base-suppression.xml index 5fb964bc377..8590a2e478e 100644 --- a/core/src/main/resources/dependencycheck-base-suppression.xml +++ b/core/src/main/resources/dependencycheck-base-suppression.xml @@ -859,20 +859,6 @@ cpe:/a:logback:logback cpe:/a:qos:logback - - - ^pkg:maven/io\.prometheus\.jmx/.*$ - CVE-2019-3826 - - - - ^io\.prometheus:simple.*$ - CVE-2019-3826 - ^pkg:maven/com\.github\.romix\.akka/akka\-kryo\-serialization_2\.12@.*$ cpe:/a:akka:akka - - - ^pkg:maven/io\.kamon/kamon\-prometheus_2\.12@.*$ - cpe:/a:prometheus:prometheus - ^io\.springfox:springfox-.+:.*$ cpe:/a:gradle:gradle - - - ^io\.micrometer:micrometer-registry-prometheus:.*$ - CVE-2019-3826 - ^pkg:maven/com\.squareup\.retrofit2?/(?!retrofit).*$ cpe:/a:squareup:retrofit - - - ^io\.prometheus:simpleclient_common:.*$ - CVE-2019-3826 - - - - ^io\.prometheus:simpleclient:.*$ - CVE-2019-3826 - ^pkg:maven/org\.apache\.xmlgraphics/batik\-i18n@.*$ cpe:/a:apache:batik - - - ^pkg:maven\/com\.github\.anti-social[.\/]prometheus-kt.*$ - cpe:/a:prometheus:prometheus - ^pkg:maven/io\.zipkin\.aws/brave-propagation-aws@.*$ cpe:/a:brave:brave - - - ^pkg:maven/io\.quarkus/quarkus-micrometer-registry-prometheus@.*$ - cpe:/a:prometheus:prometheus - ^pkg:maven/me\.dinowernli/java\-grpc\-prometheus@.*$ cpe:/a:grpc:grpc - cpe:/a:prometheus:prometheus Date: Tue, 19 May 2026 18:37:54 +0800 Subject: [PATCH 4/9] test: fix expectations for RetireJS against AngularJS integration test (#8511) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .../org/owasp/dependencycheck/analyzer/RetireJsAnalyzerIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerIT.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerIT.java index d750ce04fcd..bd7e9edd7ec 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerIT.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/RetireJsAnalyzerIT.java @@ -151,7 +151,6 @@ void testAngular() throws Exception { "CVE-2022-25869", "CVE-2024-8373", "CVE-2025-0716", - "CVE-2025-2336", "DOS in $sanitize", "GHSA-28hp-fgcr-2r4h", "GHSA-5cp4-xmrw-59wf", From 63a180c0e52e32a6522ee25b01a04d7c839b6a0c Mon Sep 17 00:00:00 2001 From: Umesh Deshmukh Date: Tue, 19 May 2026 16:16:13 +0530 Subject: [PATCH 5/9] fix: migrate git-commit-id-plugin to maintained io.github.git-commit-id coordinates (#8473) Co-authored-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index be6afa2003e..f30ee01530a 100644 --- a/pom.xml +++ b/pom.xml @@ -434,9 +434,9 @@ Copyright (c) 2012 - Jeremy Long - pl.project13.maven - git-commit-id-plugin - 4.9.10 + io.github.git-commit-id + git-commit-id-maven-plugin + 10.0.0 git-info From e070d60f14436c6bf9af3d8f6e34055378367f80 Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Tue, 19 May 2026 18:47:28 +0800 Subject: [PATCH 6/9] test: make yarn tests agnostic of corepack defaults (#8413) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .../owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java | 1 - .../yarn/yarn-classic-audit-bad-berry-lockfile/package.json | 5 +++-- core/src/test/resources/yarn/yarn-classic-audit/package.json | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java index 32b1c1a6439..c3474e96086 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java @@ -63,7 +63,6 @@ void testAnalyzePackageYarnClassic() throws Exception { testAnalyzeForUglifyJs("yarn/yarn-classic-audit/yarn.lock"); } - @Disabled("broken test case on my local machine - needs further investigation") @Test void testAnalyzePackageYarnClassicOnYarnBerryLockfile() { AnalysisException exception = assertThrows(AnalysisException.class, () -> testAnalyzeForUglifyJs("yarn/yarn-classic-audit-bad-berry-lockfile/yarn.lock")); diff --git a/core/src/test/resources/yarn/yarn-classic-audit-bad-berry-lockfile/package.json b/core/src/test/resources/yarn/yarn-classic-audit-bad-berry-lockfile/package.json index d46b8308f70..fb923d5d14d 100644 --- a/core/src/test/resources/yarn/yarn-classic-audit-bad-berry-lockfile/package.json +++ b/core/src/test/resources/yarn/yarn-classic-audit-bad-berry-lockfile/package.json @@ -5,7 +5,7 @@ "description": "A tool to learn OWASP Top 10 for node.js developers", "main": "server.js", "dependencies": { - "mime-db": "^1.52.0" + "does-not-exist": "file:../does-not-exist" }, "scripts": { "start": "node server.js", @@ -14,5 +14,6 @@ "precommit": "grunt precommit" }, "repository": "https://github.com/OWASP/NodejsGoat", - "license": "Apache 2.0" + "license": "Apache 2.0", + "packageManager": "yarn@1.22.22" } diff --git a/core/src/test/resources/yarn/yarn-classic-audit/package.json b/core/src/test/resources/yarn/yarn-classic-audit/package.json index b2fc8eaf8f0..ba0bfadbfed 100644 --- a/core/src/test/resources/yarn/yarn-classic-audit/package.json +++ b/core/src/test/resources/yarn/yarn-classic-audit/package.json @@ -57,5 +57,6 @@ "zaproxy": "^0.2.0" }, "repository": "https://github.com/OWASP/NodejsGoat", - "license": "Apache 2.0" + "license": "Apache 2.0", + "packageManager": "yarn@1.22.22" } From 3904c968713260d1fc21c8bdc0e1a467b32d9184 Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Tue, 19 May 2026 18:58:08 +0800 Subject: [PATCH 7/9] chore(fp): remove unnecessary grpc suppressions (#8505) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Co-authored-by: Jeremy Long --- .../dependencycheck-base-suppression.xml | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/core/src/main/resources/dependencycheck-base-suppression.xml b/core/src/main/resources/dependencycheck-base-suppression.xml index 8590a2e478e..e03d24d74df 100644 --- a/core/src/main/resources/dependencycheck-base-suppression.xml +++ b/core/src/main/resources/dependencycheck-base-suppression.xml @@ -95,13 +95,6 @@ ^pkg:maven/org\.zalando/spring\-boot\-etcd@.*$ cpe:/a:etcd:etcd - - - ^pkg:maven/com\.salesforce\.servicelibs/reactive\-grpc.*$ - cpe:/a:grpc:grpc - ^pkg:maven/io\.helidon\.microprofile\.server/helidon\-microprofile\-server@.*$ cpe:/a:oracle:http_server - - - ^pkg:maven/co\.elastic\.apm/apm\-.*$ - cpe:/a:grpc:grpc - cpe:/a:apache:httpclient - ^pkg:maven/.*vertx-pg-client@.*$ cpe:/a:postgresql:postgresql - - - ^pkg:maven/io\.opencensus/opencensus\-contrib\-grpc\-metrics@.*$ - cpe:/a:grpc:grpc - - - https://nvd.nist.gov/vuln/search#/nvd/home?sortOrder=1&sortDirection=1&cpeFilterMode=applicability&cpeName=cpe:2.3:a:grpc:grpc:*:*:*:*:*:*:*:*&resultType=records - ]]> - ^pkg:maven/io\.grpc/grpc\-.*$ - CVE-2017-7860 - CVE-2017-7861 - CVE-2017-8359 - CVE-2017-9431 - CVE-2020-7768 - CVE-2023-1428 - CVE-2023-32731 - CVE-2023-32732 - CVE-2023-33953 - CVE-2023-4785 - CVE-2024-11407 - CVE-2024-7246 - - - - ^pkg:maven/com\.google\.api\.grpc/grpc\-google\-common\-protos@.*$ - cpe:/a:grpc:grpc - - - - ^pkg:maven/com\.lightstep\.tracer/.*$ - cpe:/a:grpc:grpc - ^com\.typesafe\.akka:akka-persistence-cassandra:.*$ cpe:/a:akka:akka - - - ^pkg:maven/io\.opencensus/opencensus\-contrib\-grpc\-util@.*$ - cpe:/a:grpc:grpc - - - - ^com\.google\.api\.grpc:proto-.*$ - cpe:/a:grpc:grpc - ^pkg:maven/com\.google\.flatbuffers/flatbuffers-java@.*$ cpe:/a:flat(_project)?:flat.* - - - ^pkg:maven/me\.dinowernli/java\-grpc\-prometheus@.*$ - cpe:/a:grpc:grpc - Date: Tue, 19 May 2026 19:14:15 +0800 Subject: [PATCH 8/9] chore(fp): use more conservative CPE 22 prefix suppression syntax to avoid false negatives (#8509) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Co-authored-by: Jeremy Long --- .github/workflows/false-positive-ops.yml | 24 +- .../dependencycheck-base-suppression.xml | 1410 +++++++++-------- 2 files changed, 727 insertions(+), 707 deletions(-) diff --git a/.github/workflows/false-positive-ops.yml b/.github/workflows/false-positive-ops.yml index 2ecfa1b1068..ceb70fdfa3e 100644 --- a/.github/workflows/false-positive-ops.yml +++ b/.github/workflows/false-positive-ops.yml @@ -171,11 +171,11 @@ jobs: } purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); - var matchCpe; + var cpe22UriPrefix; if (cpe[1] == '2.3') { - matchcpe = 'cpe:/a:' + cpe[3] + ':' + cpe[4]; + cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; } else { - matchcpe = 'cpe:/a:' + cpe[2] + ':' + cpe[3]; + cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; } await github.rest.issues.createComment({ @@ -189,7 +189,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + matchcpe + '\n' + + ' ' + cpe22UriPrefix + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) @@ -218,11 +218,11 @@ jobs: purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); console.log(cpe); - var matchCpe; + var cpe22UriPrefix; if (cpe[1] == '2.3') { - matchcpe = 'cpe:/a:' + cpe[3] + ':' + cpe[4]; + cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; } else { - matchcpe = 'cpe:/a:' + cpe[2] + ':' + cpe[3]; + cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; } await github.rest.issues.createComment({ @@ -236,7 +236,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + matchcpe + '\n' + + ' ' + cpe22UriPrefix + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) @@ -264,11 +264,11 @@ jobs: } purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); - var matchCpe; + var cpe22UriPrefix; if (cpe[1] == '2.3') { - matchcpe = 'cpe:/a:' + cpe[3] + ':' + cpe[4]; + cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; } else { - matchcpe = 'cpe:/a:' + cpe[2] + ':' + cpe[3]; + cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; } await github.rest.issues.createComment({ @@ -282,7 +282,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + matchcpe + '\n' + + ' ' + cpe22UriPrefix + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) diff --git a/core/src/main/resources/dependencycheck-base-suppression.xml b/core/src/main/resources/dependencycheck-base-suppression.xml index e03d24d74df..01d0a2b944b 100644 --- a/core/src/main/resources/dependencycheck-base-suppression.xml +++ b/core/src/main/resources/dependencycheck-base-suppression.xml @@ -8,14 +8,14 @@ obvious fp - currently not returning any CVEs ]]> ^pkg:maven/commons\-cli/commons\-cli@.*$ - cpe:/a:spirit-project:spirit + cpe:/a:spirit-project:spirit: ^pkg:maven/javax\.xml\.bind/jaxb\-api@.*$ - cpe:/a:oracle:java_se + cpe:/a:oracle:java_se: ^pkg:maven/javax\.ws\.rs/javax\.ws\.rs\-api@.*$ - cpe:/a:oracle:web_services + cpe:/a:oracle:web_services: ^pkg:maven/org\.sonarsource\.scanner\.gradle/sonarqube-gradle-plugin@.*$ - cpe:/a:sonarsource:sonarqube + cpe:/a:sonarsource:sonarqube: ^pkg:maven/io\.quarkus/quarkus\-fs\-util@.*$ - cpe:/a:quarkus:quarkus + cpe:/a:quarkus:quarkus: ^pkg:maven/org\.apache\.james/apache\-mime4j\-storage@.*$ cpe:/a:storage(_project)?:storage.* - cpe:/a:apache:james + cpe:/a:apache:james: ^pkg:maven/io\.quarkus/quarkus\-hibernate\-orm.*$ - cpe:/a:hibernate:hibernate_orm + cpe:/a:hibernate:hibernate_orm: ^pkg:maven/io\.quarkus/quarkus\-hibernate\-validator@.*$ - cpe:/a:redhat:hibernate_validator + cpe:/a:redhat:hibernate_validator: .*unicode.*$ - cpe:/a:unicode:unicode + cpe:/a:unicode:unicode: ^pkg:maven/com\.typesafe\.akka/akka\-stream\-contrib_2\.13@.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^pkg:maven/org\.zalando/spring\-boot\-etcd@.*$ - cpe:/a:etcd:etcd + cpe:/a:etcd:etcd: ^pkg:maven/org\.apache\.geronimo\.config/geronimo\-config\-impl@.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^pkg:maven/io\.reactivex/rxjava@.*$ - cpe:/a:travis-ci:travis_ci + cpe:/a:travis-ci:travis_ci: ^pkg:maven/org\.seleniumhq\.selenium/selenium\-chromium\-driver@.*$ - cpe:/a:chromium:chromium + cpe:/a:chromium:chromium: cpe:/a:chromium(_project)?:chromium.* @@ -129,7 +129,7 @@ FP per #3572 ]]> ^pkg:maven/org\.spark\-project\.spark/unused@.*$ - cpe:/a:apache:spark + cpe:/a:apache:spark: ^pkg:swift/swift\-log@.*$ - cpe:/a:apple:swift + cpe:/a:apple:swift: ^io\.awspring\.cloud:spring-cloud-.*$ - cpe:/a:pivotal_software:spring_framework - cpe:/a:pivotal_software:spring_framework + cpe:/a:pivotal_software:spring_framework: + cpe:/a:pivotal_software:spring_framework: cpe:/a:context(_project)?:context.* @@ -174,60 +174,60 @@ ]]> ^pkg:maven/io\.github\.x\-stream/mxparser@.*$ cpe:/a:xstream(_project)?:xstream.* - cpe:/a:oracle:jdk + cpe:/a:oracle:jdk: ^pkg:maven/io\.pivotal\.cfenv/java\-cfenv\-boot\-pivotal\-scs@.*$ - cpe:/a:pivotal_software:spring_boot - cpe:/a:pivotal_software:spring_framework + cpe:/a:pivotal_software:spring_boot: + cpe:/a:pivotal_software:spring_framework: ^pkg:maven/org\.seleniumhq\.selenium/selenium\-opera\-driver@.*$ - cpe:/a:opera:opera - cpe:/a:opera_software:opera + cpe:/a:opera:opera: + cpe:/a:opera_software:opera: ^pkg:(generic|nuget)/(ProcessSnapshotCleanup|QTAgent.{0,10}|QTDCAgent|QTDCAgent32|TDEnvCleanup|UiaComWrapper|VSTestVideoRecorder|VisualStudioDatastoreConfigurationProvider|msdia140typelib_clr0200)@.*$ - cpe:/a:microsoft:visual_studio + cpe:/a:microsoft:visual_studio: ^pkg:(generic|nuget)/(Microsoft\.)?(IntelliTrace|TestPlatform|VisualStudio\.).*$ - cpe:/a:microsoft:visual_studio - cpe:/a:microsoft:ie + cpe:/a:microsoft:visual_studio: + cpe:/a:microsoft:ie: .*\.TestTools\..*\.dll - cpe:/a:microsoft:visual_studio - cpe:/a:microsoft:ie + cpe:/a:microsoft:visual_studio: + cpe:/a:microsoft:ie: ^pkg:(generic|nuget)/Microsoft\.Dia.*$ - cpe:/a:dia:dia + cpe:/a:dia:dia: ^pkg:maven/io\.helidon\.microprofile\.server/helidon\-microprofile\-server@.*$ - cpe:/a:oracle:http_server + cpe:/a:oracle:http_server: ^pkg:maven/io\.r2dbc/r2dbc\-postgresql@.*$ - cpe:/a:postgresql:postgresql + cpe:/a:postgresql:postgresql: ^pkg:maven/.*vertx-pg-client@.*$ - cpe:/a:postgresql:postgresql + cpe:/a:postgresql:postgresql: ^pkg:maven/com\.google\.http\-client/google\-http\-client@.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/org\.mybatis/mybatis\-spring@.*$ - cpe:/a:mybatis:mybatis + cpe:/a:mybatis:mybatis: ^pkg:(generic|nuget)/Microsoft\.IdentityModel\.Protocols\.OpenIdConnect@.*$ - cpe:/a:microsoft:identitymodel - cpe:/a:openid:openid - cpe:/a:openid:openid_connect + cpe:/a:microsoft:identitymodel: + cpe:/a:openid:openid: + cpe:/a:openid:openid_connect: ^pkg:composer/[^/]+/[^/]+@.*$ - cpe:/a:php:php + cpe:/a:php:php: ^pkg:composer/symfony/(polyfill-|ux-|panther|webpack-encore-bundle|mercure@|mercure-bundle|monolog-bundle).*$ - cpe:/a:sensiolabs:symfony + cpe:/a:sensiolabs:symfony: ^pkg:composer/symfony/.*(contracts|-pack)@.*$ - cpe:/a:sensiolabs:symfony + cpe:/a:sensiolabs:symfony: ^pkg:composer/(?!pimcore/pimcore@).*$ - cpe:/a:pimcore:pimcore + cpe:/a:pimcore:pimcore: ^pkg:composer/(?!pear/pear@|pear/pear-core-minimal@).*$ - cpe:/a:pear:pear + cpe:/a:pear:pear: ^pkg:composer/(?!laravel/framework@|laravel/laravel@).*$ - cpe:/a:laravel:laravel - cpe:/a:laravel:framework + cpe:/a:laravel:laravel: + cpe:/a:laravel:framework: ^pkg:composer/(?!drupal/services@).*$ - cpe:/a:services_project:services + cpe:/a:services_project:services: ^pkg:composer/(?!doctrine/orm@).*$ - cpe:/a:doctrine-project:doctrine + cpe:/a:doctrine-project:doctrine: ^pkg:maven/org\.jboss\.resteasy/resteasy\-spring\-boot\-starter@.*$ - cpe:/a:redhat:resteasy + cpe:/a:redhat:resteasy: ^pkg:maven/com\.github\.docker\-java/docker\-java.*$ - cpe:/a:docker:docker + cpe:/a:docker:docker: ^pkg:maven/io\.micrometer/micrometer\-core@.*$ - cpe:/a:vmware:ace - cpe:/a:vmware:vmware_ace + cpe:/a:vmware:ace: + cpe:/a:vmware:vmware_ace: ^pkg:maven/ch\.qos\.logback\.contrib/.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: ^pkg:maven/be\.sysa\.log\-sanitizer/log\-sanitizer\-logback@.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: ^pkg:maven/org\.graalvm.*$ - cpe:/a:oracle:openjdk - cpe:/a:sun:openjdk + cpe:/a:oracle:openjdk: + cpe:/a:sun:openjdk: ^pkg:maven/org\.graalvm\..*$ - cpe:/a:oracle:graalvm + cpe:/a:oracle:graalvm: ^pkg:maven/org\.graalvm/graal\-sdk@.*$ - cpe:/a:oracle:graalvm + cpe:/a:oracle:graalvm: ^pkg:maven/org\.graalvm/launcher\-common@.*$ - cpe:/a:oracle:graalvm + cpe:/a:oracle:graalvm: ^pkg:maven/org\.graalvm/polyglot\-tck@.*$ - cpe:/a:oracle:graalvm + cpe:/a:oracle:graalvm: ^pkg:maven/org\.netbeans\.external/.*$ - cpe:/a:apache:netbeans + cpe:/a:apache:netbeans: ^pkg:maven/com\.itextpdf\.tool/xfa\-.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/com\.itextpdf/xfa\-.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/com\.itextpdf/typography@.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/com\.itextpdf/itext\-licensekey\-volume@.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/org\.testcontainers/postgresql@.*$ - cpe:/a:postgresql:postgresql + cpe:/a:postgresql:postgresql: ^pkg:maven/org\.msgpack/msgpack@.*$ - cpe:/a:messagepack:messagepack + cpe:/a:messagepack:messagepack: ^pkg:maven/io\.dropwizard\.logback/logback\-throttling\-appender@.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: .*EntityFramework\.SqlServer*\.dll - cpe:/a:microsoft:server:6.0.0.0 - cpe:/a:microsoft:sql_server:6.0 + cpe:/a:microsoft:server:6.0.0.0: + cpe:/a:microsoft:sql_server:6.0: ^pkg:maven/org\.bouncycastle.*$ - cpe:/a:oracle:jdk + cpe:/a:oracle:jdk: .*spring-security-[^\\/]*\.jar$ - cpe:/a:mod_security:mod_security - cpe:/a:springsource:spring_framework - cpe:/a:vmware:springsource_spring_framework - cpe:/a:pivotal:spring_framework - cpe:/a:pivotal_software:spring_framework + cpe:/a:mod_security:mod_security: + cpe:/a:springsource:spring_framework: + cpe:/a:vmware:springsource_spring_framework: + cpe:/a:pivotal:spring_framework: + cpe:/a:pivotal_software:spring_framework: com\.thoughtworks\.xstream:xstream:.* - cpe:/a:springsource:spring_framework + cpe:/a:springsource:spring_framework: org\.apache\.velocity:velocity-tools:.* - cpe:/a:apache:struts + cpe:/a:apache:struts: cpe:/a:json-jwt_project:json-jwt is a ruby lib #1791 - 39. cpe:/a:zip_project:zip is an etherium related project #1788 - 40. cpe:/a:echo_project:echo is a php media wiki project #1786 - 41. cpe:/a:util-linux_project:util-linux c util on linux #2069 - 42. cpe:/a:bitmap_project:bitmap is a C library #1961 - 43. cpe:/a:security-framework_project:security-framework is a rust library - 44. cpe:/a:next:next is NeXT system. - 45. cpe:/a:property_pro:property_pro is classic ASP + 38. cpe:/a:json-jwt_project:json-jwt: is a ruby lib #1791 + 39. cpe:/a:zip_project:zip: is an etherium related project #1788 + 40. cpe:/a:echo_project:echo: is a php media wiki project #1786 + 41. cpe:/a:util-linux_project:util-linux: c util on linux #2069 + 42. cpe:/a:bitmap_project:bitmap: is a C library #1961 + 43. cpe:/a:security-framework_project:security-framework: is a rust library + 44. cpe:/a:next:next: is NeXT system. + 45. cpe:/a:property_pro:property_pro: is classic ASP 46. CVE-2020-10663 is a ruby vulnerability 47. Facebook is not a dependency 48. gitlab is not a depenency #2567 and is built using ruby @@ -598,41 +598,41 @@ 65. cpe:/a:web_project:web is a Web Server library in Go ]]> .*(\.(dll|jar|ear|war|pom|nupkg|nuspec|aar)|pom\.xml|package.json|packages.config)$ - cpe:/a:sandbox:sandbox - cpe:/a:openmedia:openmedia - cpe:/a:file_project:file - cpe:/a:file:file - cpe:/a:shim:shim + cpe:/a:sandbox:sandbox: + cpe:/a:openmedia:openmedia: + cpe:/a:file_project:file: + cpe:/a:file:file: + cpe:/a:shim:shim: cpe:/a:shim(_project)?:shim.* cpe:/a:date(_project)?:date.* - cpe:/a:net_dns:net_dns - cpe:/a:nodejs:node.js - cpe:/a:nodejs:nodejs + cpe:/a:net_dns:net_dns: + cpe:/a:nodejs:node.js: + cpe:/a:nodejs:nodejs: cpe:/a:context(_project)?:context.* cpe:/a:mail(_project)?:mail.* cpe:/a:ldap(_project)?:ldap.* cpe:/a:user_import(_project)?:user_import.* - cpe:/a:root:root + cpe:/a:root:root: cpe:/a:xmlsec(_project)?:xmlsec.* cpe:/a:rest-client(_project)?:rest-client.* cpe:/a:hub(_project)?:hub.* cpe:/a:views(_project)?:views.* cpe:/a:restful_web_services(_project)?:restful_web_services.* - cpe:/a:php:php + cpe:/a:php:php: cpe:/a:font(_project)?:font.* cpe:/a:amazon_aws(_project)?:amazon_aws.* - cpe:/a:google:android + cpe:/a:google:android: cpe:/a:first(_project)?:first.* - cpe:/a:interact:interact + cpe:/a:interact:interact: cpe:/a:finder(_project)?:finder.* cpe:/a:archiver(_project)?:archiver.* cpe:/a:r(_project)?:r.* cpe:/a:jwt(_project)?:jwt.* cpe:/a:git(_project)?:git.* - cpe:/a:git:git + cpe:/a:git:git: cpe:/a:git_for_windows(_project)?:git_for_windows.* - cpe:/a:mono-project:mono - cpe:/a:mono:mono + cpe:/a:mono-project:mono: + cpe:/a:mono:mono: cpe:/a:mono(_project)?:mono.* cpe:/a:app(_project)?:app.* cpe:/a:json-jwt(_project)?:json-jwt.* @@ -641,25 +641,25 @@ cpe:/a:util-linux(_project)?:util-linux.* cpe:/a:bitmap(_project)?:bitmap.* cpe:/a:security-framework(_project)?:security-framework.* - cpe:/a:next:next - cpe:/a:property_pro:property_pro + cpe:/a:next:next: + cpe:/a:property_pro:property_pro: CVE-2020-10663 - cpe:/a:facebook:facebook - cpe:/a:gitlab:gitlab - cpe:/a:delegate:delegate + cpe:/a:facebook:facebook: + cpe:/a:gitlab:gitlab: + cpe:/a:delegate:delegate: cpe:/a:thread(_project)?:thread.* cpe:/a:data_tools(_project)?:data_tools.* cpe:/a:tag(_project)?:tag.* - cpe:/a:kubernetes:kubernetes + cpe:/a:kubernetes:kubernetes: cpe:/a:websockets(_project)?:websockets.* cpe:/a:cron(_project)?:cron.* cpe:/a:html2pdf(_project)?:html2pdf.* cpe:/a:shadow(_project)?:shadow.* - cpe:/a:cde:cde - cpe:/a:docker:docker - cpe:/a:travis-ci:travis_ci + cpe:/a:cde:cde: + cpe:/a:docker:docker: + cpe:/a:travis-ci:travis_ci: cpe:/a:storage(_project)?:storage.* - cpe:/a:pivotal_software:rabbitmq + cpe:/a:pivotal_software:rabbitmq: cpe:/a:saml(_project)?:saml.* cpe:/a:yaml(_project)?:yaml.* cpe:/a:web(_project)?:web.* @@ -680,7 +680,7 @@ 11. sqlserver_project is a node js module (#1388) 12. auth0 is a javascript library (#1925) 13. JAR files should not be identified as github - 14. cpe:/a:data-tools_project:data_tools is a python project #1961 + 14. cpe:/a:data-tools_project:data_tools: is a python project #1961 15. flow_project reports an Ethereum token issue 16. ghost CMS is a node.js package (#3203) 17. ws_project is a node websocket client (#1535) @@ -689,26 +689,26 @@ 20. perl:perl is a C project #4254 ]]> .*(\.(jar|ear|war|pom)|pom\.xml)$ - cpe:/a:dash:dash + cpe:/a:dash:dash: cpe:/a:mustache.js(_project)?:mustache.js - cpe:/a:microsoft:active_directory - cpe:/a:microsoft:active_directory_federation_services - cpe:/a:microsoft:active_directory_services - cpe:/a:snmp:snmp - cpe:/a:net-snmp:net-snmp - cpe:/a:python:python - cpe:/a:python_software_foundation:python + cpe:/a:microsoft:active_directory: + cpe:/a:microsoft:active_directory_federation_services: + cpe:/a:microsoft:active_directory_services: + cpe:/a:snmp:snmp: + cpe:/a:net-snmp:net-snmp: + cpe:/a:python:python: + cpe:/a:python_software_foundation:python: CVE-2017-16046 cpe:/a:sqlserver(_project)?:sqlserver - cpe:/a:auth0:auth0 - cpe:/a:github:github + cpe:/a:auth0:auth0: + cpe:/a:github:github: cpe:/a:data-tools(_project)?:data_tools.* cpe:/a:flow(_project)?:flow.* - cpe:/a:ghost:ghost + cpe:/a:ghost:ghost: cpe:/a:ws(_project)?:ws.* cpe:/a:i18n(_project)?:i18n.* - cpe:/a:redis:redis - cpe:/a:perl:perl + cpe:/a:redis:redis: + cpe:/a:perl:perl: ^pkg:maven/org\.apache\.poi/ooxml\-schemas@.*$ - cpe:/a:apache:poi + cpe:/a:apache:poi: ^pkg:maven/org\.jboss\.eap/wildfly\-client\-properties@.*$ - cpe:/a:redhat:wildfly + cpe:/a:redhat:wildfly: ^pkg:(generic|nuget)/(microsoft\.)?jQuery[\.-].*$ - cpe:/a:jquery:jquery + cpe:/a:jquery:jquery: ^pkg:(generic|nuget)/System\.Security\.Cryptography\.OpenSsl@.*$ - cpe:/a:openssl:openssl + cpe:/a:openssl:openssl: ^pkg:(generic|nuget)/Microsoft\.Bcl\.AsyncInterfaces@.*$ - cpe:/a:microsoft:.net_core + cpe:/a:microsoft:.net_core: ^pkg:(generic|nuget)/Microsoft\.DiaSymReader\.Native\.arm@.*$ - cpe:/a:microsoft:visual_studio - cpe:/a:microsoft:visual_studio_2017 + cpe:/a:microsoft:visual_studio: + cpe:/a:microsoft:visual_studio_2017: ^pkg:(generic|nuget)/Microsoft\.DiaSymReader\.Native@.*$ - cpe:/a:dia:dia + cpe:/a:dia:dia: ^pkg:maven/(io\.quarkus/quarkus\-|io\.smallrye).*mutiny.*@.*$ - cpe:/a:mutiny:mutiny + cpe:/a:mutiny:mutiny: ^org\.mitre:openid-connect-client:.*$ - cpe:/a:openid:openid_connect + cpe:/a:openid:openid_connect: ^de\.siegmar:logback-gelf:.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: ^pkg:maven/io\.prometheus/simpleclient_logback@.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: (com\.sun\.jersey|org\.glassfish\.jersey\.core):jersey-(client|common):.* - cpe:/a:oracle:glassfish - cpe:/a:oracle:oracle_client + cpe:/a:oracle:glassfish: + cpe:/a:oracle:oracle_client: ^pkg:maven/org\.ehcache/ehcache@.*$ - cpe:/a:gradle:gradle + cpe:/a:gradle:gradle: ^com\.atlassian\.http:atlassian-http:.*$ - cpe:/a:atlassian:bitbucket + cpe:/a:atlassian:bitbucket: ^com\.cybersource:flex-server-sdk:.*$ cpe:/a:flex(_project)?:flex.* - cpe:/a:id:id-software + cpe:/a:id:id-software: ^pkg:maven/com\.atlassian\.httpclient/atlassian\-httpclient\-library@.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/io\.scanbot/scanbot\-sdk\-imageprocessing@.*$ - cpe:/a:image_processing_software:image_processing_software + cpe:/a:image_processing_software:image_processing_software: ^pkg:maven/co\.elastic\.apm/apm\-grails\-plugin@.*$ - cpe:/a:grails:grails + cpe:/a:grails:grails: ^pkg:maven/org\.apache\.geronimo/geronimo\-(health|metrics(-common)?)@.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^pkg:maven/co\.elastic\.logging/logback\-ecs\-encoder@.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: ^pkg:maven/com\.typesafe\.play/shaded\-oauth@.*$ - cpe:/a:playframework:play_framework + cpe:/a:playframework:play_framework: ^pkg:maven/com\.typesafe\.play/cachecontrol_2\.13@.*$ - cpe:/a:playframework:play_framework + cpe:/a:playframework:play_framework: ^pkg:maven/org\.apache\.camel/camel\-servicenow(\-starter)?@.*$ - cpe:/a:apache:camel - cpe:/a:servicenow:servicenow + cpe:/a:apache:camel: + cpe:/a:servicenow:servicenow: ^pkg:maven/org\.apache\.camel/json\-simple\-ordered@.*$ - cpe:/a:apache:camel + cpe:/a:apache:camel: ^pkg:maven/org\.apache\.camel/camel\-cxf@.*$ - cpe:/a:apache:cxf + cpe:/a:apache:cxf: ^pkg:maven/org\.apache\.camel/camel\-cxf\-transport@.*$ - cpe:/a:apache:cxf + cpe:/a:apache:cxf: ^pkg:maven/com\.atlassian\.jira/jira\-rest\-java\-client\-api@.*$ - cpe:/a:atlassian:jira - cpe:/a:atlassian:jira_core + cpe:/a:atlassian:jira: + cpe:/a:atlassian:jira_core: ^pkg:maven/com\.atlassian\.jira/jira\-rest\-java\-client\-core@.*$ - cpe:/a:atlassian:jira - cpe:/a:atlassian:jira_core + cpe:/a:atlassian:jira: + cpe:/a:atlassian:jira_core: ^pkg:maven/com\.atlassian\.jira/jira\-rest\-java\-client\-app@.*$ - cpe:/a:atlassian:jira - cpe:/a:atlassian:jira_core + cpe:/a:atlassian:jira: + cpe:/a:atlassian:jira_core: ^pkg:maven/com\.github\.docker\-java/docker\-java@.*$ - cpe:/a:docker:docker + cpe:/a:docker:docker: ^pkg:maven/com\.itextpdf/itext\-licensekey@.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/com\.datomic/(?!datomic-free).*$ - cpe:/a:cognitect:datomic + cpe:/a:cognitect:datomic: ^pkg:maven/com\.liferay\.portal/com\.liferay\.util\.slf4j@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.portal\.impl@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.support\.tomcat@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.util\.bridges@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.util\.java@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.util\.taglib@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.portal\.test@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portal/com\.liferay\.portal\.kernel@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay\.portletmvc4spring/com\.liferay\.portletmvc4spring\.test@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^pkg:maven/com\.liferay/biz\.aQute\.bnd\.annotation@.*$ - cpe:/a:liferay:liferay - cpe:/a:liferay:liferay_portal - cpe:/a:liferay:portal + cpe:/a:liferay:liferay: + cpe:/a:liferay:liferay_portal: + cpe:/a:liferay:portal: ^org\.apache\.geronimo\.javamail:geronimo-javamail_1\.4_mail:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^geronimo-spec:geronimo-spec-javamail:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^org\.apache\.geronimo\.javamail:geronimo-javamail_1\.4_provider:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^pkg:maven/com\.nimbusds/oauth2\-oidc\-sdk@.*$ - cpe:/a:openid:openid - cpe:/a:openid:openid_connect + cpe:/a:openid:openid: + cpe:/a:openid:openid_connect: ^pkg:maven/com\.azure/azure\-core\-http\-netty@.*$ - cpe:/a:netty:netty + cpe:/a:netty:netty: ^pkg:(nuget|generic)/Azure\.Core(_sdk)?@.*$ - cpe:/a:microsoft:.net_core + cpe:/a:microsoft:.net_core: ^pkg:maven/com\.nimbusds/oauth2\-oidc\-sdk@.*$ - cpe:/a:openid:openid - cpe:/a:openid:openid_connect + cpe:/a:openid:openid: + cpe:/a:openid:openid_connect: ^org\.apache\.activemq:artemis.*$ - cpe:/a:apache:apache_http_server - cpe:/a:apache:http_server - cpe:/a:apache:activemq + cpe:/a:apache:apache_http_server: + cpe:/a:apache:http_server: + cpe:/a:apache:activemq: com\.sun\.jersey\.contribs:jersey-apache-client.* - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: org\.glassfish(\.(web|grizzly)):.*(json|faces|jstl|grizzly).* - cpe:/a:oracle:glassfish - cpe:/a:oracle:glassfish_server + cpe:/a:oracle:glassfish: + cpe:/a:oracle:glassfish_server: ^pkg:maven/io\.kamon/kamon\-akka.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^pkg:maven/io\.undertow\..*$ - cpe:/a:redhat:undertow - cpe:/a:oracle:jsp + cpe:/a:redhat:undertow: + cpe:/a:oracle:jsp: ^pkg:(generic|nuget)/Microsoft\.AspNetCore\..*$ - cpe:/a:microsoft:aspnetcore - cpe:/a:microsoft:asp.net_core - cpe:/a:microsoft:asp.net + cpe:/a:microsoft:aspnetcore: + cpe:/a:microsoft:asp.net_core: + cpe:/a:microsoft:asp.net: ^pkg:(generic|nuget)/Microsoft\.Net\..*$ - cpe:/a:microsoft:aspnetcore - cpe:/a:microsoft:asp.net_core - cpe:/a:microsoft:asp.net + cpe:/a:microsoft:aspnetcore: + cpe:/a:microsoft:asp.net_core: + cpe:/a:microsoft:asp.net: ^pkg:(generic|nuget)/Microsoft\.Extensions\..*$ - cpe:/a:microsoft:aspnetcore - cpe:/a:microsoft:asp.net_core - cpe:/a:microsoft:asp.net + cpe:/a:microsoft:aspnetcore: + cpe:/a:microsoft:asp.net_core: + cpe:/a:microsoft:asp.net: ^pkg:(generic|nuget)/Swashbuckle\..*$ - cpe:/a:asp-project:asp-project + cpe:/a:asp-project:asp-project: ^pkg:(generic|nuget)/Microsoft\.VisualStudio\.Web\.CodeGenerat.*$ - cpe:/a:microsoft:visual_studio_code + cpe:/a:microsoft:visual_studio_code: ^pkg:(generic|nuget)/Microsoft\.Win32\.Registry\.AccessControl@.*$ - cpe:/a:microsoft:access + cpe:/a:microsoft:access: ^com\.github\.danielwegener:logback-kafka-appender:.*$ - cpe:/a:logback:logback - cpe:/a:qos:logback + cpe:/a:logback:logback: + cpe:/a:qos:logback: ^org\.apache\.httpcomponents:httpmime:.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/org\.eclipse\.jetty/jetty\-reactive\-httpclient@.*$ - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: ^pkg:maven/org\.eclipse\.jetty\.websocket/websocket-api@.*$ - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: ^com\.facebook\.android:facebook-android-sdk:.*$ - cpe:/a:facebook:facebook + cpe:/a:facebook:facebook: ^com\.amazonaws:aws-android-sdk-cognitoidentityprovider-asf:.*$ - cpe:/a:android:android_sdk + cpe:/a:android:android_sdk: ^pkg:maven/com\.hazelcast/hazelcast\-aws@.*$ - cpe:/a:hazelcast:hazelcast + cpe:/a:hazelcast:hazelcast: ^pkg:maven/com\.hazelcast/hazelcast\-kubernetes@.*$ - cpe:/a:hazelcast:hazelcast + cpe:/a:hazelcast:hazelcast: ^pkg:maven/com\.hazelcast/hazelcast\-client\-protocol@.*$ - cpe:/a:hazelcast:hazelcast + cpe:/a:hazelcast:hazelcast: ^pkg:maven/com\.rabbitmq/amqp\-client@.*$ - cpe:/a:pivotal_software:rabbitmq + cpe:/a:pivotal_software:rabbitmq: ^pkg:maven/io\.zipkin\.reporter2/zipkin\-sender\-activemq\-client@.*$ - cpe:/a:apache:activemq + cpe:/a:apache:activemq: ^pkg:maven/com\.lightbend\.akka\.management/akka\-management_2\.12@.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^pkg:maven/com\.github\.romix\.akka/akka\-kryo\-serialization_2\.12@.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: + + + + ^pkg:maven/io\.kamon/kamon\-prometheus_2\.12@.*$ + cpe:/a:prometheus:prometheus: ^pkg:maven/org\.jetbrains\.kotlinx/.*$ - cpe:/a:jetbrains:kotlin + cpe:/a:jetbrains:kotlin: ^org\.jetbrains:annotations:.*$ - cpe:/a:jetbrains:intellij_idea + cpe:/a:jetbrains:intellij_idea: ^pkg:maven/org\.openapitools/jackson\-databind\-nullable@.*$ - cpe:/a:fasterxml:jackson - cpe:/a:fasterxml:jackson-databind + cpe:/a:fasterxml:jackson: + cpe:/a:fasterxml:jackson-databind: org\.opensaml:xmltooling:.* - cpe:/a:shibboleth:opensaml - cpe:/a:internet2:opensaml + cpe:/a:shibboleth:opensaml: + cpe:/a:internet2:opensaml: CVE-2015-0851 CVE-2019-9628 @@ -1522,62 +1529,62 @@ Suppresses false positives on the org.opensaml:openws ]]> org\.opensaml:openws:.* - cpe:/a:internet2:opensaml + cpe:/a:internet2:opensaml: .*(\.(whl|egg)|\b(site|dist)-packages\b.*) - cpe:/a:python:python - cpe:/a:python_software_foundation:python - cpe:/a:class:class - cpe:/a:file:file - cpe:/a:gnupg:gnupg - cpe:/a:mongodb:mongodb - cpe:/a:mozilla:mozilla - cpe:/a:openssl:openssl - cpe:/a:sendfile:sendfile - cpe:/a:sendmail:sendmail - cpe:/a:yacc:yacc + cpe:/a:python:python: + cpe:/a:python_software_foundation:python: + cpe:/a:class:class: + cpe:/a:file:file: + cpe:/a:gnupg:gnupg: + cpe:/a:mongodb:mongodb: + cpe:/a:mozilla:mozilla: + cpe:/a:openssl:openssl: + cpe:/a:sendfile:sendfile: + cpe:/a:sendmail:sendmail: + cpe:/a:yacc:yacc: com\.google(\.[a-zA-Z0-9_-]+)?:.*:.* - cpe:/a:google:desktop + cpe:/a:google:desktop: com\.google\.((?!android).)*:.* - cpe:/a:google:android - cpe:/a:google:android_api + cpe:/a:google:android: + cpe:/a:google:android_api: com\.google\.android\..*:.* - cpe:/a:google:android - cpe:/a:google:android_api - cpe:/a:google:google + cpe:/a:google:android: + cpe:/a:google:android_api: + cpe:/a:google:google: ^pkg:maven/com\.google\.guava/failureaccess@.*$ - cpe:/a:google:guava + cpe:/a:google:guava: ^pkg:maven/com\.google\.guava/listenablefuture@.*$ - cpe:/a:google:guava + cpe:/a:google:guava: com.microsoft.bingads:microsoft.bingads:.* - cpe:/a:microsoft:bing + cpe:/a:microsoft:bing: .*jersey.* - cpe:/a:oracle:glassfish_server - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish_server: + cpe:/a:oracle:glassfish: .*\bhk2\b.* - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: .*\bhk2-utils.*\.jar - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: org.ow2.petals:petals-se-camel:.* - cpe:/a:apache:camel + cpe:/a:apache:camel: org.apache.mina:mina.* - cpe:/a:apache-ssl:apache-ssl + cpe:/a:apache-ssl:apache-ssl: org.apache.woden:woden.* - cpe:/a:apache-ssl:apache-ssl + cpe:/a:apache-ssl:apache-ssl: org.apache.geronimo.specs:.* - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^pkg:maven/dev\.miku/r2dbc\-mysql@.*$ - cpe:/a:mysql:mysql + cpe:/a:mysql:mysql: ^pkg:maven/org\.apache\.tomcat/tomcat\-jni@.*$ - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat - cpe:/a:apache_software_foundation:tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: + cpe:/a:apache_software_foundation:tomcat: org\.apache\.tomcat\.embed:tomcat-embed-el:.* - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat - cpe:/a:apache_software_foundation:tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: + cpe:/a:apache_software_foundation:tomcat: ^org\.apache\.tomcat:tomcat-(servlet|el)-api:.*$ - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat - cpe:/a:apache_software_foundation:tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: + cpe:/a:apache_software_foundation:tomcat: org\.apache\.tomcat:tomcat-jdbc:.* - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: org\.apache\.tomcat:tomcat-juli:.* - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: com\.google\.javascript:closure-compiler:.* - cpe:/a:google:google_apps:- + cpe:/a:google:google_apps:-: .*mongodb.*:.*:.* - cpe:/a:mongodb:mongodb + cpe:/a:mongodb:mongodb: ^org\.apache\.xbean:xbean.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^org\.openjdk\.jmh:jmh-core:.*$ - cpe:/a:sun:openjdk - cpe:/a:oracle:openjdk + cpe:/a:sun:openjdk: + cpe:/a:oracle:openjdk: ^org\.apache\.geronimo\.components:geronimo-transaction:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: info\.ganglia\.gmetric4j:gmetric4j:.* - cpe:/a:ganglia:ganglia + cpe:/a:ganglia:ganglia: io\.dropwizard\.metrics:metrics-ganglia:.* - cpe:/a:ganglia:ganglia + cpe:/a:ganglia:ganglia: ^pkg:maven/io\.dropwizard/dropwizard-jetty@.*$ - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: ^pkg:maven/org\.gagravarr/vorbis\-java\-tika@.*$ - cpe:/a:apache:tika + cpe:/a:apache:tika: cpe:/a:flac(_project)?:flac.* @@ -1782,53 +1789,53 @@ false positive per issue #1961 ]]> ^pkg:maven/edu\.usc\.ir/sentiment\-analysis\-parser@.*$ - cpe:/a:apache:opennlp - cpe:/a:apache:tika + cpe:/a:apache:opennlp: + cpe:/a:apache:tika: ^pkg:maven/org\.apache\.avro/avro\-mapred@.*$ - cpe:/a:apache:hadoop + cpe:/a:apache:hadoop: ^pkg:maven/org\.scala\-lang/scalap@.*$ - cpe:/a:scala-lang:scala + cpe:/a:scala-lang:scala: ^pkg:maven/org\.apache\.spark/spark\-sketch_2\.\d+@.*$ - cpe:/a:sketch:sketch + cpe:/a:sketch:sketch: ^pkg:maven/org\.apache\.parquet/parquet\-hadoop@.*$ - cpe:/a:apache:hadoop + cpe:/a:apache:hadoop: ^pkg:maven/com\.intellij/annotations@.*$ - cpe:/a:jetbrains:intellij_idea + cpe:/a:jetbrains:intellij_idea: ^pkg:maven/org\.ops4j\.pax\.web/pax\-web\-jetty\-bundle@.*$ - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: ^pkg:maven/org\.drizzle\.jdbc/drizzle\-jdbc@.*$ - cpe:/a:mysql:mysql + cpe:/a:mysql:mysql: ^pkg:maven/org\.apache\.kerby/mysql\-backend@.*$ - cpe:/a:mysql:mysql + cpe:/a:mysql:mysql: ^pkg:maven/com\.alibaba/druid@.*$ - cpe:/a:alibaba:alibaba + cpe:/a:alibaba:alibaba: io\.dropwizard\.metrics:metrics-httpclient:.* - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: javax\.(annotation|transaction):javax\.(annotation|transaction)-api:.* - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: ^org\.apache\.directory\.api:api-ldap.*$ - cpe:/a:apache:apache_http_server - cpe:/a:apache:directory_studio - cpe:/a:apache:ldap_studio + cpe:/a:apache:apache_http_server: + cpe:/a:apache:directory_studio: + cpe:/a:apache:ldap_studio: cpe:/a:net-ldap(_project)?:net-ldap.* @@ -1887,64 +1894,63 @@ FP per #2029 ]]> ^pkg:maven/org\.opensaml/openws@.*$ - cpe:/a:shibboleth:opensaml + cpe:/a:shibboleth:opensaml: ^org\.apache\.servicemix\.bundles:org\.apache\.servicemix\.bundles\.not-yet-commons-ssl:.*$ - cpe:/a:apache-ssl:apache-ssl + cpe:/a:apache-ssl:apache-ssl: ^org\.apache\.cxf\.fediz:fediz-core:.*$ - cpe:/a:apache:cxf + cpe:/a:apache:cxf: .*(\.(jar|ear|war|pom)|pom\.xml) - cpe:/a:tiger:tiger + cpe:/a:tiger:tiger: .*(\.(dll|jar|ear|war|pom)|pom\.xml) - cpe:/a:class:class + cpe:/a:class:class: .*(\.(jar|ear|war|pom)|pom\.xml) - cpe:/a:pam:pam - cpe:/a:pam_ssh:pam_ssh - cpe:/a:sun:linux - cpe:/a:sun:sunos - cpe:/a:oracle:linux + cpe:/a:pam:pam: + cpe:/a:pam_ssh:pam_ssh: + cpe:/a:sun:linux: + cpe:/a:sun:sunos: + cpe:/a:oracle:linux: .*(\.(jar|ear|war|pom)|pom\.xml) - cpe:/a:sun:java - cpe:/a:sun:jdk - cpe:/a:sun:j2se - cpe:/a:sun:j_se - cpe:/a:sun:j_se + cpe:/a:sun:java: + cpe:/a:sun:jdk: + cpe:/a:sun:j2se: + cpe:/a:sun:j_se: ^org\.apache\.pdfbox:fontbox:.*$ - cpe:/a:apache:pdfbox + cpe:/a:apache:pdfbox: ^(javax\.json|org\.glassfish):javax\.json(-api)?:.*$ - cpe:/a:processing:processing + cpe:/a:processing:processing: ^pkg:maven/org\.glassfish.*$ - cpe:/a:processing:processing + cpe:/a:processing:processing: ^pkg:maven/jakarta\.json/jakarta\.json\-api@.*$ - cpe:/a:processing:processing + cpe:/a:processing:processing: ^org\.ojai:ojai:.*$ - cpe:/a:mapr:mapr + cpe:/a:mapr:mapr: ^org\.jenkins-ci\.plugins:dependency-track:.*$ - cpe:/a:track%2b:track%2b + cpe:/a:track%2b:track%2b: ^org\.jenkins-ci\.plugins:.*$ - cpe:/a:jenkins:jenkins + cpe:/a:jenkins:jenkins: ^org\.jenkins-ci\.tools:maven-hpi-plugin:.*$ - cpe:/a:jenkins:jenkins - cpe:/a:jenkins:maven + cpe:/a:jenkins:jenkins: + cpe:/a:jenkins:maven: ^org\.owasp:dependency-check-gradle:.*$ - cpe:/a:gradle:gradle + cpe:/a:gradle:gradle: ^pkg:maven/com\.android\.tools\.build/gradle\-api@.*$ - cpe:/a:gradle:gradle + cpe:/a:gradle:gradle: ^io\.springfox:springfox-.+:.*$ - cpe:/a:gradle:gradle + cpe:/a:gradle:gradle: ^pkg:maven/io\.micrometer/micrometer\-spring\-legacy@.*$ - cpe:/a:pivotal_software:spring_framework + cpe:/a:pivotal_software:spring_framework: ^pkg:maven/com\.squareup\.retrofit2?/(?!retrofit).*$ - cpe:/a:squareup:retrofit + cpe:/a:squareup:retrofit: ^(?!org\.jenkins-ci\.main:jenkins-war).*$ - cpe:/a:jenkins:jenkins + cpe:/a:jenkins:jenkins: ^(?!org\.glassfish\.main\.core:glassfish).*$ - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: ^org\.scala-lang\.modules:.*$ - cpe:/a:scala-lang:scala + cpe:/a:scala-lang:scala: ^(?!org\.jenkins-ci\.main:maven-plugin):.*$ - cpe:/a:jenkins:maven + cpe:/a:jenkins:maven: ^org\.jruby\.ext.*$ - cpe:/a:jruby:jruby + cpe:/a:jruby:jruby: org\.elasticsearch:securesm:.* - cpe:/a:elasticsearch:elasticsearch + cpe:/a:elasticsearch:elasticsearch: ^org\.apache\.wink:wink-json4j:.*$ - cpe:/a:wink:wink + cpe:/a:wink:wink: ^javax\.(jws|servlet):javax\.(jws|servlet)-api:.*$ - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: org\.glassfish:javax.el:.* - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: ^pkg:maven/javax\.portlet/portlet\-api@.*$ - cpe:/a:apache:pluto + cpe:/a:apache:pluto: ^org\.pac4j:pac4j-oidc:.*$ - cpe:/a:openid:openid + cpe:/a:openid:openid: sslext:sslext:.* - cpe:/a:apache:struts + cpe:/a:apache:struts: org\.apache\.activemq:activemq(-jms)?-pool.* - cpe:/a:apache:activemq + cpe:/a:apache:activemq: ^pkg:maven/org\.apache\.activemq/activemq-(openwire-legacy|spring)@.*$ - cpe:/a:apache:activemq + cpe:/a:apache:activemq: ^pkg:maven/org\.apache\.activemq\.protobuf/activemq\-protobuf@.*$ - cpe:/a:apache:activemq + cpe:/a:apache:activemq: .*(?!gradle).* - cpe:/a:gradle:gradle + cpe:/a:gradle:gradle: ^pkg:maven/de\.ahus1\.keycloak\.dropwizard/keycloak\-dropwizard@.*$ - cpe:/a:keycloak:keycloak + cpe:/a:keycloak:keycloak: ^pkg:maven/org\.keycloak/keycloak\-jetty\-core@.*$ - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: com\.offbytwo\.jenkins:jenkins-client:.* - cpe:/a:jenkins:jenkins + cpe:/a:jenkins:jenkins: ^(?!com.thoughtworks).*xstream.*$ - cpe:/a:x-stream:xstream + cpe:/a:x-stream:xstream: ^org\.glassfish\.jersey\.ext:jersey-proxy-client:.*$ - cpe:/a:oracle:oracle_client + cpe:/a:oracle:oracle_client: ^org\.mitre\.dsmiley\.httpproxy:smiley-http-proxy-servlet:.*$ - cpe:/a:shttp:shttp + cpe:/a:shttp:shttp: ^pkg:maven/com\.fasterxml\.jackson\.dataformat/jackson\-dataformat\-(ion|cbor|avro|protobuf|smile)@.*$ - cpe:/a:fasterxml:jackson-dataformat-xml + cpe:/a:fasterxml:jackson-dataformat-xml: .*package\.json$ cpe:/a:file(_project)?:file.* - cpe:/a:file:file - cpe:/a:shim:shim + cpe:/a:file:file: + cpe:/a:shim:shim: cpe:/a:shim(_project)?:shim.* @@ -2329,67 +2335,67 @@ False positives on python. ]]> .*__init__\.py$ - cpe:/a:shim:shim - cpe:/a:python:python + cpe:/a:shim:shim: + cpe:/a:python:python: .*PKG-INFO$ - cpe:/a:nodejs:nodejs - cpe:/a:nodejs:node.js + cpe:/a:nodejs:nodejs: + cpe:/a:nodejs:node.js: .* - cpe:/a:checkpoint:check_point + cpe:/a:checkpoint:check_point: ^org\.bouncycastle:bctsp.*$ - cpe:/a:openpgp:openpgp - cpe:/a:pgp:openpgp - cpe:/a:pgp:pgp + cpe:/a:openpgp:openpgp: + cpe:/a:pgp:openpgp: + cpe:/a:pgp:pgp: ^org\.apache\.xmlgraphics:xmlgraphics-commons:.*$ - cpe:/a:apache:batik + cpe:/a:apache:batik: ^io\.jsonwebtoken:jjwt:.*$ - cpe:/a:sonatype:nexus + cpe:/a:sonatype:nexus: ^pkg:maven/io\.vertx/vertx\-auth\-common@.*$ - cpe:/a:sonatype:nexus + cpe:/a:sonatype:nexus: ^org\.apache\.james:apache-mime4j-core:.*$ - cpe:/a:jcore:jcore + cpe:/a:jcore:jcore: ^javax\.servlet:servlet-api:.*$ - cpe:/a:sun:one_application_server + cpe:/a:sun:one_application_server: ^com\.splunk:splunk:.*$ - cpe:/a:splunk:splunk + cpe:/a:splunk:splunk: ^org\.openid4java:openid4java:.*$ - cpe:/a:openid:openid - cpe:/a:openid:openid4java + cpe:/a:openid:openid: + cpe:/a:openid:openid4java: ^com\.artofsolving:jodconverter:.*$ - cpe:/a:openoffice:openoffice.org - cpe:/a:openoffice:openoffice + cpe:/a:openoffice:openoffice.org: + cpe:/a:openoffice:openoffice: ^io\.swagger:.*$ - cpe:/a:sonatype:nexus + cpe:/a:sonatype:nexus: CVE-2020-21913 CVE-2025-5222 - cpe:/a:apple:java + cpe:/a:apple:java: cpe:/a:unicode:unicode: @@ -2489,22 +2495,22 @@ False positive per issue #854 ]]> ^com\.vaadin\.external\.google:android-json:.*$ - cpe:/a:google:android + cpe:/a:google:android: ^org\.glassfish:javax\.json:.*$ - cpe:/a:oracle:glassfish + cpe:/a:oracle:glassfish: .*activerecord.*oracle.*\.gemspec - cpe:/a:ruby-i18n:i18n - cpe:/a:mikel_lindsaar:mail + cpe:/a:ruby-i18n:i18n: + cpe:/a:mikel_lindsaar:mail: cpe:/a:rest-client(_project)?:rest-client.* @@ -2513,21 +2519,21 @@ ]]> ^net\.thisptr:jackson-jq:.*$ cpe:/a:jq(_project)?:jq.* - cpe:/a:id:id-software + cpe:/a:id:id-software: ^org\.jruby\.jcodings:jcodings:.*$ - cpe:/a:jruby:jruby + cpe:/a:jruby:jruby: ^org\.jruby\.joni:joni:.*$ - cpe:/a:jruby:jruby + cpe:/a:jruby:jruby: ^org\.apache\.cxf\.xjc-utils:.*:.*$ - cpe:/a:apache:cxf + cpe:/a:apache:cxf: ^org\.apache\.cxf\.xjcplugins:.*:.*$ - cpe:/a:apache:cxf + cpe:/a:apache:cxf: ^org\.apache\.struts\.xwork:xwork-core:.*$ - cpe:/a:apache:struts + cpe:/a:apache:struts: ^org\.apache\.cassandra:cassandra-thrift:.*$ - cpe:/a:apache:thrift - cpe:/a:apache:cassandra + cpe:/a:apache:thrift: + cpe:/a:apache:cassandra: ^org\.apache\.xbean:xbean-bundleutils:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^org\.jenkins-ci:annotation-indexer:.*$ - cpe:/a:jenkins:jenkins + cpe:/a:jenkins:jenkins: ^org\.apache\.cassandra\.deps:avro:.*$ - cpe:/a:apache:cassandra + cpe:/a:apache:cassandra: ^com\.netflix\.hystrix:hystrix-request-servlet:.*$ - cpe:/a:request_it:request_it + cpe:/a:request_it:request_it: ^com\.unboundid:unboundid-ldapsdk:.*$ - cpe:/a:id:id-software + cpe:/a:id:id-software: ^activesoap:jaxb-xercesImpl:[01].*$ - cpe:/a:apache:xerces2_java + cpe:/a:apache:xerces2_java: 73a51faadb407dccdbd77234e0d5a0a648665692 - cpe:/a:apache:xerces2_java + cpe:/a:apache:xerces2_java: ^pkg:maven/com\.lightbend\.akka/akka\-stream\-alpakka\-xml_2\.12@.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^pkg:maven/com\.vaadin\.(addon|external|flow\.ai).*$ - cpe:/a:vaadin:vaadin + cpe:/a:vaadin:vaadin: ^pkg:maven/com\.vaadin\.flow\.ai.*$ - cpe:/a:vaadin:flow + cpe:/a:vaadin:flow: ^(?!pkg:maven/com\.vaadin/vaadin(-core)?@).*$ - cpe:/a:vaadin:vaadin + cpe:/a:vaadin:vaadin: ^pkg:maven/com\.vaadin/sso-kit-starter@.*$ - cpe:/a:vaadin:flow + cpe:/a:vaadin:flow: ^pkg:maven/org\.webjars\.(bower|bowergithub|npm).*$ - cpe:/a:vaadin:vaadin + cpe:/a:vaadin:vaadin: ^pkg:maven/com\.lightbend\.akka/akka\-stream\-alpakka\-xml_2\.12@.*$ - cpe:/a:lightbend:akka + cpe:/a:lightbend:akka: ^com\.typesafe\.play:play-akka-http-server_2\.\d+:.*$ - cpe:/a:akka:akka - cpe:/a:akka:http_server + cpe:/a:akka:akka: + cpe:/a:akka:http_server: ^com\.typesafe\.akka:akka-stream-kafka_2\.12:.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^com\.lightbend\.akka:akka-stream-alpakka-jms_2\.12:.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^com\.typesafe\.akka:akka-persistence-cassandra:.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^org\.apache\.chemistry\.opencmis:chemistry-opencmis.*$ - cpe:/a:apache:apache_http_server - cpe:/a:apache:http_server + cpe:/a:apache:apache_http_server: + cpe:/a:apache:http_server: ^pkg:maven/org\.apache\.directory\.server/apacheds.*$ - cpe:/a:apache:http_server + cpe:/a:apache:http_server: ^org\.alfresco\.cmis\.client:alfresco-opencmis-extension:.*$ - cpe:/a:alfresco:alfresco + cpe:/a:alfresco:alfresco: ^org\.apache\.portals\.pluto:pluto-portal-driver:.*$ - cpe:/a:in-portal:in-portal + cpe:/a:in-portal:in-portal: ^org\.apache\.directory\.api:api-ldap.*$ - cpe:/a:apache:apache_ldap_studio + cpe:/a:apache:apache_ldap_studio: ^org\.apache\.tomcat:tomcat-annotations-api:.*$ - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat - cpe:/a:apache_software_foundation:tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: + cpe:/a:apache_software_foundation:tomcat: .*winstone-?(\d*\.?){0,3}\.jar - cpe:/a:mortbay_jetty:jetty - cpe:/a:mortbay:jetty - cpe:/a:jetty:jetty - cpe:/a:eclipse:jetty + cpe:/a:mortbay_jetty:jetty: + cpe:/a:mortbay:jetty: + cpe:/a:jetty:jetty: + cpe:/a:eclipse:jetty: ^org\.apache\.maven\.wagon:wagon-webdav-jackrabbit:.*$ - cpe:/a:apache:jackrabbit + cpe:/a:apache:jackrabbit: ^org\.apache\.xbean:xbean-reflect:.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^pkg:maven/org\.apache\.aries\.transaction/org\.apache\.aries\.transaction\.manager@.*$ - cpe:/a:apache:geronimo + cpe:/a:apache:geronimo: ^org\.apache\.curator:curator-recipes:.*$ - cpe:/a:apache:zookeeper + cpe:/a:apache:zookeeper: ^pkg:maven/org\.linguafranca\.pwdb/database@.*$ - cpe:/a:keepass:keepass + cpe:/a:keepass:keepass: ^pkg:maven/org\.linguafranca\.pwdb/KeePass.*$ - cpe:/a:keepass:keepass + cpe:/a:keepass:keepass: ^pkg:maven/org\.apache\.zookeeper/zookeeper.*@.*$ - cpe:/a:apache:hadoop + cpe:/a:apache:hadoop: CVE-2021-21295 @@ -2963,35 +2969,35 @@ FP per #2650 ]]> ^pkg:maven/org\.sonatype\.nexus\.plugins/nexus\-restore\-helm@.*$ - cpe:/a:sonatype:nexus + cpe:/a:sonatype:nexus: ^pkg:maven/io\.vertx.*$ - cpe:/a:sonatype:nexus + cpe:/a:sonatype:nexus: ^org\.apache\.ant:ant-testutil:.*$ - cpe:/a:apache:apache_test + cpe:/a:apache:apache_test: ^org\.apache\.maven\.scm:maven-scm-provider-git-commons:.*$ - cpe:/a:git-scm:git + cpe:/a:git-scm:git: ^org\.samba\.jcifs:jcifs:.*$ - cpe:/a:samba:samba + cpe:/a:samba:samba: ^org\.webjars\.npm:.*$ - cpe:/a:npm:npm + cpe:/a:npm:npm: ^pkg:maven/(mysql/mysql\-connector\-java|com\.mysql/mysql\-connector\-j)@.*$ - cpe:/a:oracle:connector%2fj - cpe:/a:oracle:jdbc + cpe:/a:oracle:connector%2fj: + cpe:/a:oracle:jdbc: ^net\.sourceforge\.jtds:jtds:.*$ - cpe:/a:microsoft:sql_server + cpe:/a:microsoft:sql_server: ^cn\.guoyukun\.jdbc:db2jcc_license_cu:.*$ - cpe:/a:ibm:db2 + cpe:/a:ibm:db2: ^io\.gitlab\.arturbosch\.detekt:detekt-.+:.*$ - cpe:/a:gitlab:gitlab + cpe:/a:gitlab:gitlab: ^org\.bouncycastle:bcpg-jdk15on:.*$ - cpe:/a:openpgp:openpgp - cpe:/a:pgp:openpgp - cpe:/a:pgp:pgp + cpe:/a:openpgp:openpgp: + cpe:/a:pgp:openpgp: + cpe:/a:pgp:pgp: ^name\.neuhalfen\.projects\.crypto\.bouncycastle\.openpgp:bouncy-gpg:.*$ cpe:/a:gpg-pgp(_project)?::gpg-pgp.* - cpe:/a:openpgp:openpgp + cpe:/a:openpgp:openpgp: ^pkg:maven/com\.jcraft/jsch\.agentproxy.*$ - cpe:/a:jcraft:jsch + cpe:/a:jcraft:jsch: ^pkg:maven/org\.infinispan\.protostream/protostream.*$ - cpe:/a:infinispan:infinispan + cpe:/a:infinispan:infinispan: ^pkg:maven/io\.hawt/hawtio\-wildfly@.*$ - cpe:/a:hawt:hawtio - cpe:/a:hawt.io:hawtio - cpe:/a:redhat:wildfly + cpe:/a:hawt:hawtio: + cpe:/a:hawt.io:hawtio: + cpe:/a:redhat:wildfly: ^pkg:maven/ant\-contrib/ant\-contrib@.*$ - cpe:/a:tasks:tasks + cpe:/a:tasks:tasks: ^pkg:maven/org\.apache\.camel/camel\-hazelcast@.*$ - cpe:/a:hazelcast:hazelcast + cpe:/a:hazelcast:hazelcast: ^pkg:maven/com\.spotify/docker\-client@.*$ - cpe:/a:docker:docker - cpe:/a:spotify:spotify + cpe:/a:docker:docker: + cpe:/a:spotify:spotify: ^pkg:maven/com\.typesafe\.play/play\-.*ws\-standalone.*@.*$ - cpe:/a:playframework:play_framework + cpe:/a:playframework:play_framework: ^pkg:maven/org\.mybatis\.spring\.boot/mybatis\-spring\-boot.*$ - cpe:/a:mybatis:mybatis + cpe:/a:mybatis:mybatis: ^pkg:maven/(?!io.kubernetes).*/.*@.*$ - cpe:/a:kubernetes:java + cpe:/a:kubernetes:java: ^pkg:maven/io\.fabric8/openshift\-model\-clusterautoscaling@.*$ - cpe:/a:redhat:cluster_project - cpe:/a:redhat:mod_cluster + cpe:/a:redhat:cluster_project: + cpe:/a:redhat:mod_cluster: ^pkg:maven/io\.fabric8/openshift\-model\-machineconfig@.*$ - cpe:/a:redhat:machine-config-operator + cpe:/a:redhat:machine-config-operator: ^pkg:maven/com\.amazonaws/aws\-java\-sdk\-sagemakerruntime@.*$ - cpe:/a:sage:sage + cpe:/a:sage:sage: ^pkg:maven/org\.mybatis\.generator/mybatis\-generator\-core@.*$ - cpe:/a:mybatis:mybatis + cpe:/a:mybatis:mybatis: ^pkg:maven/org\.wildfly\.core/wildfly\-protocol@.*$ - cpe:/a:redhat:wildfly - cpe:/a:redhat:wildfly_core - cpe:/a:wildfly:wildfly + cpe:/a:redhat:wildfly: + cpe:/a:redhat:wildfly_core: + cpe:/a:wildfly:wildfly: ^pkg:maven/org\.hibernate/quarkus\-local\-cache@.*$ - cpe:/a:hibernate:hibernate_orm + cpe:/a:hibernate:hibernate_orm: ^pkg:maven/org\.owasp/csrfguard\-.*$ - cpe:/a:php:com_extensions + cpe:/a:php:com_extensions: ^pkg:maven/io\.quarkus\.(security|gizmo|http)/.*$ - cpe:/a:quarkus:quarkus + cpe:/a:quarkus:quarkus: ^(?!(io\.quarkus)).*:.*quarkus.*$ - cpe:/a:quarkus:quarkus + cpe:/a:quarkus:quarkus: ^pkg:maven/io\.quarkus/quarkus-jdbc-.*$ - cpe:/a:mysql:mysql - cpe:/a:oracle:jdbc - cpe:/a:postgresql:postgresql + cpe:/a:mysql:mysql: + cpe:/a:oracle:jdbc: + cpe:/a:postgresql:postgresql: ^pkg:maven/io\.quarkus.*/.*resteasy.*@.*$ - cpe:/a:redhat:resteasy + cpe:/a:redhat:resteasy: ^pkg:maven/org\.apache\.xmlgraphics/batik\-i18n@.*$ - cpe:/a:apache:batik + cpe:/a:apache:batik: + + + + ^pkg:maven\/com\.github\.anti-social[.\/]prometheus-kt.*$ + cpe:/a:prometheus:prometheus: ^pkg:maven/com\.oracle\.database\.jdbc/.*@.*$ - cpe:/a:oracle:database - cpe:/a:oracle:oracle_database + cpe:/a:oracle:database: + cpe:/a:oracle:oracle_database: ^pkg:maven/com\.oracle\.database\.security/.*@.*$ - cpe:/a:oracle:database - cpe:/a:oracle:oracle_database + cpe:/a:oracle:database: + cpe:/a:oracle:oracle_database: ^pkg:maven/com\.oracle\.database\.ha/.*@.*$ - cpe:/a:oracle:database - cpe:/a:oracle:oracle_database + cpe:/a:oracle:database: + cpe:/a:oracle:oracle_database: ^pkg:maven/io\.pivotal\.cfenv/.*@.*$ - cpe:/a:pivotal_software:spring_boot - cpe:/a:pivotal_software:spring_framework + cpe:/a:pivotal_software:spring_boot: + cpe:/a:pivotal_software:spring_framework: ^pkg:maven/io\.qameta\.allure/allure\-httpclient@.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/com\.google\.http\-client/google\-http\-client\-apache\-v2@.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/com\.adobe\.aem/uber\-jar@.*$ - cpe:/a:adobe:experience_manager_forms + cpe:/a:adobe:experience_manager_forms: ^pkg:maven/com\.internetitem/logback\-elasticsearch\-appender@.*$ - cpe:/a:elasticsearch:elasticsearch + cpe:/a:elasticsearch:elasticsearch: ^pkg:maven/org\.exist\-db\.thirdparty\.xerces/xercesImpl@.*$ - cpe:/a:exist-db:exist + cpe:/a:exist-db:exist: ^pkg:pypi/.*python.*$ - cpe:/a:python:python - cpe:/a:python_software_foundation:python + cpe:/a:python:python: + cpe:/a:python_software_foundation:python: ^pkg:pypi/python\-keycloak@.*$ - cpe:/a:keycloak:keycloak + cpe:/a:keycloak:keycloak: ^pkg:maven/org\.sonarsource\.java/.*$ - cpe:/a:sonarsource:sonarqube + cpe:/a:sonarsource:sonarqube: ^pkg:maven/org\.apache\.struts/struts\-annotations@.*$ - cpe:/a:apache:struts + cpe:/a:apache:struts: @@ -3684,23 +3697,23 @@ broad suppressions for all cpan ]]> ^pkg:cpan/.*$ - cpe:/a:mysql:mysql - cpe:/a:next:next + cpe:/a:mysql:mysql: + cpe:/a:next:next: ^pkg:cpan/(?!perl@).*$ - cpe:/a:perl:perl + cpe:/a:perl:perl: ^pkg:cpan/Catmandu%3A%3AStore/ElasticSearch@.*$ - cpe:/a:elastic:elasticsearch - cpe:/a:elasticsearch:elasticsearch + cpe:/a:elastic:elasticsearch: + cpe:/a:elasticsearch:elasticsearch: ^pkg:cpan/Net/SSH2@.*$ - cpe:/a:ssh:ssh + cpe:/a:ssh:ssh: ^pkg:maven/com\.nimbusds/lang\-tag@.*$ - cpe:/a:nim-lang:nim-lang + cpe:/a:nim-lang:nim-lang: ^pkg:maven/org\.apache\.logging\.log4j/log4j\-api\-kotlin@.*$ - cpe:/a:apache:log4j + cpe:/a:apache:log4j: ^pkg:maven/javax\.xml\.crypto/jsr105-api@.*$ - cpe:/a:apache:xml_security_for_java + cpe:/a:apache:xml_security_for_java: ^pkg:maven/nginx\-clojure/nginx\-clojure@.*$ - cpe:/a:nginx:nginx + cpe:/a:nginx:nginx: ^pkg:maven/io\.netty/netty\-tcnative\-classes@.*$ - cpe:/a:netty:netty + cpe:/a:netty:netty: ^pkg:maven/io\.netty\.incubator/.*$ - cpe:/a:netty:netty + cpe:/a:netty:netty: ^pkg:maven/io\.projectreactor\.kafka/reactor\-kafka@[1-9]+?[\.]+(((([4-9]+)|[0-9]{2,3}?)+[\.]+[0-9]{1,3}?)|([3\.]+([2-9]|[1-9]+[0-9]+?)))+(.*)$ - cpe:/a:apache:kafka + cpe:/a:apache:kafka: ^pkg:maven/com\.itextpdf/pdfrender@.*$ - cpe:/a:itextpdf:itext + cpe:/a:itextpdf:itext: ^pkg:maven/org\.apache\.tomcat/annotations\-api@.*$ - cpe:/a:apache:tomcat - cpe:/a:apache_tomcat:apache_tomcat + cpe:/a:apache:tomcat: + cpe:/a:apache_tomcat:apache_tomcat: ^pkg:maven/jakarta\.ws\.rs/jakarta\.ws\.rs-api@.*$ - cpe:/a:oracle:java_se - cpe:/a:oracle:web_services + cpe:/a:oracle:java_se: + cpe:/a:oracle:web_services: ^pkg:maven/org\.graylog2/gelfclient@.*$ - cpe:/a:graylog:graylog + cpe:/a:graylog:graylog: ^pkg:maven/org\.mariadb\.jdbc/mariadb-java-client@.*$ - cpe:/a:mariadb:mariadb + cpe:/a:mariadb:mariadb: ^pkg:maven/org\.eclipse/yasson@.*$ - cpe:/a:oracle:projects + cpe:/a:oracle:projects: ^pkg:maven/org\.aspectj/aspectj.*@.*$ - cpe:/a:vmware:tools + cpe:/a:vmware:tools: ^pkg:maven/org\.apache\.kafka/kafka-log4j-appender@.*$ - cpe:/a:apache:log4j - cpe:/a:apache:kafka + cpe:/a:apache:log4j: + cpe:/a:apache:kafka: ^pkg:maven/com\.lightbend\.akka\.management/akka-management-cluster-bootstrap_2\.13@.*$ - cpe:/a:akka:akka + cpe:/a:akka:akka: ^pkg:maven/io\.netty/netty-tcnative-boringssl-static@.*$ - cpe:/a:chromium:chromium + cpe:/a:chromium:chromium: cpe:/a:chromium(_project)?:chromium.* @@ -3899,35 +3912,35 @@ #4123, #4128, #4132, #4136, #4137, #4138, #4145, #4146 ]]> ^pkg:maven/(?!org\.apache\.james/).*$ - cpe:/a:apache:james + cpe:/a:apache:james: ^pkg:maven/com\.ibm\.etcd/etcd\-java@.*$ - cpe:/a:etcd:etcd + cpe:/a:etcd:etcd: ^pkg:maven/org\.cryptomator/(?!cryptomator).*$ - cpe:/a:cryptomator:cryptomator + cpe:/a:cryptomator:cryptomator: ^pkg:maven/io\.quarkus/quarkus\-apache\-httpclient@.*$ - cpe:/a:apache:httpclient + cpe:/a:apache:httpclient: ^pkg:maven/com\.github\.blagerweij/liquibase-sessionlock@.*$ - cpe:/a:liquibase:liquibase + cpe:/a:liquibase:liquibase: ^pkg:maven/org\.typelevel/log4cats\-core_2\.13@.*$ - cpe:/a:davenport:davenport - cpe:/a:protonmail:protonmail + cpe:/a:davenport:davenport: + cpe:/a:protonmail:protonmail: ^pkg:maven/org\.codehaus\.jackson/jackson-jaxrs@.*$ - cpe:/a:fasterxml:jackson-databind + cpe:/a:fasterxml:jackson-databind: ^pkg:maven/io\.minio/minio@.*$ - cpe:/a:minio:minio + cpe:/a:minio:minio: ^pkg:maven/ch\.qos\.reload4j/reload4j@.*$ - cpe:/a:apache:log4j + cpe:/a:apache:log4j: ^pkg:maven/com\.mattbertolini/liquibase-slf4j@.*$ - cpe:/a:liquibase:liquibase + cpe:/a:liquibase:liquibase: ^pkg:maven/com\.pinterest\.ktlint/ktlint-reporter-checkstyle@.*$ - cpe:/a:checkstyle:checkstyle + cpe:/a:checkstyle:checkstyle: ^pkg:maven/org\.apache\.james/apache\-mime4j\-dom@.*$ - cpe:/a:jdom:jdom + cpe:/a:jdom:jdom: ^pkg:maven/pl\.allegro\..*$ - cpe:/a:allegro:allegro + cpe:/a:allegro:allegro: ^pkg:maven/struts/struts-legacy@.*$ - cpe:/a:apache:struts + cpe:/a:apache:struts: ^pkg:maven/(org\.apache\.)?ant/ant\-apache\-log4j@.*$ - cpe:/a:apache:ant - cpe:/a:apache:log4j + cpe:/a:apache:ant: + cpe:/a:apache:log4j: ^pkg:maven/io\.swagger\.codegen\.v3/swagger-codegen-generators@.*$ - cpe:/a:swagger:swagger-codegen + cpe:/a:swagger:swagger-codegen: ^pkg:maven/org\.flywaydb/.*$ - cpe:/a:mysql:mysql - cpe:/a:firebird:firebird + cpe:/a:mysql:mysql: + cpe:/a:firebird:firebird: ^pkg:maven/org\.pac4j/ratpack\-pac4j@.*$ - cpe:/a:pac4j:pac4j - cpe:/a:ratpack:ratpack + cpe:/a:pac4j:pac4j: + cpe:/a:ratpack:ratpack: cpe:/a:ratpack(_project)?:ratpack.* @@ -4082,102 +4095,102 @@ FP per issue #4440 ]]> ^pkg:maven/org\.apache\.activemq/activeio\-core@.*$ - cpe:/a:apache:activemq + cpe:/a:apache:activemq: ^pkg:maven/org\.rxtx/rxtx@.*$ - cpe:/a:gnu:parallel + cpe:/a:gnu:parallel: ^pkg:maven/org\.iq80\.snappy/snappy@.*$ - cpe:/a:electrum:electrum + cpe:/a:electrum:electrum: ^pkg:maven/com\.lightbend\.akka\.management/akka\-.*@.*$ - cpe:/a:akka:akka - cpe:/a:lightbend:akka - cpe:/a:lightbend:akka-http - cpe:/a:lightbend:akka_http + cpe:/a:akka:akka: + cpe:/a:lightbend:akka: + cpe:/a:lightbend:akka-http: + cpe:/a:lightbend:akka_http: ^pkg:maven/com\.lightbend\.akka\.discovery/akka\-.*@.*$ - cpe:/a:akka:akka - cpe:/a:lightbend:akka + cpe:/a:akka:akka: + cpe:/a:lightbend:akka: ^pkg:maven/com\.google\.cloud/spring-cloud-gcp-starter-sql-postgresql@.*$ - cpe:/a:postgresql:postgresql + cpe:/a:postgresql:postgresql: ^pkg:maven/io\.github\.detekt\.sarif4k/sarif4k@.*$ - cpe:/a:detekt:detekt + cpe:/a:detekt:detekt: ^pkg:maven/com\.princexml/prince-java-wrapper@.*$ - cpe:/a:princexml:princexml + cpe:/a:princexml:princexml: ^pkg:maven/com\.google\.(?!code\.gson).*/.*gson.*$ - cpe:/a:google:gson + cpe:/a:google:gson: ^pkg:maven/com\.datastax\.cassandra/cassandra-driver-core@.*$ - cpe:/a:apache:cassandra + cpe:/a:apache:cassandra: ^pkg\:maven/io\.quarkus.*/quarkus\-liquibase(-[a-z]+)?@.* - cpe:/a:liquibase:liquibase + cpe:/a:liquibase:liquibase: ^pkg\:maven/io\.quarkus/quarkus\-container\-image\-docker@.* - cpe:/a:docker:docker + cpe:/a:docker:docker: ^pkg:maven/io\.quarkus/quarkus\-spring\-boot\-orm\-api@.*$ - cpe:/a:quarkus:quarkus + cpe:/a:quarkus:quarkus: ^pkg:maven/io\.quarkus/quarkus\-spring\-data\-(commons|jpa)\-api@.*$ - cpe:/a:quarkus:quarkus + cpe:/a:quarkus:quarkus: ^(?!(org\.)springframework).*:.*spring.*$ - cpe:/a:pivotal_software:spring_framework - cpe:/a:springsource:spring_framework - cpe:/a:vmware:spring_framework - cpe:/a:vmware:springsource_spring_framework + cpe:/a:pivotal_software:spring_framework: + cpe:/a:springsource:spring_framework: + cpe:/a:vmware:spring_framework: + cpe:/a:vmware:springsource_spring_framework: ^pkg:maven/org\.apache\.axis2/axis2-xmlbeans@.*$ - cpe:/a:apache:xmlbeans + cpe:/a:apache:xmlbeans: ^pkg:maven/org\.apache\.tomcat/jakartaee-migration@.*$ - cpe:/a:apache:tomcat + cpe:/a:apache:tomcat: ^pkg:maven/tyrex/tyrex@.*$ - cpe:/a:sun:j2ee + cpe:/a:sun:j2ee: ^pkg:maven/io\.zipkin\.aws/brave-propagation-aws@.*$ - cpe:/a:brave:brave + cpe:/a:brave:brave: + + + + ^pkg:maven/io\.quarkus/quarkus-micrometer-registry-prometheus@.*$ + cpe:/a:prometheus:prometheus: ^pkg:maven/org\.apache\.activemq/activemq\-artemis\-native@.*$ - cpe:/a:apache:activemq - cpe:/a:apache:activemq_artemis + cpe:/a:apache:activemq: + cpe:/a:apache:activemq_artemis: ^pkg:maven/org\.springframework\..*$ - cpe:/a:springsource:spring_framework - cpe:/a:pivotal:spring_framework - cpe:/a:pivotal_software:spring_framework - cpe:/a:vmware:springsource_spring_framework - cpe:/a:vmware:spring_framework + cpe:/a:springsource:spring_framework: + cpe:/a:pivotal:spring_framework: + cpe:/a:pivotal_software:spring_framework: + cpe:/a:vmware:springsource_spring_framework: + cpe:/a:vmware:spring_framework: @@ -4262,20 +4282,20 @@ False positive per #3622, #4561. Spring-boot-starter-oauth2-client gets flagged with wrong spring CPEs. ]]> ^pkg:maven/org\.springframework\.boot/spring\-boot\-starter\-oauth2\-client@.*$ - cpe:/a:pivotal:spring_security_oauth - cpe:/a:pivotal:spring_security - cpe:/a:pivotal_software:spring_security_oauth - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal:spring_security_oauth: + cpe:/a:pivotal:spring_security: + cpe:/a:pivotal_software:spring_security_oauth: + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.boot/spring-boot-starter-security@.*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security - cpe:/a:vmware:springsource_spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: + cpe:/a:vmware:springsource_spring_security: @@ -4283,8 +4303,8 @@ Suppress false positives per #3400 and #4597 ]]> ^pkg:maven/org\.springframework\.cloud/spring\-cloud\-(common\-)?security.*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: @@ -4292,10 +4312,10 @@ Suppresses false positives per issue #1665, #3219, #4562. ]]> ^pkg:maven/org\.springframework\.security/spring-security-rsa.*$ - cpe:/a:pivotal:spring_security_oauth - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security - cpe:/a:vmware:springsource_spring_security + cpe:/a:pivotal:spring_security_oauth: + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: + cpe:/a:vmware:springsource_spring_security: cpe:/a:security-framework(_project)?:security-framework.* @@ -4303,9 +4323,9 @@ Spring Security JWT false positive. ]]> ^pkg:maven/org\.springframework\.security/spring-security-jwt.*$ - cpe:/a:vmware:springsource_spring_security - cpe:/a:vmware:spring_security - cpe:/a:pivotal_software:spring_security + cpe:/a:vmware:springsource_spring_security: + cpe:/a:vmware:spring_security: + cpe:/a:pivotal_software:spring_security: cpe:/a:security-framework(_project)?:security-framework.* @@ -4313,8 +4333,8 @@ Suppresses false positives per issue #1595 ]]> ^pkg:maven/org\.springframework\.security/spring-security-jwt@.*$ - cpe:/a:pivotal:spring_security_oauth - cpe:/a:pivotal_software:spring_security_oauth + cpe:/a:pivotal:spring_security_oauth: + cpe:/a:pivotal_software:spring_security_oauth: cpe:/a:jwt(_project)?:jwt.* @@ -4324,16 +4344,16 @@ ]]> ^pkg:maven/org\.springframework\.security\.extensions/spring-security-saml2-core@.*$ cpe:/a:saml(_project)?:saml.* - cpe:/a:vmware:spring_security + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.security\.extensions/spring-security-saml.*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:springsource_spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:springsource_spring_security: + cpe:/a:vmware:spring_security: cpe:/a:security-framework(_project)?:security-framework.* @@ -4341,9 +4361,9 @@ Suppresses false positives per issue #2235, #4596, #4601 ]]> ^pkg:maven/org\.springframework\.security\.kerberos/spring-security-kerberos.*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:springsource_spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:springsource_spring_security: + cpe:/a:vmware:spring_security: cpe:/a:security-framework(_project)?:security-framework.* @@ -4352,25 +4372,25 @@ Supress false positives per issue #1872, #4577 ]]> ^pkg:maven/org\.springframework\.security\.oauth/spring-security-oauth2@.*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.security\.oauth\.boot/spring-security-oauth2-autoconfigure@.*$ - cpe:/a:pivotal_software:spring_boot - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_boot: + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.security\.oauth\.boot/spring-security-oauth2-autoconfigure@.*$ - cpe:/a:pivotal_software:spring_security_oauth - cpe:/a:pivotal:spring_security_oauth + cpe:/a:pivotal_software:spring_security_oauth: + cpe:/a:pivotal:spring_security_oauth: @@ -4378,25 +4398,25 @@ spring-session has a different version numbering than spring-core. See #1399 ]]> ^pkg:maven/org\.springframework\.session/spring-session-(core|data-).*$ - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.session/spring\-session\-hazelcast@.*$ - cpe:/a:hazelcast:hazelcast - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:spring_security + cpe:/a:hazelcast:hazelcast: + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.ws/spring\-ws\-security@.*$ - cpe:/a:vmware:spring_security - cpe:/a:pivotal_software:spring_security + cpe:/a:vmware:spring_security: + cpe:/a:pivotal_software:spring_security: @@ -4405,36 +4425,36 @@ FP per issues #4581, #4582 ]]> ^pkg:maven/org\.springframework\.cloud/spring-cloud-dataflow-rest-.*$ - cpe:/a:vmware:spring_data_rest - cpe:/a:pivotal_software:spring_data_rest + cpe:/a:vmware:spring_data_rest: + cpe:/a:pivotal_software:spring_data_rest: ^pkg:maven/org\.springframework\.cloud/spring\-cloud\-.*$ - cpe:/a:vmware:cloud_foundation + cpe:/a:vmware:cloud_foundation: ^pkg:maven/org\.springframework\.batch/spring\-batch\-infrastructure@.*$ - cpe:/a:vmware:infrastructure + cpe:/a:vmware:infrastructure: ^pkg:maven/org\.springframework\.boot/spring\-boot\-loader\-tools@.*$ - cpe:/a:vmware:tools + cpe:/a:vmware:tools: ^pkg:maven/org\.springframework\.security/spring.*$ - cpe:/a:mod_security:mod_security + cpe:/a:mod_security:mod_security: CVE-2018-1258 @@ -4444,33 +4464,33 @@ being flagged as spring-data-rest with the wrong version number) ]]> ^pkg:maven/org\.springframework\.boot/spring-boot-starter-data-rest@.*$ - cpe:/a:pivotal_software:spring_data_rest - cpe:/a:pivotal_software:spring_boot + cpe:/a:pivotal_software:spring_data_rest: + cpe:/a:pivotal_software:spring_boot: ^pkg:maven/org\.springframework\.boot/.*$ - cpe:/a:vmware:spring_integration - cpe:/a:pivotal:spring_security_oauth - cpe:/a:pivotal_software:spring_security - cpe:/a:vmware:server - cpe:/a:vmware:spring_security + cpe:/a:vmware:spring_integration: + cpe:/a:pivotal:spring_security_oauth: + cpe:/a:pivotal_software:spring_security: + cpe:/a:vmware:server: + cpe:/a:vmware:spring_security: ^pkg:maven/org\.springframework\.batch/spring\-batch\-integration@.*$ - cpe:/a:vmware:spring_integration + cpe:/a:vmware:spring_integration: ^pkg:maven/org\.springframework\.boot/spring\-boot\-starter\-web\-services@.*$ - cpe:/a:pivotal_software:spring_web_services + cpe:/a:pivotal_software:spring_web_services: ^pkg:maven/org\.springframework\.data/spring-data-mongodb.*$ - cpe:/a:mongodb:mongodb + cpe:/a:mongodb:mongodb: ^pkg:maven/org\.springframework\.(data|boot)/spring-(boot-starter-)?data-neo4j@.*$ - cpe:/a:neo4j:neo4j + cpe:/a:neo4j:neo4j: ^pkg:maven/org\.springframework\.data/spring-data-solr@.*$ - cpe:/a:apache:solr + cpe:/a:apache:solr: ^pkg:maven/org\.springframework\.social/spring-social-facebook@.*$ - cpe:/a:facebook:facebook + cpe:/a:facebook:facebook: ^pkg:maven/org\.springframework\.boot/spring\-boot\-starter\-batch@.*$ - cpe:/a:pivotal_software:spring_batch + cpe:/a:pivotal_software:spring_batch: ^pkg:maven/org\.springframework\.cloud/spring-cloud-.*$ cpe:/a:context(_project)?:context.* - cpe:/a:pivotal_software:spring_batch + cpe:/a:pivotal_software:spring_batch: ^pkg:maven/org\.springframework\.boot/.*$ - cpe:/a:pivotal_software:spring_data_jpa + cpe:/a:pivotal_software:spring_data_jpa: ^pkg:maven/org\.springframework\.boot/spring-boot-starter-data-mongodb.*@.*$ - cpe:/a:mongodb:mongodb - cpe:/a:vmware:spring_data_mongodb + cpe:/a:mongodb:mongodb: + cpe:/a:vmware:spring_data_mongodb: ^pkg:maven/org\.springframework\.boot/spring\-boot\-starter\-amqp@.*$ - cpe:/a:pivotal_software:rabbitmq - cpe:/a:pivotal_software:spring_boot - cpe:/a:vmware:rabbitmq + cpe:/a:pivotal_software:rabbitmq: + cpe:/a:pivotal_software:spring_boot: + cpe:/a:vmware:rabbitmq: ^pkg:maven/org\.springframework\.cloud/spring\-cloud\-(starter\-)?kubernetes\-fabric8\-(auto)?config@.*$ - cpe:/a:vmware:spring_cloud_config + cpe:/a:vmware:spring_cloud_config: ^pkg:maven/org\.springframework\.cloud/spring\-cloud\-deployer\-.*$ - cpe:/a:vmware:spring_cloud_config - cpe:/a:vmware:spring_cloud_data_flow + cpe:/a:vmware:spring_cloud_config: + cpe:/a:vmware:spring_cloud_data_flow: ^pkg:maven/org\.springframework\.cloud/spring-cloud-kubernetes-fabric8-config@.*$ - cpe:/a:vmware:spring_cloud_config + cpe:/a:vmware:spring_cloud_config: ^pkg:maven/rubygems/jruby\-.*@.*$ - cpe:/a:jruby:jruby + cpe:/a:jruby:jruby: ^pkg:maven/rubygems/jruby\-openssl@.*$ - cpe:/a:openssl:openssl + cpe:/a:openssl:openssl: ^pkg:maven/io\.opentracing\.contrib/opentracing\-elasticsearch.*@.*$ - cpe:/a:elasticsearch:elasticsearch + cpe:/a:elasticsearch:elasticsearch: ^pkg:maven/com\.otaliastudios/zoomlayout@.*$ - cpe:/a:zoom:zoom + cpe:/a:zoom:zoom: ^pkg:maven/org\.apache\.hive/.*spark\-client@.*$ - cpe:/a:apache:spark + cpe:/a:apache:spark: ^pkg:maven/org\.apache\.twill/twill\-yarn@.*$ - cpe:/a:apache:hadoop + cpe:/a:apache:hadoop: ^pkg:maven/org\.apache\.ranger/ranger\-hive\-plugin@.*$ - cpe:/a:apache:hive + cpe:/a:apache:hive: ^pkg:maven/org\.apache\.hive/hive\-hbase\-handler@.*$ - cpe:/a:apache:hbase + cpe:/a:apache:hbase: ^pkg:maven/org\.apache\.hbase/hbase\-server@.*$ - cpe:/a:apache:http_server + cpe:/a:apache:http_server: ^pkg:cocoapods/GoogleUtilities%2FAppDelegateSwizzler@.*$ cpe:/a:app(_project)?:app.* - cpe:/a:delegate:delegate + cpe:/a:delegate:delegate: ^pkg:maven/org\.springframework\.security/spring\-security\-oauth2\-authorization\-server@.*$ - cpe:/a:pivotal:spring_security_oauth - cpe:/a:pivotal_software:spring_security - cpe:/a:pivotal_software:spring_security_oauth - cpe:/a:vmware:spring_security - cpe:/a:vmware:server - cpe:/a:vmware:vmware_server + cpe:/a:pivotal:spring_security_oauth: + cpe:/a:pivotal_software:spring_security: + cpe:/a:pivotal_software:spring_security_oauth: + cpe:/a:vmware:spring_security: + cpe:/a:vmware:server: + cpe:/a:vmware:vmware_server: ^pkg:maven/tailrecursion/cljs\-priority\-map@.*$ - cpe:/a:priority-software:priority + cpe:/a:priority-software:priority: ^pkg:maven/org\.clojure/data\.priority\-map@.*$ - cpe:/a:priority-software:priority + cpe:/a:priority-software:priority: ^pkg:maven/io\.quarkus/quarkus\-keycloak\-authorization@.*$ - cpe:/a:keycloak:keycloak - cpe:/a:redhat:keycloak + cpe:/a:keycloak:keycloak: + cpe:/a:redhat:keycloak: ^(?!pkg:maven/commons-net/commons-net).*$ - cpe:/a:apache:commons_net + cpe:/a:apache:commons_net: ^pkg:pypi/.*docker.*$ - cpe:/a:docker:docker + cpe:/a:docker:docker: ^(?!pkg:maven/org.eclipse.platform).+$ - cpe:/a:eclipse:eclipse_ide + cpe:/a:eclipse:eclipse_ide: ^pkg:maven/cloud\.localstack/localstack-utils@.*$ - cpe:/a:utils(_project)?:utils + cpe:/a:utils(_project)?:utils: ^pkg:pypi/zabbix-utils@.*$ - cpe:/a:zabbix:zabbix + cpe:/a:zabbix:zabbix: From f03b7dcf1d1c75793d1917fba2f6c2b81e731308 Mon Sep 17 00:00:00 2001 From: Jeremy Long Date: Tue, 19 May 2026 07:43:21 -0400 Subject: [PATCH 9/9] fix: publish-suppressions action trigger (#8516) --- .github/workflows/publish-suppressions.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish-suppressions.yml b/.github/workflows/publish-suppressions.yml index 87030db6127..2d5d7d2963a 100644 --- a/.github/workflows/publish-suppressions.yml +++ b/.github/workflows/publish-suppressions.yml @@ -2,6 +2,9 @@ name: Publish Suppressions on: workflow_dispatch: + push: + branches: + - generatedSuppressions permissions: {} jobs: