mirror of
https://github.com/morluto/rea
synced 2026-10-27 14:42:18 -04:00
cbc0dd1e7c
* fix(contracts)!: require absolute local paths for remaining caller-supplied file inputs Follow-up to the session snapshot/export tightening: the same invisible server-cwd resolution applied to open_binary target paths (parseBinaryTarget(path, process.cwd())), import_evidence_bundle bundle reads, firmware input files (resolve(raw)), browser executable launch paths (resolve-then-spawn), Electron executable/application/root paths (realpath on raw input), and the expected server path check. Each field gains the shared isAbsoluteLocalPath refinement plus a catalog-visible description, since JSON Schema cannot express the rule. Examples, scenario fixtures, and contract tests updated to absolute paths; new schema cases cover rejection/acceptance/omission per field. Explicitly unchanged (verified rooted or out of scope): inspect_plist paths (resolved against the target bundle dir), firmware output_directory (already provider-enforced), filesystem observation paths (resolved against the caller-supplied working_directory), keyed-archive member paths (archive-relative), JS evidence member paths (evidence-relative under root_alias), provider-internal execution schemas, CLI-only doctor flows, and CLI options (own resolve against the operator cwd). * fix(cli): resolve operator-relative paths before shared absolute-path validation The contract tightening rejects relative paths, but three CLI commands forward operator-relative input into the shared schemas: firmware commands pass raw args to FirmwareAnalysisService, and the Electron/browser scenario commands validate parsed scenario JSON directly. CLI paths are relative to the operator shell, so resolve them against the working directory first: resolve() in firmwareCommands, and a shared resolveCliJsonPaths JSON key-path helper (cliJsonInput) applied in electronCommands and cliBrowserScenarioCommands. MCP behavior is unchanged — MCP callers must still send absolute paths. Covers the regression with a relative-path firmware CLI acceptance test (asserts provider-stage capability_unavailable, not a validation error) and unit tests for the helper.
275 lines
8.0 KiB
TypeScript
275 lines
8.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { browserScenarioBrowserSchema } from "../domain/browserScenarioValues.js";
|
|
import { firmwareInputSchemas } from "../domain/firmware/firmwareAnalysis.js";
|
|
import { electronActiveObservationInputSchema } from "../domain/javascript/electronActiveObservation.js";
|
|
import { managedArtifactInputSchema } from "./managed/managedToolContracts.js";
|
|
import { exportEvidenceBundleInputSchema } from "./sessionToolContracts.js";
|
|
import {
|
|
closeBinaryInputSchema,
|
|
openBinaryInputSchema,
|
|
} from "./sessionLifecycleInputs.js";
|
|
import { importEvidenceBundleInputSchema } from "./sessionToolSchemas.js";
|
|
import { binarySessionInputSchema } from "./sessionStatusContract.js";
|
|
|
|
const openBinaryAbsolute = (snapshot_path: string) =>
|
|
openBinaryInputSchema.safeParse({ path: "/tmp/fixture", snapshot_path });
|
|
|
|
describe("session snapshot path inputs require absolute local paths", () => {
|
|
it("rejects a relative snapshot path when opening a binary", () => {
|
|
expect(openBinaryAbsolute("relative/analysis.json").success).toBe(false);
|
|
});
|
|
|
|
it("rejects a parent-relative snapshot path when opening a binary", () => {
|
|
expect(openBinaryAbsolute("../outside/analysis.json").success).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute snapshot path when opening a binary", () => {
|
|
expect(openBinaryAbsolute("/tmp/rea/analysis.json").success).toBe(true);
|
|
});
|
|
|
|
it("still accepts open_binary without a snapshot path", () => {
|
|
expect(
|
|
openBinaryInputSchema.safeParse({ path: "/tmp/fixture" }).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("rejects a relative snapshot path when closing a binary", () => {
|
|
expect(
|
|
closeBinaryInputSchema.safeParse({ snapshot_path: "analysis.json" })
|
|
.success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute snapshot path when closing a binary", () => {
|
|
expect(
|
|
closeBinaryInputSchema.safeParse({ snapshot_path: "/tmp/analysis.json" })
|
|
.success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("still accepts close_binary without a snapshot path", () => {
|
|
expect(closeBinaryInputSchema.safeParse({}).success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("evidence bundle export path requires an absolute local path", () => {
|
|
it("rejects a relative export path", () => {
|
|
expect(
|
|
exportEvidenceBundleInputSchema.safeParse({ path: "out/bundle.json" })
|
|
.success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects a parent-relative export path", () => {
|
|
expect(
|
|
exportEvidenceBundleInputSchema.safeParse({
|
|
path: "../../bundle.json",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute export path and keeps overwrite semantics", () => {
|
|
expect(
|
|
exportEvidenceBundleInputSchema.safeParse({
|
|
path: "/tmp/bundle.json",
|
|
}),
|
|
).toMatchObject({ success: true, data: { overwrite: false } });
|
|
});
|
|
});
|
|
|
|
describe("managed target path requires an absolute local path", () => {
|
|
it("rejects a relative managed target path", () => {
|
|
expect(
|
|
managedArtifactInputSchema.safeParse({ path: "bin/app.dll" }).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute managed target path", () => {
|
|
expect(
|
|
managedArtifactInputSchema.safeParse({ path: "/tmp/app.dll" }).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("still accepts omitting the path to reuse the active target", () => {
|
|
expect(managedArtifactInputSchema.safeParse({}).success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe.runIf(process.platform === "win32")(
|
|
"windows absolute path forms on windows hosts",
|
|
() => {
|
|
it("accepts drive-letter backslash snapshot paths", () => {
|
|
expect(
|
|
closeBinaryInputSchema.safeParse({
|
|
snapshot_path: "C:\\rea\\analysis.json",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("accepts drive-letter forward-slash snapshot paths", () => {
|
|
expect(
|
|
closeBinaryInputSchema.safeParse({
|
|
snapshot_path: "C:/rea/analysis.json",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("accepts forward-slash export paths", () => {
|
|
expect(
|
|
exportEvidenceBundleInputSchema.safeParse({
|
|
path: "C:/rea/bundle.json",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
},
|
|
);
|
|
|
|
describe("open binary target path requires an absolute local path", () => {
|
|
it("rejects a relative target path", () => {
|
|
expect(
|
|
openBinaryInputSchema.safeParse({ path: "fixtures/app.bin" }).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute target path", () => {
|
|
expect(
|
|
openBinaryInputSchema.safeParse({ path: "/tmp/fixture.bin" }).success,
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("evidence bundle import path requires an absolute local path", () => {
|
|
it("rejects a relative bundle path", () => {
|
|
expect(
|
|
importEvidenceBundleInputSchema.safeParse({ path: "evidence.json" })
|
|
.success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute bundle path", () => {
|
|
expect(
|
|
importEvidenceBundleInputSchema.safeParse({ path: "/tmp/evidence.json" })
|
|
.success,
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("firmware input path requires an absolute local path", () => {
|
|
it("rejects a relative firmware path when inspecting regions", () => {
|
|
expect(
|
|
firmwareInputSchemas.inspect_firmware_regions.safeParse({
|
|
path: "firmware.bin",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects a relative firmware path when extracting", () => {
|
|
expect(
|
|
firmwareInputSchemas.extract_firmware.safeParse({
|
|
path: "firmware.bin",
|
|
output_directory: "/tmp/firmware-output",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute firmware path", () => {
|
|
expect(
|
|
firmwareInputSchemas.inspect_firmware_regions.safeParse({
|
|
path: "/tmp/firmware.bin",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("browser executable path requires an absolute local path", () => {
|
|
it("rejects a bare executable name", () => {
|
|
expect(
|
|
browserScenarioBrowserSchema.safeParse({
|
|
mode: "launch",
|
|
executable_path: "chrome",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute executable path", () => {
|
|
expect(
|
|
browserScenarioBrowserSchema.safeParse({
|
|
mode: "launch",
|
|
executable_path: "/opt/chromium/chrome",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("electron observation paths require absolute local paths", () => {
|
|
const valid = {
|
|
executable_path: "/Applications/Electron.app/Contents/MacOS/Electron",
|
|
application_path: "/tmp/electron-app/main.js",
|
|
};
|
|
|
|
it("rejects a relative executable path", () => {
|
|
expect(
|
|
electronActiveObservationInputSchema.safeParse({
|
|
...valid,
|
|
executable_path: "Electron",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects a relative application path", () => {
|
|
expect(
|
|
electronActiveObservationInputSchema.safeParse({
|
|
...valid,
|
|
application_path: "main.js",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("rejects a relative application root", () => {
|
|
expect(
|
|
electronActiveObservationInputSchema.safeParse({
|
|
...valid,
|
|
application_root: "app",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts absolute observation paths", () => {
|
|
expect(
|
|
electronActiveObservationInputSchema.safeParse({
|
|
...valid,
|
|
application_root: "/tmp/electron-app",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("still accepts omitting the application root", () => {
|
|
expect(electronActiveObservationInputSchema.safeParse(valid).success).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("expected server path requires an absolute local path", () => {
|
|
it("rejects a relative server path", () => {
|
|
expect(
|
|
binarySessionInputSchema.safeParse({
|
|
expected_server_path: "dist/main.js",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("accepts an absolute server path", () => {
|
|
expect(
|
|
binarySessionInputSchema.safeParse({
|
|
expected_server_path: "/opt/rea/dist/main.js",
|
|
}).success,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("still accepts omitting the server path", () => {
|
|
expect(binarySessionInputSchema.safeParse({}).success).toBe(true);
|
|
});
|
|
});
|