[GH-ISSUE #3912] [Bug] symlink parent traversal selects the wrong objdump and Rizin input #1523

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/3912

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

rea inspect-with-objdump and rea execute-rizin-command change a valid caller-selected artifact when its path contains a directory symlink followed by ... Both command builders collapse the path lexically. The shared service hashes the same unintended file, so successful analysis can produce internally consistent Evidence for the wrong input. The supplied research establishes the public result through source tracing and one executed command-builder check; REA CLI/MCP and provider execution were not run.

The initial command-builder check observed the following. ROOT substitutes only the disposable directory prefix in the recorded output.

{"tool":"objdump","source_input":"ROOT/via/../sample.bin","argument":"ROOT/sample.bin","input_reads_real_binary":true,"argument_reads_other_binary":true,"same_bytes":false}
{"tool":"rizin","source_input":"ROOT/via/../sample.bin","argument":"ROOT/sample.bin","input_reads_real_binary":true,"argument_reads_other_binary":true,"same_bytes":false}

Source tracing shows that, when a provider successfully inspects the sibling, the service hashes it before and after inspection and binds its path and digest to Evidence. This public outcome was not executed or observed as REA output.

Reproduction

These public CLI steps are source-derived and unexecuted. In an empty disposable directory, create the following arrangement and keep both binaries stable throughout inspection:

mkdir -p real/nested
cp /bin/ls real/sample.bin
cp /usr/bin/true sample.bin
ln -s real/nested via
rea inspect-with-objdump via/../sample.bin file_headers --json
rea inspect-with-objdump real/sample.bin file_headers --json
rea execute-rizin-command via/../sample.bin iI --json
rea execute-rizin-command real/sample.bin iI --json

Filesystem traversal resolves via/../sample.bin to real/sample.bin. For each provider, compare the Evidence subject SHA-256, artifact facts and parameters.command_arguments with the direct-target control. An absolute spelling retaining the symlink and parent component has the same trigger. Use a fresh, open, uncancelled service with fewer than eight active calls and successfully configured providers.

The initial executed check used this arrangement with copies of the same system files on macOS. One Node process imported the actual source objdumpCommand and rizinCommand, passed an absolute symlink-parent path, and compared filesystem reads of the input, generated argument, intended file and sibling. Neither copied binary nor provider executed. The command-builder check ran once, and its disposable directory was removed.

Expected behavior

The selected spelling and direct target identify the same filesystem artifact and must yield its digest and equivalent artifact facts. Explicitly selecting sibling sample.bin must still identify that distinct file. The selected-path contract applies to these ordinary static operations and their SHA-256 Evidence.

Impact and scope

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid selected path deterministically becomes another readable file at the observed argument boundary, and the supplied public source trace binds successful analysis and Evidence to that file. The trigger is bounded and does not mutate source files. Real-world frequency is unknown.

REA version

The inspected source baseline is published rea-agents-6.5.0, revision 3b79af28e80785afce9f5998d8f03289402a1847. The source review compared fetched main cf0a38ad9774ee9acff3164052688f7a58a1c72c and found no changes to the affected service, command builders, composition or public adapters. The executed boundary check used macOS arm64 and Node v26.10.0. Proposed public steps require Linux with GNU objdump and Rizin supporting the two ELF inputs. Provider versions, provider stdout and public exit statuses are NOT_ENOUGH_INFO because those processes were not run. Windows semantics were not assessed.

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

Two distinct stable native executables selected through a directory symlink followed by a parent component; ELF for proposed Linux provider runs, Mach-O for the supplied macOS command-builder check.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The public-result proof comes from supplied source research; the initial command-builder and byte-identity observation supports its path premise. Runtime REA reproduction was not run.

Public reachability in that research is src/cli/reverseEngineeringCommands.ts:106-147,172-191 forwarding the unchanged path; src/domain/reverseEngineering.ts:3-17,25-37 accepting a nonempty path; service lines 182-219 obtaining the profile through src/composition/reverseEngineering.ts:14-36; and service lines 222-280 admitting the fresh service described above. Provider handling retries any cleanup and hashes resolve(path) at lines 290-298, invokes the generated arguments at lines 332-348, hashes that resolved file again at lines 530-537, and binds the path/digest at lines 641-664. Stable readable files and successful provider inspection satisfy the remaining premises. Completeness describes the static proof, not an end-to-end run.

Typed outcome or recovery expected

Using the exact arrangement, both selected spellings must produce the intended file's SHA-256 and equivalent artifact facts for each provider. Repeat with an absolute path retaining the symlink-parent component. Selecting the sibling directly must retain its distinct digest. Preserve the subjectless architectures operation as a control. These proposed checks have not run.

Additional context

Initial duplicate screening distinguished existing Go/Rust CLI path handling in #3864 and SQLite CLI conversion in #3324 from this shared service and its command builders. The two providers belong together because they share the same selection failure and restoration criterion. This report concerns ordinary static inspection, without claims about arbitrary Rizin command effects.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid selected path deterministically becomes another readable file at the observed argument boundary, and the supplied public source trace binds successful analysis and Evidence to that file. The trigger is bounded and does not mutate source files. Real-world frequency is unknown.
  • Workaround status: available
  • Workaround basis: Supply direct target real/sample.bin or first resolve the selected path through filesystem semantics. The executed byte comparison established the intended direct target; that spelling has no symlink-parent component for the builders to collapse. Full provider execution of the workaround was not run.
Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3912 ## 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 `rea inspect-with-objdump` and `rea execute-rizin-command` change a valid caller-selected artifact when its path contains a directory symlink followed by `..`. Both command builders collapse the path lexically. The shared service hashes the same unintended file, so successful analysis can produce internally consistent Evidence for the wrong input. The supplied research establishes the public result through source tracing and one executed command-builder check; REA CLI/MCP and provider execution were not run. The initial command-builder check observed the following. `ROOT` substitutes only the disposable directory prefix in the recorded output. ```json {"tool":"objdump","source_input":"ROOT/via/../sample.bin","argument":"ROOT/sample.bin","input_reads_real_binary":true,"argument_reads_other_binary":true,"same_bytes":false} {"tool":"rizin","source_input":"ROOT/via/../sample.bin","argument":"ROOT/sample.bin","input_reads_real_binary":true,"argument_reads_other_binary":true,"same_bytes":false} ``` Source tracing shows that, when a provider successfully inspects the sibling, the service hashes it before and after inspection and binds its path and digest to Evidence. This public outcome was not executed or observed as REA output. ## Reproduction These public CLI steps are source-derived and unexecuted. In an empty disposable directory, create the following arrangement and keep both binaries stable throughout inspection: ```sh mkdir -p real/nested cp /bin/ls real/sample.bin cp /usr/bin/true sample.bin ln -s real/nested via rea inspect-with-objdump via/../sample.bin file_headers --json rea inspect-with-objdump real/sample.bin file_headers --json rea execute-rizin-command via/../sample.bin iI --json rea execute-rizin-command real/sample.bin iI --json ``` Filesystem traversal resolves `via/../sample.bin` to `real/sample.bin`. For each provider, compare the Evidence subject SHA-256, artifact facts and `parameters.command_arguments` with the direct-target control. An absolute spelling retaining the symlink and parent component has the same trigger. Use a fresh, open, uncancelled service with fewer than eight active calls and successfully configured providers. The initial executed check used this arrangement with copies of the same system files on macOS. One Node process imported the actual source `objdumpCommand` and `rizinCommand`, passed an absolute symlink-parent path, and compared filesystem reads of the input, generated argument, intended file and sibling. Neither copied binary nor provider executed. The command-builder check ran once, and its disposable directory was removed. ## Expected behavior The selected spelling and direct target identify the same filesystem artifact and must yield its digest and equivalent artifact facts. Explicitly selecting sibling `sample.bin` must still identify that distinct file. The selected-path contract applies to these ordinary static operations and their SHA-256 Evidence. ## Impact and scope - Impact level: P2 - Assessment status: supported - Impact basis: A valid selected path deterministically becomes another readable file at the observed argument boundary, and the supplied public source trace binds successful analysis and Evidence to that file. The trigger is bounded and does not mutate source files. Real-world frequency is unknown. ## REA version The inspected source baseline is published `rea-agents-6.5.0`, revision `3b79af28e80785afce9f5998d8f03289402a1847`. The source review compared fetched main `cf0a38ad9774ee9acff3164052688f7a58a1c72c` and found no changes to the affected service, command builders, composition or public adapters. The executed boundary check used macOS arm64 and Node `v26.10.0`. Proposed public steps require Linux with GNU objdump and Rizin supporting the two ELF inputs. Provider versions, provider stdout and public exit statuses are NOT_ENOUGH_INFO because those processes were not run. Windows semantics were not assessed. ## 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 Two distinct stable native executables selected through a directory symlink followed by a parent component; ELF for proposed Linux provider runs, Mach-O for the supplied macOS command-builder check. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence - Expected source: [selected-input contract](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/AGENTS.md#L13), `docs/reverse-engineering-tools.md:3,17,52`, and `src/contracts/reverseEngineeringToolContracts.ts:28-34,47-53` require preserving the caller's artifact and digest. - Failure source: The initial observed output above and source trace through [public CLI forwarding](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/cli/reverseEngineeringCommands.ts#L106), [objdump arguments](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/objdump/ObjdumpCommand.ts#L12), [Rizin arguments](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/rizin/RizinCommand.ts#L12), and [service hashing and Evidence binding](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/application/reverse/ReverseEngineeringService.ts#L283) establish the wrong artifact selection. - Evidence provenance: static - Local verification: static-proof - Reproduction completeness: complete The public-result proof comes from supplied source research; the initial command-builder and byte-identity observation supports its path premise. Runtime REA reproduction was not run. Public reachability in that research is `src/cli/reverseEngineeringCommands.ts:106-147,172-191` forwarding the unchanged path; `src/domain/reverseEngineering.ts:3-17,25-37` accepting a nonempty path; service lines 182-219 obtaining the profile through `src/composition/reverseEngineering.ts:14-36`; and service lines 222-280 admitting the fresh service described above. Provider handling retries any cleanup and hashes `resolve(path)` at lines 290-298, invokes the generated arguments at lines 332-348, hashes that resolved file again at lines 530-537, and binds the path/digest at lines 641-664. Stable readable files and successful provider inspection satisfy the remaining premises. Completeness describes the static proof, not an end-to-end run. ## Typed outcome or recovery expected Using the exact arrangement, both selected spellings must produce the intended file's SHA-256 and equivalent artifact facts for each provider. Repeat with an absolute path retaining the symlink-parent component. Selecting the sibling directly must retain its distinct digest. Preserve the subjectless `architectures` operation as a control. These proposed checks have not run. ## Additional context Initial duplicate screening distinguished existing Go/Rust CLI path handling in #3864 and SQLite CLI conversion in #3324 from this shared service and its command builders. The two providers belong together because they share the same selection failure and restoration criterion. This report concerns ordinary static inspection, without claims about arbitrary Rizin command effects. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A valid selected path deterministically becomes another readable file at the observed argument boundary, and the supplied public source trace binds successful analysis and Evidence to that file. The trigger is bounded and does not mutate source files. Real-world frequency is unknown. - Workaround status: available - Workaround basis: Supply direct target `real/sample.bin` or first resolve the selected path through filesystem semantics. The executed byte comparison established the intended direct target; that spelling has no symlink-parent component for the builders to collapse. Full provider execution of the 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#1523
No description provided.