Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/ida-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ was established; failed registrations remain unavailable. Correct
launch environment. Restart/reconnect that server after changing its environment;
an export in another shell does not update a running server.

An HTTP registration's `url` must use `http` or `https` without URL credentials,
and its host must be `localhost`, `127.0.0.1`, `[::1]`, or the Docker/Podman
host-gateway name `host.docker.internal` or `host.containers.internal`. Use a
gateway name when REA runs in a container and IDA MCP listens on the container
host. REA refuses every other host.

Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows. Attached profiles, including native mode, refuse universal Mach-O inputs at admission: every slice shares the recorded whole-file hash, so REA cannot verify which slice the GUI loaded. Open a thin file extracted with `lipo -thin` in both IDA and REA, or use headless mode, which analyzes the selected slice.

REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.

## Optional native IDA MCP bridge

IDA Pro and IDA Free can use the separate [ida-free-mcp native plugin](https://github.com/0xshlomil/ida-free-mcp) through an explicit attached registration. Install and start that plugin using its upstream instructions, open the original binary in the IDA GUI, and wait for analysis. The native MCP 1.1.2 release binds `0.0.0.0` without a listen-address setting. Restrict inbound access with the host firewall or use an upstream build that binds only to loopback before starting it. REA's loopback URL restriction does not change the plugin's listener. REA does not install IDA, build the plugin, activate a license, or launch the GUI.
IDA Pro and IDA Free can use the separate [ida-free-mcp native plugin](https://github.com/0xshlomil/ida-free-mcp) through an explicit attached registration. Install and start that plugin using its upstream instructions, open the original binary in the IDA GUI, and wait for analysis. The native MCP 1.1.2 release binds `0.0.0.0` without a listen-address setting. Restrict inbound access with the host firewall or use an upstream build that binds only to loopback before starting it. REA's local URL restriction does not change the plugin's listener. REA does not install IDA, build the plugin, activate a license, or launch the GUI.

```json
{
Expand Down
10 changes: 10 additions & 0 deletions src/ida/IdaConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ describe("IDA registration and MCP result boundaries", () => {
for (const input of [
{ url: "https://example.com/mcp" },
{ url: "http://token@127.0.0.1/mcp" },
{ url: "http://token@host.docker.internal:13337/mcp" },
{ url: "http://host.docker.internal.example.com/mcp" },
{ url: "ftp://host.docker.internal/mcp" },
{ command: "python", mode: "force_gui" },
{ command: "python", workspaceRoot: "relative" },
{ command: "python", args: "server.py" },
Expand All @@ -64,6 +67,13 @@ describe("IDA registration and MCP result boundaries", () => {
}),
);
expect((await read(path)).ok).toBe(true);
for (const url of [
"http://host.docker.internal:13337/mcp",
"http://HOST.CONTAINERS.INTERNAL:13337/mcp",
]) {
await writeFile(path, JSON.stringify({ url }));
expect(await read(path)).toMatchObject({ ok: true, value: { url } });
}
await writeFile(path, "malformed");
expect((await read(path)).ok).toBe(false);
expect((await read(join(root, "absent"))).ok).toBe(false);
Expand Down
10 changes: 9 additions & 1 deletion src/ida/IdaConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export const DEFAULT_IDA_REQUEST_TIMEOUT_MS = 300_000;
const MIN_IDA_REQUEST_TIMEOUT_MS = 1;
/** Node timer ceiling. Larger registration values would be clamped instead of honored. */
export const MAX_IDA_REQUEST_TIMEOUT_MS = 2_147_483_647;
/** Loopback names plus the Docker and Podman gateway names for the container's host. */
const LOCAL_IDA_HTTP_HOSTS: ReadonlySet<string> = new Set([
"localhost",
"127.0.0.1",
"[::1]",
"host.docker.internal",
"host.containers.internal",
]);

const common = {
sensitive: z
Expand Down Expand Up @@ -64,7 +72,7 @@ const registrationSchema = z
const url = new URL(value);
return (
["http:", "https:"].includes(url.protocol) &&
["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) &&
LOCAL_IDA_HTTP_HOSTS.has(url.hostname) &&
url.username === "" &&
url.password === ""
);
Expand Down
Loading