Files
rea/tests/boundary/cli/reverseEngineeringTargetInput.test.ts
T

152 lines
4.2 KiB
TypeScript
Raw Normal View History

import { chmod, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { analysisErrorProjectionSchema } from "../../../src/contracts/errorSchemas.js";
import { REVERSE_ENGINEERING_TOOL_CONTRACTS } from "../../../src/contracts/reverseEngineeringToolContracts.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest(
"reports missing and directory objdump inputs with structured reasons",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-objdump-invalid-target-");
const missing = join(root, "missing.bin");
const directory = root;
for (const [path, expectedReason] of [
[missing, "not found"],
[directory, "not a regular file"],
] as const) {
const result = await cli.run({
arguments: [
"inspect-with-objdump",
path,
"symbols",
"--format",
"json",
],
cwd: root,
});
expect(result.exitCode).toBe(1);
expect(analysisErrorProjectionSchema.safeParse(result.json).success).toBe(
true,
);
expect(result.json).toMatchObject({
code: "invalid_request",
category: "invalid_input",
retryable: false,
message: expect.stringContaining(path),
details: {
operation: "inspect_with_objdump",
issues: [
{
path: ["path"],
reason: "invalid_value",
message: expect.stringContaining(expectedReason),
},
],
},
});
}
},
);
cliTest(
"passes Rizin decoder and map flags before the artifact",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-rizin-load-flags-");
const provider = join(root, "argv-rizin");
const artifact = join(root, "flash.bin");
await writeFile(artifact, "flash");
await writeFile(
provider,
`#!${process.execPath}\nprocess.stdout.write(JSON.stringify(process.argv.slice(2)));\n`,
);
await chmod(provider, 0o755);
const result = await cli.run({
arguments: [
"execute-rizin-command",
artifact,
"oml",
"--asm-arch",
"riscv",
"--asm-bits",
"32",
"--asm-cpu",
"rv32imc",
"--base-address",
"0x42000000",
"--format",
"json",
],
cwd: root,
environment: { REA_RIZIN_COMMAND: provider },
});
expect(result.exitCode, result.stderr).toBe(0);
const rizinContract = REVERSE_ENGINEERING_TOOL_CONTRACTS.find(
(contract) => contract.name === "execute_rizin_command",
);
if (rizinContract === undefined)
throw new Error("execute_rizin_command contract is missing");
const evidence = rizinContract.outputSchema.parse(result.json);
const argv = [
"-N",
"-q",
"-e",
"scr.color=0",
"-a",
"riscv",
"-b",
"32",
"-e",
"asm.cpu=rv32imc",
"-n",
"-m",
"0x42000000",
"-c",
"oml",
artifact,
];
expect(JSON.parse(evidence.normalized_result.stdout)).toEqual(argv);
expect(evidence.parameters).toMatchObject({
asm_arch: "riscv",
asm_bits: 32,
asm_cpu: "rv32imc",
base_address: "0x42000000",
command_arguments: argv,
});
},
);
cliTest(
"reports the rejected Rizin command before inspecting the target",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-rizin-invalid-command-");
const result = await cli.run({
arguments: [
"execute-rizin-command",
join(root, "unopened.bin"),
"iI\nq",
"--format",
"json",
],
cwd: root,
});
expect(result.exitCode).toBe(1);
expect(analysisErrorProjectionSchema.parse(result.json)).toMatchObject({
code: "invalid_request",
details: {
operation: "execute_rizin_command",
issues: [
{
path: ["command"],
reason: "invalid_value",
message: "A single command line is required",
},
],
},
});
},
);