mirror of
https://github.com/actions/setup-java.git
synced 2026-06-29 18:40:33 +03:00
feat: Add verify-signature plumbing and Temurin+Microsoft verification support (#1060)
* Add verify-signature plumbing and Temurin verification support * Rebuild dist after signature verification changes * Refine signature verification errors and regenerate dist * refactor: make gpg.ts generic, move Adoptium-specific constant to temurin distribution * fix: mock renameWinArchive in temurin tests and add signature e2e job * refactor: bundle Adoptium public key, replace keyserver lookup with local import * feat: add verify-signature-public-key input to allow custom GPG key override * refactor: extract Adoptium public key to adoptium-key.ts; tighten gpg.ts cleanup scope * Add verify-signature plumbing and Temurin verification support * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Add Microsoft signature verification support * Regenerate dist bundles for Microsoft signature checks * Harden Microsoft signature URL handling * Add setup-java-microsoft-signature-verification e2e job * chore: regenerate dist files * Fix e2e-versions: remove duplicate job, update signature jobs to checkout@v7 with env vars * Fix Prettier formatting in test files * fix: mock renameWinArchive in microsoft-installer tests to fix Windows CI failure * fix: use --homedir flag instead of GNUPGHOME env var for Windows GPG compatibility The Git-bundled GPG on Windows (MSYS2-based) does not automatically convert Windows-style paths in environment variables like GNUPGHOME. This caused GPG to fail with exit code 2 when verifying Microsoft JDK signatures on Windows, because the GNUPGHOME path (D:\a\_temp\...) was not recognized as a valid POSIX path. Fix: pass --homedir as an explicit command-line argument to both gpg --import and gpg --verify. MSYS2 does correctly convert Windows paths in command-line arguments, so this approach works reliably on Windows, Linux, and macOS. * fix: convert Windows paths to POSIX format for MSYS2 GPG on Windows The Git-bundled GPG on Windows (C:\Program Files\Git\usr\bin\gpg.exe) is an MSYS2-based binary that uses POSIX path conventions internally. When Windows-style paths with backslashes and drive letters (D:\a\_temp\...) are passed as arguments, GPG may fail to resolve them correctly, resulting in a fatal error (exit code 2). Fix: add a toGpgPath() helper that converts Windows paths to MSYS2 POSIX format (/d/a/_temp/...) before passing them to any gpg command. On Linux and macOS the helper is a no-op. Applied to all four paths used in verifyPackageSignature: - gpgHome (--homedir argument) - publicKeyFile (--import argument) - signaturePath (--verify signature argument) - archivePath (--verify data argument) * Fix gpg test formatting --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com>
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import {HttpClient} from '@actions/http-client';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import {
|
||||
TemurinDistribution,
|
||||
TemurinImplementation
|
||||
TemurinImplementation,
|
||||
ADOPTIUM_PUBLIC_KEY
|
||||
} from '../../src/distributions/temurin/installer';
|
||||
import {JavaInstallerOptions} from '../../src/distributions/base-models';
|
||||
import * as util from '../../src/util';
|
||||
import * as gpg from '../../src/gpg';
|
||||
|
||||
import manifestData from '../data/temurin.json';
|
||||
import * as core from '@actions/core';
|
||||
@@ -231,6 +236,7 @@ describe('findPackageForDownload', () => {
|
||||
distribution['getAvailableVersions'] = async () => manifestData as any;
|
||||
const resolvedVersion = await distribution['findPackageForDownload'](input);
|
||||
expect(resolvedVersion.version).toBe(expected);
|
||||
expect(resolvedVersion.signatureUrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('version is found but binaries list is empty', async () => {
|
||||
@@ -281,3 +287,109 @@ describe('findPackageForDownload', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadTool', () => {
|
||||
let spyDownloadTool: jest.SpyInstance;
|
||||
let spyVerifySignature: jest.SpyInstance;
|
||||
let spyExtractJdkFile: jest.SpyInstance;
|
||||
let spyCacheDir: jest.SpyInstance;
|
||||
let spyReadDirSync: jest.SpyInstance;
|
||||
let spyRenameWinArchive: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
spyDownloadTool = jest.spyOn(tc, 'downloadTool');
|
||||
spyDownloadTool.mockResolvedValue('/tmp/jdk.tar.gz');
|
||||
spyVerifySignature = jest.spyOn(gpg, 'verifyPackageSignature');
|
||||
spyVerifySignature.mockResolvedValue(undefined);
|
||||
spyExtractJdkFile = jest.spyOn(util, 'extractJdkFile');
|
||||
spyExtractJdkFile.mockResolvedValue('/tmp/extracted');
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockResolvedValue('/tmp/toolcache');
|
||||
spyReadDirSync = jest.spyOn(fs, 'readdirSync');
|
||||
spyReadDirSync.mockReturnValue(['jdk-17'] as any);
|
||||
spyRenameWinArchive = jest.spyOn(util, 'renameWinArchive');
|
||||
spyRenameWinArchive.mockReturnValue('/tmp/jdk.tar.gz.zip');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('verifies signature when enabled', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '17',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false,
|
||||
verifySignature: true
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
|
||||
await distribution['downloadTool']({
|
||||
version: '17.0.14+7',
|
||||
url: 'https://example.com/jdk.tar.gz',
|
||||
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
|
||||
});
|
||||
|
||||
expect(spyVerifySignature).toHaveBeenCalledWith(
|
||||
'/tmp/jdk.tar.gz',
|
||||
'https://example.com/jdk.tar.gz.sig',
|
||||
ADOPTIUM_PUBLIC_KEY
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when signature is missing and verification is enabled', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '17',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false,
|
||||
verifySignature: true
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
|
||||
await expect(
|
||||
distribution['downloadTool']({
|
||||
version: '17.0.14+7',
|
||||
url: 'https://example.com/jdk.tar.gz'
|
||||
})
|
||||
).rejects.toThrow(
|
||||
"Input 'verify-signature' is enabled, but no signature URL was found"
|
||||
);
|
||||
expect(spyVerifySignature).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses custom public key when verifySignaturePublicKey is provided', async () => {
|
||||
const customKey =
|
||||
'-----BEGIN PGP PUBLIC KEY BLOCK-----\ncustom\n-----END PGP PUBLIC KEY BLOCK-----';
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '17',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false,
|
||||
verifySignature: true,
|
||||
verifySignaturePublicKey: customKey
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
|
||||
await distribution['downloadTool']({
|
||||
version: '17.0.14+7',
|
||||
url: 'https://example.com/jdk.tar.gz',
|
||||
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
|
||||
});
|
||||
|
||||
expect(spyVerifySignature).toHaveBeenCalledWith(
|
||||
'/tmp/jdk.tar.gz',
|
||||
'https://example.com/jdk.tar.gz.sig',
|
||||
customKey
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user