[GH-ISSUE #7668] [Bug] Invalid search-bytes patterns are hidden by target-admission errors #4593

Closed
opened 2026-10-31 00:51:06 +00:00 by bsakaguchi · 0 comments
Owner

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

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

With an eleven-byte text fixture, node scripts/rea.mjs search-bytes ./not-a-binary.bin '??' --json exits 1 with target_unavailable, category unsupported_target, and message Cannot open artifact: unsupported binary format. The response advises selecting a supported target and does not identify the unconstrained pattern. Replacing ?? with ZZ, an empty string, or AB --mask FFFF produces the same target-format error.

The corresponding production MCP stdio calls reject these four arguments as invalid_request / invalid_input, naming the invalid pattern or unequal mask in their diagnostic text. A valid { "pattern": "AB" } control reaches the normal no-open-target error. No native target or provider session was opened.

Reproduction

  1. Start at the built baseline's repository root with its locked dependencies and Node.js v24.22.1.
  2. Run this block. It creates the text target inline, isolates product state, puts inert package-manager fixtures first on PATH, invokes the production CLI and MCP stdio entrypoints, and removes its temporary files on completion.
node --input-type=module <<'JS'
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
const cli = resolve('scripts/rea.mjs');
const work = mkdtempSync(join(tmpdir(), 'rea-byte-pattern-'));
const env = { PATH: '', NO_COLOR: '1' };
for (const [key, dir] of Object.entries({ HOME: 'home', XDG_CONFIG_HOME: 'config',
  XDG_CACHE_HOME: 'cache', XDG_DATA_HOME: 'data', XDG_STATE_HOME: 'state',
  TMPDIR: 'runtime', npm_config_prefix: 'prefix' })) {
  env[key] = join(work, dir);
  mkdirSync(env[key]);
}
const bin = join(work, 'bin');
mkdirSync(bin);
for (const name of ['npx', 'npm', 'bun'])
  writeFileSync(join(bin, name), "#!/bin/sh\nprintf '0.0.0\\n'\n", { mode: 0o755 });
env.PATH = `${bin}:${process.env.PATH}`;
writeFileSync(join(work, 'not-a-binary.bin'), 'hello world');
const cases = [{ pattern: '??' }, { pattern: 'ZZ' }, { pattern: '' },
  { pattern: 'AB', mask: 'FFFF' }];
const client = new Client({ name: 'byte-pattern-verifier', version: '1' });
const transport = new StdioClientTransport({ command: process.execPath,
  args: [cli, 'mcp'], env, cwd: work, stderr: 'pipe' });
transport.stderr?.on('data', () => {});
try {
  console.log('version:', spawnSync(process.execPath, [cli, '--version'],
    { cwd: work, env, encoding: 'utf8' }).stdout.trim());
  for (const input of cases) {
    const args = [cli, 'search-bytes', './not-a-binary.bin', input.pattern];
    if (input.mask !== undefined) args.push('--mask', input.mask);
    const result = spawnSync(process.execPath, [...args, '--json'],
      { cwd: work, env, encoding: 'utf8', timeout: 15000 });
    const error = JSON.parse(result.stdout);
    console.log(JSON.stringify({ interface: 'CLI', input, exit: result.status,
      code: error.code, category: error.category, message: error.message,
      constraint: error.details?.constraint }));
  }
  const schema = JSON.parse(spawnSync(process.execPath,
    [cli, 'search-bytes', '--schema', '--json'],
    { cwd: work, env, encoding: 'utf8' }).stdout);
  console.log(JSON.stringify({ cliPattern: schema.args.properties.pattern,
    cliMask: schema.options.properties.mask }));
  await client.connect(transport);
  const definition = (await client.listTools()).tools.find(t => t.name === 'search_bytes');
  console.log(JSON.stringify({ mcpPattern: definition.inputSchema.properties.pattern,
    mcpMask: definition.inputSchema.properties.mask }));
  for (const input of [...cases, { pattern: 'AB' }]) {
    const result = await client.callTool({ name: 'search_bytes', arguments: input });
    console.log(JSON.stringify({ interface: 'MCP', input, isError: result.isError,
      content: result.content }));
  }
  console.log('ping:', JSON.stringify(await client.ping()));
} finally {
  await client.close();
  await transport.close();
  rmSync(work, { recursive: true, force: true });
}
JS

Each CLI case reproduced in two initial public-dispatcher replays. The complete reduced block above was also executed successfully. The initial MCP replay and the reduced block both returned the expected four input errors, the no-open-target control, and a successful ping.

Expected behavior

Reject an invalid byte-search query at CLI request admission, before accessing its target, and identify the pattern or mask that needs correction. The canonical rules require complete hexadecimal byte pairs or whole-byte wildcards, at least one constrained bit, and an equal-length hexadecimal mask when supplied.

The project's CLI translation policy requires reuse of canonical semantic fields and aggregate admission before target access when positional and option schemas cannot express their relationship. A positional pattern and optional mask need that aggregate check. Contract locators are listed under Evidence.

Descriptive string advertisements are supported: neither CLI nor MCP needs an advertised regular expression to establish correct runtime validation.

Impact and scope

P3: these malformed requests correctly fail, but the CLI's first diagnostic directs the caller toward target repair without identifying the invalid query. The demonstrated effect is diagnostic precedence and correction guidance. No valid search failure, wrong match result, target mutation, unwanted successful open, provider startup cost, or failed caller recovery was demonstrated.

The executed scope is the built source baseline on macOS with an unsupported text fixture and a target-free MCP session. Supplying a valid byte pattern avoids the pattern defect; the text fixture will still correctly fail format admission. MCP provides an observed way to check these malformed arguments without opening a target. Behavior after successful native admission and other platforms remains untested.

REA version

Exact node scripts/rea.mjs --version stdout:

6.8.0

Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; Darwin arm64. Used the built runtime and locked MCP client. No external analysis provider was used.

Target and artifact context

An authored regular file containing exactly hello world, eleven ASCII bytes, with no supported executable header. MCP calls use no open target. Package-manager commands are fixture executables, preventing package discovery from installing anything.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

The executed CLI cases all returned exit 1 and these fields:

{
  "code": "target_unavailable",
  "category": "unsupported_target",
  "message": "Cannot open artifact: unsupported binary format.",
  "details": {
    "reason": "unsupported binary format",
    "constraint": "file_format_rejected"
  }
}

The selected fixture path is omitted from this excerpt. CLI stderr was empty. The response's remediation asks the caller to review the file-format reason and select a supported, well-formed target.

For the same four arguments, production MCP stdio returned isError: true, invalid_request / invalid_input, and these diagnostic messages:

Input MCP issue message
{"pattern":"??"} pattern: Pattern must constrain at least one bit
{"pattern":"ZZ"} pattern: Use hexadecimal byte pairs or ?? wildcards
{"pattern":""} pattern: Use hexadecimal byte pairs or ?? wildcards
{"pattern":"AB","mask":"FFFF"} mask: Mask length must equal pattern length

The valid AB control instead returned target_unavailable with No app is open...; subsequent MCP ping returned {}. MCP issue paths are root paths at this baseline; the field names above occur in diagnostic prose.

search-bytes --schema and its JSON variant show plain string fields for the pattern and mask. MCP tools/list also advertises these fields as strings. This discovery result alone is not evidence of missing runtime validation.

Contract and source references at the baseline:

  • docs/tool-design.md:339-346: CLI semantic-field parity and aggregate admission before target access for relationships split across positional arguments and options.
  • docs/cli.md:3-5: shared application workflows and evidence contracts across CLI and MCP.
  • docs/mcp-contracts.md:1290-1294: descriptive byte-pattern advertisements are intentional; canonical MCP validation rejects malformed or unconstrained patterns before dispatch.
  • src/domain/native/bytePattern.ts:10-24,65-99: canonical syntax, mask alignment, and effective constrained-bit rules.
  • src/contracts/hopperMemoryContracts.ts:16-24 and src/ghidra/GhidraInventoryValues.ts:92-101: shared semantic refinement on public MCP and provider input contracts.
  • src/cli/coreNativeCommands.ts:35-47,58-73: separate plain-string pattern/mask inputs forwarded to the direct analysis workflow.
  • src/application/DirectAnalysis.ts:454-465: session open precedes operation execution and its admission failure returns immediately.

Analysis or suspected cause

Source-derived: the CLI registration lacks the aggregate canonical byte-pattern check before entering the target-opening workflow. This explains why an unsupported target masks the query correction. The reproduction does not establish that later provider validation is absent, or that invalid patterns can produce successful search results.

Additional context

Duplicate searches covered open and closed issues, open and merged PRs, and issue comments. Related #5257 fixes large-pattern MCP regex stack overflow; the small MCP inputs here reject normally, so no regression of that fix is claimed. #7436 concerns empty target paths, while this reproduction supplies a nonempty path and invalid query. #7661 and #7572 concern correction metadata in input errors; restoring those fields would not move byte-pattern admission before target access. Original feature PR #2341 and alias PR #2148, including comments, contain no same-closure report or fix.

Triage assessment

  • Impact level: P3
  • Assessment status: supported
  • Impact basis: Malformed requests remain refused, but target-format guidance hides their query corrections; no consequential effect on valid results or lifecycle behavior was demonstrated.
  • Workaround status: available
  • Workaround basis: The observed target-free MCP calls report each invalid pattern or mask, permitting correction without target admission or a code change; this does not repair CLI error precedence.
Originally created by @coygeek on GitHub (Oct 28, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7668 ### 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 With an eleven-byte text fixture, `node scripts/rea.mjs search-bytes ./not-a-binary.bin '??' --json` exits 1 with `target_unavailable`, category `unsupported_target`, and message `Cannot open artifact: unsupported binary format.` The response advises selecting a supported target and does not identify the unconstrained pattern. Replacing `??` with `ZZ`, an empty string, or `AB --mask FFFF` produces the same target-format error. The corresponding production MCP stdio calls reject these four arguments as `invalid_request` / `invalid_input`, naming the invalid pattern or unequal mask in their diagnostic text. A valid `{ "pattern": "AB" }` control reaches the normal no-open-target error. No native target or provider session was opened. ### Reproduction 1. Start at the built baseline's repository root with its locked dependencies and Node.js v24.22.1. 2. Run this block. It creates the text target inline, isolates product state, puts inert package-manager fixtures first on PATH, invokes the production CLI and MCP stdio entrypoints, and removes its temporary files on completion. ```sh node --input-type=module <<'JS' import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; import { Client } from '@modelcontextprotocol/client'; import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; const cli = resolve('scripts/rea.mjs'); const work = mkdtempSync(join(tmpdir(), 'rea-byte-pattern-')); const env = { PATH: '', NO_COLOR: '1' }; for (const [key, dir] of Object.entries({ HOME: 'home', XDG_CONFIG_HOME: 'config', XDG_CACHE_HOME: 'cache', XDG_DATA_HOME: 'data', XDG_STATE_HOME: 'state', TMPDIR: 'runtime', npm_config_prefix: 'prefix' })) { env[key] = join(work, dir); mkdirSync(env[key]); } const bin = join(work, 'bin'); mkdirSync(bin); for (const name of ['npx', 'npm', 'bun']) writeFileSync(join(bin, name), "#!/bin/sh\nprintf '0.0.0\\n'\n", { mode: 0o755 }); env.PATH = `${bin}:${process.env.PATH}`; writeFileSync(join(work, 'not-a-binary.bin'), 'hello world'); const cases = [{ pattern: '??' }, { pattern: 'ZZ' }, { pattern: '' }, { pattern: 'AB', mask: 'FFFF' }]; const client = new Client({ name: 'byte-pattern-verifier', version: '1' }); const transport = new StdioClientTransport({ command: process.execPath, args: [cli, 'mcp'], env, cwd: work, stderr: 'pipe' }); transport.stderr?.on('data', () => {}); try { console.log('version:', spawnSync(process.execPath, [cli, '--version'], { cwd: work, env, encoding: 'utf8' }).stdout.trim()); for (const input of cases) { const args = [cli, 'search-bytes', './not-a-binary.bin', input.pattern]; if (input.mask !== undefined) args.push('--mask', input.mask); const result = spawnSync(process.execPath, [...args, '--json'], { cwd: work, env, encoding: 'utf8', timeout: 15000 }); const error = JSON.parse(result.stdout); console.log(JSON.stringify({ interface: 'CLI', input, exit: result.status, code: error.code, category: error.category, message: error.message, constraint: error.details?.constraint })); } const schema = JSON.parse(spawnSync(process.execPath, [cli, 'search-bytes', '--schema', '--json'], { cwd: work, env, encoding: 'utf8' }).stdout); console.log(JSON.stringify({ cliPattern: schema.args.properties.pattern, cliMask: schema.options.properties.mask })); await client.connect(transport); const definition = (await client.listTools()).tools.find(t => t.name === 'search_bytes'); console.log(JSON.stringify({ mcpPattern: definition.inputSchema.properties.pattern, mcpMask: definition.inputSchema.properties.mask })); for (const input of [...cases, { pattern: 'AB' }]) { const result = await client.callTool({ name: 'search_bytes', arguments: input }); console.log(JSON.stringify({ interface: 'MCP', input, isError: result.isError, content: result.content })); } console.log('ping:', JSON.stringify(await client.ping())); } finally { await client.close(); await transport.close(); rmSync(work, { recursive: true, force: true }); } JS ``` Each CLI case reproduced in two initial public-dispatcher replays. The complete reduced block above was also executed successfully. The initial MCP replay and the reduced block both returned the expected four input errors, the no-open-target control, and a successful ping. ### Expected behavior Reject an invalid byte-search query at CLI request admission, before accessing its target, and identify the pattern or mask that needs correction. The canonical rules require complete hexadecimal byte pairs or whole-byte wildcards, at least one constrained bit, and an equal-length hexadecimal mask when supplied. The project's CLI translation policy requires reuse of canonical semantic fields and aggregate admission before target access when positional and option schemas cannot express their relationship. A positional pattern and optional mask need that aggregate check. Contract locators are listed under Evidence. Descriptive string advertisements are supported: neither CLI nor MCP needs an advertised regular expression to establish correct runtime validation. ### Impact and scope P3: these malformed requests correctly fail, but the CLI's first diagnostic directs the caller toward target repair without identifying the invalid query. The demonstrated effect is diagnostic precedence and correction guidance. No valid search failure, wrong match result, target mutation, unwanted successful open, provider startup cost, or failed caller recovery was demonstrated. The executed scope is the built source baseline on macOS with an unsupported text fixture and a target-free MCP session. Supplying a valid byte pattern avoids the pattern defect; the text fixture will still correctly fail format admission. MCP provides an observed way to check these malformed arguments without opening a target. Behavior after successful native admission and other platforms remains untested. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js `v24.22.1`; macOS `27.0.1`; Darwin `arm64`. Used the built runtime and locked MCP client. No external analysis provider was used. ### Target and artifact context An authored regular file containing exactly `hello world`, eleven ASCII bytes, with no supported executable header. MCP calls use no open target. Package-manager commands are fixture executables, preventing package discovery from installing anything. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence The executed CLI cases all returned exit 1 and these fields: ```json { "code": "target_unavailable", "category": "unsupported_target", "message": "Cannot open artifact: unsupported binary format.", "details": { "reason": "unsupported binary format", "constraint": "file_format_rejected" } } ``` The selected fixture path is omitted from this excerpt. CLI stderr was empty. The response's remediation asks the caller to review the file-format reason and select a supported, well-formed target. For the same four arguments, production MCP stdio returned `isError: true`, `invalid_request` / `invalid_input`, and these diagnostic messages: | Input | MCP issue message | | --- | --- | | `{"pattern":"??"}` | `pattern: Pattern must constrain at least one bit` | | `{"pattern":"ZZ"}` | `pattern: Use hexadecimal byte pairs or ?? wildcards` | | `{"pattern":""}` | `pattern: Use hexadecimal byte pairs or ?? wildcards` | | `{"pattern":"AB","mask":"FFFF"}` | `mask: Mask length must equal pattern length` | The valid `AB` control instead returned `target_unavailable` with `No app is open...`; subsequent MCP ping returned `{}`. MCP issue paths are root paths at this baseline; the field names above occur in diagnostic prose. `search-bytes --schema` and its JSON variant show plain string fields for the pattern and mask. MCP tools/list also advertises these fields as strings. This discovery result alone is not evidence of missing runtime validation. Contract and source references at the baseline: - `docs/tool-design.md:339-346`: CLI semantic-field parity and aggregate admission before target access for relationships split across positional arguments and options. - `docs/cli.md:3-5`: shared application workflows and evidence contracts across CLI and MCP. - `docs/mcp-contracts.md:1290-1294`: descriptive byte-pattern advertisements are intentional; canonical MCP validation rejects malformed or unconstrained patterns before dispatch. - `src/domain/native/bytePattern.ts:10-24,65-99`: canonical syntax, mask alignment, and effective constrained-bit rules. - `src/contracts/hopperMemoryContracts.ts:16-24` and `src/ghidra/GhidraInventoryValues.ts:92-101`: shared semantic refinement on public MCP and provider input contracts. - `src/cli/coreNativeCommands.ts:35-47,58-73`: separate plain-string pattern/mask inputs forwarded to the direct analysis workflow. - `src/application/DirectAnalysis.ts:454-465`: session open precedes operation execution and its admission failure returns immediately. ### Analysis or suspected cause Source-derived: the CLI registration lacks the aggregate canonical byte-pattern check before entering the target-opening workflow. This explains why an unsupported target masks the query correction. The reproduction does not establish that later provider validation is absent, or that invalid patterns can produce successful search results. ### Additional context Duplicate searches covered open and closed issues, open and merged PRs, and issue comments. Related [#5257](https://github.com/morluto/rea/issues/5257) fixes large-pattern MCP regex stack overflow; the small MCP inputs here reject normally, so no regression of that fix is claimed. [#7436](https://github.com/morluto/rea/issues/7436) concerns empty target paths, while this reproduction supplies a nonempty path and invalid query. [#7661](https://github.com/morluto/rea/issues/7661) and [#7572](https://github.com/morluto/rea/issues/7572) concern correction metadata in input errors; restoring those fields would not move byte-pattern admission before target access. Original feature [PR #2341](https://github.com/morluto/rea/pull/2341) and alias [PR #2148](https://github.com/morluto/rea/pull/2148), including comments, contain no same-closure report or fix. ## Triage assessment - Impact level: P3 - Assessment status: supported - Impact basis: Malformed requests remain refused, but target-format guidance hides their query corrections; no consequential effect on valid results or lifecycle behavior was demonstrated. - Workaround status: available - Workaround basis: The observed target-free MCP calls report each invalid pattern or mask, permitting correction without target admission or a code change; this does not repair CLI error precedence.
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#4593
No description provided.