mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(contracts): reject NUL bytes in Frida, Rizin, and GDB local path inputs (#3575)
Route the caller-selected local path fields in load_frida_script, instrument_with_frida, start_frida_session, start_rizin_debug_session, and start_gdb_session through the shared localPathStringSchema so a NUL byte is refused at the advertised contract boundary instead of reaching the filesystem or a provider subprocess and surfacing as a raw error. Extend the OS-invalid-local-input acceptance harness to the tools that were missing from it.
This commit is contained in:
1 parent
10aa74dfed
commit
a732c859dd
4 files changed
+42
-9
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import type { ToolContract } from "./toolContractTypes.js";
|
||||
import { evidenceResultOf } from "./toolOutputSchemaPrimitives.js";
|
||||
import { toolContractMetadata } from "./toolEffects.js";
|
||||
@@ -122,7 +123,7 @@ export const FRIDA_TOOL_CONTRACTS = [
|
||||
mode: z.enum(["attach", "spawn"]),
|
||||
...deviceSelector.shape,
|
||||
pid: z.number().int().positive().optional(),
|
||||
program: z.string().min(1).optional(),
|
||||
program: localPathStringSchema.optional(),
|
||||
argv: z.array(z.string()).optional(),
|
||||
})
|
||||
.refine(
|
||||
@@ -156,7 +157,7 @@ export const FRIDA_TOOL_CONTRACTS = [
|
||||
session_id: sessionId,
|
||||
source_kind: z.enum(["inline", "file"]),
|
||||
source: z.string().optional(),
|
||||
path: z.string().min(1).optional(),
|
||||
path: localPathStringSchema.optional(),
|
||||
})
|
||||
.refine(
|
||||
(value) =>
|
||||
@@ -271,11 +272,11 @@ export const FRIDA_TOOL_CONTRACTS = [
|
||||
mode: z.enum(["attach", "spawn"]),
|
||||
...deviceSelector.shape,
|
||||
pid: z.number().int().positive().optional(),
|
||||
program: z.string().min(1).optional(),
|
||||
program: localPathStringSchema.optional(),
|
||||
argv: z.array(z.string()).optional(),
|
||||
source_kind: z.enum(["inline", "file"]),
|
||||
source: z.string().optional(),
|
||||
path: z.string().min(1).optional(),
|
||||
path: localPathStringSchema.optional(),
|
||||
duration_ms: z.number().int().min(0).max(60_000).default(1_000),
|
||||
})
|
||||
.refine(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import type { ToolContract } from "./toolContractTypes.js";
|
||||
import { evidenceResultOf } from "./toolOutputSchemaPrimitives.js";
|
||||
import { toolContractMetadata } from "./toolEffects.js";
|
||||
@@ -29,9 +30,7 @@ export const GDB_TOOL_CONTRACTS = [
|
||||
description:
|
||||
"Start a BYO GDB process in MI3 mode when available, suppress startup files, and disable auto-loading. Persistent GDB sessions are available only on Linux, where REA can verify live inferior output descriptors through /proc before detaching; macOS descriptor verification is not implemented, and Windows Job Object cleanup can terminate debugger inferiors. Optionally set inferior_tty to a caller-selected terminal for future inferior runs; REA verifies the setting through GDB/MI before the session becomes ready. The selected terminal must remain available after GDB exits. A live inferior can be detached during close only while this durable output sink remains verified. The session persists in this REA MCP server process and receives an explicit session ID.",
|
||||
inputSchema: z.strictObject({
|
||||
inferior_tty: z
|
||||
.string()
|
||||
.min(1)
|
||||
inferior_tty: localPathStringSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Caller-selected terminal path for future inferior runs. GDB must report the exact configured path before the session is ready.",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import type { ToolContract } from "./toolContractTypes.js";
|
||||
import { evidenceResultOf } from "./toolOutputSchemaPrimitives.js";
|
||||
import { toolContractMetadata } from "./toolEffects.js";
|
||||
@@ -24,8 +25,8 @@ export const RIZIN_DEBUG_TOOL_CONTRACTS = [
|
||||
description:
|
||||
"Start a persistent Rizin debugger session using `-N` to suppress user settings and scripts while retaining plugins, `-0` for NUL framing, and `-d` with an optional caller-selected IO debugger backend. Requires a caller-selected Linux TTY for inferior stdout and stderr; the target may write directly to that terminal while running and after detach. REA verifies both destinations and `dbg.exitkills=false` before detaching. Backend and target capabilities depend on the installed build. The session lives in this REA MCP process. Persistent sessions require Linux `/proc` descriptor readback. In a shared MCP server, starting this session retires any active binary-analysis session; after target effects have settled, call open_binary before further analysis. Close an active Ghidra session explicitly first.",
|
||||
inputSchema: z.strictObject({
|
||||
path: z.string().min(1),
|
||||
inferior_tty: z.string().min(1),
|
||||
path: localPathStringSchema,
|
||||
inferior_tty: localPathStringSchema,
|
||||
backend: z.string().min(1).optional(),
|
||||
}),
|
||||
outputSchema: z.strictObject({
|
||||
|
||||
@@ -156,6 +156,38 @@ const pathRequests: readonly [string, Record<string, unknown>][] = [
|
||||
import_map: { path: nul, base_url: "http://127.0.0.1/a" },
|
||||
},
|
||||
],
|
||||
["start_frida_session", { mode: "spawn", device_id: "local", program: nul }],
|
||||
[
|
||||
"load_frida_script",
|
||||
{
|
||||
session_id: "00000000-0000-4000-8000-000000000001",
|
||||
source_kind: "file",
|
||||
path: nul,
|
||||
},
|
||||
],
|
||||
[
|
||||
"instrument_with_frida",
|
||||
{
|
||||
mode: "spawn",
|
||||
device_id: "local",
|
||||
program: nul,
|
||||
source_kind: "inline",
|
||||
source: "send(1);",
|
||||
},
|
||||
],
|
||||
[
|
||||
"instrument_with_frida",
|
||||
{
|
||||
mode: "spawn",
|
||||
device_id: "local",
|
||||
program: "/bin/true",
|
||||
source_kind: "file",
|
||||
path: nul,
|
||||
},
|
||||
],
|
||||
["start_rizin_debug_session", { path: nul, inferior_tty: "/dev/pts/7" }],
|
||||
["start_rizin_debug_session", { path: "/tmp/program", inferior_tty: nul }],
|
||||
["start_gdb_session", { inferior_tty: nul }],
|
||||
];
|
||||
|
||||
it("rejects OS-invalid local inputs through the real advertised MCP contracts", async () => {
|
||||
|
||||
Reference in new issue
Block a user