mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
1bfa60ac20
Keep POSIX symlink-parent traversal at the shared JSON input boundary and forward the selected browser executable without a second lexical normalization. Verify Electron, browser, and Evidence-file consumers through registered commands and the built CLI. Refs #5103
139 lines
4.6 KiB
TypeScript
139 lines
4.6 KiB
TypeScript
import { mkdir, mkdtemp, rm, symlink } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { resolveCliJsonPaths } from "./cliJsonInput.js";
|
|
import { electronActiveObservationInputSchema } from "./domain/javascript/electronActiveObservation.js";
|
|
|
|
const paths = [
|
|
["executable_path"],
|
|
["application_path"],
|
|
["application_root"],
|
|
] as const;
|
|
|
|
describe("CLI JSON path normalization", () => {
|
|
it.each([
|
|
["executable_path", ""],
|
|
["executable_path", " \t "],
|
|
["application_path", ""],
|
|
["application_path", " \t "],
|
|
["application_root", ""],
|
|
["application_root", " \t "],
|
|
])(
|
|
"keeps blank %s (%j) invalid at the shared input boundary",
|
|
(field, value) => {
|
|
const input = {
|
|
executable_path: "/synthetic/electron",
|
|
application_path: "/synthetic/app",
|
|
application_root: "/synthetic",
|
|
[field]: value,
|
|
};
|
|
const parsed = electronActiveObservationInputSchema.safeParse(
|
|
resolveCliJsonPaths(input, paths),
|
|
);
|
|
expect(parsed.success).toBe(false);
|
|
if (parsed.success) throw new Error("Expected blank path rejection");
|
|
expect(parsed.error.issues).toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ path: [field] })]),
|
|
);
|
|
expect(input).toHaveProperty(field, value);
|
|
},
|
|
);
|
|
|
|
it("resolves selected relative paths without trimming names or changing unrelated fields", () => {
|
|
const input = {
|
|
browser: { executable_path: " browser ", label: "relative-label" },
|
|
application_path: "app",
|
|
};
|
|
expect(
|
|
resolveCliJsonPaths(input, [["browser", "executable_path"]]),
|
|
).toEqual({
|
|
browser: {
|
|
executable_path: resolve(" browser "),
|
|
label: "relative-label",
|
|
},
|
|
application_path: "app",
|
|
});
|
|
expect(input.browser.executable_path).toBe(" browser ");
|
|
});
|
|
|
|
it.skipIf(process.platform === "win32")(
|
|
"keeps POSIX symlink-parent traversal for each selected JSON path",
|
|
async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "rea-json-caller-path-"));
|
|
const originalCwd = process.cwd();
|
|
try {
|
|
await mkdir(join(root, "real/inner"), { recursive: true });
|
|
await symlink(join(root, "real/inner"), join(root, "link"));
|
|
process.chdir(root);
|
|
const electronInput = {
|
|
executable_path: "link/../tool",
|
|
application_path: "link/../app",
|
|
application_root: "link/../root",
|
|
};
|
|
const normalizedElectron = resolveCliJsonPaths(electronInput, paths);
|
|
expect(normalizedElectron).toEqual({
|
|
executable_path: `${root}/link/../tool`,
|
|
application_path: `${root}/link/../app`,
|
|
application_root: `${root}/link/../root`,
|
|
});
|
|
expect(electronInput.executable_path).toBe("link/../tool");
|
|
expect(
|
|
electronActiveObservationInputSchema.safeParse(normalizedElectron)
|
|
.success,
|
|
).toBe(true);
|
|
const browserInput = {
|
|
browser: { executable_path: "link/../browser", label: "keep" },
|
|
};
|
|
expect(
|
|
resolveCliJsonPaths(browserInput, [["browser", "executable_path"]]),
|
|
).toEqual({
|
|
browser: {
|
|
executable_path: `${root}/link/../browser`,
|
|
label: "keep",
|
|
},
|
|
});
|
|
expect(browserInput.browser.executable_path).toBe("link/../browser");
|
|
} finally {
|
|
process.chdir(originalCwd);
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
"file:///opt/electron",
|
|
"FILE:electron",
|
|
"https://example.test/app",
|
|
])("keeps URL %j distinguishable from a local path", (value) => {
|
|
const input = {
|
|
executable_path: value,
|
|
application_path: resolve("/synthetic/app"),
|
|
};
|
|
const normalized = resolveCliJsonPaths(input, paths);
|
|
expect(normalized).toEqual(input);
|
|
const parsed = electronActiveObservationInputSchema.safeParse(normalized);
|
|
expect(parsed.success).toBe(false);
|
|
if (parsed.success) throw new Error("Expected URL path rejection");
|
|
expect(parsed.error.issues).toEqual([
|
|
expect.objectContaining({
|
|
path: ["executable_path"],
|
|
code: "invalid_format",
|
|
message: expect.stringContaining("absolute local filesystem path"),
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it.each([
|
|
"bin/electron",
|
|
"release:v1/electron",
|
|
"C:\\Electron\\electron.exe",
|
|
])("keeps resolving local path %j", (value) => {
|
|
expect(resolveCliJsonPaths({ executable_path: value }, paths)).toEqual({
|
|
executable_path: resolve(value),
|
|
});
|
|
});
|
|
});
|