mirror of
https://github.com/BillyOutlast/rea.git
synced 2026-10-12 02:26:55 +00:00
fix(process): clarify Windows capture support and recovery (#798)
This commit is contained in:
@@ -631,9 +631,9 @@ Process Capture records behavior and is not a security sandbox.
|
||||
Capture a scenario or compare two saved Process Capture Evidence records:
|
||||
|
||||
```bash
|
||||
rea capture-process ./scenario.json > authority.json
|
||||
rea capture-process ./reconstruction.json > reconstruction.json
|
||||
rea compare-process-captures authority.json reconstruction.json
|
||||
rea capture-process ./authority-scenario.json --json > authority-capture.json
|
||||
rea capture-process ./reconstruction-scenario.json --json > reconstruction-capture.json
|
||||
rea compare-process-captures authority-capture.json reconstruction-capture.json --json
|
||||
```
|
||||
|
||||
The comparison reports each observed dimension separately and identifies the
|
||||
@@ -641,9 +641,16 @@ first terminal, interaction, exit, filesystem, or process divergence.
|
||||
See [Process Capture](docs/process-capture.md) for scenario fields, limits, and
|
||||
evidence boundaries.
|
||||
|
||||
REA installs a prebuilt PTY backend for supported macOS, Linux, and Windows
|
||||
architectures. If the capability check reports that the backend is unavailable,
|
||||
reinstall REA for the current platform and architecture.
|
||||
Process Capture currently runs on Linux and macOS with a working native PTY
|
||||
backend. Native Windows capture remains unavailable until the PTY adapter can
|
||||
verify descendant cleanup; installing or reinstalling its Windows PTY binary
|
||||
does not enable capture. For Linux commands, run Linux REA inside WSL.
|
||||
Comparing existing capture Evidence remains available on Windows.
|
||||
|
||||
On supported capture hosts, a missing or incompatible PTY binary can be fixed
|
||||
by reinstalling REA for the current platform, architecture, and Node.js version
|
||||
with optional dependencies enabled. Use separate scenario and output files;
|
||||
`--json` produces the JSON consumed by the comparison command.
|
||||
|
||||
ASAR inventory verifies Electron integrity metadata for both archive entries
|
||||
and `.asar.unpacked` companion files. Integrity failures identify the logical
|
||||
|
||||
@@ -12,6 +12,24 @@ sandbox. Filesystem observation paths select what REA snapshots; they do not
|
||||
restrict what the process can read or write. The inherited environment is not
|
||||
recorded, so its influence may remain unknown.
|
||||
|
||||
## Host support
|
||||
|
||||
Capturing a new scenario currently requires Linux or macOS and a working
|
||||
native PTY backend. Native Windows capture is unavailable because the PTY
|
||||
adapter does not yet verify descendant cleanup. The native Job Object controls
|
||||
used by other REA providers do not establish PTY capture support. Reinstalling
|
||||
the Windows PTY binary does not enable this workflow.
|
||||
|
||||
For Linux commands, use Linux REA inside WSL. Adapt the scenario to that host;
|
||||
this does not establish capture of a native Windows process tree. Comparing
|
||||
existing capture Evidence through the CLI or MCP remains available on Windows
|
||||
and does not launch a PTY or the captured target.
|
||||
|
||||
On supported capture hosts, a missing or incompatible native PTY binary has a
|
||||
different recovery: reinstall REA for the active platform, architecture, and
|
||||
Node.js version with optional dependencies enabled. Capability diagnostics
|
||||
distinguish this from the Windows capture-adapter limitation.
|
||||
|
||||
## Capture a command
|
||||
|
||||
Write a JSON scenario and pass its path to the CLI:
|
||||
@@ -22,6 +40,8 @@ rea capture-process ./scenario.json --json > capture.json
|
||||
|
||||
`--json` is required when saving input for JSON consumers; the default terminal
|
||||
format is TOON. The file contains the complete capture Evidence record.
|
||||
Choose an output file distinct from the scenario input: shell redirection opens
|
||||
and truncates the output before REA reads the scenario.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export const processCaptureOwnershipUnavailableReason = (
|
||||
platform: NodeJS.Platform,
|
||||
): string | undefined =>
|
||||
platform === "win32"
|
||||
? "Windows process-tree ownership and cleanup are unavailable; REA cannot verify that descendants have stopped."
|
||||
? "Windows PTY process capture is unavailable because this adapter does not yet verify descendant cleanup. Use Linux (including WSL for Linux commands) or macOS. Reinstalling the PTY backend does not enable native Windows capture."
|
||||
: undefined;
|
||||
|
||||
/** Dependencies that make the native PTY probe's host boundary testable. */
|
||||
|
||||
@@ -5,6 +5,7 @@ import { captureProcessScenario } from "./ProcessHarness.js";
|
||||
import { settleProcessCaptureJournal } from "./ProcessCaptureLifecycle.js";
|
||||
import { processCaptureSchema } from "../domain/processCapture.js";
|
||||
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "../domain/processCapture.fixture.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
|
||||
it("rejects legacy replay output instead of silently discarding it", () => {
|
||||
expect(
|
||||
@@ -48,9 +49,19 @@ it("fails closed on Windows before resolving or launching scenario paths", async
|
||||
const result = await captureProcessScenario(scenario, undefined, "win32");
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) throw new Error("expected Windows ownership refusal");
|
||||
expect(result.error).toBeInstanceOf(AnalysisCapabilityUnavailableError);
|
||||
if (!(result.error instanceof AnalysisCapabilityUnavailableError))
|
||||
throw new Error("expected a capability-unavailable outcome");
|
||||
expect(result.error).toMatchObject({
|
||||
operation: "capture_process_scenario",
|
||||
reason: expect.stringContaining("Windows process-tree ownership"),
|
||||
reason: expect.stringContaining("Windows PTY process capture"),
|
||||
});
|
||||
expect(projectAnalysisError(result.error)).toMatchObject({
|
||||
code: "capability_unavailable",
|
||||
category: "unsupported_provider",
|
||||
message: expect.stringContaining("does not yet verify descendant cleanup"),
|
||||
details: {
|
||||
operation: "capture_process_scenario",
|
||||
reason: result.error.reason,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -409,6 +409,7 @@ export const captureProcessScenario = async (
|
||||
"rea-process",
|
||||
"capture_process_scenario",
|
||||
ownershipReason,
|
||||
{ userMessage: ownershipReason },
|
||||
),
|
||||
);
|
||||
try {
|
||||
|
||||
@@ -52,13 +52,19 @@ export class AnalysisOutputError extends AnalysisError {
|
||||
/** Selected provider cannot execute a declared analysis operation. */
|
||||
export class AnalysisCapabilityUnavailableError extends AnalysisError {
|
||||
readonly _tag = "AnalysisCapabilityUnavailableError";
|
||||
override readonly userMessage: string | undefined;
|
||||
|
||||
constructor(
|
||||
readonly providerId: string,
|
||||
readonly operation: string,
|
||||
readonly reason: string,
|
||||
options?: ErrorOptions & { readonly userMessage?: string },
|
||||
) {
|
||||
super(`Provider ${providerId} cannot execute ${operation}: ${reason}`);
|
||||
super(
|
||||
`Provider ${providerId} cannot execute ${operation}: ${reason}`,
|
||||
options,
|
||||
);
|
||||
this.userMessage = options?.userMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,8 @@ describe("analysis error projection: caller contract", () => {
|
||||
]);
|
||||
expect(projected[1]).toMatchObject({
|
||||
category: "unsupported_provider",
|
||||
message:
|
||||
"This analysis is unavailable for the current target. Choose another analysis or target.",
|
||||
details: {
|
||||
provider_id: "fixture",
|
||||
operation: "overview",
|
||||
@@ -213,6 +215,31 @@ describe("analysis error projection: caller contract", () => {
|
||||
expect(JSON.stringify(projected)).not.toContain("secret-token");
|
||||
});
|
||||
|
||||
it("uses explicit capability recovery while retaining the constraint", () => {
|
||||
const projected = projectAnalysisError(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
"fixture",
|
||||
"capture",
|
||||
"host constraint",
|
||||
{
|
||||
userMessage: "Use the supported host workflow.",
|
||||
cause: new Error("private internal cause"),
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(projected).toMatchObject({
|
||||
code: "capability_unavailable",
|
||||
category: "unsupported_provider",
|
||||
message: "Use the supported host workflow.",
|
||||
details: {
|
||||
provider_id: "fixture",
|
||||
operation: "capture",
|
||||
reason: "host constraint",
|
||||
},
|
||||
});
|
||||
expect(JSON.stringify(projected)).not.toContain("private internal cause");
|
||||
});
|
||||
|
||||
it("preserves exact artifact-integrity coordinates", () => {
|
||||
expect(
|
||||
projectAnalysisError(
|
||||
|
||||
@@ -54,7 +54,29 @@ describe("process capture capability diagnostics", () => {
|
||||
|
||||
it("keeps the ownership limitation separate from native PTY support", () => {
|
||||
expect(processCaptureOwnershipUnavailableReason("win32")).toContain(
|
||||
"process-tree ownership",
|
||||
"Windows PTY process capture",
|
||||
);
|
||||
});
|
||||
|
||||
it("explains Windows capture admission before loading or spawning a PTY", async () => {
|
||||
let loaded = false;
|
||||
const capability = await probeProcessCaptureCapability({
|
||||
platform: "win32",
|
||||
loadPty: async () => {
|
||||
loaded = true;
|
||||
throw new Error("Windows admission must precede PTY loading");
|
||||
},
|
||||
});
|
||||
expect(loaded).toBe(false);
|
||||
expect(capability).toMatchObject({
|
||||
available: false,
|
||||
reason: expect.stringContaining("does not yet verify descendant cleanup"),
|
||||
});
|
||||
if (capability.available)
|
||||
throw new Error("expected Windows capture refusal");
|
||||
expect(capability.reason).toContain("WSL for Linux commands");
|
||||
expect(capability.reason).toContain(
|
||||
"Reinstalling the PTY backend does not enable native Windows capture",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user