From 9edc88d824f2d29444f484aa211a5c496127a4e0 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 20 Oct 2026 20:04:43 -0400 Subject: [PATCH] feat: Block compromised certs --- README.md | 1 + __tests__/blocked-certs.test.ts | 118 ++++++++++++++++++++++ __tests__/setup-java.test.ts | 2 + action.yml | 4 + dist/cleanup/index.js | 3 +- dist/setup/126.index.js | 2 +- dist/setup/151.index.js | 2 +- dist/setup/182.index.js | 2 +- dist/setup/19.index.js | 2 +- dist/setup/220.index.js | 4 +- dist/setup/228.index.js | 2 +- dist/setup/282.index.js | 2 +- dist/setup/{242.index.js => 370.index.js} | 88 +++++++++++++++- dist/setup/463.index.js | 4 +- dist/setup/524.index.js | 2 +- dist/setup/557.index.js | 2 +- dist/setup/63.index.js | 2 +- dist/setup/675.index.js | 2 +- dist/setup/735.index.js | 2 +- dist/setup/939.index.js | 2 +- dist/setup/968.index.js | 2 +- dist/setup/978.index.js | 2 +- dist/setup/index.js | 42 ++++---- docs/advanced-usage.md | 20 ++++ src/blocked-certs.ts | 100 ++++++++++++++++++ src/constants.ts | 1 + src/distributions/base-installer.ts | 7 ++ src/distributions/base-models.ts | 1 + src/setup-java.ts | 9 ++ 29 files changed, 392 insertions(+), 40 deletions(-) create mode 100644 __tests__/blocked-certs.test.ts rename dist/setup/{242.index.js => 370.index.js} (89%) create mode 100644 src/blocked-certs.ts diff --git a/README.md b/README.md index 58822e688..368de39c6 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ steps: | `force-download` | Always download Java and replace any matching version in the tool cache. | `false` | | `set-default` | Add Java to `PATH` and set `JAVA_HOME`. When `false`, only version-specific `JAVA_HOME__` variables are set. | `true` | | `problem-matcher` | Register Java compiler and uncaught exception problem matchers. | `true` | +| `block-compromised-certs` | Add known compromised CA certificates to the installed JDK's blocked certificates list. See [Blocking compromised CA certificates](docs/advanced-usage.md#blocking-compromised-ca-certificates). | `true` | | `verify-signature` | Verify downloaded Java package signatures when supported. Explicitly setting this to `true` makes verification failures fatal. | Distribution-dependent; see [Download integrity and signatures](#download-integrity-and-signatures) | | `verify-signature-public-key` | One or more ASCII-armored GPG public keys used for signature verification. Concatenate multiple armored key blocks. Custom keys replace the bundled distribution keys. | | | `token` | Token for fetching GitHub.com-hosted version manifests, useful on GitHub Enterprise Server when unauthenticated requests are rate-limited. | `${{ github.token }}` on GitHub.com; empty string on GHES | diff --git a/__tests__/blocked-certs.test.ts b/__tests__/blocked-certs.test.ts new file mode 100644 index 000000000..6f639ca67 --- /dev/null +++ b/__tests__/blocked-certs.test.ts @@ -0,0 +1,118 @@ +import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals'; +import * as fs from 'fs'; +import os from 'os'; +import path from 'path'; + +const mockWarning = jest.fn<(...args: any[]) => any>(); + +jest.unstable_mockModule('@actions/core', () => ({ + info: jest.fn(), + debug: jest.fn(), + warning: mockWarning +})); + +const {blockCompromisedCertificates, COMPROMISED_CERT_FINGERPRINTS} = + await import('../src/blocked-certs.js'); + +describe('blockCompromisedCertificates', () => { + let javaHome: string; + + const writeList = (relative: string, content: string): string => { + const file = path.join(javaHome, relative); + fs.mkdirSync(path.dirname(file), {recursive: true}); + fs.writeFileSync(file, content); + return file; + }; + + const fingerprintsIn = (file: string): string[] => + fs + .readFileSync(file, 'utf8') + .split('\n') + .filter(line => /^[0-9A-F]{64}$/.test(line)); + + beforeEach(() => { + javaHome = fs.mkdtempSync(path.join(os.tmpdir(), 'blocked-certs-')); + }); + + afterEach(() => { + fs.rmSync(javaHome, {recursive: true, force: true}); + jest.resetAllMocks(); + }); + + it('appends the fingerprints to lib/security/blocked.certs', () => { + const file = writeList( + 'lib/security/blocked.certs', + 'Algorithm=SHA-256\nAAAA' + ); + + blockCompromisedCertificates(javaHome); + + const content = fs.readFileSync(file, 'utf8'); + expect(content.startsWith('Algorithm=SHA-256\nAAAA\n')).toBe(true); + expect(fingerprintsIn(file)).toEqual([...COMPROMISED_CERT_FINGERPRINTS]); + expect(mockWarning).not.toHaveBeenCalled(); + }); + + it('is idempotent', () => { + const file = writeList('lib/security/blocked.certs', 'Algorithm=SHA-256\n'); + + blockCompromisedCertificates(javaHome); + blockCompromisedCertificates(javaHome); + + expect(fingerprintsIn(file)).toHaveLength( + COMPROMISED_CERT_FINGERPRINTS.length + ); + }); + + it('only adds fingerprints that are missing', () => { + const file = writeList( + 'lib/security/blocked.certs', + `Algorithm=SHA-256\n${COMPROMISED_CERT_FINGERPRINTS[0].toLowerCase()}\n` + ); + + blockCompromisedCertificates(javaHome); + + expect(fingerprintsIn(file)).toEqual( + COMPROMISED_CERT_FINGERPRINTS.slice(1) + ); + }); + + it('updates blacklisted.certs in both the JDK and bundled JRE of older JDKs', () => { + const jdk = writeList( + 'lib/security/blacklisted.certs', + 'Algorithm=SHA-256\n' + ); + const jre = writeList( + 'jre/lib/security/blacklisted.certs', + 'Algorithm=SHA-256\n' + ); + + blockCompromisedCertificates(javaHome); + + expect(fingerprintsIn(jdk)).toHaveLength( + COMPROMISED_CERT_FINGERPRINTS.length + ); + expect(fingerprintsIn(jre)).toHaveLength( + COMPROMISED_CERT_FINGERPRINTS.length + ); + }); + + it('warns and leaves the file alone when it does not use SHA-256', () => { + const file = writeList('lib/security/blocked.certs', 'Algorithm=SHA-1\n'); + + blockCompromisedCertificates(javaHome); + + expect(fs.readFileSync(file, 'utf8')).toBe('Algorithm=SHA-1\n'); + expect(mockWarning).toHaveBeenCalledWith( + expect.stringContaining('does not use SHA-256') + ); + }); + + it('warns when no blocked certificates list exists', () => { + blockCompromisedCertificates(javaHome); + + expect(mockWarning).toHaveBeenCalledWith( + expect.stringContaining('Could not find a blocked certificates list') + ); + }); +}); diff --git a/__tests__/setup-java.test.ts b/__tests__/setup-java.test.ts index c75ead639..d3718d65b 100644 --- a/__tests__/setup-java.test.ts +++ b/__tests__/setup-java.test.ts @@ -232,6 +232,7 @@ describe('setup action orchestration', () => { booleanInputs.set('check-latest', true); booleanInputs.set('force-download', true); booleanInputs.set('set-default', false); + booleanInputs.set('block-compromised-certs', false); inputs.set('verify-signature-public-key', 'public-key'); (fs.readFileSync as jest.Mock).mockReturnValue( Buffer.from('java=21.0.5-tem') @@ -263,6 +264,7 @@ describe('setup action orchestration', () => { forceDownload: true, cacheJdk: false, setDefault: false, + blockCompromisedCerts: false, verifySignature: undefined, verifySignaturePublicKey: 'public-key' }, diff --git a/action.yml b/action.yml index eb32ec42c..27e08af3c 100644 --- a/action.yml +++ b/action.yml @@ -138,6 +138,10 @@ inputs: description: 'Whether to register the Java problem matcher (compiler errors/warnings and uncaught exceptions). Set to "false" to disable annotations.' required: false default: true + block-compromised-certs: + description: 'Add the fingerprints of known compromised CA certificates to the installed JDK''s blocked certificates list, so TLS connections through those CAs are rejected. Set to "false" to leave the JDK''s list unchanged.' + required: false + default: true outputs: distribution: description: 'Distribution of Java that has been installed' diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 03ab6fe1f..c0adff045 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -31040,7 +31040,7 @@ module.exports = { /* harmony export */ gk: () => (/* binding */ INPUT_CACHE), /* harmony export */ wG: () => (/* binding */ INPUT_JOB_STATUS) /* harmony export */ }); -/* unused harmony exports MACOS_JAVA_CONTENT_POSTFIX, INPUT_JAVA_VERSION, INPUT_JAVA_VERSION_FILE, INPUT_ARCHITECTURE, INPUT_JAVA_PACKAGE, INPUT_DISTRIBUTION, INPUT_JDK_FILE, INPUT_JDK_FILE_DEPRECATED, INPUT_CHECK_LATEST, INPUT_FORCE_DOWNLOAD, INPUT_SET_DEFAULT, INPUT_PROBLEM_MATCHER, INPUT_VERIFY_SIGNATURE, INPUT_VERIFY_SIGNATURE_PUBLIC_KEY, SIGNATURE_VERIFICATION_DOCUMENTATION_URL, SIGNATURE_VERIFICATION_FAILURE_HELP, INPUT_MVN_SERVER_CREDENTIALS, INPUT_MVN_SERVER_REPOSITORY_ORIGINS, INPUT_MVN_REPOSITORIES, INPUT_MVN_PLUGIN_REPOSITORIES, INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL, INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL, INPUT_SERVER_ID, INPUT_SERVER_USERNAME_ENV_VAR, INPUT_SERVER_PASSWORD_ENV_VAR, INPUT_SERVER_USERNAME_DEPRECATED, INPUT_SERVER_PASSWORD_DEPRECATED, INPUT_SETTINGS_PATH, INPUT_OVERWRITE_SETTINGS, INPUT_GPG_PRIVATE_KEY, INPUT_GPG_PASSPHRASE_ENV_VAR, INPUT_GPG_PASSPHRASE_DEPRECATED, INPUT_DEFAULT_SERVER_USERNAME, INPUT_DEFAULT_SERVER_PASSWORD, INPUT_DEFAULT_GPG_PRIVATE_KEY, INPUT_DEFAULT_GPG_PASSPHRASE, MAVEN_GPG_PASSPHRASE_DEFAULT_ENV, GPG_PASSPHRASE_PROFILE_ID, MAVEN_REPOSITORIES_PROFILE_ID, MAVEN_CENTRAL_REPOSITORY_ID, MAVEN_CENTRAL_REPOSITORY_URL, INPUT_CACHE_DEPENDENCY_PATH, INPUT_CACHE_PATH, M2_DIR, MVN_SETTINGS_FILE, MVN_TOOLCHAINS_FILE, INPUT_MVN_TOOLCHAIN_ID, INPUT_MVN_TOOLCHAIN_VENDOR, INPUT_SHOW_DOWNLOAD_PROGRESS, MAVEN_ARGS_ENV, MAVEN_NO_TRANSFER_PROGRESS_FLAG, MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG, DISTRIBUTIONS_ONLY_MAJOR_VERSION */ +/* unused harmony exports MACOS_JAVA_CONTENT_POSTFIX, INPUT_JAVA_VERSION, INPUT_JAVA_VERSION_FILE, INPUT_ARCHITECTURE, INPUT_JAVA_PACKAGE, INPUT_DISTRIBUTION, INPUT_JDK_FILE, INPUT_JDK_FILE_DEPRECATED, INPUT_CHECK_LATEST, INPUT_FORCE_DOWNLOAD, INPUT_SET_DEFAULT, INPUT_PROBLEM_MATCHER, INPUT_BLOCK_COMPROMISED_CERTS, INPUT_VERIFY_SIGNATURE, INPUT_VERIFY_SIGNATURE_PUBLIC_KEY, SIGNATURE_VERIFICATION_DOCUMENTATION_URL, SIGNATURE_VERIFICATION_FAILURE_HELP, INPUT_MVN_SERVER_CREDENTIALS, INPUT_MVN_SERVER_REPOSITORY_ORIGINS, INPUT_MVN_REPOSITORIES, INPUT_MVN_PLUGIN_REPOSITORIES, INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL, INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL, INPUT_SERVER_ID, INPUT_SERVER_USERNAME_ENV_VAR, INPUT_SERVER_PASSWORD_ENV_VAR, INPUT_SERVER_USERNAME_DEPRECATED, INPUT_SERVER_PASSWORD_DEPRECATED, INPUT_SETTINGS_PATH, INPUT_OVERWRITE_SETTINGS, INPUT_GPG_PRIVATE_KEY, INPUT_GPG_PASSPHRASE_ENV_VAR, INPUT_GPG_PASSPHRASE_DEPRECATED, INPUT_DEFAULT_SERVER_USERNAME, INPUT_DEFAULT_SERVER_PASSWORD, INPUT_DEFAULT_GPG_PRIVATE_KEY, INPUT_DEFAULT_GPG_PASSPHRASE, MAVEN_GPG_PASSPHRASE_DEFAULT_ENV, GPG_PASSPHRASE_PROFILE_ID, MAVEN_REPOSITORIES_PROFILE_ID, MAVEN_CENTRAL_REPOSITORY_ID, MAVEN_CENTRAL_REPOSITORY_URL, INPUT_CACHE_DEPENDENCY_PATH, INPUT_CACHE_PATH, M2_DIR, MVN_SETTINGS_FILE, MVN_TOOLCHAINS_FILE, INPUT_MVN_TOOLCHAIN_ID, INPUT_MVN_TOOLCHAIN_VENDOR, INPUT_SHOW_DOWNLOAD_PROGRESS, MAVEN_ARGS_ENV, MAVEN_NO_TRANSFER_PROGRESS_FLAG, MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG, DISTRIBUTIONS_ONLY_MAJOR_VERSION */ const MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; const INPUT_JAVA_VERSION = 'java-version'; const INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -31053,6 +31053,7 @@ const INPUT_CHECK_LATEST = 'check-latest'; const INPUT_FORCE_DOWNLOAD = 'force-download'; const INPUT_SET_DEFAULT = 'set-default'; const INPUT_PROBLEM_MATCHER = 'problem-matcher'; +const INPUT_BLOCK_COMPROMISED_CERTS = 'block-compromised-certs'; const INPUT_VERIFY_SIGNATURE = 'verify-signature'; const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; const SIGNATURE_VERIFICATION_DOCUMENTATION_URL = 'https://github.com/actions/setup-java#download-integrity-and-signatures'; diff --git a/dist/setup/126.index.js b/dist/setup/126.index.js index 410f70ed8..9718e5d53 100644 --- a/dist/setup/126.index.js +++ b/dist/setup/126.index.js @@ -14,7 +14,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4527); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7444); diff --git a/dist/setup/151.index.js b/dist/setup/151.index.js index 4ffcd31fb..9cf5906f7 100644 --- a/dist/setup/151.index.js +++ b/dist/setup/151.index.js @@ -15,7 +15,7 @@ export const modules = { /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); diff --git a/dist/setup/182.index.js b/dist/setup/182.index.js index cfb75b889..885ca5cd7 100644 --- a/dist/setup/182.index.js +++ b/dist/setup/182.index.js @@ -13,7 +13,7 @@ export const modules = { /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4527); /* harmony import */ var _actions_http_client__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4942); diff --git a/dist/setup/19.index.js b/dist/setup/19.index.js index d9aaa6f1f..308cfd478 100644 --- a/dist/setup/19.index.js +++ b/dist/setup/19.index.js @@ -13,7 +13,7 @@ export const modules = { /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4527); /* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7242); /* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6982); diff --git a/dist/setup/220.index.js b/dist/setup/220.index.js index d1ae361fb..63b487c5b 100644 --- a/dist/setup/220.index.js +++ b/dist/setup/220.index.js @@ -13,8 +13,8 @@ __webpack_require__.d(__webpack_exports__, { // UNUSED EXPORTS: MICROSOFT_PUBLIC_KEY -// EXTERNAL MODULE: ./src/distributions/base-installer.ts + 2 modules -var base_installer = __webpack_require__(6242); +// EXTERNAL MODULE: ./src/distributions/base-installer.ts + 3 modules +var base_installer = __webpack_require__(370); // EXTERNAL MODULE: ./src/util.ts var util = __webpack_require__(4527); // EXTERNAL MODULE: ./src/gpg.ts diff --git a/dist/setup/228.index.js b/dist/setup/228.index.js index 09e4b5d1c..a60d3c4ff 100644 --- a/dist/setup/228.index.js +++ b/dist/setup/228.index.js @@ -16,7 +16,7 @@ export const modules = { /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4527); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(370); /* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7444); diff --git a/dist/setup/282.index.js b/dist/setup/282.index.js index 68edab3e5..f0681fda8 100644 --- a/dist/setup/282.index.js +++ b/dist/setup/282.index.js @@ -15,7 +15,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); /* harmony import */ var _actions_http_client__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4942); diff --git a/dist/setup/242.index.js b/dist/setup/370.index.js similarity index 89% rename from dist/setup/242.index.js rename to dist/setup/370.index.js index ccbf91db4..e56dfe356 100644 --- a/dist/setup/242.index.js +++ b/dist/setup/370.index.js @@ -1,8 +1,8 @@ -export const id = 242; -export const ids = [242]; +export const id = 370; +export const ids = [370]; export const modules = { -/***/ 6242: +/***/ 370: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { @@ -194,6 +194,82 @@ async function verifyChecksum(filePath, checksum, context) { // EXTERNAL MODULE: ./src/distributions/platform-types.ts var platform_types = __webpack_require__(7444); +;// CONCATENATED MODULE: ./src/blocked-certs.ts + + + +/** + * SHA-256 fingerprints of compromised eMudhra intermediate CA certificates. + * The JDK refuses to trust any certificate chain containing one of these. + */ +const COMPROMISED_CERT_FINGERPRINTS = [ + '42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493', + '45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE', + '98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B', + '5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699', + 'E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838', + '579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512', + '0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19', + '62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72', + 'CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F', + '7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489', + '432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3', + 'FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F', + '9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4', + 'A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8', + '4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1', + '84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD' +]; +// Newer JDKs use blocked.certs; older ones (JDK 8/11) use blacklisted.certs. +// A JDK 8 bundles a separate JRE under jre/ that has its own copy. +const BLOCKED_CERTS_CANDIDATES = [ + ['lib', 'security', 'blocked.certs'], + ['lib', 'security', 'blacklisted.certs'], + ['jre', 'lib', 'security', 'blocked.certs'], + ['jre', 'lib', 'security', 'blacklisted.certs'] +]; +const SHA256_ALGORITHM_LINE = /^\s*Algorithm\s*=\s*SHA-256\s*$/i; +/** + * Adds the compromised CA fingerprints to the blocked certificates list of + * the JDK at `javaHome`. Fingerprints already present are left alone, so + * running this repeatedly on a tool-cached JDK is safe. + */ +function blockCompromisedCertificates(javaHome, fingerprints = COMPROMISED_CERT_FINGERPRINTS) { + const files = BLOCKED_CERTS_CANDIDATES.map(parts => external_path_default().join(javaHome, ...parts)).filter(file => external_fs_.existsSync(file)); + if (!files.length) { + core/* warning */.$e(`Could not find a blocked certificates list in ${javaHome}; compromised CA certificates were not blocked.`); + return; + } + for (const file of files) { + try { + appendFingerprints(file, fingerprints); + } + catch (error) { + core/* warning */.$e(`Failed to block compromised CA certificates in ${file}: ${error.message}`); + } + } +} +function appendFingerprints(file, fingerprints) { + const content = external_fs_.readFileSync(file, 'utf8'); + const lines = content.split(/\r?\n/).map(line => line.trim()); + const algorithmLine = lines.find(line => line.startsWith('Algorithm')); + if (!algorithmLine || !SHA256_ALGORITHM_LINE.test(algorithmLine)) { + core/* warning */.$e(`${file} does not use SHA-256 fingerprints; compromised CA certificates were not blocked.`); + return; + } + const existing = new Set(lines.map(line => line.toUpperCase())); + const missing = fingerprints + .map(fingerprint => fingerprint.toUpperCase()) + .filter(fingerprint => !existing.has(fingerprint)); + if (!missing.length) { + core/* debug */.Yz(`${file} already blocks the compromised CA certificates`); + return; + } + const prefix = content.length && !content.endsWith('\n') ? '\n' : ''; + external_fs_.appendFileSync(file, `${prefix}${missing.join('\n')}\n`); + core/* info */.pq(`Blocked ${missing.length} compromised CA certificate(s) in ${file}`); +} + ;// CONCATENATED MODULE: ./src/distributions/base-installer.ts @@ -207,6 +283,7 @@ var platform_types = __webpack_require__(7444); + class JavaBase { distribution; http; @@ -225,6 +302,7 @@ class JavaBase { */ floatingVersionVerified = false; setDefault; + blockCompromisedCerts; verifySignature; verifySignatureExplicitlyRequested; verifySignaturePublicKey; @@ -245,6 +323,7 @@ class JavaBase { installerOptions.setDefault !== undefined ? installerOptions.setDefault : true; + this.blockCompromisedCerts = installerOptions.blockCompromisedCerts ?? true; this.verifySignature = installerOptions.verifySignature ?? this.supportsSignatureVerification(); this.verifySignatureExplicitlyRequested = @@ -411,6 +490,9 @@ class JavaBase { if (process.platform === 'darwin' && external_fs_.existsSync(macOSPostfixPath)) { foundJava.path = macOSPostfixPath; } + if (this.blockCompromisedCerts) { + blockCompromisedCertificates(foundJava.path); + } if (this.setDefault) { core/* info */.pq(`Setting Java ${foundJava.version} as the default`); this.setJavaDefault(foundJava.version, foundJava.path); diff --git a/dist/setup/463.index.js b/dist/setup/463.index.js index 5267d4c81..54b166775 100644 --- a/dist/setup/463.index.js +++ b/dist/setup/463.index.js @@ -62,8 +62,8 @@ wI4qF/KKq9BfyfucAs0ykA== =XLag -----END PGP PUBLIC KEY BLOCK-----`; -// EXTERNAL MODULE: ./src/distributions/base-installer.ts + 2 modules -var base_installer = __webpack_require__(6242); +// EXTERNAL MODULE: ./src/distributions/base-installer.ts + 3 modules +var base_installer = __webpack_require__(370); // EXTERNAL MODULE: ./src/constants.ts var constants = __webpack_require__(7242); // EXTERNAL MODULE: ./src/util.ts diff --git a/dist/setup/524.index.js b/dist/setup/524.index.js index 4f570d2f6..c6fecc0cb 100644 --- a/dist/setup/524.index.js +++ b/dist/setup/524.index.js @@ -8,7 +8,7 @@ export const modules = { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ LibericaNikDistributions: () => (/* binding */ LibericaNikDistributions) /* harmony export */ }); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(370); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527); diff --git a/dist/setup/557.index.js b/dist/setup/557.index.js index 541609e0f..a90acb9b8 100644 --- a/dist/setup/557.index.js +++ b/dist/setup/557.index.js @@ -16,7 +16,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4527); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(370); /* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7444); diff --git a/dist/setup/63.index.js b/dist/setup/63.index.js index 44eb30a17..c616cc25c 100644 --- a/dist/setup/63.index.js +++ b/dist/setup/63.index.js @@ -8,7 +8,7 @@ export const modules = { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ LibericaDistributions: () => (/* binding */ LibericaDistributions) /* harmony export */ }); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(370); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527); diff --git a/dist/setup/675.index.js b/dist/setup/675.index.js index 9e71f4130..7cd1c268a 100644 --- a/dist/setup/675.index.js +++ b/dist/setup/675.index.js @@ -15,7 +15,7 @@ export const modules = { /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); /* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7444); diff --git a/dist/setup/735.index.js b/dist/setup/735.index.js index cbe1d98c3..821dc89e8 100644 --- a/dist/setup/735.index.js +++ b/dist/setup/735.index.js @@ -15,7 +15,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); diff --git a/dist/setup/939.index.js b/dist/setup/939.index.js index 1b2406a0a..fb892dee9 100644 --- a/dist/setup/939.index.js +++ b/dist/setup/939.index.js @@ -8,7 +8,7 @@ export const modules = { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ SemeruDistribution: () => (/* binding */ SemeruDistribution) /* harmony export */ }); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(370); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527); diff --git a/dist/setup/968.index.js b/dist/setup/968.index.js index b4f7f49a4..6089ece56 100644 --- a/dist/setup/968.index.js +++ b/dist/setup/968.index.js @@ -16,7 +16,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2088); /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(370); /* harmony import */ var _actions_http_client__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4942); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4527); diff --git a/dist/setup/978.index.js b/dist/setup/978.index.js index fe57a00e2..753274de1 100644 --- a/dist/setup/978.index.js +++ b/dist/setup/978.index.js @@ -13,7 +13,7 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9896); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6242); +/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(370); /* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7444); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); diff --git a/dist/setup/index.js b/dist/setup/index.js index 5c0536646..56ec62335 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -31073,6 +31073,7 @@ module.exports = { /* harmony export */ ko: () => (/* binding */ MAVEN_GPG_PASSPHRASE_DEFAULT_ENV), /* harmony export */ m7: () => (/* binding */ INPUT_MVN_TOOLCHAIN_VENDOR), /* harmony export */ nr: () => (/* binding */ INPUT_MVN_TOOLCHAIN_ID), +/* harmony export */ ot: () => (/* binding */ INPUT_BLOCK_COMPROMISED_CERTS), /* harmony export */ p1: () => (/* binding */ INPUT_JAVA_PACKAGE), /* harmony export */ qm: () => (/* binding */ MAVEN_ARGS_ENV), /* harmony export */ qs: () => (/* binding */ MVN_TOOLCHAINS_FILE), @@ -31103,6 +31104,7 @@ const INPUT_CHECK_LATEST = 'check-latest'; const INPUT_FORCE_DOWNLOAD = 'force-download'; const INPUT_SET_DEFAULT = 'set-default'; const INPUT_PROBLEM_MATCHER = 'problem-matcher'; +const INPUT_BLOCK_COMPROMISED_CERTS = 'block-compromised-certs'; const INPUT_VERIFY_SIGNATURE = 'verify-signature'; const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; const SIGNATURE_VERIFICATION_DOCUMENTATION_URL = 'https://github.com/actions/setup-java#download-integrity-and-signatures'; @@ -36555,71 +36557,71 @@ async function getJavaDistribution(distributionName, installerOptions, jdkFile) }; switch (distributionName) { case package_types/* JavaDistribution */.zS.JdkFile: { - const { LocalDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(19)]).then(__nccwpck_require__.bind(__nccwpck_require__, 1019)); + const { LocalDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(19)]).then(__nccwpck_require__.bind(__nccwpck_require__, 1019)); return new LocalDistribution(normalizedInstallerOptions, jdkFile); } case package_types/* JavaDistribution */.zS.Temurin: { - const { TemurinDistribution, TemurinImplementation } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(463)]).then(__nccwpck_require__.bind(__nccwpck_require__, 463)); + const { TemurinDistribution, TemurinImplementation } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(463)]).then(__nccwpck_require__.bind(__nccwpck_require__, 463)); return new TemurinDistribution(normalizedInstallerOptions, TemurinImplementation.Hotspot); } case package_types/* JavaDistribution */.zS.Zulu: { - const { ZuluDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(978)]).then(__nccwpck_require__.bind(__nccwpck_require__, 9597)); + const { ZuluDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(978)]).then(__nccwpck_require__.bind(__nccwpck_require__, 9597)); return new ZuluDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Liberica: { - const { LibericaDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(63)]).then(__nccwpck_require__.bind(__nccwpck_require__, 2063)); + const { LibericaDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(63)]).then(__nccwpck_require__.bind(__nccwpck_require__, 2063)); return new LibericaDistributions(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.LibericaNik: { - const { LibericaNikDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(524)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8524)); + const { LibericaNikDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(524)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8524)); return new LibericaNikDistributions(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Microsoft: { - const { MicrosoftDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(220)]).then(__nccwpck_require__.bind(__nccwpck_require__, 3220)); + const { MicrosoftDistributions } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(220)]).then(__nccwpck_require__.bind(__nccwpck_require__, 3220)); return new MicrosoftDistributions(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Semeru: { - const { SemeruDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(939)]).then(__nccwpck_require__.bind(__nccwpck_require__, 9939)); + const { SemeruDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(939)]).then(__nccwpck_require__.bind(__nccwpck_require__, 9939)); return new SemeruDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Corretto: { - const { CorrettoDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(126)]).then(__nccwpck_require__.bind(__nccwpck_require__, 4126)); + const { CorrettoDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(126)]).then(__nccwpck_require__.bind(__nccwpck_require__, 4126)); return new CorrettoDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Oracle: { - const { OracleDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(182)]).then(__nccwpck_require__.bind(__nccwpck_require__, 1182)); + const { OracleDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(182)]).then(__nccwpck_require__.bind(__nccwpck_require__, 1182)); return new OracleDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Dragonwell: { - const { DragonwellDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(675)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7675)); + const { DragonwellDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(675)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7675)); return new DragonwellDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.SapMachine: { - const { SapMachineDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(557)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7557)); + const { SapMachineDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(557)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7557)); return new SapMachineDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.GraalVM: { - const { GraalVMDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(968)]).then(__nccwpck_require__.bind(__nccwpck_require__, 6968)); + const { GraalVMDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(968)]).then(__nccwpck_require__.bind(__nccwpck_require__, 6968)); return new GraalVMDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.GraalVMCommunity: { - const { GraalVMCommunityDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(968)]).then(__nccwpck_require__.bind(__nccwpck_require__, 6968)); + const { GraalVMCommunityDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(968)]).then(__nccwpck_require__.bind(__nccwpck_require__, 6968)); return new GraalVMCommunityDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.JetBrains: { - const { JetBrainsDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(282)]).then(__nccwpck_require__.bind(__nccwpck_require__, 2282)); + const { JetBrainsDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(282)]).then(__nccwpck_require__.bind(__nccwpck_require__, 2282)); return new JetBrainsDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.Kona: { - const { KonaDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(151)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8151)); + const { KonaDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(151)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8151)); return new KonaDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.OracleOpenJdk: { - const { OpenJdkDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(735)]).then(__nccwpck_require__.bind(__nccwpck_require__, 3735)); + const { OpenJdkDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(735)]).then(__nccwpck_require__.bind(__nccwpck_require__, 3735)); return new OpenJdkDistribution(normalizedInstallerOptions); } case package_types/* JavaDistribution */.zS.RedHat: { - const { RedHatDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(242), __nccwpck_require__.e(228)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8228)); + const { RedHatDistribution } = await Promise.all(/* import() */[__nccwpck_require__.e(370), __nccwpck_require__.e(228)]).then(__nccwpck_require__.bind(__nccwpck_require__, 8228)); return new RedHatDistribution(normalizedInstallerOptions); } default: @@ -36691,6 +36693,7 @@ async function run() { const checkLatest = (0,util/* getBooleanInput */.Vt)(constants/* INPUT_CHECK_LATEST */.YM, false); const forceDownload = (0,util/* getBooleanInput */.Vt)(constants/* INPUT_FORCE_DOWNLOAD */.I9, false); const setDefault = (0,util/* getBooleanInput */.Vt)(constants/* INPUT_SET_DEFAULT */.E8, true); + const blockCompromisedCerts = (0,util/* getBooleanInput */.Vt)(constants/* INPUT_BLOCK_COMPROMISED_CERTS */.ot, true); const verifySignaturePublicKey = setup_java_core/* getInput */.V4(constants/* INPUT_VERIFY_SIGNATURE_PUBLIC_KEY */.u) || undefined; const toolchainIds = setup_java_core/* getMultilineInput */.q3(constants/* INPUT_MVN_TOOLCHAIN_ID */.nr); let actionError; @@ -36726,6 +36729,7 @@ async function run() { forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, distributionName, @@ -36751,6 +36755,7 @@ async function run() { forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, distributionName, @@ -36814,7 +36819,7 @@ function getVerifySignatureInput() { : undefined; } async function installVersion(version, options, toolchainId = 0) { - const { distributionName, jdkFile, architecture, packageType, checkLatest, forceDownload, cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, toolchainIds } = options; + const { distributionName, jdkFile, architecture, packageType, checkLatest, forceDownload, cacheJdk, setDefault, blockCompromisedCerts, verifySignature, verifySignaturePublicKey, toolchainIds } = options; const installerOptions = { architecture, packageType, @@ -36822,6 +36827,7 @@ async function installVersion(version, options, toolchainId = 0) { forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, version diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 8e1a61507..f04593b18 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -31,6 +31,7 @@ - [Apache Maven with a settings path](#apache-maven-with-a-settings-path) - [Maven transfer progress (download logs)](#Maven-transfer-progress-download-logs) - [Java problem matcher (compiler annotations)](#java-problem-matcher-compiler-annotations) +- [Blocking compromised CA certificates](#blocking-compromised-ca-certificates) - [Publishing using Gradle](#Publishing-using-Gradle) - [Hosted Tool Cache](#Hosted-Tool-Cache) - [Modifying Maven Toolchains](#Modifying-Maven-Toolchains) @@ -1260,6 +1261,25 @@ jobs: Disabling the matcher only stops annotations from being created. Compiler output remains in the log, and compilation errors still fail the build step. +## Blocking compromised CA certificates + +By default, `setup-java` adds the SHA-256 fingerprints of known compromised CA certificates to the installed JDK's blocked certificates list (`lib/security/blocked.certs`, or `blacklisted.certs` on older JDKs, including the bundled `jre/` of JDK 8). Java then rejects any TLS certificate chain that includes one of those CAs, even if the CA is still in the JDK's `cacerts` trust store. + +The list currently contains the compromised eMudhra intermediate CAs. Fingerprints already present in the JDK's list are not added again. + +If a build must still connect to a server whose certificate chains through one of these CAs, opt out: + +```yaml +steps: + - uses: actions/setup-java@v6 + with: + distribution: 'temurin' + java-version: '21' + block-compromised-certs: false +``` + +> **Note:** The fingerprints are written into the JDK installation itself. On self-hosted runners, a JDK that an earlier job already set up from the tool cache keeps them even when a later job sets `block-compromised-certs: false`; use `force-download: true` to get an unmodified copy. + ## Publishing using Gradle ```yaml jobs: diff --git a/src/blocked-certs.ts b/src/blocked-certs.ts new file mode 100644 index 000000000..5b12de35d --- /dev/null +++ b/src/blocked-certs.ts @@ -0,0 +1,100 @@ +import * as core from '@actions/core'; +import * as fs from 'fs'; +import path from 'path'; + +/** + * SHA-256 fingerprints of compromised eMudhra intermediate CA certificates. + * The JDK refuses to trust any certificate chain containing one of these. + */ +export const COMPROMISED_CERT_FINGERPRINTS: readonly string[] = [ + '42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493', + '45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE', + '98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B', + '5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699', + 'E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838', + '579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512', + '0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19', + '62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72', + 'CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F', + '7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489', + '432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3', + 'FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F', + '9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4', + 'A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8', + '4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1', + '84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD' +]; + +// Newer JDKs use blocked.certs; older ones (JDK 8/11) use blacklisted.certs. +// A JDK 8 bundles a separate JRE under jre/ that has its own copy. +const BLOCKED_CERTS_CANDIDATES = [ + ['lib', 'security', 'blocked.certs'], + ['lib', 'security', 'blacklisted.certs'], + ['jre', 'lib', 'security', 'blocked.certs'], + ['jre', 'lib', 'security', 'blacklisted.certs'] +]; + +const SHA256_ALGORITHM_LINE = /^\s*Algorithm\s*=\s*SHA-256\s*$/i; + +/** + * Adds the compromised CA fingerprints to the blocked certificates list of + * the JDK at `javaHome`. Fingerprints already present are left alone, so + * running this repeatedly on a tool-cached JDK is safe. + */ +export function blockCompromisedCertificates( + javaHome: string, + fingerprints: readonly string[] = COMPROMISED_CERT_FINGERPRINTS +): void { + const files = BLOCKED_CERTS_CANDIDATES.map(parts => + path.join(javaHome, ...parts) + ).filter(file => fs.existsSync(file)); + + if (!files.length) { + core.warning( + `Could not find a blocked certificates list in ${javaHome}; compromised CA certificates were not blocked.` + ); + return; + } + + for (const file of files) { + try { + appendFingerprints(file, fingerprints); + } catch (error) { + core.warning( + `Failed to block compromised CA certificates in ${file}: ${(error as Error).message}` + ); + } + } +} + +function appendFingerprints( + file: string, + fingerprints: readonly string[] +): void { + const content = fs.readFileSync(file, 'utf8'); + const lines = content.split(/\r?\n/).map(line => line.trim()); + + const algorithmLine = lines.find(line => line.startsWith('Algorithm')); + if (!algorithmLine || !SHA256_ALGORITHM_LINE.test(algorithmLine)) { + core.warning( + `${file} does not use SHA-256 fingerprints; compromised CA certificates were not blocked.` + ); + return; + } + + const existing = new Set(lines.map(line => line.toUpperCase())); + const missing = fingerprints + .map(fingerprint => fingerprint.toUpperCase()) + .filter(fingerprint => !existing.has(fingerprint)); + + if (!missing.length) { + core.debug(`${file} already blocks the compromised CA certificates`); + return; + } + + const prefix = content.length && !content.endsWith('\n') ? '\n' : ''; + fs.appendFileSync(file, `${prefix}${missing.join('\n')}\n`); + core.info( + `Blocked ${missing.length} compromised CA certificate(s) in ${file}` + ); +} diff --git a/src/constants.ts b/src/constants.ts index bdba92242..e28d32f44 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,6 +10,7 @@ export const INPUT_CHECK_LATEST = 'check-latest'; export const INPUT_FORCE_DOWNLOAD = 'force-download'; export const INPUT_SET_DEFAULT = 'set-default'; export const INPUT_PROBLEM_MATCHER = 'problem-matcher'; +export const INPUT_BLOCK_COMPROMISED_CERTS = 'block-compromised-certs'; export const INPUT_VERIFY_SIGNATURE = 'verify-signature'; export const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; export const SIGNATURE_VERIFICATION_DOCUMENTATION_URL = diff --git a/src/distributions/base-installer.ts b/src/distributions/base-installer.ts index 42f4b7de9..f4afe322f 100644 --- a/src/distributions/base-installer.ts +++ b/src/distributions/base-installer.ts @@ -26,6 +26,7 @@ import { normalizeArchitecture } from './platform-types.js'; import type {JdkCache} from '../jdk-cache.js'; +import {blockCompromisedCertificates} from '../blocked-certs.js'; export abstract class JavaBase { protected http: httpm.HttpClient; @@ -44,6 +45,7 @@ export abstract class JavaBase { */ private floatingVersionVerified = false; protected setDefault: boolean; + protected blockCompromisedCerts: boolean; protected verifySignature: boolean; protected verifySignatureExplicitlyRequested: boolean; protected verifySignaturePublicKey: SignatureVerificationKey | undefined; @@ -70,6 +72,7 @@ export abstract class JavaBase { installerOptions.setDefault !== undefined ? installerOptions.setDefault : true; + this.blockCompromisedCerts = installerOptions.blockCompromisedCerts ?? true; this.verifySignature = installerOptions.verifySignature ?? this.supportsSignatureVerification(); this.verifySignatureExplicitlyRequested = @@ -280,6 +283,10 @@ export abstract class JavaBase { foundJava.path = macOSPostfixPath; } + if (this.blockCompromisedCerts) { + blockCompromisedCertificates(foundJava.path); + } + if (this.setDefault) { core.info(`Setting Java ${foundJava.version} as the default`); this.setJavaDefault(foundJava.version, foundJava.path); diff --git a/src/distributions/base-models.ts b/src/distributions/base-models.ts index b12f78dac..72d990f18 100644 --- a/src/distributions/base-models.ts +++ b/src/distributions/base-models.ts @@ -8,6 +8,7 @@ export interface JavaInstallerOptions { forceDownload?: boolean; cacheJdk?: boolean; setDefault?: boolean; + blockCompromisedCerts?: boolean; verifySignature?: boolean; verifySignaturePublicKey?: SignatureVerificationKey; } diff --git a/src/setup-java.ts b/src/setup-java.ts index 23de15472..6bd702b75 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -30,6 +30,10 @@ export async function run() { const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); const forceDownload = getBooleanInput(constants.INPUT_FORCE_DOWNLOAD, false); const setDefault = getBooleanInput(constants.INPUT_SET_DEFAULT, true); + const blockCompromisedCerts = getBooleanInput( + constants.INPUT_BLOCK_COMPROMISED_CERTS, + true + ); const verifySignaturePublicKey = core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined; const toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); @@ -86,6 +90,7 @@ export async function run() { forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, distributionName, @@ -115,6 +120,7 @@ export async function run() { forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, distributionName, @@ -213,6 +219,7 @@ async function installVersion( forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, toolchainIds @@ -225,6 +232,7 @@ async function installVersion( forceDownload, cacheJdk, setDefault, + blockCompromisedCerts, verifySignature, verifySignaturePublicKey, version @@ -270,6 +278,7 @@ interface installerInputsOptions { forceDownload: boolean; cacheJdk: boolean; setDefault: boolean; + blockCompromisedCerts: boolean; verifySignature: boolean | undefined; verifySignaturePublicKey: string | undefined; distributionName: string;