[GH-ISSUE #3351] [Bug] Managed/native verification contradicts Windows module paths on macOS #1221

Closed
opened 2026-10-16 21:02:57 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

verify-managed-native-boundaries reports contradicted with basis: "module-mismatch" when a managed declaration names C:\deps\helper.dll and the supplied native function Evidence names helper.dll. Changing only the declaration's separators to C:/deps/helper.dll makes the same function Evidence verify successfully. The backslashes are metadata from the Windows target, but their interpretation depends on the analysis host.

Both managed/native boundary inspections and both verification commands exit with status 0. The contradicted result also says that the supplied native Evidence module identity does not match the managed import scope. The declaration and native record both have known module names; this case does not depend on missing module identity.

Reproduction

  1. Start from a clean checkout at the reproduced baseline and install the locked dependencies and build the runtime using the repository's pinned development toolchain:
git clone https://github.com/morluto/rea.git rea-module-path
cd rea-module-path
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. From the repository root on macOS, run the following command. It creates two PE/CLI fixtures with the same P/Invoke entry point and a validated synthetic native function Evidence record. It also creates every verification input inline and isolates CLI state under sample-home.
node --input-type=module <<'NODE'
import { mkdirSync, writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { buildManagedPeFixture } from './scripts/fixtures/managed/pe.mjs';
import { functionDossier } from './scripts/fixtures/managed/function-dossier.mjs';
import { createEvidence } from './dist/domain/evidence.js';
mkdirSync('sample-home', { recursive: true });
const run = (...args) => {
  const result = spawnSync(process.execPath, ['scripts/rea.mjs', ...args, '--format', 'json', '--full-output'], {
    encoding: 'utf8', env: { ...process.env, HOME: `${process.cwd()}/sample-home` }
  });
  const output = JSON.parse(result.stdout);
  if (result.status !== 0) throw new Error(JSON.stringify(output));
  return output.data;
};
const native = createEvidence(
  { path: 'helper.dll', sha256: '1'.repeat(64), format: 'pe' },
  { id: 'fixture-native', name: 'Fixture native', version: '1' },
  { operation: 'analyze_function', parameters: {}, result: functionDossier('native_call'), rawResult: null }
);
for (const [name, moduleName] of [
  ['sample-backslash', String.raw`C:\deps\helper.dll`],
  ['sample-slash', 'C:/deps/helper.dll']
]) {
  const path = `${name}.dll`;
  writeFileSync(path, buildManagedPeFixture({ pinvoke: {
    moduleName, importName: 'native_call', mappingFlags: 0x0101
  } }));
  const managed = run('inspect-managed-native-boundaries', path);
  const inputPath = `${name}.json`;
  writeFileSync(inputPath, JSON.stringify({ managed_boundaries: managed, native_observations: [native] }));
  const verification = run('verify-managed-native-boundaries', inputPath).normalized_result;
  const item = verification.pinvoke_imports[0];
  console.log(JSON.stringify({ declaration: item.managed.import_scope_name, status: item.status,
    basis: item.basis, native_module: item.matched_native?.module_name, coverage: verification.coverage.status }));
}
NODE
  1. Compare the two printed records. The exact same native function Evidence produces opposite verification states when only the declared path separators change. The synthetic observation isolates declaration matching; it does not establish real-provider or CLR behavior. The dependency installation and build commands in step 1 prepare a clean checkout and are not additional observed reproduction results.

Expected behavior

The workflow should interpret a Windows-style module path as Windows target metadata when comparing its module identity. Equivalent Windows separator spellings should not turn a successful declaration link into a module contradiction merely because the analysis runs on macOS. Preserve the original declared spelling in the result, and continue to distinguish genuinely different module names.

docs/managed-code-analysis.md:323-325, under managed/native boundary rules, states that verification compares declared P/Invoke module and entry-point identities with supplied native export or function evidence. AGENTS.md, Architecture and Cleanup, requires format-aware interpretation and preservation of identity across boundaries, specifically AGENTS.md:19 and AGENTS.md:23. These matches establish declaration links, not that a call executed.

Impact and scope

Impact level: P2. Assessment status: supported. Cross-host declaration verification reports a false module contradiction for a Windows path even though the same module and symbol verify with forward separators. The observed failure affects declaration matching, not measured runtime binding.

Workaround status: unverified. Changing separator bytes in the target makes the control pass but changes the inspected artifact identity. No identity-preserving public normalization option was demonstrated for existing artifacts and their authenticated Evidence.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0 on macOS 27.0.1, arm64.

Target and artifact context

The Windows-format target files are source-owned PE/CLI byte fixtures, sample-backslash.dll and sample-slash.dll, with the same native_call P/Invoke entry point and mappingFlags: 0x0101. Verification uses a synthetic analyze_function Evidence record for helper.dll, PE format, with a 64-character SHA-256 fixture digest consisting of 1 characters. The inline command creates the record and both JSON inputs. No target code or external engine was executed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The observed CLI reproduction was complete and locally reproduced. The exact same native function Evidence produced:

{"declaration":"C:\\deps\\helper.dll","status":"contradicted","basis":"module-mismatch","native_module":"helper.dll","coverage":"complete-within-inputs"}
{"declaration":"C:/deps/helper.dll","status":"verified","basis":"exact-function-name","native_module":"helper.dll","coverage":"complete-within-inputs"}

Both inspections and both verification commands exited with status 0. Both records report coverage: "complete-within-inputs"; the backslash declaration receives contradicted/module-mismatch, while the forward-slash declaration receives verified/exact-function-name.

Typed outcome or recovery expected

On macOS, run both separator variants and require the same declaration-link result against the same helper.dll function Evidence, while retaining each original declaration string. Replace the native module with other.dll as a negative control; that known mismatch must still be contradicted. Do not weaken module checking or make a runtime-binding claim to obtain parity. The other.dll case is a restoration check, not an observed result of this reproduction.

Analysis or suspected cause

Source-derived reasoning: src/domain/managed/managedNativeVerificationMatch.ts:108-131 derives module identities with the host's basename before matching them. On macOS, that host path interpretation leaves Windows backslashes in the declared module identity, while forward separators allow extraction of helper.dll. This source reasoning explains the separator-sensitive results of the executed CLI reproduction; it does not establish real-provider or CLR behavior.

Additional context

  • #3201 concerns false module contradictions when a native module identity is absent or a managed scope is empty; this report concerns equivalent separator spellings when both module identities are known.
  • #786, closed as completed, concerned forward-slash filesystem and executable path admission at native boundaries on Windows; this report concerns Windows module metadata comparison on macOS.
  • #2621, closed as completed, concerned an extra duplicate-ID validation diagnostic for an already-invalid managed/native request; this report concerns an incorrect verification result for accepted inputs.
  • #2619, closed as completed, concerned preserving literal POSIX backslash filenames in directory and ASAR inventory; this report concerns interpreting Windows separators in managed module metadata.
  • #3333 concerns retention collisions for distinct unresolved MCP workflows, including managed/native verification; this report concerns the separator-sensitive declaration match returned by the CLI.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3351 ### 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 Evidence, artifacts, or reconstruction ### Observed behavior `verify-managed-native-boundaries` reports `contradicted` with `basis: "module-mismatch"` when a managed declaration names `C:\deps\helper.dll` and the supplied native function Evidence names `helper.dll`. Changing only the declaration's separators to `C:/deps/helper.dll` makes the same function Evidence verify successfully. The backslashes are metadata from the Windows target, but their interpretation depends on the analysis host. Both managed/native boundary inspections and both verification commands exit with status 0. The contradicted result also says that the supplied native Evidence module identity does not match the managed import scope. The declaration and native record both have known module names; this case does not depend on missing module identity. ### Reproduction 1. Start from a clean checkout at the reproduced baseline and install the locked dependencies and build the runtime using the repository's pinned development toolchain: ```sh git clone https://github.com/morluto/rea.git rea-module-path cd rea-module-path git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. From the repository root on macOS, run the following command. It creates two PE/CLI fixtures with the same P/Invoke entry point and a validated synthetic native function Evidence record. It also creates every verification input inline and isolates CLI state under `sample-home`. ```sh node --input-type=module <<'NODE' import { mkdirSync, writeFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; import { buildManagedPeFixture } from './scripts/fixtures/managed/pe.mjs'; import { functionDossier } from './scripts/fixtures/managed/function-dossier.mjs'; import { createEvidence } from './dist/domain/evidence.js'; mkdirSync('sample-home', { recursive: true }); const run = (...args) => { const result = spawnSync(process.execPath, ['scripts/rea.mjs', ...args, '--format', 'json', '--full-output'], { encoding: 'utf8', env: { ...process.env, HOME: `${process.cwd()}/sample-home` } }); const output = JSON.parse(result.stdout); if (result.status !== 0) throw new Error(JSON.stringify(output)); return output.data; }; const native = createEvidence( { path: 'helper.dll', sha256: '1'.repeat(64), format: 'pe' }, { id: 'fixture-native', name: 'Fixture native', version: '1' }, { operation: 'analyze_function', parameters: {}, result: functionDossier('native_call'), rawResult: null } ); for (const [name, moduleName] of [ ['sample-backslash', String.raw`C:\deps\helper.dll`], ['sample-slash', 'C:/deps/helper.dll'] ]) { const path = `${name}.dll`; writeFileSync(path, buildManagedPeFixture({ pinvoke: { moduleName, importName: 'native_call', mappingFlags: 0x0101 } })); const managed = run('inspect-managed-native-boundaries', path); const inputPath = `${name}.json`; writeFileSync(inputPath, JSON.stringify({ managed_boundaries: managed, native_observations: [native] })); const verification = run('verify-managed-native-boundaries', inputPath).normalized_result; const item = verification.pinvoke_imports[0]; console.log(JSON.stringify({ declaration: item.managed.import_scope_name, status: item.status, basis: item.basis, native_module: item.matched_native?.module_name, coverage: verification.coverage.status })); } NODE ``` 3. Compare the two printed records. The exact same native function Evidence produces opposite verification states when only the declared path separators change. The synthetic observation isolates declaration matching; it does not establish real-provider or CLR behavior. The dependency installation and build commands in step 1 prepare a clean checkout and are not additional observed reproduction results. ### Expected behavior The workflow should interpret a Windows-style module path as Windows target metadata when comparing its module identity. Equivalent Windows separator spellings should not turn a successful declaration link into a module contradiction merely because the analysis runs on macOS. Preserve the original declared spelling in the result, and continue to distinguish genuinely different module names. `docs/managed-code-analysis.md:323-325`, under managed/native boundary rules, states that verification compares declared P/Invoke module and entry-point identities with supplied native export or function evidence. `AGENTS.md`, Architecture and Cleanup, requires format-aware interpretation and preservation of identity across boundaries, specifically `AGENTS.md:19` and `AGENTS.md:23`. These matches establish declaration links, not that a call executed. ### Impact and scope Impact level: P2. Assessment status: supported. Cross-host declaration verification reports a false module contradiction for a Windows path even though the same module and symbol verify with forward separators. The observed failure affects declaration matching, not measured runtime binding. Workaround status: unverified. Changing separator bytes in the target makes the control pass but changes the inspected artifact identity. No identity-preserving public normalization option was demonstrated for existing artifacts and their authenticated Evidence. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0 on macOS 27.0.1, arm64. ### Target and artifact context The Windows-format target files are source-owned PE/CLI byte fixtures, `sample-backslash.dll` and `sample-slash.dll`, with the same `native_call` P/Invoke entry point and `mappingFlags: 0x0101`. Verification uses a synthetic `analyze_function` Evidence record for `helper.dll`, PE format, with a 64-character SHA-256 fixture digest consisting of `1` characters. The inline command creates the record and both JSON inputs. No target code or external engine was executed. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The observed CLI reproduction was complete and locally reproduced. The exact same native function Evidence produced: ```text {"declaration":"C:\\deps\\helper.dll","status":"contradicted","basis":"module-mismatch","native_module":"helper.dll","coverage":"complete-within-inputs"} {"declaration":"C:/deps/helper.dll","status":"verified","basis":"exact-function-name","native_module":"helper.dll","coverage":"complete-within-inputs"} ``` Both inspections and both verification commands exited with status 0. Both records report `coverage: "complete-within-inputs"`; the backslash declaration receives `contradicted/module-mismatch`, while the forward-slash declaration receives `verified/exact-function-name`. ### Typed outcome or recovery expected On macOS, run both separator variants and require the same declaration-link result against the same `helper.dll` function Evidence, while retaining each original declaration string. Replace the native module with `other.dll` as a negative control; that known mismatch must still be contradicted. Do not weaken module checking or make a runtime-binding claim to obtain parity. The `other.dll` case is a restoration check, not an observed result of this reproduction. ### Analysis or suspected cause Source-derived reasoning: `src/domain/managed/managedNativeVerificationMatch.ts:108-131` derives module identities with the host's `basename` before matching them. On macOS, that host path interpretation leaves Windows backslashes in the declared module identity, while forward separators allow extraction of `helper.dll`. This source reasoning explains the separator-sensitive results of the executed CLI reproduction; it does not establish real-provider or CLR behavior. ### Additional context - [#3201](https://github.com/morluto/rea/issues/3201) concerns false module contradictions when a native module identity is absent or a managed scope is empty; this report concerns equivalent separator spellings when both module identities are known. - [#786](https://github.com/morluto/rea/issues/786), closed as completed, concerned forward-slash filesystem and executable path admission at native boundaries on Windows; this report concerns Windows module metadata comparison on macOS. - [#2621](https://github.com/morluto/rea/issues/2621), closed as completed, concerned an extra duplicate-ID validation diagnostic for an already-invalid managed/native request; this report concerns an incorrect verification result for accepted inputs. - [#2619](https://github.com/morluto/rea/issues/2619), closed as completed, concerned preserving literal POSIX backslash filenames in directory and ASAR inventory; this report concerns interpreting Windows separators in managed module metadata. - [#3333](https://github.com/morluto/rea/issues/3333) concerns retention collisions for distinct unresolved MCP workflows, including managed/native verification; this report concerns the separator-sensitive declaration match returned by the CLI.
Author
Owner

@hichipli commented on GitHub (Oct 15, 2026):

Thanks for the detailed controls. PR #4144 is now ready for review. The shared module matcher recognizes absolute Windows drive/UNC paths independently of the analysis host while preserving original declarations and literal backslashes in POSIX paths.

On macOS arm64, actual compiled CLI and production stdio MCP calls now verify both C:\deps\helper.dll and C:/deps/helper.dll against the same explicitly synthetic native function Evidence; C:\deps\other.dll remains contradicted. Fixture bytes are unchanged. All 30 focused tests and npm run check passed. The PR records the failing-base cases, unavailable real-engine/platform coverage, and the independent reused-session unknown-registry limitation (#3333).

Review follow-up: literal POSIX filenames beginning with drive/rooted/UNC-looking prefixes are now protected against a second path interpretation. Both negative cases and matching literal-name controls pass through the CLI and stdio MCP.

<!-- gh-comment-id:6194222770 --> @hichipli commented on GitHub (Oct 15, 2026): Thanks for the detailed controls. PR #4144 is now ready for review. The shared module matcher recognizes absolute Windows drive/UNC paths independently of the analysis host while preserving original declarations and literal backslashes in POSIX paths. On macOS arm64, actual compiled CLI and production stdio MCP calls now verify both `C:\deps\helper.dll` and `C:/deps/helper.dll` against the same explicitly synthetic native function Evidence; `C:\deps\other.dll` remains contradicted. Fixture bytes are unchanged. All 30 focused tests and `npm run check` passed. The PR records the failing-base cases, unavailable real-engine/platform coverage, and the independent reused-session unknown-registry limitation (#3333). Review follow-up: literal POSIX filenames beginning with drive/rooted/UNC-looking prefixes are now protected against a second path interpretation. Both negative cases and matching literal-name controls pass through the CLI and stdio MCP.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked resolved on main at d87e73f2db.

I replayed the original authored PE/CLI fixture and public inspection/verification commands with validated synthetic native Evidence. Backslash, forward-slash and the report's literal doubled-backslash declarations all preserve their original spelling and give the same result against basename-only helper.dll: unresolved / module-identity-unknown. All still contradict other.dll as module-mismatch. Conventional single-backslash and slash declarations both verify against the full native path C:/deps/helper.dll and contradict a different full path. This resolves the reported separator-dependent false contradiction while preserving unknown full-path identity. It does not claim that basename-only Evidence verifies an absolute path, or that repeated separators are generally canonicalized. All 15 public commands exited 0; no CLR or native target was executed.

Relevant implementation: managedNativeVerificationMatch.ts, managedNativeVerificationMatch.ts.

Closing as completed on main.

<!-- gh-comment-id:6380756641 --> @coygeek commented on GitHub (Oct 24, 2026): Checked resolved on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. I replayed the original authored PE/CLI fixture and public inspection/verification commands with validated synthetic native Evidence. Backslash, forward-slash and the report's literal doubled-backslash declarations all preserve their original spelling and give the same result against basename-only `helper.dll`: `unresolved / module-identity-unknown`. All still contradict `other.dll` as `module-mismatch`. Conventional single-backslash and slash declarations both verify against the full native path `C:/deps/helper.dll` and contradict a different full path. This resolves the reported separator-dependent false contradiction while preserving unknown full-path identity. It does not claim that basename-only Evidence verifies an absolute path, or that repeated separators are generally canonicalized. All 15 public commands exited 0; no CLR or native target was executed. Relevant implementation: [managedNativeVerificationMatch.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/managed/managedNativeVerificationMatch.ts#L58), [managedNativeVerificationMatch.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/managed/managedNativeVerificationMatch.ts#L68). Closing as completed on main.
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#1221
No description provided.