mirror of
https://github.com/actions/setup-java.git
synced 2026-08-21 21:22:52 +00:00
* Isolate Maven signing keys Import signing keys into an action-owned temporary GPG home, export GNUPGHOME, and remove the owned directory in the post action. Cover import failure, multiple keys and invocations, unrelated keyrings, missing state, and Windows path conversion. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix cleanup state assertion Account for isolated GPG-home cleanup when cache saving is disabled. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Update generated action bundles Apply repository formatting and commit the setup and cleanup bundles produced by the validated Node 24 build. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Address isolated GPG home review feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com>
151 lines
3.8 KiB
TypeScript
151 lines
3.8 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import {randomUUID} from 'crypto';
|
|
import * as io from '@actions/io';
|
|
import * as exec from '@actions/exec';
|
|
import * as tc from '@actions/tool-cache';
|
|
import * as util from './util.js';
|
|
import {ExecOptions} from '@actions/exec';
|
|
|
|
export const GPG_HOME_PREFIX = 'setup-java-gpg-';
|
|
const VERIFY_GPG_HOME_PREFIX = 'verify-signature-gpg-home-';
|
|
|
|
// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...).
|
|
// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions
|
|
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
|
|
// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows.
|
|
export function toGpgPath(p: string): string {
|
|
if (process.platform !== 'win32') return p;
|
|
return p
|
|
.replace(/\\/g, '/')
|
|
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
|
|
}
|
|
|
|
function createGpgHome(prefix: string): string {
|
|
const gpgHome = fs.mkdtempSync(path.join(util.getTempDir(), prefix));
|
|
if (process.platform !== 'win32') {
|
|
fs.chmodSync(gpgHome, 0o700);
|
|
}
|
|
return gpgHome;
|
|
}
|
|
|
|
export async function importKey(privateKey: string): Promise<string> {
|
|
const gpgHome = createGpgHome(GPG_HOME_PREFIX);
|
|
const privateKeyFile = path.join(gpgHome, `private-key-${randomUUID()}.asc`);
|
|
|
|
try {
|
|
fs.writeFileSync(privateKeyFile, privateKey, {
|
|
encoding: 'utf-8',
|
|
flag: 'wx',
|
|
mode: 0o600
|
|
});
|
|
|
|
try {
|
|
await exec.exec(
|
|
'gpg',
|
|
[
|
|
'--homedir',
|
|
toGpgPath(gpgHome),
|
|
'--batch',
|
|
'--import',
|
|
toGpgPath(privateKeyFile)
|
|
],
|
|
{silent: true}
|
|
);
|
|
} finally {
|
|
fs.rmSync(privateKeyFile, {force: true});
|
|
}
|
|
|
|
return gpgHome;
|
|
} catch (error) {
|
|
await removeGpgHome(gpgHome);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function removeGpgHome(gpgHome: string): Promise<void> {
|
|
if (!gpgHome) {
|
|
return;
|
|
}
|
|
|
|
const resolvedGpgHome = path.resolve(gpgHome);
|
|
const resolvedTempDir = path.resolve(util.getTempDir());
|
|
if (
|
|
path.dirname(resolvedGpgHome) !== resolvedTempDir ||
|
|
!path.basename(resolvedGpgHome).startsWith(GPG_HOME_PREFIX)
|
|
) {
|
|
throw new Error(`Refusing to remove unexpected GPG home: ${gpgHome}`);
|
|
}
|
|
|
|
if (!fs.existsSync(resolvedGpgHome)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await exec.exec(
|
|
'gpgconf',
|
|
['--homedir', toGpgPath(resolvedGpgHome), '--kill', 'gpg-agent'],
|
|
{silent: true, ignoreReturnCode: true}
|
|
);
|
|
} catch {
|
|
// gpgconf may be unavailable, but directory removal must still be attempted.
|
|
}
|
|
|
|
await io.rmRF(resolvedGpgHome);
|
|
}
|
|
|
|
export async function verifyPackageSignature(
|
|
archivePath: string,
|
|
signatureUrl: string,
|
|
publicKeyContent: string
|
|
) {
|
|
const signaturePath = await tc.downloadTool(signatureUrl);
|
|
let gpgHome: string;
|
|
try {
|
|
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
|
|
} catch (error) {
|
|
try {
|
|
await io.rmRF(signaturePath);
|
|
} catch {
|
|
// ignore cleanup failures
|
|
}
|
|
throw new Error(
|
|
`Failed to create temporary GPG home directory for signature verification: ${
|
|
(error as Error).message
|
|
}`,
|
|
{cause: error}
|
|
);
|
|
}
|
|
try {
|
|
const publicKeyFile = path.join(gpgHome, 'public-key.asc');
|
|
fs.writeFileSync(publicKeyFile, publicKeyContent, {encoding: 'utf-8'});
|
|
const options: ExecOptions = {silent: true};
|
|
await exec.exec(
|
|
'gpg',
|
|
[
|
|
'--homedir',
|
|
toGpgPath(gpgHome),
|
|
'--batch',
|
|
'--import',
|
|
toGpgPath(publicKeyFile)
|
|
],
|
|
options
|
|
);
|
|
await exec.exec(
|
|
'gpg',
|
|
[
|
|
'--homedir',
|
|
toGpgPath(gpgHome),
|
|
'--batch',
|
|
'--verify',
|
|
toGpgPath(signaturePath),
|
|
toGpgPath(archivePath)
|
|
],
|
|
options
|
|
);
|
|
} finally {
|
|
await io.rmRF(signaturePath);
|
|
await io.rmRF(gpgHome);
|
|
}
|
|
}
|