rea/tests/ghidraBridgeTruthfulness.te...

104 lines
3.6 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { beforeAll, describe, expect, it } from "vitest";
let bridgeSource = "";
let queueSource = "";
beforeAll(async () => {
[bridgeSource, queueSource] = await Promise.all([
readFile(
fileURLToPath(
new URL("../bridge/ghidra/ReaGhidraBridge.java", import.meta.url),
),
"utf8",
),
readFile(
fileURLToPath(
new URL("../src/ghidra/GhidraRequestQueue.ts", import.meta.url),
),
"utf8",
),
]);
});
describe("Ghidra bridge truthfulness", () => {
it("owns one persistent decompiler interface for the Program lifetime", () => {
expect(bridgeSource).toContain("private DecompInterface decompiler;");
expect(bridgeSource.match(/new DecompInterface\(\)/gu)).toHaveLength(1);
expect(
bridgeSource.match(/decompiler\.openProgram\(currentProgram\)/gu),
).toHaveLength(1);
expect(bridgeSource).toContain("decompiler.dispose()");
expect(bridgeSource).toContain("DECOMPILE_TIMEOUT_SECONDS = 30");
expect(bridgeSource).toContain(
"decompileFunction(\n function,\n DECOMPILE_TIMEOUT_SECONDS",
);
});
it("projects references and CFG from public Ghidra models", () => {
expect(bridgeSource).toContain("getReferenceManager().getReferencesTo");
expect(bridgeSource).toContain(
"getReferencesFrom(instruction.getAddress())",
);
expect(bridgeSource).toContain(
"new BasicBlockModel(currentProgram, false)",
);
expect(bridgeSource).toContain("block.getDestinations(monitor)");
expect(bridgeSource).toContain(
'result.addProperty("computed", type.isComputed())',
);
expect(bridgeSource).toContain(
'result.addProperty("indirect", type.isIndirect())',
);
expect(bridgeSource).toContain(
'result.addProperty("external", reference.isExternalReference())',
);
expect(bridgeSource).toContain("reference.isEntryPointReference()");
});
it("keeps function classification and targetless-flow uncertainty explicit", () => {
expect(bridgeSource).toContain("function.isThunk()");
expect(bridgeSource).toContain("function.isExternal()");
expect(bridgeSource).toContain('"ghidra-function-manager"');
expect(bridgeSource).toContain(
"Unresolved computed or indirect flows without target addresses",
);
expect(bridgeSource).toContain(
"not original source or Hopper-equivalent text",
);
});
it("serializes a bounded request queue before crossing the socket", () => {
expect(queueSource).toContain("class GhidraRequestQueue");
expect(queueSource).toContain("if (this.#size >= this.maximum)");
expect(queueSource).toContain("if (this.#active) return");
expect(queueSource).toContain(
"remaining = entry.deadline - performance.now()",
);
});
it("binds the Windows fallback to authenticated IPv4 loopback", () => {
expect(bridgeSource).toContain(
'descriptor.transport.equals("unix-socket")',
);
expect(bridgeSource).toContain(
'descriptor.transport.equals("authenticated-loopback-tcp")',
);
expect(bridgeSource).toContain('InetAddress.getByName("127.0.0.1")');
expect(bridgeSource).toContain('endpoint.addProperty("host", "127.0.0.1")');
expect(bridgeSource).toContain("StandardCopyOption.ATOMIC_MOVE");
});
it("does not admit mutation methods into the Java dispatch", () => {
for (const method of [
'case "set_comment"',
'case "set_address_name"',
'case "set_inline_comment"',
'case "set_bookmark"',
])
expect(bridgeSource).not.toContain(method);
});
});