From f2c940f59c1fd9a217ac1f6e4d82c8f8c7f0979d Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Sat, 23 May 2026 23:01:11 +0800 Subject: [PATCH 1/3] ci: fix FP ops workflow to generate CPE regex for optional suffix matches (#8531) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> --- .github/workflows/false-positive-ops.yml | 34 +++++++++--------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/.github/workflows/false-positive-ops.yml b/.github/workflows/false-positive-ops.yml index ceb70fdfa3e..c3ca6838b65 100644 --- a/.github/workflows/false-positive-ops.yml +++ b/.github/workflows/false-positive-ops.yml @@ -171,12 +171,9 @@ jobs: } purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); - var cpe22UriPrefix; - if (cpe[1] == '2.3') { - cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; - } else { - cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; - } + var vendor = cpe[1] === '2.3' ? cpe[3] : cpe[2]; + var product = cpe[1] === '2.3' ? cpe[4] : cpe[3]; + var cpe22UriRegex = 'cpe:/a:' + vendor.replaceAll('.','\\.') + ':' + product.replaceAll('.','\\.') + '(:.*)?$'; await github.rest.issues.createComment({ issue_number: context.issue.number, @@ -189,7 +186,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + cpe22UriPrefix + '\n' + + ' ' + cpe22UriRegex + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) @@ -217,13 +214,9 @@ jobs: } purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); - console.log(cpe); - var cpe22UriPrefix; - if (cpe[1] == '2.3') { - cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; - } else { - cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; - } + var vendor = cpe[1] === '2.3' ? cpe[3] : cpe[2]; + var product = cpe[1] === '2.3' ? cpe[4] : cpe[3]; + var cpe22UriRegex = 'cpe:/a:' + vendor.replaceAll('.','\\.') + ':' + product.replaceAll('.','\\.') + '(:.*)?$'; await github.rest.issues.createComment({ issue_number: context.issue.number, @@ -236,7 +229,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + cpe22UriPrefix + '\n' + + ' ' + cpe22UriRegex + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) @@ -264,12 +257,9 @@ jobs: } purl += '@.*$'; var cpe = process.env.CPE.trim().replaceAll(/^`|`$/g,'').split(':'); - var cpe22UriPrefix; - if (cpe[1] == '2.3') { - cpe22UriPrefix = 'cpe:/a:' + cpe[3] + ':' + cpe[4] + ':'; - } else { - cpe22UriPrefix = 'cpe:/a:' + cpe[2] + ':' + cpe[3] + ':'; - } + var vendor = cpe[1] === '2.3' ? cpe[3] : cpe[2]; + var product = cpe[1] === '2.3' ? cpe[4] : cpe[3]; + var cpe22UriRegex = 'cpe:/a:' + vendor.replaceAll('.','\\.') + ':' + product.replaceAll('.','\\.') + '(:.*)?$'; await github.rest.issues.createComment({ issue_number: context.issue.number, @@ -282,7 +272,7 @@ jobs: ' FP per issue #' + context.issue.number + '\n' + ' ]]>\n' + ' ' + purl + '\n' + - ' ' + cpe22UriPrefix + '\n' + + ' ' + cpe22UriRegex + '\n' + '\n```\n\n' + 'Link to test results: ' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId }) From 783dc83b949a19d9bac8df8dbd6eb06a10c5f445 Mon Sep 17 00:00:00 2001 From: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Date: Sat, 23 May 2026 23:03:36 +0800 Subject: [PATCH 2/3] ci: automatically publish suppressions after merged PRs (#8527) Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/lint-pr.yml | 4 ++++ .github/workflows/publish-suppressions.yml | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index 637dcd9957b..3d2ba14a1fd 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -2,6 +2,10 @@ name: "Lint PR" on: pull_request_target: + # BE CAREFUL - this event runs in the context of the default branch (`main`) workflow definition in the target + # repository (NOT the fork's context), so it has potentially sensitive access. + # It is critical that this only runs on very limited events and/or access to the repo + # Read https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target types: - opened - edited diff --git a/.github/workflows/publish-suppressions.yml b/.github/workflows/publish-suppressions.yml index 2d5d7d2963a..bc0acf4ddf5 100644 --- a/.github/workflows/publish-suppressions.yml +++ b/.github/workflows/publish-suppressions.yml @@ -2,13 +2,21 @@ name: Publish Suppressions on: workflow_dispatch: - push: - branches: - - generatedSuppressions + pull_request_target: + # BE CAREFUL - this event runs in the context of the default branch (`main`) workflow definition in the target + # repository (NOT the fork's context), so it has potentially sensitive access. + # It is critical that this only runs on very limited events and/or access to the repo + # Read https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target + types: [ closed ] + branches: [ generatedSuppressions ] + paths: [ generatedSuppressions.xml ] + permissions: {} jobs: update_suppression: + if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true + permissions: contents: write # to push changes in repo (jamesives/github-pages-deploy-action) From c5f5f681eedab927ff3cad2ed5c229cbce02b206 Mon Sep 17 00:00:00 2001 From: Shawn Huang Date: Sun, 24 May 2026 01:10:18 +0800 Subject: [PATCH 3/3] fix(analyzer): detect OpenSSL 3.x in OpenSSLAnalyzer (#8484) --- .../analyzer/OpenSSLAnalyzer.java | 48 ++++--- .../analyzer/OpenSSLAnalyzerTest.java | 11 ++ core/src/test/resources/openssl-3x/opensslv.h | 131 ++++++++++++++++++ 3 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 core/src/test/resources/openssl-3x/opensslv.h diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java index 33f7ce947e0..47123e657ae 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java @@ -68,11 +68,20 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { */ private static final FileFilter OPENSSLV_FILTER = FileFilterBuilder.newInstance().addFilenames(OPENSSLV_H).build(); /** - * Open SSL Version number pattern. + * Open SSL Version number pattern (OpenSSL 1.x and earlier — literal hex constant). */ private static final Pattern VERSION_PATTERN = Pattern.compile( "define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); + /** + * Open SSL version string pattern (OpenSSL 3.x — {@code OPENSSL_VERSION_STR "X.Y.Z"}). + * In 3.x, {@code OPENSSL_VERSION_NUMBER} is a macro expression that the + * legacy {@link #VERSION_PATTERN} cannot match, so this is the authoritative + * source of the version on 3.x headers. + */ + private static final Pattern VERSION_STR_PATTERN = Pattern.compile( + "define\\s+OPENSSL_VERSION_STR\\s+\"([^\"]+)\"", Pattern.DOTALL + | Pattern.CASE_INSENSITIVE); /** * The offset of the major version number. */ @@ -191,28 +200,31 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { final File file = dependency.getActualFile(); final String parentName = file.getParentFile().getName(); - boolean found = false; final String contents = getFileContents(file); + String version = null; if (!contents.isEmpty()) { - final Matcher matcher = VERSION_PATTERN.matcher(contents); - if (matcher.find()) { - found = true; - final String version = getOpenSSLVersion(Long.parseLong(matcher.group(1), HEXADECIMAL)); - dependency.addEvidence(EvidenceType.VERSION, OPENSSLV_H, "Version Constant", - version, Confidence.HIGH); - try { - final PackageURL purl = PackageURLBuilder.aPackageURL().withType("generic") - .withName("openssl").withVersion(version).build(); - dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST)); - } catch (MalformedPackageURLException ex) { - LOGGER.debug("Unable to build package url for openssl", ex); - final GenericIdentifier id = new GenericIdentifier("generic:openssl@" + version, Confidence.HIGHEST); - dependency.addSoftwareIdentifier(id); + final Matcher strMatcher = VERSION_STR_PATTERN.matcher(contents); + if (strMatcher.find()) { + version = strMatcher.group(1); + } else { + final Matcher matcher = VERSION_PATTERN.matcher(contents); + if (matcher.find()) { + version = getOpenSSLVersion(Long.parseLong(matcher.group(1), HEXADECIMAL)); } - } } - if (found) { + if (version != null) { + dependency.addEvidence(EvidenceType.VERSION, OPENSSLV_H, "Version Constant", + version, Confidence.HIGH); + try { + final PackageURL purl = PackageURLBuilder.aPackageURL().withType("generic") + .withName("openssl").withVersion(version).build(); + dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST)); + } catch (MalformedPackageURLException ex) { + LOGGER.debug("Unable to build package url for openssl", ex); + final GenericIdentifier id = new GenericIdentifier("generic:openssl@" + version, Confidence.HIGHEST); + dependency.addSoftwareIdentifier(id); + } dependency.setDisplayFileName(parentName + File.separatorChar + OPENSSLV_H); dependency.addEvidence(EvidenceType.VENDOR, OPENSSLV_H, "Vendor", "OpenSSL", Confidence.HIGHEST); dependency.addEvidence(EvidenceType.PRODUCT, OPENSSLV_H, "Product", "OpenSSL", Confidence.HIGHEST); diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java index a9b14445a9a..e8a6d7781b3 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java @@ -115,4 +115,15 @@ void testOpenSSLVersionHeaderFile() throws AnalysisException { assertThat(result.getEvidence(EvidenceType.VENDOR).toString(), containsString("OpenSSL")); assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("1.0.2c")); } + + @Test + void testOpenSSL3xVersionHeaderFile() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile( + this, + "openssl-3x/opensslv.h")); + analyzer.analyze(result, null); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("OpenSSL")); + assertThat(result.getEvidence(EvidenceType.VENDOR).toString(), containsString("OpenSSL")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("3.5.6")); + } } diff --git a/core/src/test/resources/openssl-3x/opensslv.h b/core/src/test/resources/openssl-3x/opensslv.h new file mode 100644 index 00000000000..299ba102298 --- /dev/null +++ b/core/src/test/resources/openssl-3x/opensslv.h @@ -0,0 +1,131 @@ +/* + * WARNING: do not edit! + * Generated by Makefile from include/openssl/opensslv.h.in + * + * Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OPENSSL_OPENSSLV_H +#define OPENSSL_OPENSSLV_H +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SECTION 1: VERSION DATA. These will change for each release + */ + +/* + * Base version macros + * + * These macros express version number MAJOR.MINOR.PATCH exactly + */ +/* clang-format off */ +# define OPENSSL_VERSION_MAJOR 3 +/* clang-format on */ +/* clang-format off */ +# define OPENSSL_VERSION_MINOR 5 +/* clang-format on */ +/* clang-format off */ +# define OPENSSL_VERSION_PATCH 6 +/* clang-format on */ + +/* + * Additional version information + * + * These are also part of the new version scheme, but aren't part + * of the version number itself. + */ + +/* Could be: #define OPENSSL_VERSION_PRE_RELEASE "-alpha.1" */ +/* clang-format off */ +# define OPENSSL_VERSION_PRE_RELEASE "" +/* clang-format on */ +/* Could be: #define OPENSSL_VERSION_BUILD_METADATA "+fips" */ +/* Could be: #define OPENSSL_VERSION_BUILD_METADATA "+vendor.1" */ +/* clang-format off */ +# define OPENSSL_VERSION_BUILD_METADATA "" +/* clang-format on */ + +/* + * Note: The OpenSSL Project will never define OPENSSL_VERSION_BUILD_METADATA + * to be anything but the empty string. Its use is entirely reserved for + * others + */ + +/* + * Shared library version + * + * This is strictly to express ABI version, which may or may not + * be related to the API version expressed with the macros above. + * This is defined in free form. + */ +/* clang-format off */ +# define OPENSSL_SHLIB_VERSION 3 +/* clang-format on */ + +/* + * SECTION 2: USEFUL MACROS + */ + +/* For checking general API compatibility when preprocessing */ +#define OPENSSL_VERSION_PREREQ(maj, min) \ + ((OPENSSL_VERSION_MAJOR << 16) + OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min)) + +/* + * Macros to get the version in easily digested string form, both the short + * "MAJOR.MINOR.PATCH" variant (where MAJOR, MINOR and PATCH are replaced + * with the values from the corresponding OPENSSL_VERSION_ macros) and the + * longer variant with OPENSSL_VERSION_PRE_RELEASE_STR and + * OPENSSL_VERSION_BUILD_METADATA_STR appended. + */ +/* clang-format off */ +# define OPENSSL_VERSION_STR "3.5.6" +/* clang-format on */ +/* clang-format off */ +# define OPENSSL_FULL_VERSION_STR "3.5.6" +/* clang-format on */ + +/* + * SECTION 3: ADDITIONAL METADATA + * + * These strings are defined separately to allow them to be parsable. + */ +/* clang-format off */ +# define OPENSSL_RELEASE_DATE "7 Apr 2026" +/* clang-format on */ + +/* + * SECTION 4: BACKWARD COMPATIBILITY + */ + +/* clang-format off */ +# define OPENSSL_VERSION_TEXT "OpenSSL 3.5.6 7 Apr 2026" +/* clang-format on */ + +/* clang-format off */ +/* Synthesize OPENSSL_VERSION_NUMBER with the layout 0xMNN00PP0L */ +# define OPENSSL_VERSION_NUMBER \ + ( (OPENSSL_VERSION_MAJOR<<28) \ + |(OPENSSL_VERSION_MINOR<<20) \ + |(OPENSSL_VERSION_PATCH<<4) \ + |0x0L ) +/* clang-format on */ + +#ifdef __cplusplus +} +#endif + +#include +#ifndef OPENSSL_NO_DEPRECATED_3_0 +#define HEADER_OPENSSLV_H +#endif + +#endif /* OPENSSL_OPENSSLV_H */