Files
codex-action/test/linuxCredentials.test.mjs
T
viyatb-oaiandCodex 86365089eb
Verify TypeScript Build / verify (push) Failing after 1m13s
fix: improve runner setup and configuration handling
Co-authored-by: Codex <[email protected]>
2026-08-20 16:38:51 -07:00

108 lines
4.8 KiB
JavaScript

import assert from "node:assert/strict";
import { buildSync } from "esbuild";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
import { EventEmitter } from "node:events";
import vm from "node:vm";
const { outputFiles } = buildSync({
entryPoints: [fileURLToPath(new URL("../src/linuxCredentials.ts", import.meta.url))],
bundle: true,
format: "esm",
platform: "node",
write: false,
});
const { parseLinuxRunnerCredentials, includeAccountGroups } = await import(
`data:text/javascript;base64,${Buffer.from(outputFiles[0].text).toString("base64")}`
);
const original = { userId: 1001, primaryGroupId: 999, supplementaryGroupIds: [27, 998] };
const require = createRequire(import.meta.url);
function loadLauncher(file, spawn) {
const { outputFiles } = buildSync({
entryPoints: [fileURLToPath(new URL(`../src/${file}.ts`, import.meta.url))],
bundle: true, format: "cjs", platform: "node", write: false,
external: ["./checkOutput", "@actions/core"],
});
const module = { exports: {} };
vm.runInNewContext(outputFiles[0].text, {
module, exports: module.exports,
require(name) {
if (name === "child_process" || name === "node:child_process") return { spawn };
if (name === "./checkOutput") return { checkOutput: async (args) => args[0] === "which" ? "/synthetic/codex" : "" };
if (name === "@actions/core") return { setOutput() {} };
if (name === "os") return { userInfo: () => ({ username: "runner", homedir: "/synthetic" }) };
return require(name);
},
process: { platform: "linux", getuid: () => original.userId,
getgid: () => original.primaryGroupId, getgroups: () => original.supplementaryGroupIds,
execPath: "/synthetic/node", argv: ["node", "/synthetic/main.js"], execArgv: [], env: {} },
console: { log() {} },
});
return module.exports;
}
test("both Linux launch paths pass the original process credentials", async () => {
const stopped = new Error("captured before privileged execution");
let captured;
const run = loadLauncher("runCodexExec", (program, args) => {
captured = { program, args };
throw stopped;
});
await assert.rejects(run.runCodexExec({
prompt: { type: "inline", content: "test" }, codexHome: null, cd: "/synthetic",
extraArgs: [], explicitOutputFile: "/synthetic/output", outputSchema: null,
model: null, effort: null, safetyStrategy: "drop-sudo", codexUser: null,
sandbox: null, permissionProfile: null,
}), (error) => error === stopped);
const marker = captured.args.indexOf("codex-action-drop-sudo");
assert.deepEqual(JSON.parse(captured.args[marker + 8]), original);
assert.equal(captured.args[marker + 9], "/synthetic/codex");
assert.match(captured.args[captured.args.indexOf("-c") + 1], /--runner-credentials "\$runner_credentials"/);
const drop = loadLauncher("dropSudo", (program, args) => {
if (args.includes("--root-phase")) {
captured = { program, args };
throw stopped;
}
const child = new EventEmitter();
child.stdout = new EventEmitter();
child.stderr = new EventEmitter();
child.stdout.setEncoding = child.stderr.setEncoding = () => {};
process.nextTick(() => child.emit("close", 0));
return child;
});
await assert.rejects(drop.dropSudo({ user: "runner", group: "sudo", rootPhase: false }), (error) => error === stopped);
assert.deepEqual(JSON.parse(captured.args[captured.args.indexOf("--runner-credentials") + 1]), original);
});
test("captures the live credentials without serializing them", () => {
const { captureLinuxRunnerCredentials } = loadLauncher("linuxCredentials", () => {
assert.fail("capturing credentials must not launch a process");
});
const captured = captureLinuxRunnerCredentials();
assert.equal(captured.userId, original.userId);
assert.equal(captured.primaryGroupId, original.primaryGroupId);
assert.equal(captured.supplementaryGroupIds, original.supplementaryGroupIds);
});
test("retains live-only primary and supplementary groups", () => {
const result = includeAccountGroups(original, 1001, [1001, 27, 997]);
assert.equal(result.primaryGroupId, 999);
assert.deepEqual(result.supplementaryGroupIds, [27, 998, 1001, 997]);
assert.throws(() => includeAccountGroups(original, 1002, [1002]), /does not match/);
});
test("rejects malformed or root runner credentials", () => {
for (const value of [
null, {}, { ...original, userId: 0 }, { ...original, userId: -1 },
{ ...original, userId: "1001" }, { ...original, primaryGroupId: 0xffffffff },
{ ...original, supplementaryGroupIds: [1.5] },
{ ...original, supplementaryGroupIds: [null] },
{ ...original, supplementaryGroupIds: Array(65537).fill(1) },
]) {
assert.throws(() => parseLinuxRunnerCredentials(JSON.stringify(value)), /Invalid original/);
}
});