Files
reverse-engineering-anything/tests/boundary/process/packageTarListing.test.ts
T

98 lines
4.1 KiB
TypeScript

import { execFile } from "node:child_process";
import { join, resolve } from "node:path";
import { promisify } from "node:util";
import { pathToFileURL } from "node:url";
import { expect, it } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execFileAsync = promisify(execFile);
const packerUrl = pathToFileURL(
resolve("scripts/verify-package-pack.mjs"),
).href;
const catalogUrl = pathToFileURL(resolve("dist/catalogIdentity.js")).href;
const bridge = "package/bridge/hopper_bridge.py";
const members = [
bridge,
"package/bridge/cutter/rea_bridge.py",
"package/bridge/ghidra/ReaGhidraBridge.java",
"package/bridge/ghidra/ReaGhidraBundleActivator.java",
"package/bridge/ghidra/ReaGhidraExtensions.java",
"package/bridge/ghidra/ReaGhidraPrepareCom.java",
"package/bridge/ghidra/ReaGhidraPrepareRaw.java",
"package/bridge/ghidra/ReaGhidraPrepareDol.java",
"package/bridge/ghidra/ReaGhidraApplySeeds.java",
"package/bridge/ghidra/ReaGhidraAnalysisOptions.java",
"package/bridge/ghidra/ReaGhidraNoReturnFix.java",
"package/bridge/ghidra/ReaGhidraMipsSoftFloat.java",
"package/scripts/lib/cli-process-environment.mjs",
];
async function verifyListing(listing: string) {
const workspace = await createTestTempDirectory("rea-tar-listing-");
const { stdout } = await execFileAsync(process.execPath, [
"--input-type=module",
"--eval",
`import { FULL_CATALOG_IDENTITY } from ${JSON.stringify(catalogUrl)};
let bridgeParsed = false;
const exec = async (command, args) => {
let stdout;
if ((command === "npm" && args[0] === "pack") || (command === process.execPath && args[1] === "pack")) stdout = "fixture.tgz\\n";
else if (command === "tar" && args[0] === "-tf") stdout = ${JSON.stringify(listing)};
else if (command === "tar" && args[0] === "-xOf" && args[2] === "package/package.json")
stdout = JSON.stringify({ bin: { rea: "scripts/rea.mjs", "rea-agents": "scripts/rea.mjs" }, mcpName: "io.github.morluto/rea", dependencies: { "@lydell/node-pty": "1.1.0" } });
else if (command === "tar" && args[0] === "-xOf" && args[2] === "package/skills/reverse-engineer-anything/SKILL.md")
stdout = '---\\nname: reverse-engineer-anything\\nmetadata:\\n version: "fixture"\\n tool_count: ' + FULL_CATALOG_IDENTITY.counts.mcp_tools + "\\n---\\n";
else if (command === "tar" && args[0] === "-xf") stdout = "";
else if (command === "python3") { bridgeParsed = true; stdout = ""; }
else throw new Error("Unexpected command: " + command + " " + JSON.stringify(args));
return { stdout, stderr: "" };
};
const commands = { exec, execNpm: (args) => exec("npm", args) };
const { verifyPackagePack } = await import(${JSON.stringify(packerUrl)});
try {
const result = await verifyPackagePack({ root: process.cwd(), workspace: ${JSON.stringify(workspace)}, commands });
process.stdout.write(JSON.stringify({ ...result, bridgeParsed }));
} catch (cause) {
process.stdout.write(JSON.stringify({ error: cause.message, bridgeParsed }));
}`,
]);
return { result: JSON.parse(stdout), workspace };
}
it("preserves a literal trailing CR in an LF-listed member", async () => {
const listing =
members.map((path) => (path === bridge ? `${path}\r` : path)).join("\n") +
"\n";
const { result } = await verifyListing(listing);
expect(result).toEqual({
error: `package omitted ${bridge}`,
bridgeParsed: false,
});
});
it("accepts exact bridge entries from a CRLF tar listing and still rejects generated bytecode", async () => {
const ending = "\r\n";
const { result, workspace } = await verifyListing(
members.join(ending) + ending,
);
expect(result).toEqual({
tarball: join(workspace, "pack/fixture.tgz"),
bridgeParsed: true,
});
const rejected = await verifyListing(
[...members, "package/bridge/stale.pyc"].join(ending) + ending,
);
expect(rejected.result).toEqual({
error: "package contained generated Python bytecode",
bridgeParsed: false,
});
});
it("accepts exact bridge entries from an LF tar listing", async () => {
const { result } = await verifyListing(members.join("\n") + "\n");
expect(result.bridgeParsed).toBe(true);
});