[GH-ISSUE #4383] [Bug] Direct protocol enumeration discards input before strict validation #1740

Closed
opened 2026-10-16 21:09:42 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

The schema rejects {unexpected: "benign"}, but direct execution returns success with the same empty protocol inventory as {} and invokes the provider. The adjacent get_objc_classes control returns AnalysisInputError without invoking the provider.

Reproduction

Use the main source baseline below with its pinned Node toolchain and locked dependencies (npm ci). Save this script as repro.mts in that checkout and run REA_CHECKOUT="$PWD" node_modules/.bin/tsx repro.mts. It imports application source and injects a benign local provider; it does not start an engine or read a binary.

import assert from 'node:assert/strict';
import {pathToFileURL} from 'node:url';
const root=process.env.REA_CHECKOUT!;
const load=(p:string)=>import(pathToFileURL(`${root}/${p}`).href);
const {EnhancedTools}=await load('src/application/EnhancedTools.ts');
const {createAnalysisExecution}=await load('src/application/AnalysisProvider.ts');
const {enhancedInputSchemas}=await load('src/contracts/enhancedInputs.ts');
let calls=0;
const tools=new EnhancedTools({execute:async()=>{calls++;return {ok:true,value:createAnalysisExecution([],{id:'fixture',name:'fixture',version:'1'})};}});
assert.equal(enhancedInputSchemas.get_objc_protocols.safeParse({unexpected:'benign'}).success,false);
const valid=await tools.execute('get_objc_protocols',{});
assert.equal(valid.ok,true);
const extra=await tools.execute('get_objc_protocols',{unexpected:'benign'});
assert.equal(extra.ok,true);
const before=calls;
const control=await tools.execute('get_objc_classes',{unexpected:'benign'});
assert.equal(control.ok,false);assert.equal(control.error._tag,'AnalysisInputError');assert.equal(calls,before);
console.log(JSON.stringify({schema_rejects_extra:true,valid,extra,control_error:control.error._tag,provider_calls:calls}));

This probe ran once against each source baseline using prepared locked dependencies and the main checkout's TSX runner; both exited 0. These are source-boundary execution results, not installed CLI, MCP transport, or real-provider reproduction.

Expected behavior

EnhancedTools.execute documents parsing untrusted direct-call input once. The shared get_objc_protocols schema is z.strictObject({}), so an object with an undeclared property should produce AnalysisInputError before provider invocation. The valid empty object should still succeed. No protocol-filter feature is implied.

Impact and scope

Direct application callers receive success after a request-shape mistake instead of a typed diagnostic. The provider executes despite invalid input. This is limited to the explicit direct-call boundary: MCP owns separate validation and calls executeValidated, so this report does not claim the public MCP path accepts the same request. No real-provider consequence or security impact was measured.

REA version

Main source 2c7583599b; latest published rea-agents 6.5.0 source 3b79af28e8.

Runtime and platform

Node.js v24.18.0; Darwin arm64; TSX from main locked dependencies. No native engine was started.

Target and artifact context

Synthetic empty Objective-C name inventory from an injected AnalysisOperationPort; no binary target.

Interface

Other

Earliest failure stage

Request parsing or validation

Relevant output or evidence

The executed probe emitted the same output for both baselines:

{"schema_rejects_extra":true,"valid":{"ok":true,"value":{"count":0,"protocols":[]}},"extra":{"ok":true,"value":{"count":0,"protocols":[]}},"control_error":"AnalysisInputError","provider_calls":2}

The direct-call contract and dispatch show the protocol branch substituting input: {}; the canonical input schema rejects extra properties. The MCP registration uses the separately validated adapter path. MCP transport was not exercised.

Typed outcome or recovery expected

Return ok: false with AnalysisInputError before provider invocation for the extra-property object; preserve successful empty-object calls. Direct callers can currently validate with the exported canonical schema first.

Analysis or suspected cause

Source inspection shows the protocol branch constructs input: {} unconditionally, while adjacent branches parse the supplied input before executeValidated.

Additional context

The probe covers application source with a synthetic port. Installed CLI, MCP transport, and real engines were not executed. Earlier MCP schema work concerns adapter validation; existing empty protocol-name and Swift symbol-discovery reports concern different decoder/discovery contracts.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Direct callers receive success instead of the documented typed diagnostic for an invalid request, and the provider is invoked. The failure is bounded to this direct protocol operation; no real-provider consequence was measured.
  • Workaround status: available
  • Workaround basis: The executed probe confirms the exported canonical schema rejects the same object, so direct callers can validate it before execution.
Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4383 ### 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 Native analysis or decompilation ### Observed behavior The schema rejects `{unexpected: "benign"}`, but direct execution returns success with the same empty protocol inventory as `{}` and invokes the provider. The adjacent `get_objc_classes` control returns `AnalysisInputError` without invoking the provider. ### Reproduction Use the main source baseline below with its pinned Node toolchain and locked dependencies (`npm ci`). Save this script as `repro.mts` in that checkout and run `REA_CHECKOUT="$PWD" node_modules/.bin/tsx repro.mts`. It imports application source and injects a benign local provider; it does not start an engine or read a binary. ```ts import assert from 'node:assert/strict'; import {pathToFileURL} from 'node:url'; const root=process.env.REA_CHECKOUT!; const load=(p:string)=>import(pathToFileURL(`${root}/${p}`).href); const {EnhancedTools}=await load('src/application/EnhancedTools.ts'); const {createAnalysisExecution}=await load('src/application/AnalysisProvider.ts'); const {enhancedInputSchemas}=await load('src/contracts/enhancedInputs.ts'); let calls=0; const tools=new EnhancedTools({execute:async()=>{calls++;return {ok:true,value:createAnalysisExecution([],{id:'fixture',name:'fixture',version:'1'})};}}); assert.equal(enhancedInputSchemas.get_objc_protocols.safeParse({unexpected:'benign'}).success,false); const valid=await tools.execute('get_objc_protocols',{}); assert.equal(valid.ok,true); const extra=await tools.execute('get_objc_protocols',{unexpected:'benign'}); assert.equal(extra.ok,true); const before=calls; const control=await tools.execute('get_objc_classes',{unexpected:'benign'}); assert.equal(control.ok,false);assert.equal(control.error._tag,'AnalysisInputError');assert.equal(calls,before); console.log(JSON.stringify({schema_rejects_extra:true,valid,extra,control_error:control.error._tag,provider_calls:calls})); ``` This probe ran once against each source baseline using prepared locked dependencies and the main checkout's TSX runner; both exited 0. These are source-boundary execution results, not installed CLI, MCP transport, or real-provider reproduction. ### Expected behavior `EnhancedTools.execute` documents parsing untrusted direct-call input once. The shared `get_objc_protocols` schema is `z.strictObject({})`, so an object with an undeclared property should produce `AnalysisInputError` before provider invocation. The valid empty object should still succeed. No protocol-filter feature is implied. ### Impact and scope Direct application callers receive success after a request-shape mistake instead of a typed diagnostic. The provider executes despite invalid input. This is limited to the explicit direct-call boundary: MCP owns separate validation and calls `executeValidated`, so this report does not claim the public MCP path accepts the same request. No real-provider consequence or security impact was measured. ### REA version Main source 2c7583599b9ac1c847bd77b1940930fd8d4594c7; latest published rea-agents 6.5.0 source 3b79af28e80785afce9f5998d8f03289402a1847. ### Runtime and platform Node.js v24.18.0; Darwin arm64; TSX from main locked dependencies. No native engine was started. ### Target and artifact context Synthetic empty Objective-C name inventory from an injected AnalysisOperationPort; no binary target. ### Interface Other ### Earliest failure stage Request parsing or validation ### Relevant output or evidence The executed probe emitted the same output for both baselines: ```json {"schema_rejects_extra":true,"valid":{"ok":true,"value":{"count":0,"protocols":[]}},"extra":{"ok":true,"value":{"count":0,"protocols":[]}},"control_error":"AnalysisInputError","provider_calls":2} ``` The [direct-call contract and dispatch](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/src/application/EnhancedTools.ts#L101-L164) show the protocol branch substituting `input: {}`; the [canonical input schema](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/src/contracts/enhancedInputs.ts#L8-L15) rejects extra properties. The [MCP registration](https://github.com/morluto/rea/blob/2c7583599b9ac1c847bd77b1940930fd8d4594c7/src/server/registerEnhancedTools.ts#L101-L112) uses the separately validated adapter path. MCP transport was not exercised. ### Typed outcome or recovery expected Return `ok: false` with `AnalysisInputError` before provider invocation for the extra-property object; preserve successful empty-object calls. Direct callers can currently validate with the exported canonical schema first. ### Analysis or suspected cause Source inspection shows the protocol branch constructs `input: {}` unconditionally, while adjacent branches parse the supplied input before `executeValidated`. ### Additional context The probe covers application source with a synthetic port. Installed CLI, MCP transport, and real engines were not executed. Earlier MCP schema work concerns adapter validation; existing empty protocol-name and Swift symbol-discovery reports concern different decoder/discovery contracts. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Direct callers receive success instead of the documented typed diagnostic for an invalid request, and the provider is invoked. The failure is bounded to this direct protocol operation; no real-provider consequence was measured. - Workaround status: available - Workaround basis: The executed probe confirms the exported canonical schema rejects the same object, so direct callers can validate it before execution.
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#1740
No description provided.