mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(process): identify unknown live processes in cleanup diagnostics
This commit is contained in:
@@ -208,7 +208,7 @@ describe("process environment permission diagnostics", () => {
|
||||
),
|
||||
).resolves.toMatchObject({
|
||||
cleaned: false,
|
||||
reason: `process ownership token could not be read for 1 live process(es): environment_errno_${code}=1`,
|
||||
reason: `process ownership token could not be read for 1 live process(es): environment_errno_${code}=1; live candidates 900=environment_errno_${code}`,
|
||||
failures: [{ pid: 900, reason: "environment-unreadable", diagnostic }],
|
||||
});
|
||||
expect(signalGroup).not.toHaveBeenCalled();
|
||||
|
||||
@@ -193,6 +193,13 @@ describe("opaque process neighbors during cleanup", () => {
|
||||
cleaned: false,
|
||||
reason: expect.stringContaining("environment_unavailable=1"),
|
||||
});
|
||||
if (result.cleaned)
|
||||
throw new Error("Expected unknown ownership to fail closed");
|
||||
expect(result.reason).toContain(
|
||||
"live candidates 900=environment_unavailable",
|
||||
);
|
||||
expect(result.reason).not.toContain("unowned");
|
||||
expect(result.reason).not.toContain("run-token");
|
||||
expect(signalGroup.mock.calls).toEqual([[100, "SIGKILL"]]);
|
||||
expect(signalGroup).not.toHaveBeenCalledWith(900, "SIGKILL");
|
||||
});
|
||||
|
||||
@@ -566,9 +566,15 @@ const cleanupValidationFailure = (
|
||||
.sort(([left], [right]) => left.localeCompare(right))
|
||||
.map(([category, count]) => `${category}=${String(count)}`)
|
||||
.join(", ");
|
||||
const candidates = unreadable
|
||||
.map(
|
||||
({ pid, diagnostic }) =>
|
||||
`${String(pid)}=${sanitizedTokenReadFailure(diagnostic)}`,
|
||||
)
|
||||
.sort((left, right) => left.localeCompare(right));
|
||||
return {
|
||||
cleaned: false,
|
||||
reason: `process ownership token could not be read for ${String(unreadable.length)} live process(es): ${breakdown}`,
|
||||
reason: `process ownership token could not be read for ${String(unreadable.length)} live process(es): ${breakdown}; live candidates ${candidates.join(", ")}`,
|
||||
failures,
|
||||
...unverifiedEntry,
|
||||
};
|
||||
|
||||
@@ -304,7 +304,7 @@ it("retains observations and both causes when process cleanup is unverifiable",
|
||||
owned_process_group: {
|
||||
state: "unverified" as const,
|
||||
reason:
|
||||
"process ownership token could not be read for 1 live process(es): environment_unavailable=1",
|
||||
"process ownership token could not be read for 1 live process(es): environment_unavailable=1; live candidates 900=environment_unavailable",
|
||||
unverified_processes: [
|
||||
{ pid: 900, reason: "platform_binary_environment_withheld" },
|
||||
],
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
projectProcessCliError,
|
||||
} from "../../../src/application/process/ProcessCli.js";
|
||||
import { createEvidence } from "../../../src/domain/evidence.js";
|
||||
import { ProcessCaptureError } from "../../../src/process/capture/ProcessCaptureError.js";
|
||||
import { PROCESS_PROVIDER } from "../../../src/application/process/ProcessEvidence.js";
|
||||
import { INVESTIGATION_EXAMPLES } from "../../../src/contracts/investigationExamples.js";
|
||||
|
||||
@@ -229,6 +230,30 @@ describe("process CLI environment key diagnostics", () => {
|
||||
});
|
||||
|
||||
describe("process CLI evidence validation", () => {
|
||||
it("preserves sanitized unknown-process IDs and reasons through CLI projection", () => {
|
||||
const reason =
|
||||
"process ownership token could not be read for 1 live process(es): environment_unavailable=1; live candidates 900=environment_unavailable";
|
||||
const projected = projectProcessCliError(
|
||||
new ProcessCaptureError("cleanup incomplete", {
|
||||
reason: "cleanup_incomplete",
|
||||
cleanupResources: ["owned_process_group"],
|
||||
cleanupReport: {
|
||||
owned_process_group: { state: "unverified", reason },
|
||||
terminal_renderer: { state: "cleaned", reason: null },
|
||||
temporary_root: { state: "cleaned", reason: null },
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(projected).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
details: {
|
||||
cleanup_report: {
|
||||
owned_process_group: { reason },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("captures the minimal executable-and-arguments scenario", async () => {
|
||||
const root = await fixture();
|
||||
const scenario = join(root, "scenario.json");
|
||||
|
||||
@@ -46,12 +46,14 @@ const captureObservations = (
|
||||
const partial = result.error.partialObservation;
|
||||
expect(result.error.reason).toBe("cleanup_incomplete");
|
||||
expect(report?.owned_process_group.state).toBe("unverified");
|
||||
const [summary, breakdown] = (report?.owned_process_group.reason ?? "").split(
|
||||
": ",
|
||||
);
|
||||
const [summary, diagnostics] = (
|
||||
report?.owned_process_group.reason ?? ""
|
||||
).split(": ");
|
||||
expect(summary).toMatch(
|
||||
/^process ownership token could not be read for [1-9][0-9]* live process\(es\)$/u,
|
||||
);
|
||||
const [breakdown, liveCandidates] =
|
||||
diagnostics?.split("; live candidates ") ?? [];
|
||||
const categories = breakdown?.split(", ") ?? [];
|
||||
expect(categories.length).toBeGreaterThan(0);
|
||||
const expectedCategory =
|
||||
@@ -59,6 +61,9 @@ const captureObservations = (
|
||||
? /^environment_errno_(?:EACCES|EPERM)=[1-9][0-9]*$/u
|
||||
: /^environment_unavailable=[1-9][0-9]*$/u;
|
||||
for (const category of categories) expect(category).toMatch(expectedCategory);
|
||||
expect(liveCandidates).toMatch(
|
||||
/^(?:[1-9][0-9]*=(?:environment_unavailable|environment_errno_(?:EACCES|EPERM)))(?:, [1-9][0-9]*=(?:environment_unavailable|environment_errno_(?:EACCES|EPERM)))*$/u,
|
||||
);
|
||||
expect(report?.terminal_renderer.state).toBe("cleaned");
|
||||
expect(report?.temporary_root.state).toBe("cleaned");
|
||||
if (partial === undefined || !("capture" in partial))
|
||||
|
||||
Reference in New Issue
Block a user