mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
199 lines
5.8 KiB
TypeScript
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,
|
|
});
|
|
});
|