Compare commits

...
Author SHA1 Message Date
Bruno Borges 9395a40c79 Merge branch 'main' into copilot/macos-self-hosted-runners-fix 2026-09-09 02:20:38 -04:00
Bruno BorgesandCopilot App a5aaf7ca60 Keep macOS GPG verification homes within socket limits
Use /tmp for signature verification on macOS while preserving runner temp behavior elsewhere. Cover long and canonical OS temp paths and regenerate action bundles.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32d31c8d-ddbc-4e57-a5c3-f70588fef3f3
2026-09-09 02:12:24 -04:00
copilot-swe-agent[bot] 7f2b3ca2cf Fix signature verification GPG homes on long runner paths 2026-09-09 04:53:15 +00:00
copilot-swe-agent[bot] d8572d4b37 Initial plan 2026-09-09 04:49:15 +00:00
6 changed files with 108 additions and 15 deletions
+80
View File
@@ -9,10 +9,18 @@ import {
} from '@jest/globals';
import {fileURLToPath} from 'url';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as io from '@actions/io';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const mockTmpDir = jest.fn(os.tmpdir);
jest.unstable_mockModule('os', () => ({
...os,
default: {...os, tmpdir: mockTmpDir},
tmpdir: mockTmpDir
}));
jest.unstable_mockModule('@actions/exec', () => ({
exec: jest.fn()
@@ -34,6 +42,7 @@ describe('gpg tests', () => {
await io.rmRF(tempDir);
await io.mkdirP(tempDir);
jest.clearAllMocks();
mockTmpDir.mockImplementation(os.tmpdir);
(exec.exec as jest.Mock<any>).mockResolvedValue(0);
});
@@ -222,6 +231,77 @@ describe('gpg tests', () => {
});
describe('verifyPackageSignature', () => {
describe.each(['long', 'canonical macOS'])('%s TMPDIR', tempDirKind => {
afterEach(() => {
process.env['RUNNER_TEMP'] = tempDir;
});
it.each(['success', 'import failure', 'verification failure'])(
'uses a short macOS home or RUNNER_TEMP elsewhere and cleans up after %s',
async outcome => {
const longRunnerTemp = path.join(
tempDir,
'long-runner-path-'.repeat(8)
);
const signaturePath = path.join(tempDir, 'jdk.tar.gz.sig');
const expectedParent =
process.platform === 'darwin' ? '/tmp' : longRunnerTemp;
let gpgHome = '';
process.env['RUNNER_TEMP'] = longRunnerTemp;
mockTmpDir.mockReturnValue(
tempDirKind === 'long'
? longRunnerTemp
: `/private/var/folders/ab/${'c'.repeat(31)}/T`
);
fs.mkdirSync(longRunnerTemp, {recursive: true});
fs.writeFileSync(signaturePath, 'signature');
(tc.downloadTool as jest.Mock<any>).mockResolvedValue(signaturePath);
(exec.exec as jest.Mock<any>).mockImplementation(
async (_command: string, args: string[]) => {
gpgHome = path.join(expectedParent, path.posix.basename(args[1]));
expect(args[1]).toBe(gpg.toGpgPath(gpgHome));
if (process.platform === 'darwin') {
expect(
Buffer.byteLength(path.join(gpgHome, 'S.gpg-agent.browser'))
).toBeLessThan(104);
}
expect(
fs.readFileSync(path.join(gpgHome, 'public-key-0.asc'), 'utf8')
).toBe('public key');
if (process.platform !== 'win32') {
expect(fs.statSync(gpgHome).mode & 0o777).toBe(0o700);
}
if (
(outcome === 'import failure' && args.includes('--import')) ||
(outcome === 'verification failure' &&
args.includes('--verify'))
) {
throw new Error(outcome);
}
return 0;
}
);
const verification = gpg.verifyPackageSignature(
path.join(tempDir, 'jdk.tar.gz'),
'https://example.com/jdk.tar.gz.sig',
'public key'
);
if (outcome === 'success') {
await verification;
} else {
await expect(verification).rejects.toThrow(outcome);
}
expect(exec.exec).toHaveBeenCalledTimes(
outcome === 'import failure' ? 1 : 2
);
expect(fs.existsSync(gpgHome)).toBe(false);
expect(fs.existsSync(signaturePath)).toBe(false);
expect(fs.readdirSync(longRunnerTemp)).toEqual([]);
}
);
});
it('imports bundled key and verifies package', async () => {
const publicKeyContent =
'-----BEGIN PGP PUBLIC KEY BLOCK-----\ntest\n-----END PGP PUBLIC KEY BLOCK-----';
+5 -3
View File
@@ -35784,8 +35784,8 @@ function toGpgPath(p) {
.replace(/\\/g, '/')
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
}
function createGpgHome(prefix) {
const gpgHome = fs.mkdtempSync(path.join(util.getTempDir(), prefix));
function createGpgHome(prefix, tempDir = util.getTempDir()) {
const gpgHome = fs.mkdtempSync(path.join(tempDir, prefix));
if (process.platform !== 'win32') {
fs.chmodSync(gpgHome, 0o700);
}
@@ -35844,7 +35844,9 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
const signaturePath = await tc.downloadTool(signatureUrl);
let gpgHome;
try {
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
const tempDir = process.platform === 'darwin' ? '/tmp' : util.getTempDir();
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
}
catch (error) {
try {
+5 -3
View File
@@ -215,8 +215,8 @@ function toGpgPath(p) {
.replace(/\\/g, '/')
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
}
function createGpgHome(prefix) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4(), prefix));
function createGpgHome(prefix, tempDir = _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4()) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(tempDir, prefix));
if (process.platform !== 'win32') {
fs__WEBPACK_IMPORTED_MODULE_0__.chmodSync(gpgHome, 0o700);
}
@@ -275,7 +275,9 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
let gpgHome;
try {
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
const tempDir = process.platform === 'darwin' ? '/tmp' : _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4();
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
}
catch (error) {
try {
+5 -3
View File
@@ -327,8 +327,8 @@ function toGpgPath(p) {
.replace(/\\/g, '/')
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
}
function createGpgHome(prefix) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4(), prefix));
function createGpgHome(prefix, tempDir = _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4()) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(tempDir, prefix));
if (process.platform !== 'win32') {
fs__WEBPACK_IMPORTED_MODULE_0__.chmodSync(gpgHome, 0o700);
}
@@ -387,7 +387,9 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
let gpgHome;
try {
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
const tempDir = process.platform === 'darwin' ? '/tmp' : _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4();
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
}
catch (error) {
try {
+5 -3
View File
@@ -302,8 +302,8 @@ function toGpgPath(p) {
.replace(/\\/g, '/')
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
}
function createGpgHome(prefix) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4(), prefix));
function createGpgHome(prefix, tempDir = _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4()) {
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(tempDir, prefix));
if (process.platform !== 'win32') {
fs__WEBPACK_IMPORTED_MODULE_0__.chmodSync(gpgHome, 0o700);
}
@@ -362,7 +362,9 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
let gpgHome;
try {
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
const tempDir = process.platform === 'darwin' ? '/tmp' : _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4();
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
}
catch (error) {
try {
+8 -3
View File
@@ -26,8 +26,11 @@ export function toGpgPath(p: string): string {
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
}
function createGpgHome(prefix: string): string {
const gpgHome = fs.mkdtempSync(path.join(util.getTempDir(), prefix));
function createGpgHome(
prefix: string,
tempDir: string = util.getTempDir()
): string {
const gpgHome = fs.mkdtempSync(path.join(tempDir, prefix));
if (process.platform !== 'win32') {
fs.chmodSync(gpgHome, 0o700);
}
@@ -107,7 +110,9 @@ export async function verifyPackageSignature(
const signaturePath = await tc.downloadTool(signatureUrl);
let gpgHome: string;
try {
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX);
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
const tempDir = process.platform === 'darwin' ? '/tmp' : util.getTempDir();
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
} catch (error) {
try {
await io.rmRF(signaturePath);