[GH-ISSUE #5479] [Bug] inspect-artifact loses the caller-selected symlink path from complete Evidence #3331

Closed
opened 2026-10-31 00:39:15 +00:00 by bsakaguchi · 3 comments
Owner

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

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

inspect-artifact selected-alias.zip --json succeeds when selected-alias.zip points to program.zip, but complete Evidence contains only the resolved target path. The caller-selected alias appears nowhere in the JSON. Inspecting the target directly returns an identical complete Evidence record. Archive contents and target digest remain correct.

Reproduction

An independent verifier ran one alias inspection and one direct-target control through a Python subprocess driver, with an isolated HOME and four XDG directories. This is a reduced, runnable version of that driver; run from the built checkout with dependencies available:

import json, os, pathlib, subprocess, tempfile, zipfile
repo = pathlib.Path.cwd()
with tempfile.TemporaryDirectory() as work:
    work = pathlib.Path(work)
    env = os.environ.copy()
    for key, name in [('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'),
                      ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'),
                      ('XDG_STATE_HOME', 'state')]:
        env[key] = str(work / name)
        (work / name).mkdir()
    with zipfile.ZipFile(work / 'program.zip', 'w') as archive:
        archive.write(repo / 'tests/fixtures/interface-builder/UIKitOutlet.xml',
                      'UIKitOutlet.xml')
    (work / 'selected-alias.zip').symlink_to('program.zip')
    results = []
    for name in ['selected-alias.zip', 'program.zip']:
        result = subprocess.run(
            ['node', str(repo / 'scripts/rea.mjs'), 'inspect-artifact', name, '--json'],
            cwd=work, env=env, capture_output=True, text=True, check=True)
        results.append(json.loads(result.stdout))
    alias, direct = results
    print('parameters:', alias['parameters'])
    print('subject name:', alias['subject']['name'])
    print('alias retained:', 'selected-alias.zip' in json.dumps(alias))
    print('complete records equal:', alias == direct)
    print('evidence identifiers equal:', alias['evidence_id'] == direct['evidence_id'])

Both executed CLI invocations exited 0. No deep analysis provider or network service was required. Default detail was complete, with no output filtering.

Expected behavior

Preserve the caller-selected input path as a request fact, alongside the resolved subject identity and digest. AGENTS.md (Product and Authority) requires preserving caller-selected inputs and local evidence, including paths; docs/tool-design.md (Define the contract) requires preserving meaningful target choices. These are source-derived expectations; no particular parameter name is required.

Impact and scope

The saved complete Evidence cannot establish which alias the caller selected. This is a bounded loss of request provenance (P2), reproduced for a POSIX symlink to a ZIP through this CLI route. A caller can retain the selected path in a separate sidecar for future invocations; the existing Evidence alone cannot recover it. No security boundary crossing was established. Application directories, other commands, MCP and Windows were not tested.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

Node v26.12.0; macOS (darwin), arm64. NOT_ENOUGH_INFO: the exact macOS version was not recorded. Hopper/Ghidra were not involved.

Target and artifact context

A local ZIP containing the repository's tests/fixtures/interface-builder/UIKitOutlet.xml, selected through a relative file symlink, plus a direct-target control.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

The independent replay's complete-output comparisons produced:

parameters: {'integrity_policy': 'fail'}
subject name: program.zip
alias retained: False
complete records equal: True
evidence identifiers equal: True

The subject's local path points to the resolved target. Both results report the same archive digest. The submitter also inspected the verifier's saved JSON and confirmed these comparisons.

Typed outcome or recovery expected

Successful complete Evidence should retain the selected path separately while preserving canonical archive identity, digest and inventory. Changing only the subject name to the alias would not preserve both facts.

Analysis or suspected cause

Source-derived: src/cli/artifactCommands.ts:257 passes the target separately from parameters containing only integrity_policy; src/application/BinaryTargetResolver.ts:86-96 resolves the input and uses its canonical path on POSIX; src/application/DirectAnalysis.ts:487 constructs Evidence with the supplied parameters. The original spelling is not retained as an independent request fact. src/domain/evidence.ts:253 intentionally excludes subject local paths from semantic identity, so equal Evidence IDs are a consequence of the missing facts rather than a separate identifier defect.

Additional context

Prepared with AI assistance and independently reproduced. Searches covered open and closed issues and PRs. Related #5258 and #5261 concern JavaScript application analysis and its separate Evidence producer; #1171 concerns extraction refusal diagnostics, rather than successful inspection Evidence. No post-baseline changes to the cited contract or implementation files were present on fetched main. NOT_ENOUGH_INFO: a last known good version has not been established.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5479 ### 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 `inspect-artifact selected-alias.zip --json` succeeds when `selected-alias.zip` points to `program.zip`, but complete Evidence contains only the resolved target path. The caller-selected alias appears nowhere in the JSON. Inspecting the target directly returns an identical complete Evidence record. Archive contents and target digest remain correct. ### Reproduction An independent verifier ran one alias inspection and one direct-target control through a Python subprocess driver, with an isolated HOME and four XDG directories. This is a reduced, runnable version of that driver; run from the built checkout with dependencies available: ```python import json, os, pathlib, subprocess, tempfile, zipfile repo = pathlib.Path.cwd() with tempfile.TemporaryDirectory() as work: work = pathlib.Path(work) env = os.environ.copy() for key, name in [('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'), ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'), ('XDG_STATE_HOME', 'state')]: env[key] = str(work / name) (work / name).mkdir() with zipfile.ZipFile(work / 'program.zip', 'w') as archive: archive.write(repo / 'tests/fixtures/interface-builder/UIKitOutlet.xml', 'UIKitOutlet.xml') (work / 'selected-alias.zip').symlink_to('program.zip') results = [] for name in ['selected-alias.zip', 'program.zip']: result = subprocess.run( ['node', str(repo / 'scripts/rea.mjs'), 'inspect-artifact', name, '--json'], cwd=work, env=env, capture_output=True, text=True, check=True) results.append(json.loads(result.stdout)) alias, direct = results print('parameters:', alias['parameters']) print('subject name:', alias['subject']['name']) print('alias retained:', 'selected-alias.zip' in json.dumps(alias)) print('complete records equal:', alias == direct) print('evidence identifiers equal:', alias['evidence_id'] == direct['evidence_id']) ``` Both executed CLI invocations exited 0. No deep analysis provider or network service was required. Default detail was complete, with no output filtering. ### Expected behavior Preserve the caller-selected input path as a request fact, alongside the resolved subject identity and digest. `AGENTS.md` (Product and Authority) requires preserving caller-selected inputs and local evidence, including paths; `docs/tool-design.md` (Define the contract) requires preserving meaningful target choices. These are source-derived expectations; no particular parameter name is required. ### Impact and scope The saved complete Evidence cannot establish which alias the caller selected. This is a bounded loss of request provenance (P2), reproduced for a POSIX symlink to a ZIP through this CLI route. A caller can retain the selected path in a separate sidecar for future invocations; the existing Evidence alone cannot recover it. No security boundary crossing was established. Application directories, other commands, MCP and Windows were not tested. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform Node v26.12.0; macOS (darwin), arm64. NOT_ENOUGH_INFO: the exact macOS version was not recorded. Hopper/Ghidra were not involved. ### Target and artifact context A local ZIP containing the repository's `tests/fixtures/interface-builder/UIKitOutlet.xml`, selected through a relative file symlink, plus a direct-target control. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence The independent replay's complete-output comparisons produced: ```text parameters: {'integrity_policy': 'fail'} subject name: program.zip alias retained: False complete records equal: True evidence identifiers equal: True ``` The subject's local path points to the resolved target. Both results report the same archive digest. The submitter also inspected the verifier's saved JSON and confirmed these comparisons. ### Typed outcome or recovery expected Successful complete Evidence should retain the selected path separately while preserving canonical archive identity, digest and inventory. Changing only the subject name to the alias would not preserve both facts. ### Analysis or suspected cause Source-derived: `src/cli/artifactCommands.ts:257` passes the target separately from parameters containing only `integrity_policy`; `src/application/BinaryTargetResolver.ts:86-96` resolves the input and uses its canonical path on POSIX; `src/application/DirectAnalysis.ts:487` constructs Evidence with the supplied parameters. The original spelling is not retained as an independent request fact. `src/domain/evidence.ts:253` intentionally excludes subject local paths from semantic identity, so equal Evidence IDs are a consequence of the missing facts rather than a separate identifier defect. ### Additional context Prepared with AI assistance and independently reproduced. Searches covered open and closed issues and PRs. Related #5258 and #5261 concern JavaScript application analysis and its separate Evidence producer; #1171 concerns extraction refusal diagnostics, rather than successful inspection Evidence. No post-baseline changes to the cited contract or implementation files were present on fetched main. NOT_ENOUGH_INFO: a last known good version has not been established.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with sibling path normalization and symlink issues in Cluster X: #5103 ([Bug] Electron scenario CLI rejects an existing application path after symlink/..) and #5258. Both issues isolate how resolving symlinks overwrites or drops the caller-selected entrypoint path from produced Evidence.

Consolidated under Cluster X (Application Target Path Symlink Resolution & Caller-Selected Path Loss) in tracker #4330.

<!-- gh-comment-id:6290882573 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with sibling path normalization and symlink issues in **Cluster X**: #5103 (`[Bug] Electron scenario CLI rejects an existing application path after symlink/..`) and #5258. Both issues isolate how resolving symlinks overwrites or drops the caller-selected entrypoint path from produced Evidence. Consolidated under **Cluster X (Application Target Path Symlink Resolution & Caller-Selected Path Loss)** in tracker #4330.
Author
Owner

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

The same selected-path loss also affects the native app-bundle route. Independently reproduced on macOS arm64 with REA 6.7.0 at 074768901f3cdc8205b61773fb1faf6f6c7b3ca8, using a fresh minimal ad-hoc-signed Probe.app and Selected.app -> Probe.app. For each command, I ran node scripts/rea.mjs <command> <scratch>/Selected.app --format json and the direct <scratch>/Probe.app control; all six invocations exited 0.

Command subject.local_path (both runs) parameters locations Alias/direct evidence_id
inspect-signature <scratch>/Probe.app/Contents/MacOS/Probe {} [] Equal
inspect-plist <scratch>/Probe.app/Contents/MacOS/Probe {} Canonical Probe.app/Contents/Info.plist artifact path Equal
inspect-asset-catalog <scratch>/Probe.app/Contents/MacOS/Probe {"offset":0,"limit":1000} [] Equal

For each command, the complete alias/direct Evidence records were identical, and Selected.app appeared nowhere in the alias output. The minimal fixture has no compiled asset catalog; that command's successful absent-catalog inspection still loses the selected path.

Source-derived: src/application/BinaryTargetResolver.ts:91 canonicalizes the caller path before bundle resolution; src/application/AppBundleExecutable.ts:247 returns the canonical inner executable.

A fix to #5479 should cover this native app-bundle route too.

<!-- gh-comment-id:6292586236 --> @coygeek commented on GitHub (Oct 20, 2026): The same selected-path loss also affects the native app-bundle route. Independently reproduced on macOS arm64 with REA 6.7.0 at `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`, using a fresh minimal ad-hoc-signed `Probe.app` and `Selected.app -> Probe.app`. For each command, I ran `node scripts/rea.mjs <command> <scratch>/Selected.app --format json` and the direct `<scratch>/Probe.app` control; all six invocations exited 0. | Command | `subject.local_path` (both runs) | `parameters` | `locations` | Alias/direct `evidence_id` | | --- | --- | --- | --- | --- | | `inspect-signature` | `<scratch>/Probe.app/Contents/MacOS/Probe` | `{}` | `[]` | Equal | | `inspect-plist` | `<scratch>/Probe.app/Contents/MacOS/Probe` | `{}` | Canonical `Probe.app/Contents/Info.plist` artifact path | Equal | | `inspect-asset-catalog` | `<scratch>/Probe.app/Contents/MacOS/Probe` | `{"offset":0,"limit":1000}` | `[]` | Equal | For each command, the complete alias/direct Evidence records were identical, and `Selected.app` appeared nowhere in the alias output. The minimal fixture has no compiled asset catalog; that command's successful absent-catalog inspection still loses the selected path. Source-derived: `src/application/BinaryTargetResolver.ts:91` canonicalizes the caller path before bundle resolution; `src/application/AppBundleExecutable.ts:247` returns the canonical inner executable. A fix to #5479 should cover this native app-bundle route too.
Author
Owner

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

This is partially addressed at d87e73f. The original ZIP case now has a CLI regression that requires the selected alias in parameters.input_path while keeping the canonical subject and inspected contents unchanged.

The later app-bundle report also needs a narrower status: inspect-plist now emits resource_root.caller_path, and the asset-catalog resource route serializes and forwards the same selection provenance. Those paths should no longer be described as losing the alias solely because their parameters omit it.

The remaining source-derived gap is inspect-signature. It uses the executable route, and the direct Evidence constructor only adds the selected path for inspect_artifact. The earlier app-bundle runtime results are author-reported evidence from the older revision; I have not rerun them on this revision.

For closure, replay alias/direct controls for all three commands on a minimal signed app, including the successful absent-catalog case. Require the signature result to retain the selected app alias separately, and confirm the plist/asset provenance survives complete CLI delivery. Preserve canonical identity, digests and inspected facts; changing the subject display name alone would not satisfy the request. No particular provenance field name or Evidence-ID equality is required.

<!-- gh-comment-id:6380891557 --> @coygeek commented on GitHub (Oct 24, 2026): This is partially addressed at `d87e73f`. The original ZIP case now has a [CLI regression](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/cli/artifactCallerSelection.test.ts#L9-L32) that requires the selected alias in `parameters.input_path` while keeping the canonical subject and inspected contents unchanged. The later app-bundle report also needs a narrower status: `inspect-plist` now emits [resource_root.caller_path](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/native/NativeMacOSProvider.ts#L1464-L1479), and the asset-catalog resource route [serializes and forwards the same selection provenance](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/ArtifactProvider.ts#L88-L155). Those paths should no longer be described as losing the alias solely because their parameters omit it. The remaining source-derived gap is `inspect-signature`. It uses the executable route, and the [direct Evidence constructor](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/DirectAnalysis.ts#L617-L637) only adds the selected path for `inspect_artifact`. The earlier app-bundle runtime results are author-reported evidence from the older revision; I have not rerun them on this revision. For closure, replay alias/direct controls for all three commands on a minimal signed app, including the successful absent-catalog case. Require the signature result to retain the selected app alias separately, and confirm the plist/asset provenance survives complete CLI delivery. Preserve canonical identity, digests and inspected facts; changing the subject display name alone would not satisfy the request. No particular provenance field name or Evidence-ID equality is required.
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#3331
No description provided.