Verify TypeScript Build / verify (push) Failing after 1m13s
Co-authored-by: Codex <[email protected]>
621 lines
20 KiB
JavaScript
621 lines
20 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import {
|
|
chmodSync,
|
|
copyFileSync,
|
|
existsSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
statSync,
|
|
writeFileSync,
|
|
} from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { test } from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const mainPath = fileURLToPath(new URL("../dist/main.js", import.meta.url));
|
|
const dockerSocket = "/var/run/docker.sock";
|
|
const canTestLinuxIsolation =
|
|
process.platform === "linux" &&
|
|
process.getuid?.() !== 0 &&
|
|
existsSync(dockerSocket) &&
|
|
spawnSync("sudo", ["-n", "true"]).status === 0;
|
|
const canTestSocketAcls =
|
|
canTestLinuxIsolation &&
|
|
spawnSync("/usr/bin/setfacl", ["--version"], { stdio: "ignore" }).status === 0;
|
|
|
|
function sudo(args) {
|
|
const result = spawnSync("sudo", ["-n", "--", ...args], {
|
|
encoding: "utf8",
|
|
timeout: 30_000,
|
|
});
|
|
assert.equal(result.status, 0, result.stderr);
|
|
return result.stdout.trim();
|
|
}
|
|
|
|
function snapshotRootSockets() {
|
|
const output = sudo([
|
|
"find",
|
|
"/run",
|
|
"-type",
|
|
"s",
|
|
"-uid",
|
|
"0",
|
|
"-printf",
|
|
"%p\t%U\t%G\t%m\n",
|
|
]);
|
|
return output.length === 0
|
|
? []
|
|
: output.split("\n").map((line) => {
|
|
const [socketPath, uid, gid, mode] = line.split("\t");
|
|
return { socketPath, uid, gid, mode };
|
|
});
|
|
}
|
|
|
|
function restoreRootSockets(sockets) {
|
|
for (const { socketPath, uid, gid, mode } of sockets) {
|
|
const exists = spawnSync("sudo", ["-n", "--", "test", "-S", socketPath]);
|
|
if (exists.status === 0) {
|
|
sudo(["chown", `${uid}:${gid}`, socketPath]);
|
|
sudo(["chmod", mode, socketPath]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function createRootSocket(socketPath, group, mode) {
|
|
sudo([
|
|
process.execPath,
|
|
"-e",
|
|
'require("node:net").createServer().listen(process.argv[1], () => process.exit(0))',
|
|
socketPath,
|
|
]);
|
|
sudo(["chown", `0:${group}`, socketPath]);
|
|
sudo(["chmod", mode, socketPath]);
|
|
}
|
|
|
|
test(
|
|
"drop-sudo closes inherited Linux credential and service-socket bypasses",
|
|
{ skip: !canTestLinuxIsolation, timeout: 300_000 },
|
|
async (t) => {
|
|
if (!canTestSocketAcls) {
|
|
t.diagnostic("setfacl unavailable; named socket ACL coverage skipped");
|
|
}
|
|
|
|
for (const [index, scenario] of [
|
|
{
|
|
name: "workspace sandbox preserves compatible workflow arguments",
|
|
extraArgs: [
|
|
"--enable",
|
|
"use_legacy_landlock",
|
|
"--search",
|
|
"--ephemeral",
|
|
"--model",
|
|
"gpt-5.4",
|
|
"-c",
|
|
'model_reasoning_effort="high"',
|
|
"--config",
|
|
'service_tier="fast"',
|
|
],
|
|
verifyForwardedArgs: true,
|
|
},
|
|
{ name: "stale live groups cannot retain socket access", staleGroups: true },
|
|
{
|
|
name: "permission profiles cannot gain privileges",
|
|
profile: ":workspace",
|
|
},
|
|
{
|
|
name: "built-in read-only profiles cannot gain privileges",
|
|
profile: ":read-only",
|
|
},
|
|
{
|
|
name: "named permission profiles cannot gain privileges",
|
|
profile: "public-review",
|
|
},
|
|
{
|
|
name: "unconfined execution cannot gain privileges",
|
|
sandbox: "danger-full-access",
|
|
},
|
|
{
|
|
name: "the unconfined permission profile cannot gain privileges",
|
|
profile: ":danger-full-access",
|
|
},
|
|
{
|
|
name: "the explicit sandbox bypass cannot gain privileges",
|
|
extraArgs: ["--dangerously-bypass-approvals-and-sandbox"],
|
|
},
|
|
{
|
|
name: "the sandbox bypass alias cannot gain privileges",
|
|
extraArgs: ["--yolo"],
|
|
},
|
|
{
|
|
name: "hook trust bypasses fail before irreversible cleanup",
|
|
extraArgs: ["--dangerously-bypass-hook-trust"],
|
|
rejectedBeforeCleanup: true,
|
|
},
|
|
{
|
|
name: "read-only full-auto conflicts fail before irreversible cleanup",
|
|
sandbox: "read-only",
|
|
extraArgs: ["--full-auto"],
|
|
rejectedBeforeCleanup: true,
|
|
},
|
|
{
|
|
name: "MCP command overrides fail before irreversible cleanup",
|
|
extraArgs: ["--config", 'mcp_servers.evil.command="/bin/sh"'],
|
|
rejectedBeforeCleanup: true,
|
|
},
|
|
{
|
|
name: "the replacement primary group cannot inherit sudo policy",
|
|
replacementGroupSudoGrant: true,
|
|
},
|
|
{
|
|
name: "a command-specific User_Alias cannot gain privileges",
|
|
userAliasSudoGrant: true,
|
|
},
|
|
{ name: "remaining sudo grants fail closed", remainingSudoGrant: true },
|
|
].entries()) {
|
|
await t.test(scenario.name, { timeout: 45_000 }, (subtest) => {
|
|
const suffix = `${process.pid}${Date.now().toString(36)}${index}`;
|
|
const user = `codexdrop${suffix}`;
|
|
const privilegedGroup = `cdxp${suffix}`;
|
|
const worldGroup = `cdxw${suffix}`;
|
|
const remainingGroup = `cdxr${suffix}`;
|
|
const tempDir = mkdtempSync(
|
|
path.join(tmpdir(), "codex-action-drop-sudo-")
|
|
);
|
|
const capturePath = path.join(tempDir, "capture.json");
|
|
const outputPath = path.join(tempDir, "output.md");
|
|
const codexPath = path.join(tempDir, "codex");
|
|
const bundledActionPath = path.join(tempDir, "main.js");
|
|
const sudoersPath = `/etc/sudoers.d/${user}`;
|
|
const serviceSocket = `/run/codex-action-${suffix}-service.sock`;
|
|
const worldSocket = `/run/codex-action-${suffix}-world.sock`;
|
|
const fallbackSocket = `/run/codex-action-${suffix}-fallback.sock`;
|
|
const namedUserAclSocket = `/run/codex-action-${suffix}-acl-user.sock`;
|
|
const fallbackAclSocket = `/run/codex-action-${suffix}-acl-fallback.sock`;
|
|
const staleAclSocket = `/run/codex-action-${suffix}-acl-stale.sock`;
|
|
const liveGroupsPath = path.join(tempDir, "live-groups.txt");
|
|
const originalDockerSocket = statSync(dockerSocket);
|
|
const safeGroup = Number(sudo(["id", "-g", "nobody"]));
|
|
const safeGroupName = sudo(["id", "-gn", "nobody"]);
|
|
const sudoAlias = `CODEX_DROP_${suffix}`.toUpperCase();
|
|
const groupsCreated = [];
|
|
let userCreated = false;
|
|
let originalSockets = [];
|
|
|
|
subtest.after(() => {
|
|
sudo(["rm", "-f", sudoersPath]);
|
|
restoreRootSockets(originalSockets);
|
|
sudo([
|
|
"rm",
|
|
"-f",
|
|
serviceSocket,
|
|
worldSocket,
|
|
fallbackSocket,
|
|
namedUserAclSocket,
|
|
fallbackAclSocket,
|
|
staleAclSocket,
|
|
]);
|
|
if (userCreated) {
|
|
sudo(["userdel", user]);
|
|
}
|
|
for (const group of groupsCreated) {
|
|
sudo(["groupdel", group]);
|
|
}
|
|
sudo(["rm", "-rf", tempDir]);
|
|
});
|
|
|
|
for (const group of [
|
|
privilegedGroup,
|
|
worldGroup,
|
|
...(scenario.remainingSudoGrant ? [remainingGroup] : []),
|
|
]) {
|
|
sudo(["groupadd", group]);
|
|
groupsCreated.push(group);
|
|
}
|
|
const supplementaryGroups = [
|
|
"sudo",
|
|
privilegedGroup,
|
|
...(scenario.remainingSudoGrant ? [remainingGroup] : []),
|
|
];
|
|
sudo([
|
|
"useradd",
|
|
"--no-create-home",
|
|
"--gid",
|
|
String(originalDockerSocket.gid),
|
|
"--groups",
|
|
supplementaryGroups.join(","),
|
|
user,
|
|
]);
|
|
userCreated = true;
|
|
const userId = Number(sudo(["id", "-u", user]));
|
|
|
|
const sudoersSource = path.join(tempDir, "sudoers");
|
|
const sudoRules = [`${user} ALL=(ALL) NOPASSWD:ALL`];
|
|
if (scenario.remainingSudoGrant) {
|
|
sudoRules.push(`%${remainingGroup} ALL=(ALL) NOPASSWD:ALL`);
|
|
}
|
|
if (scenario.replacementGroupSudoGrant) {
|
|
sudoRules.push(
|
|
`%${safeGroupName} ALL=(root) NOPASSWD: /usr/bin/true`
|
|
);
|
|
}
|
|
if (scenario.userAliasSudoGrant) {
|
|
sudoRules.push(
|
|
`User_Alias ${sudoAlias} = ${user}`,
|
|
`${sudoAlias} ALL=(root) NOPASSWD: /usr/bin/id -u`
|
|
);
|
|
}
|
|
writeFileSync(
|
|
sudoersSource,
|
|
`${sudoRules.join("\n")}\n`
|
|
);
|
|
sudo(["install", "--mode=0440", sudoersSource, sudoersPath]);
|
|
|
|
createRootSocket(serviceSocket, privilegedGroup, "0660");
|
|
createRootSocket(worldSocket, worldGroup, "0666");
|
|
createRootSocket(fallbackSocket, String(safeGroup), "0660");
|
|
if (canTestSocketAcls) {
|
|
createRootSocket(namedUserAclSocket, worldGroup, "0660");
|
|
createRootSocket(fallbackAclSocket, worldGroup, "0660");
|
|
sudo([
|
|
"/usr/bin/setfacl",
|
|
"-m",
|
|
`u:${user}:rw`,
|
|
namedUserAclSocket,
|
|
]);
|
|
sudo([
|
|
"/usr/bin/setfacl",
|
|
"-m",
|
|
`g:${safeGroup}:rw`,
|
|
fallbackAclSocket,
|
|
]);
|
|
if (scenario.staleGroups) {
|
|
createRootSocket(staleAclSocket, worldGroup, "0660");
|
|
sudo(["/usr/bin/setfacl", "-m", `g:${privilegedGroup}:rw`, staleAclSocket]);
|
|
}
|
|
}
|
|
originalSockets = snapshotRootSockets();
|
|
for (const socket of [
|
|
dockerSocket,
|
|
serviceSocket,
|
|
worldSocket,
|
|
...(canTestSocketAcls ? [namedUserAclSocket] : []),
|
|
...(canTestSocketAcls && scenario.staleGroups ? [staleAclSocket] : []),
|
|
]) {
|
|
sudo(["/usr/bin/sudo", "-n", "-u", user, "--", "test", "-w", socket]);
|
|
}
|
|
assert.notEqual(
|
|
spawnSync("sudo", [
|
|
"-n",
|
|
"-u",
|
|
user,
|
|
"--",
|
|
"test",
|
|
"-w",
|
|
fallbackSocket,
|
|
]).status,
|
|
0
|
|
);
|
|
if (canTestSocketAcls) {
|
|
assert.notEqual(
|
|
spawnSync("sudo", [
|
|
"-n",
|
|
"-u",
|
|
user,
|
|
"--",
|
|
"test",
|
|
"-w",
|
|
fallbackAclSocket,
|
|
]).status,
|
|
0
|
|
);
|
|
sudo([
|
|
"/usr/bin/setpriv",
|
|
`--reuid=${userId}`,
|
|
`--regid=${safeGroup}`,
|
|
"--clear-groups",
|
|
"--",
|
|
"/usr/bin/test",
|
|
"-w",
|
|
fallbackAclSocket,
|
|
]);
|
|
}
|
|
|
|
copyFileSync(mainPath, bundledActionPath);
|
|
writeFileSync(
|
|
codexPath,
|
|
`#!${process.execPath}
|
|
const { accessSync, constants, readFileSync, writeFileSync } = require("node:fs");
|
|
const { spawnSync } = require("node:child_process");
|
|
const args = process.argv.slice(2);
|
|
const output = args[args.indexOf("--output-last-message") + 1];
|
|
function canWrite(socket) {
|
|
try {
|
|
accessSync(socket, constants.W_OK);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
const status = Object.fromEntries(
|
|
readFileSync("/proc/self/status", "utf8")
|
|
.split("\\n")
|
|
.map((line) => line.split(/:\\s*/, 2))
|
|
);
|
|
const child = spawnSync(process.execPath, ["-p", "process.env.NODE_OPTIONS"], {
|
|
encoding: "utf8",
|
|
});
|
|
const sudoTrue = spawnSync("/usr/bin/sudo", ["-n", "/usr/bin/true"], {
|
|
encoding: "utf8",
|
|
});
|
|
const sudoId = spawnSync("/usr/bin/sudo", ["-n", "/usr/bin/id", "-u"], {
|
|
encoding: "utf8",
|
|
});
|
|
writeFileSync(process.env.CODEX_CAPTURE_PATH, JSON.stringify({
|
|
args,
|
|
uid: process.getuid(),
|
|
gid: process.getgid(),
|
|
groups: process.getgroups(),
|
|
supplementaryGroups: status.Groups.trim(),
|
|
dockerAccessible: canWrite("/var/run/docker.sock"),
|
|
serviceAccessible: canWrite(process.env.CODEX_SERVICE_SOCKET),
|
|
worldAccessible: canWrite(process.env.CODEX_WORLD_SOCKET),
|
|
fallbackAccessible: canWrite(process.env.CODEX_FALLBACK_SOCKET),
|
|
namedUserAclAccessible: process.env.CODEX_NAMED_USER_ACL_SOCKET
|
|
? canWrite(process.env.CODEX_NAMED_USER_ACL_SOCKET)
|
|
: null,
|
|
fallbackAclAccessible: process.env.CODEX_FALLBACK_ACL_SOCKET
|
|
? canWrite(process.env.CODEX_FALLBACK_ACL_SOCKET)
|
|
: null,
|
|
sudoStatus: sudoTrue.status,
|
|
sudoIdStatus: sudoId.status,
|
|
sudoIdStdout: sudoId.stdout.trim(),
|
|
noNewPrivs: status.NoNewPrivs,
|
|
capBounding: status.CapBnd,
|
|
capEffective: status.CapEff,
|
|
capInheritable: status.CapInh,
|
|
capPermitted: status.CapPrm,
|
|
capAmbient: status.CapAmb,
|
|
nodeOptions: process.env.NODE_OPTIONS,
|
|
childNodeOptions: child.stdout.trim(),
|
|
home: process.env.HOME,
|
|
marker: process.env.CODEX_TEST_MARKER,
|
|
prompt: readFileSync(0, "utf8"),
|
|
}));
|
|
writeFileSync(output, "fake final message\\n");
|
|
`
|
|
);
|
|
chmodSync(codexPath, 0o755);
|
|
chmodSync(tempDir, 0o711);
|
|
sudo(["chown", "-R", user, tempDir]);
|
|
|
|
const command = [
|
|
"-n",
|
|
"-u",
|
|
user,
|
|
"--",
|
|
"/usr/bin/env",
|
|
`HOME=${tempDir}`,
|
|
`PATH=${tempDir}:${process.env.PATH ?? ""}`,
|
|
"NODE_OPTIONS=--disable-sigusr1",
|
|
`CODEX_CAPTURE_PATH=${capturePath}`,
|
|
`CODEX_SERVICE_SOCKET=${serviceSocket}`,
|
|
`CODEX_WORLD_SOCKET=${worldSocket}`,
|
|
`CODEX_FALLBACK_SOCKET=${fallbackSocket}`,
|
|
...(canTestSocketAcls
|
|
? [
|
|
`CODEX_NAMED_USER_ACL_SOCKET=${namedUserAclSocket}`,
|
|
`CODEX_FALLBACK_ACL_SOCKET=${fallbackAclSocket}`,
|
|
]
|
|
: []),
|
|
"CODEX_TEST_MARKER=preserved",
|
|
process.execPath,
|
|
bundledActionPath,
|
|
"run-codex-exec",
|
|
"--prompt",
|
|
"test prompt\nsecond line",
|
|
"--prompt-file",
|
|
"",
|
|
"--codex-home",
|
|
"",
|
|
"--cd",
|
|
tempDir,
|
|
"--extra-args",
|
|
scenario.extraArgs ? JSON.stringify(scenario.extraArgs) : "",
|
|
"--output-file",
|
|
outputPath,
|
|
"--output-schema-file",
|
|
"",
|
|
"--output-schema",
|
|
"",
|
|
"--sandbox",
|
|
scenario.sandbox ?? "",
|
|
"--permission-profile",
|
|
scenario.profile ?? "",
|
|
"--model",
|
|
"",
|
|
"--effort",
|
|
"",
|
|
"--safety-strategy",
|
|
"drop-sudo",
|
|
"--codex-user",
|
|
"",
|
|
];
|
|
const launchCommand = scenario.staleGroups
|
|
? [
|
|
...command.slice(0, 4),
|
|
"/bin/sh", "-ec",
|
|
'/usr/bin/sudo -n /usr/bin/gpasswd -d "$1" "$2"; /usr/bin/id -G > "$3"; shift 3; exec "$@"',
|
|
"codex-stale-groups", user, privilegedGroup, liveGroupsPath,
|
|
...command.slice(4),
|
|
]
|
|
: command;
|
|
const result = spawnSync("sudo", launchCommand, {
|
|
encoding: "utf8",
|
|
timeout: 35_000,
|
|
});
|
|
|
|
if (scenario.rejectedBeforeCleanup) {
|
|
assert.notEqual(result.status, 0);
|
|
assert.match(result.stderr, /codex-args/);
|
|
assert.equal(existsSync(capturePath), false);
|
|
|
|
const stillPrivileged = spawnSync(
|
|
"/usr/bin/sudo",
|
|
["-n", "-u", user, "--", "/usr/bin/sudo", "-n", "/usr/bin/true"],
|
|
{ encoding: "utf8" }
|
|
);
|
|
assert.equal(stillPrivileged.status, 0, stillPrivileged.stderr);
|
|
|
|
const accountGroups = sudo(["id", "-nG", user]).split(/\s+/);
|
|
assert.equal(accountGroups.includes("sudo"), true);
|
|
assert.equal(accountGroups.includes(privilegedGroup), true);
|
|
for (const { socketPath, mode } of originalSockets) {
|
|
assert.equal(
|
|
Number.parseInt(sudo(["stat", "-c", "%a", socketPath]), 8),
|
|
Number.parseInt(mode, 8),
|
|
`${socketPath} changed before argument rejection`
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (scenario.remainingSudoGrant) {
|
|
assert.notEqual(result.status, 0);
|
|
assert.match(result.stderr, /Expected sudo to be disabled/);
|
|
assert.equal(existsSync(capturePath), false);
|
|
return;
|
|
}
|
|
|
|
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
assert.match(
|
|
result.stdout,
|
|
/Confirmed the standard sudo probe is disabled/
|
|
);
|
|
assert.match(
|
|
result.stdout,
|
|
/Launching Codex with no_new_privs and empty capability sets/
|
|
);
|
|
const capture = JSON.parse(readFileSync(capturePath, "utf8"));
|
|
assert.equal(capture.uid, userId);
|
|
assert.equal(capture.gid, safeGroup);
|
|
assert.notEqual(capture.gid, originalDockerSocket.gid);
|
|
assert.deepEqual(capture.groups, [safeGroup]);
|
|
assert.equal(capture.supplementaryGroups, "");
|
|
assert.equal(capture.dockerAccessible, false);
|
|
assert.equal(capture.serviceAccessible, false);
|
|
assert.equal(capture.worldAccessible, false);
|
|
assert.equal(capture.fallbackAccessible, false);
|
|
assert.equal(
|
|
capture.namedUserAclAccessible,
|
|
canTestSocketAcls ? false : null
|
|
);
|
|
assert.equal(
|
|
capture.fallbackAclAccessible,
|
|
canTestSocketAcls ? false : null
|
|
);
|
|
assert.ok(Number.isInteger(capture.sudoStatus));
|
|
assert.notEqual(capture.sudoStatus, 0);
|
|
assert.ok(Number.isInteger(capture.sudoIdStatus));
|
|
assert.notEqual(capture.sudoIdStatus, 0);
|
|
assert.notEqual(capture.sudoIdStdout, "0");
|
|
assert.equal(capture.noNewPrivs, "1");
|
|
assert.equal(capture.capBounding, "0000000000000000");
|
|
assert.equal(capture.capEffective, "0000000000000000");
|
|
assert.equal(capture.capInheritable, "0000000000000000");
|
|
assert.equal(capture.capPermitted, "0000000000000000");
|
|
assert.equal(capture.capAmbient, "0000000000000000");
|
|
assert.equal(capture.nodeOptions, "--disable-sigusr1");
|
|
assert.equal(capture.childNodeOptions, "--disable-sigusr1");
|
|
assert.equal(capture.home, tempDir);
|
|
assert.equal(capture.marker, "preserved");
|
|
assert.equal(capture.prompt, "test prompt\nsecond line");
|
|
if (scenario.verifyForwardedArgs) {
|
|
const forwardedStart = capture.args.indexOf(scenario.extraArgs[0]);
|
|
assert.notEqual(forwardedStart, -1);
|
|
assert.deepEqual(
|
|
capture.args.slice(
|
|
forwardedStart,
|
|
forwardedStart + scenario.extraArgs.length
|
|
),
|
|
scenario.extraArgs
|
|
);
|
|
}
|
|
assert.equal(readFileSync(outputPath, "utf8"), "fake final message\n");
|
|
|
|
if (
|
|
scenario.replacementGroupSudoGrant ||
|
|
scenario.userAliasSudoGrant
|
|
) {
|
|
const grantedCommand = scenario.replacementGroupSudoGrant
|
|
? ["/usr/bin/true"]
|
|
: ["/usr/bin/id", "-u"];
|
|
const unprotected = spawnSync(
|
|
"/usr/bin/sudo",
|
|
[
|
|
"-n",
|
|
"--",
|
|
"/usr/bin/setpriv",
|
|
`--reuid=${userId}`,
|
|
`--regid=${safeGroup}`,
|
|
"--clear-groups",
|
|
"--",
|
|
"/usr/bin/sudo",
|
|
"-n",
|
|
...grantedCommand,
|
|
],
|
|
{ encoding: "utf8" }
|
|
);
|
|
assert.equal(
|
|
unprotected.status,
|
|
0,
|
|
`expected the residual sudo rule to remain effective without no_new_privs: ${unprotected.stderr}`
|
|
);
|
|
if (scenario.userAliasSudoGrant) {
|
|
assert.equal(unprotected.stdout.trim(), "0");
|
|
}
|
|
}
|
|
|
|
const accountGroups = sudo(["id", "-nG", user]).split(/\s+/);
|
|
assert.equal(accountGroups.includes("sudo"), false);
|
|
assert.equal(accountGroups.includes(privilegedGroup), false);
|
|
if (scenario.staleGroups) {
|
|
const staleGid = sudo(["getent", "group", privilegedGroup]).split(":")[2];
|
|
assert.ok(readFileSync(liveGroupsPath, "utf8").trim().split(/\s+/).includes(staleGid));
|
|
}
|
|
for (const socket of [
|
|
dockerSocket,
|
|
serviceSocket,
|
|
worldSocket,
|
|
fallbackSocket,
|
|
...(canTestSocketAcls
|
|
? [namedUserAclSocket, fallbackAclSocket]
|
|
: []),
|
|
...(canTestSocketAcls && scenario.staleGroups ? [staleAclSocket] : []),
|
|
]) {
|
|
assert.equal(statSync(socket).mode & 0o077, 0);
|
|
const access = spawnSync("sudo", [
|
|
"-n",
|
|
"-u",
|
|
user,
|
|
"--",
|
|
"test",
|
|
"-w",
|
|
socket,
|
|
]);
|
|
assert.notEqual(access.status, 0);
|
|
}
|
|
|
|
const failed = spawnSync("sudo", command, {
|
|
encoding: "utf8",
|
|
timeout: 30_000,
|
|
});
|
|
assert.notEqual(failed.status, 0);
|
|
assert.match(failed.stderr, /requires passwordless sudo.*separate job/);
|
|
assert.deepEqual(JSON.parse(readFileSync(capturePath, "utf8")), capture);
|
|
});
|
|
}
|
|
}
|
|
);
|