Files
reverse-engineering-anything/src/contracts/toolEffects.test.ts
T

199 lines
5.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { TOOL_CONTRACTS } from "./toolContracts.js";
import {
TOOL_EFFECTS,
annotationsFromEffects,
toolSessionEffects,
type ToolEffects,
} from "./toolEffects.js";
describe("tool effects", () => {
it("declares the web bundle worker's private file and child process effects", () => {
const contract = TOOL_CONTRACTS.find(
({ name }) => name === "analyze_web_bundle",
);
expect(contract?.effects).toMatchObject({
mutatesSession: true,
accessesNetwork: true,
launchesProcess: true,
writesFilesystem: true,
executesForeignCode: false,
mutatesTarget: false,
});
});
it("audits every public tool exactly once", () => {
expect(Object.keys(TOOL_EFFECTS).sort()).toEqual(
TOOL_CONTRACTS.map(({ name }) => name).sort(),
);
});
it("marks foreign execution and device mutation as destructive", () => {
const destructive = TOOL_CONTRACTS.filter(
({ effects }) => effects.executesForeignCode || effects.mutatesDevice,
);
expect(destructive.map(({ name }) => name).sort()).toEqual([
"act_on_build_computer",
"capture_browser_scenario",
"capture_electron_scenario",
"capture_native_ui_scenario",
"capture_process_scenario",
"close_frida_session",
"close_gdb_session",
"close_rizin_debug_session",
"cutter_command",
"execute_rizin_command",
"gdb_console",
"install_adb_package",
"instrument_with_frida",
"load_frida_script",
"observe_native_calls",
"push_adb_file",
"resume_frida_session",
"rizin_debug_command",
"start_adb_activity",
"start_adb_app",
"start_build_capture",
"start_build_computer",
"start_rizin_debug_session",
"stop_adb_app",
"uninstall_adb_package",
]);
for (const contract of destructive)
expect(contract.annotations, contract.name).toMatchObject({
readOnlyHint: false,
destructiveHint: true,
});
});
it("derives destructiveHint from each destructive effect alone", () => {
// Keep annotation algebra independent of any operation's audited effects.
const base: ToolEffects = {
mutatesTarget: false,
mutatesSession: false,
writesFilesystem: false,
launchesProcess: false,
accessesNetwork: false,
changesUiState: false,
mayDiscardData: false,
executesForeignCode: false,
mutatesDevice: false,
idempotent: true,
};
expect(annotationsFromEffects(base)).toEqual({
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
});
for (const flag of [
"mayDiscardData",
"executesForeignCode",
"mutatesDevice",
] as const)
expect(annotationsFromEffects({ ...base, [flag]: true }), flag).toEqual({
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false,
});
});
it("marks automatic GDB detach as destructive target execution", () => {
const effects = TOOL_EFFECTS["close_gdb_session"];
if (effects === undefined)
throw new Error("close_gdb_session is unaudited");
expect(effects).toMatchObject({
mutatesTarget: true,
executesForeignCode: true,
});
expect(annotationsFromEffects(effects).destructiveHint).toBe(true);
});
it("publishes the advertised annotations derived from canonical effects", () => {
for (const contract of TOOL_CONTRACTS)
expect(contract.annotations, contract.name).toEqual(
annotationsFromEffects(contract.effects),
);
});
});
it("advertises IDA provider effects while retaining session bases", () => {
for (const name of [
"procedure_pseudo_code",
"procedure_assembly",
"read_function_instructions",
"analyze_function",
]) {
expect(TOOL_EFFECTS[name], name).toMatchObject({
mutatesSession: true,
changesUiState: true,
accessesNetwork: true,
});
expect(toolSessionEffects(name), name).toMatchObject({
mutatesSession: true,
changesUiState: false,
accessesNetwork: false,
});
const contract = TOOL_CONTRACTS.find(
(candidate) => candidate.name === name,
);
expect(contract?.annotations, name).toMatchObject({
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
});
}
for (const name of [
"batch_decompile",
"decompile_all",
"trace_native_values",
"inspect_native_api",
]) {
expect(TOOL_EFFECTS[name], name).toMatchObject({
changesUiState: true,
accessesNetwork: true,
});
expect(toolSessionEffects(name), name).toMatchObject({
changesUiState: false,
accessesNetwork: false,
});
}
});
it("audits network access during lifecycle cleanup across provider changes", () => {
for (const name of [
"open_binary",
"open_bundle_resources",
"close_binary",
"reset_investigation",
"start_rizin_debug_session",
"close_rizin_debug_session",
"close_gdb_session",
]) {
expect(toolSessionEffects(name).accessesNetwork, name).toBe(true);
expect(TOOL_EFFECTS[name]?.accessesNetwork, name).toBe(true);
expect(
TOOL_CONTRACTS.find((contract) => contract.name === name)?.annotations
.openWorldHint,
name,
).toBe(true);
}
});
it("advertises signature inspection's temporary snapshot filesystem writes", () => {
expect(TOOL_EFFECTS.inspect_signature).toMatchObject({
writesFilesystem: true,
mutatesTarget: false,
launchesProcess: true,
});
});
it("advertises inspect_macho's temporary private operand filesystem writes", () => {
expect(TOOL_EFFECTS.inspect_macho).toMatchObject({
writesFilesystem: true,
mutatesTarget: false,
launchesProcess: true,
});
});