diff --git a/scripts/lib/process.mjs b/scripts/lib/process.mjs index ba532df..0e91d78 100644 --- a/scripts/lib/process.mjs +++ b/scripts/lib/process.mjs @@ -163,11 +163,12 @@ export function validateProcessIdentity(pid, expectedIdentity) { } } -export function isProcessAlive(pid) { +export function isProcessAlive(pid, options = {}) { + const killImpl = options.killImpl ?? process.kill.bind(process); try { - process.kill(pid, 0); + killImpl(pid, 0); return true; - } catch { - return false; + } catch (error) { + return error?.code === "EPERM"; } } diff --git a/tests/process.test.mjs b/tests/process.test.mjs index b6514b6..07b1ecf 100644 --- a/tests/process.test.mjs +++ b/tests/process.test.mjs @@ -324,6 +324,36 @@ describe("isProcessAlive", () => { // PID 99999999 is extremely unlikely to exist assert.equal(isProcessAlive(99999999), false); }); + + it("treats EPERM as alive when the sandbox denies the probe", () => { + const permissionError = Object.assign(new Error("operation not permitted"), { + code: "EPERM", + }); + + assert.equal( + isProcessAlive(12345, { + killImpl: () => { + throw permissionError; + }, + }), + true, + ); + }); + + it("treats ESRCH as dead when the process does not exist", () => { + const missingError = Object.assign(new Error("no such process"), { + code: "ESRCH", + }); + + assert.equal( + isProcessAlive(12345, { + killImpl: () => { + throw missingError; + }, + }), + false, + ); + }); }); // ---------------------------------------------------------------------------