[GH-ISSUE #3911] [Bug] PE resource inspection changes the selected file after a symlink parent component #1522

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

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

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

inspect-pe-resources lexically resolves its input before filesystem access. On POSIX, a directory symlink followed by .. can make it inspect a different PE image and publish that image's resources and SHA-256 as observed Evidence. This source-only finding concerns ordinary local artifact selection. Neither image is executed, and no security impact is claimed.

The source derives the sibling sample.exe as service input for the first command. That file is PE32+ with payload OTHER and SHA-256 9a251f9f713c57d9b06b95931a7cfc23b1acc4a5f762d129d1897f290914c608. Evidence is internally consistent for the wrong image, so the ordinary success path does not detect the changed selection.

The initial investigation ran one fixture/filesystem control using readFile(requested) and readFile(resolve(requested)) on the same absolute symlink/parent spelling. Both fixtures had 1,536 bytes; the raw path selected the intended bytes and the resolved path selected the sibling. The control recorded the two digests above and PE magic values 267 and 523 respectively. It did not run the REA CLI, provider, or parser. No REA output or exit status was observed.

Reproduction

These public steps are proposed and unexecuted. Use the baseline below. From its source checkout, create two small files using the existing dependency-free PE fixture writer:

fixture_root=$(mktemp -d /tmp/agent-work.XXXXXX)
FIXTURE_ROOT="$fixture_root" node --input-type=module - <<'JS'
import { mkdir, writeFile, symlink } from 'node:fs/promises';
import { join } from 'node:path';
import { peResourceFixture } from './src/native/pe/PeResources.fixture.ts';
const root = process.env.FIXTURE_ROOT;
await mkdir(join(root, 'target/child'), { recursive: true });
await symlink('target/child', join(root, 'link'));
const selected = peResourceFixture(false, [{ type: 10, name: 1, language: 1033, payload: Buffer.from('SELECTED') }]);
const other = peResourceFixture(true, [{ type: 10, name: 1, language: 1033, payload: Buffer.from('OTHER') }]);
await writeFile(join(root, 'target/sample.exe'), selected.bytes);
await writeFile(join(root, 'sample.exe'), other.bytes);
JS
rea inspect-pe-resources "$fixture_root/link/../sample.exe" --json
rea inspect-pe-resources "$fixture_root/target/sample.exe" --json

Under POSIX traversal, both inputs name target/sample.exe. Both files are stable, regular PE resource images within default budgets. Neither final file is a symlink; only an ancestor is. The path is nonempty and NUL-free. This is a concrete fixture, not a claimed experimental minimization. Remove the exact fixture directory after collecting results.

Expected behavior

Both commands should identify PE32 with the SELECTED resource payload and artifact SHA-256 fd3b2f0395d1a84ef2780d11cf01413ee1ba4379711d8b4cf7a3366e29490f3d. The portable PE contract inspects the explicitly selected file and binds Evidence to its stable path, size, and digest. Anchoring a path must preserve filesystem selection, including an absolute path with a symlink/parent sequence.

Impact and scope

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded class of valid POSIX inputs can yield a successful resource inventory and digests for the wrong PE image. Files are not modified; runtime REA results and population frequency were not measured.

REA version

Reviewed release: rea-agents-6.5.0, revision 3b79af28e80785afce9f5998d8f03289402a1847. The source review compared fetched main at cf0a38ad9774ee9acff3164052688f7a58a1c72c; the handler, service, provider/parser/layout, schema, and stable reader were unchanged. The premise check used Node v26.10.0, macOS arm64. Windows semantics and other commands are outside scope; no last-known-good release was established.

Runtime and platform

REA runtime reproduction was not run. Runtime and platform details for any smaller premise checks are recorded in the version/environment field above. Real-provider versions and end-to-end output are NOT_ENOUGH_INFO because no provider was launched.

Target and artifact context

Local POSIX paths to distinct PE32 and PE32+ resource fixtures; directory symlink followed by a parent component.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

  • Expected source: docs/binary-diagnostics.md:152-170 documents explicit PE32/PE32+ inspection and stable identity; AGENTS.md:13 requires preservation of selected paths and digests.
  • Failure source: src/cli/binaryDiagnosticsCommands.ts:23-51 exposes the command and passes resolve(args.path) at line 42. The supplied filesystem control establishes the changed selection, and the downstream public path follows below.
  • Evidence provenance: static
  • Local verification: static-proof
  • Reproduction completeness: complete

The fixture/filesystem observation separately supports the path-selection premise and exact fixture identities. REA runtime reproduction was not run. The traced public path and guards establish acceptance of both valid regular images.

scripts/rea.mjs:67-87 enters the CLI after global output/configuration checks; --json is supported. src/cli.ts:120 registers diagnostics. binaryDiagnosticsCommands.ts:26-44 accepts the string, supplies default budgets, and rewrites even absolute paths before pe.inspect; this trigger supplies no cancellation.

src/composition/binaryDiagnostics.ts:10-12 supplies PeResourcesService(new PeResourcesProvider()). src/application/binaryDiagnostics/PeResourcesService.ts:32-52 validates cancellation, input schema, and absolute path shape against the already changed path. src/domain/localPath.ts:16-22 rejects empty/NUL values; src/domain/native/peResources.ts:18-41 supplies 64 MiB and 16,384-entry defaults. Each fixture has three resource-tree entries.

src/native/pe/PeResourcesProvider.ts:30-51 reads the changed path through src/artifacts/readStableArtifact.ts:56-72. Final-file checks reject nonregular files and final symlinks; later identity/size/timestamp checks establish sibling stability, not equivalence to the original selection. src/native/pe/PeResources.fixture.ts:30-41 and its writer build a numeric RCDATA type/name/language tree. PeResourceLayout.ts:38-81 admits both formats; PeResourceParser.ts:210-245 hashes payloads and returns artifact identity and complete inventory. No names, cycles, overlaps, or icon references complicate admission. These PE files are under src/native/pe/.

The service's :56-89 compares report identity to its transformed input and creates confidence: "observed" Evidence. src/cli/finishAnalysisCommand.ts:23-35,68, src/cliLogging.ts:82-99, and src/cli/cliResultOutput.ts:134-141,213-225 reach successful JSON projection after cleanup. MCP bypasses this CLI handler; no MCP failure is claimed.

Typed outcome or recovery expected

With the exact fixture, symlink/parent and direct selected paths must both report the PE32 image and its artifact/resource digests. The sibling direct path must still report PE32+ and its distinct digests. Suppressing identity, rejecting the valid path, or conflating images does not restore selection. Preserve budgets, stable-file checks, and typed failures. These checks are proposed and unexecuted.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded class of valid POSIX inputs can yield a successful resource inventory and digests for the wrong PE image. Files are not modified; runtime REA results and population frequency were not measured.
  • Workaround status: available
  • Workaround basis: Supply the direct selected path or a filesystem-canonical absolute path without the symlink/parent sequence. Source tracing and observed fixture selection establish availability; REA execution of this workaround was not run.
Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3911 ## 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 `inspect-pe-resources` lexically resolves its input before filesystem access. On POSIX, a directory symlink followed by `..` can make it inspect a different PE image and publish that image's resources and SHA-256 as observed Evidence. This source-only finding concerns ordinary local artifact selection. Neither image is executed, and no security impact is claimed. The source derives the sibling `sample.exe` as service input for the first command. That file is PE32+ with payload `OTHER` and SHA-256 `9a251f9f713c57d9b06b95931a7cfc23b1acc4a5f762d129d1897f290914c608`. Evidence is internally consistent for the wrong image, so the ordinary success path does not detect the changed selection. The initial investigation ran one fixture/filesystem control using `readFile(requested)` and `readFile(resolve(requested))` on the same absolute symlink/parent spelling. Both fixtures had 1,536 bytes; the raw path selected the intended bytes and the resolved path selected the sibling. The control recorded the two digests above and PE magic values 267 and 523 respectively. It did not run the REA CLI, provider, or parser. No REA output or exit status was observed. ## Reproduction These public steps are proposed and unexecuted. Use the baseline below. From its source checkout, create two small files using the existing dependency-free PE fixture writer: ```sh fixture_root=$(mktemp -d /tmp/agent-work.XXXXXX) FIXTURE_ROOT="$fixture_root" node --input-type=module - <<'JS' import { mkdir, writeFile, symlink } from 'node:fs/promises'; import { join } from 'node:path'; import { peResourceFixture } from './src/native/pe/PeResources.fixture.ts'; const root = process.env.FIXTURE_ROOT; await mkdir(join(root, 'target/child'), { recursive: true }); await symlink('target/child', join(root, 'link')); const selected = peResourceFixture(false, [{ type: 10, name: 1, language: 1033, payload: Buffer.from('SELECTED') }]); const other = peResourceFixture(true, [{ type: 10, name: 1, language: 1033, payload: Buffer.from('OTHER') }]); await writeFile(join(root, 'target/sample.exe'), selected.bytes); await writeFile(join(root, 'sample.exe'), other.bytes); JS rea inspect-pe-resources "$fixture_root/link/../sample.exe" --json rea inspect-pe-resources "$fixture_root/target/sample.exe" --json ``` Under POSIX traversal, both inputs name `target/sample.exe`. Both files are stable, regular PE resource images within default budgets. Neither final file is a symlink; only an ancestor is. The path is nonempty and NUL-free. This is a concrete fixture, not a claimed experimental minimization. Remove the exact fixture directory after collecting results. ## Expected behavior Both commands should identify PE32 with the `SELECTED` resource payload and artifact SHA-256 `fd3b2f0395d1a84ef2780d11cf01413ee1ba4379711d8b4cf7a3366e29490f3d`. The portable PE contract inspects the explicitly selected file and binds Evidence to its stable path, size, and digest. Anchoring a path must preserve filesystem selection, including an absolute path with a symlink/parent sequence. ## Impact and scope - Impact level: P2 - Assessment status: supported - Impact basis: A bounded class of valid POSIX inputs can yield a successful resource inventory and digests for the wrong PE image. Files are not modified; runtime REA results and population frequency were not measured. ## REA version Reviewed release: `rea-agents-6.5.0`, revision `3b79af28e80785afce9f5998d8f03289402a1847`. The source review compared fetched main at `cf0a38ad9774ee9acff3164052688f7a58a1c72c`; the handler, service, provider/parser/layout, schema, and stable reader were unchanged. The premise check used Node `v26.10.0`, macOS arm64. Windows semantics and other commands are outside scope; no last-known-good release was established. ## Runtime and platform REA runtime reproduction was not run. Runtime and platform details for any smaller premise checks are recorded in the version/environment field above. Real-provider versions and end-to-end output are NOT_ENOUGH_INFO because no provider was launched. ## Target and artifact context Local POSIX paths to distinct PE32 and PE32+ resource fixtures; directory symlink followed by a parent component. ## Interface CLI ## Earliest failure stage Request parsing or validation ## Relevant output or evidence - Expected source: [docs/binary-diagnostics.md:152-170](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/docs/binary-diagnostics.md#L152-L170) documents explicit PE32/PE32+ inspection and stable identity; [AGENTS.md:13](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/AGENTS.md#L13) requires preservation of selected paths and digests. - Failure source: [src/cli/binaryDiagnosticsCommands.ts:23-51](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/cli/binaryDiagnosticsCommands.ts#L23-L51) exposes the command and passes `resolve(args.path)` at line 42. The supplied filesystem control establishes the changed selection, and the downstream public path follows below. - Evidence provenance: static - Local verification: static-proof - Reproduction completeness: complete The fixture/filesystem observation separately supports the path-selection premise and exact fixture identities. REA runtime reproduction was not run. The traced public path and guards establish acceptance of both valid regular images. `scripts/rea.mjs:67-87` enters the CLI after global output/configuration checks; `--json` is supported. `src/cli.ts:120` registers diagnostics. `binaryDiagnosticsCommands.ts:26-44` accepts the string, supplies default budgets, and rewrites even absolute paths before `pe.inspect`; this trigger supplies no cancellation. `src/composition/binaryDiagnostics.ts:10-12` supplies `PeResourcesService(new PeResourcesProvider())`. `src/application/binaryDiagnostics/PeResourcesService.ts:32-52` validates cancellation, input schema, and absolute path shape against the already changed path. `src/domain/localPath.ts:16-22` rejects empty/NUL values; `src/domain/native/peResources.ts:18-41` supplies 64 MiB and 16,384-entry defaults. Each fixture has three resource-tree entries. `src/native/pe/PeResourcesProvider.ts:30-51` reads the changed path through `src/artifacts/readStableArtifact.ts:56-72`. Final-file checks reject nonregular files and final symlinks; later identity/size/timestamp checks establish sibling stability, not equivalence to the original selection. `src/native/pe/PeResources.fixture.ts:30-41` and its writer build a numeric RCDATA type/name/language tree. `PeResourceLayout.ts:38-81` admits both formats; `PeResourceParser.ts:210-245` hashes payloads and returns artifact identity and complete inventory. No names, cycles, overlaps, or icon references complicate admission. These PE files are under `src/native/pe/`. The service's `:56-89` compares report identity to its transformed input and creates `confidence: "observed"` Evidence. `src/cli/finishAnalysisCommand.ts:23-35,68`, `src/cliLogging.ts:82-99`, and `src/cli/cliResultOutput.ts:134-141,213-225` reach successful JSON projection after cleanup. MCP bypasses this CLI handler; no MCP failure is claimed. ## Typed outcome or recovery expected With the exact fixture, symlink/parent and direct selected paths must both report the PE32 image and its artifact/resource digests. The sibling direct path must still report PE32+ and its distinct digests. Suppressing identity, rejecting the valid path, or conflating images does not restore selection. Preserve budgets, stable-file checks, and typed failures. These checks are proposed and unexecuted. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A bounded class of valid POSIX inputs can yield a successful resource inventory and digests for the wrong PE image. Files are not modified; runtime REA results and population frequency were not measured. - Workaround status: available - Workaround basis: Supply the direct selected path or a filesystem-canonical absolute path without the symlink/parent sequence. Source tracing and observed fixture selection establish availability; REA execution of this workaround was not run.
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#1522
No description provided.