[GH-ISSUE #3184] [Bug] Frida attach accepts and silently ignores spawn arguments #1122

Closed
opened 2026-10-15 18:31:15 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3184

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

CLI

Observed behavior

instrument-with-frida attach 71 --script 'send(1)' --duration-ms 0 --format json --argument ignored accepts the spawn-only argument option, starts the instrumentation workflow, and returns successful Evidence without reporting that it discarded the option. The independently executed compiled CLI returned exit callback status 0 and cleanup_error: null. The recorder received the same attach start input as the control without --argument.

The spawn control forwarded argv: ["selected"]. Complete canonical MCP attach inputs parsed successfully without argv and failed when argv: ["ignored"] alone was added. The verification exercised production CLI parsing, the command handler, the shared service and Evidence projection with recorder provider methods. It did not exercise MCP transport or a real Frida target.

Reproduction

  1. Start from a clean checkout of the stated baseline and prepare the runtime. These setup commands are prerequisites; verification used the already installed dependencies and already built artifacts.
npm ci
npm run build:cached
  1. Create this local recorder fixture. It executes the compiled CLI boundary used by the executable dispatcher and replaces only the Frida provider methods. It also compares otherwise valid canonical MCP inputs with and without arguments.
mkdir -p .repro/home/config .repro/home/cache .repro/home/data
cat > .repro/repro.mjs <<'JS'
import assert from "node:assert/strict";
import { pathToFileURL } from "node:url";
import { resolve } from "node:path";
const checkout = resolve(process.argv[2]);
const { createCli } = await import(pathToFileURL(resolve(checkout, "dist/cli-runtime/cli.js")));
const { FRIDA_TOOL_CONTRACTS } = await import(pathToFileURL(resolve(checkout, "dist/contracts/fridaToolContracts.js")));
const ok = value => ({ ok: true, value });
for (const [mode, target, argument] of [
  ["attach", "71", "ignored"],
  ["attach", "71", undefined],
  ["spawn", "fixture-program", "selected"],
]) {
  const calls = [];
  const provider = {
    async startSession(input) {
      calls.push({ operation: "start", input });
      return ok({ sessionId: "00000000-0000-4000-8000-000000000001", deviceId: "local", target, pid: 71, mode, state: mode === "spawn" ? "paused" : "running" });
    },
    async loadScript() {
      calls.push({ operation: "load" });
      return ok({ scriptId: "00000000-0000-4000-8000-000000000002", sourceKind: "inline", sourcePath: null, sourceSha256: "a".repeat(64), messages: [], messagesTruncated: false });
    },
    async resumeSession() { calls.push({ operation: "resume" }); return ok(null); },
    status() { return undefined; },
    async closeSession() { calls.push({ operation: "close" }); return ok({ targetResumeObserved: false }); },
    async closeAll() { calls.push({ operation: "cleanup" }); },
  };
  let stdout = "";
  let exitCode = 0;
  const args = ["instrument-with-frida", mode, target, "--script", "send(1)", "--duration-ms", "0", "--format", "json"];
  if (argument !== undefined) args.push("--argument", argument);
  await createCli(process.env, undefined, provider).serve(args, {
    env: process.env,
    stdout: text => { stdout += text; },
    exit: code => { exitCode = code; },
  });
  const output = JSON.parse(stdout);
  assert.equal(exitCode, 0);
  assert.ok(output.evidence);
  assert.equal(output.cleanup_error, null);
  assert.equal(calls[0].input.argv === undefined, mode === "attach");
  if (mode === "spawn") assert.deepEqual(calls[0].input.argv, [argument]);
  console.log(JSON.stringify({ args, exitCode, calls, output }));
}
for (const name of ["instrument_with_frida", "start_frida_session"]) {
  const schema = FRIDA_TOOL_CONTRACTS.find(c => c.name === name).inputSchema;
  const attach = { mode: "attach", device_id: "local", pid: 71, ...(name === "instrument_with_frida" ? { source_kind: "inline", source: "send(1)", duration_ms: 0 } : {}) };
  const valid = schema.safeParse(attach);
  const invalid = schema.safeParse({ ...attach, argv: ["ignored"] });
  assert.equal(valid.success, true);
  assert.equal(invalid.success, false);
  console.log(JSON.stringify({ schema: name, attachWithoutArgv: valid.success, attachWithArgv: invalid.success, issues: invalid.error.issues }));
}
JS
  1. Run the fixture from the checkout root with disposable home and configuration directories.
env HOME="$PWD/.repro/home" XDG_CONFIG_HOME="$PWD/.repro/home/config" XDG_CACHE_HOME="$PWD/.repro/home/cache" XDG_DATA_HOME="$PWD/.repro/home/data" node .repro/repro.mjs .
  1. Inspect the first JSON record. Its command is instrument-with-frida attach 71 --script 'send(1)' --duration-ms 0 --format json --argument ignored. It returns successful Evidence and calls the recorder without argv. The next attach record omits the option and produces the same start input. The spawn record preserves the supplied argument. The final two records show that adding arguments causes the otherwise valid MCP schemas to reject attach input.

Expected behavior

Attach mode should reject --argument with an invalid-input diagnostic identifying the incompatible option before starting instrumentation. The CLI help describes --argument as "Arguments passed to a spawned target". docs/tool-design.md, under "Define the contract", requires strict inputs and removal of ignored options. docs/cli.md:3 states that CLI commands use the same application workflows and evidence contracts as MCP. The shared one-shot contract in src/contracts/fridaToolContracts.ts:268 and its attach predicate at lines 284-287 require argv to be absent in attach mode.

Impact and scope

This is a P2 input-validation defect affecting CLI callers who combine attach mode with spawn arguments. The CLI begins the instrumentation workflow instead of reporting the incompatible caller choice. Omitting --argument in attach mode is a demonstrated workaround. The spawn control preserves arguments when the caller intends to launch a program.

The observation establishes validation and dispatch behavior with a recorder provider. No real process was launched, attached or injected, and no evidence establishes severe harm or a security boundary crossing.

REA version

6.4.0 (main at 5830088520)

Runtime and platform

Node.js v26.10.0; macOS. The verifier did not record the OS version or architecture.

Target and artifact context

Synthetic PID 71 and program name fixture-program; inline source send(1); prepared compiled artifacts from the stated baseline. The fixture substitutes Frida provider methods and requires no real target or Frida server. The install and build commands above are clean-checkout prerequisites; the independent verification used already installed dependencies and already built artifacts.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Reduced observed results from the recorder and schema comparisons:

attach with --argument ignored: exit callback 0; successful Evidence; cleanup_error null
start input: {"deviceId":"local","mode":"attach","pid":71,"source":{"sourceKind":"inline","source":"send(1)"},"durationMs":0}
provider operations: start, load, close, cleanup
attach without --argument: same start input; success
spawn with --argument selected: argv ["selected"]; resume called; success
instrument_with_frida schema: attach without argv accepted; with argv rejected
start_frida_session schema: attach without argv accepted; with argv rejected

Typed outcome or recovery expected

Return an invalid-input diagnostic that identifies --argument as incompatible with attach mode, and make no provider calls for that request. Attach without the option must continue to return successful Evidence. Spawn with the option must continue to forward its argument array, and canonical MCP attach inputs containing argv must remain rejected.

Analysis or suspected cause

Source-derived at the stated baseline: src/cli/fridaCommands.ts:194 accepts an optional argument array. Its option refinements at lines 206-214 check device selection and remote credentials without checking the command mode. The attach input construction at lines 241-251 omits the supplied argument option, while the spawn branch at lines 256-258 maps it to argv. The recorder observation confirms that the resulting attach input reaches provider dispatch without argv.

Source-derived contract comparison: src/contracts/fridaToolContracts.ts:284 requires argv === undefined in attach mode, and docs/tool-design.md:48 requires strict inputs and preservation of meaningful choices. The fixture follows the existing CLI provider-injection pattern in tests/boundary/cli/fridaCli.test.ts:8; it isolates input handling from native-engine availability.

Additional context

Related closed reports #2213 and #2209 cover missing details on existing input rejections and unreadable script-file validation, respectively; this report concerns a spawn-only argument combination that the CLI accepts and discards despite a valid inline script.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3184 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area CLI ### Observed behavior `instrument-with-frida attach 71 --script 'send(1)' --duration-ms 0 --format json --argument ignored` accepts the spawn-only argument option, starts the instrumentation workflow, and returns successful Evidence without reporting that it discarded the option. The independently executed compiled CLI returned exit callback status 0 and `cleanup_error: null`. The recorder received the same attach start input as the control without `--argument`. The spawn control forwarded `argv: ["selected"]`. Complete canonical MCP attach inputs parsed successfully without `argv` and failed when `argv: ["ignored"]` alone was added. The verification exercised production CLI parsing, the command handler, the shared service and Evidence projection with recorder provider methods. It did not exercise MCP transport or a real Frida target. ### Reproduction 1. Start from a clean checkout of the stated baseline and prepare the runtime. These setup commands are prerequisites; verification used the already installed dependencies and already built artifacts. ```sh npm ci npm run build:cached ``` 2. Create this local recorder fixture. It executes the compiled CLI boundary used by the executable dispatcher and replaces only the Frida provider methods. It also compares otherwise valid canonical MCP inputs with and without arguments. ```sh mkdir -p .repro/home/config .repro/home/cache .repro/home/data cat > .repro/repro.mjs <<'JS' import assert from "node:assert/strict"; import { pathToFileURL } from "node:url"; import { resolve } from "node:path"; const checkout = resolve(process.argv[2]); const { createCli } = await import(pathToFileURL(resolve(checkout, "dist/cli-runtime/cli.js"))); const { FRIDA_TOOL_CONTRACTS } = await import(pathToFileURL(resolve(checkout, "dist/contracts/fridaToolContracts.js"))); const ok = value => ({ ok: true, value }); for (const [mode, target, argument] of [ ["attach", "71", "ignored"], ["attach", "71", undefined], ["spawn", "fixture-program", "selected"], ]) { const calls = []; const provider = { async startSession(input) { calls.push({ operation: "start", input }); return ok({ sessionId: "00000000-0000-4000-8000-000000000001", deviceId: "local", target, pid: 71, mode, state: mode === "spawn" ? "paused" : "running" }); }, async loadScript() { calls.push({ operation: "load" }); return ok({ scriptId: "00000000-0000-4000-8000-000000000002", sourceKind: "inline", sourcePath: null, sourceSha256: "a".repeat(64), messages: [], messagesTruncated: false }); }, async resumeSession() { calls.push({ operation: "resume" }); return ok(null); }, status() { return undefined; }, async closeSession() { calls.push({ operation: "close" }); return ok({ targetResumeObserved: false }); }, async closeAll() { calls.push({ operation: "cleanup" }); }, }; let stdout = ""; let exitCode = 0; const args = ["instrument-with-frida", mode, target, "--script", "send(1)", "--duration-ms", "0", "--format", "json"]; if (argument !== undefined) args.push("--argument", argument); await createCli(process.env, undefined, provider).serve(args, { env: process.env, stdout: text => { stdout += text; }, exit: code => { exitCode = code; }, }); const output = JSON.parse(stdout); assert.equal(exitCode, 0); assert.ok(output.evidence); assert.equal(output.cleanup_error, null); assert.equal(calls[0].input.argv === undefined, mode === "attach"); if (mode === "spawn") assert.deepEqual(calls[0].input.argv, [argument]); console.log(JSON.stringify({ args, exitCode, calls, output })); } for (const name of ["instrument_with_frida", "start_frida_session"]) { const schema = FRIDA_TOOL_CONTRACTS.find(c => c.name === name).inputSchema; const attach = { mode: "attach", device_id: "local", pid: 71, ...(name === "instrument_with_frida" ? { source_kind: "inline", source: "send(1)", duration_ms: 0 } : {}) }; const valid = schema.safeParse(attach); const invalid = schema.safeParse({ ...attach, argv: ["ignored"] }); assert.equal(valid.success, true); assert.equal(invalid.success, false); console.log(JSON.stringify({ schema: name, attachWithoutArgv: valid.success, attachWithArgv: invalid.success, issues: invalid.error.issues })); } JS ``` 3. Run the fixture from the checkout root with disposable home and configuration directories. ```sh env HOME="$PWD/.repro/home" XDG_CONFIG_HOME="$PWD/.repro/home/config" XDG_CACHE_HOME="$PWD/.repro/home/cache" XDG_DATA_HOME="$PWD/.repro/home/data" node .repro/repro.mjs . ``` 4. Inspect the first JSON record. Its command is `instrument-with-frida attach 71 --script 'send(1)' --duration-ms 0 --format json --argument ignored`. It returns successful Evidence and calls the recorder without `argv`. The next attach record omits the option and produces the same start input. The spawn record preserves the supplied argument. The final two records show that adding arguments causes the otherwise valid MCP schemas to reject attach input. ### Expected behavior Attach mode should reject `--argument` with an invalid-input diagnostic identifying the incompatible option before starting instrumentation. The CLI help describes `--argument` as "Arguments passed to a spawned target". `docs/tool-design.md`, under "Define the contract", requires strict inputs and removal of ignored options. `docs/cli.md:3` states that CLI commands use the same application workflows and evidence contracts as MCP. The shared one-shot contract in `src/contracts/fridaToolContracts.ts:268` and its attach predicate at lines 284-287 require `argv` to be absent in attach mode. ### Impact and scope This is a P2 input-validation defect affecting CLI callers who combine attach mode with spawn arguments. The CLI begins the instrumentation workflow instead of reporting the incompatible caller choice. Omitting `--argument` in attach mode is a demonstrated workaround. The spawn control preserves arguments when the caller intends to launch a program. The observation establishes validation and dispatch behavior with a recorder provider. No real process was launched, attached or injected, and no evidence establishes severe harm or a security boundary crossing. ### REA version 6.4.0 (main at 58300885207133422af235e0f2b10658cf958b03) ### Runtime and platform Node.js v26.10.0; macOS. The verifier did not record the OS version or architecture. ### Target and artifact context Synthetic PID 71 and program name `fixture-program`; inline source `send(1)`; prepared compiled artifacts from the stated baseline. The fixture substitutes Frida provider methods and requires no real target or Frida server. The install and build commands above are clean-checkout prerequisites; the independent verification used already installed dependencies and already built artifacts. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Reduced observed results from the recorder and schema comparisons: ```text attach with --argument ignored: exit callback 0; successful Evidence; cleanup_error null start input: {"deviceId":"local","mode":"attach","pid":71,"source":{"sourceKind":"inline","source":"send(1)"},"durationMs":0} provider operations: start, load, close, cleanup attach without --argument: same start input; success spawn with --argument selected: argv ["selected"]; resume called; success instrument_with_frida schema: attach without argv accepted; with argv rejected start_frida_session schema: attach without argv accepted; with argv rejected ``` ### Typed outcome or recovery expected Return an invalid-input diagnostic that identifies `--argument` as incompatible with attach mode, and make no provider calls for that request. Attach without the option must continue to return successful Evidence. Spawn with the option must continue to forward its argument array, and canonical MCP attach inputs containing `argv` must remain rejected. ### Analysis or suspected cause Source-derived at the stated baseline: `src/cli/fridaCommands.ts:194` accepts an optional argument array. Its option refinements at lines 206-214 check device selection and remote credentials without checking the command mode. The attach input construction at lines 241-251 omits the supplied argument option, while the spawn branch at lines 256-258 maps it to `argv`. The recorder observation confirms that the resulting attach input reaches provider dispatch without `argv`. Source-derived contract comparison: `src/contracts/fridaToolContracts.ts:284` requires `argv === undefined` in attach mode, and `docs/tool-design.md:48` requires strict inputs and preservation of meaningful choices. The fixture follows the existing CLI provider-injection pattern in `tests/boundary/cli/fridaCli.test.ts:8`; it isolates input handling from native-engine availability. ### Additional context Related closed reports #2213 and #2209 cover missing details on existing input rejections and unreadable script-file validation, respectively; this report concerns a spawn-only argument combination that the CLI accepts and discards despite a valid inline script.
bsakaguchi 2026-10-15 18:31:15 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Delivered on main in fffb07419.

Frida CLI attach silently discarded --argument even though the canonical contract rejects attach argv. CLI admission now rejects it before script-file or provider work.

Validation: Frida CLI negative admission and spawn-argument controls. Full exact-revision qualification passed.

<!-- gh-comment-id:6177625504 --> @morluto commented on GitHub (Oct 14, 2026): Delivered on main in [fffb07419](https://github.com/morluto/rea/commit/fffb0741936067530d1ce9777461cd53e87398c2). Frida CLI attach silently discarded --argument even though the canonical contract rejects attach argv. CLI admission now rejects it before script-file or provider work. Validation: Frida CLI negative admission and spawn-argument controls. Full exact-revision [qualification](https://github.com/morluto/rea/actions/runs/38629313022) passed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1122
No description provided.