Files
reverse-engineering-anything/tests/boundary/process/packageTarListing.test.ts
T
Ariel Fikru Avicennaandgensokyo-dev cab78de506 feat(ghidra): analyze GameCube/Wii DOL executables through Ghidra (#6240)
* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* fix(ghidra): preserve bridge inventory symbol provenance

* fix(ghidra): align DOL verification with shared workflow contracts

* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* docs(ghidra): qualify the DOL memory window

* refactor(ghidra): own DOL BSS ranges in the parsed header

* docs(ghidra): record real DOL workflows and task costs

* docs(ghidra): record final DOL integration verification

* fix(mcp): share the native procedure search row contract

* docs(ghidra): record public procedure search verification

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 17:56:31 +08:00

98 lines
4.0 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 = " tool_count: " + FULL_CATALOG_IDENTITY.counts.mcp_tools + "\\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);
});