From 350d52d52be94b9c0e6e858a521285bfc100457e Mon Sep 17 00:00:00 2001 From: Shawn Huang Date: Wed, 6 May 2026 19:26:20 +0800 Subject: [PATCH] fix(analyzer): detect OpenSSL 3.x in OpenSSLAnalyzer OpenSSL 3.x defines OPENSSL_VERSION_NUMBER as a macro expression rather than a literal hex constant, so the existing regex never matches and the analyzer drops the dependency. As a result, 3.x source trees produce no CPE and never match NVD CVEs. Add a second pattern that captures OPENSSL_VERSION_STR "X.Y.Z" (introduced in 3.0); fall back to the legacy NUMBER pattern for 1.x and earlier. Adds a test fixture/case using opensslv.h from OpenSSL 3.5.6. --- .../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 33f7ce947e..47123e657a 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 a9b14445a9..e8a6d7781b 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 0000000000..299ba10229 --- /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 */