[GH-ISSUE #4785] [Bug] Keyed archive scalar $top is accepted as wrapper-named roots #2488

Closed
opened 2026-10-17 21:05:37 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

  • Searched open and closed reports for $top scalar/root failures.
  • Only authored harmless fixture paths and values are included.

Area

Evidence, artifacts, or reconstruction

Observed behavior

The built CLI accepts an NSKeyedArchiver plist whose $top is a data scalar and invents root names from REA's JSON wrapper. inspect-keyed-archive exits0 with roots: {"$plist_type":"data","base64":"AA=="}.

Reproduction

Executed once: use plist.js build to write {$archiver:"NSKeyedArchiver",$version:100000,$objects:["$null"],$top:new Uint8Array([0])} to an XML plist. Run node scripts/rea.mjs inspect-keyed-archive <file> --json. The command succeeds with the fake roots above. Three source-decoder regressions for data/date/non-finite-real $top values also fail (XML branches executed before their binary branches).

Expected behavior

Reject the archive's scalar $top rather than treating generated presentation fields as serialized root names. The graph's existing contract requires a nonempty root dictionary: projectKeyedArchive in src/domain/apple/keyedArchive.ts already has a Keyed archive has no $top roots guard.

Impact and scope

One authored public CLI case reproduced false archive acceptance and fabricated root names. No target execution or mutation. Real-world frequency is unknown. Valid root dictionaries, including dictionaries with literal $plist_type fields, must retain their original names.

REA version

Working tree based on 5d6029ea6 with the #4295 scalar-kind fix under development. $top admission remains unchanged from main in this probe.

Runtime and platform

Node.js24.18.0, macOS arm64, installed plist5.0.0. No deep provider.

Target and artifact context

Authored XML plist; data payload is one zero byte.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Observed exit0, normalized roots {"$plist_type":"data","base64":"AA=="}. The new source regression tests/boundary/filesystem/keyedArchiveScalarStructure.test.ts expects the existing missing-root error; all3scalar cases fail before their fix. Binary malformed-top and MCP behavior were not executed at report time.

Typed outcome or recovery expected

A typed malformed/unsupported archive outcome. Keep valid root selection, original indices and explicit unknown values intact. Check XML and binary encodings plus a literal-dictionary control.

Analysis or suspected cause

Source-derived: plist scalars become ordinary JSON wrapper objects. $top's dictionary check then consumes that presentation shape instead of the producer's scalar identity. Apply preserved scalar facts consistently to all dictionary consumers.

Additional context

Found while addressing #4295 (object-kind misclassification). #3913 concerns a genuine scalar root named UID; #4714 concerns XML parser recovery. This is a structurally valid plist with an invalid NSKeyedArchiver root-map type.

Originally created by @morluto on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4785 ### Before submitting - [x] Searched open and closed reports for `$top` scalar/root failures. - [x] Only authored harmless fixture paths and values are included. ### Area Evidence, artifacts, or reconstruction ### Observed behavior The built CLI accepts an NSKeyedArchiver plist whose `$top` is a data scalar and invents root names from REA's JSON wrapper. `inspect-keyed-archive` exits0 with `roots: {"$plist_type":"data","base64":"AA=="}`. ### Reproduction Executed once: use plist.js `build` to write `{$archiver:"NSKeyedArchiver",$version:100000,$objects:["$null"],$top:new Uint8Array([0])}` to an XML plist. Run `node scripts/rea.mjs inspect-keyed-archive <file> --json`. The command succeeds with the fake roots above. Three source-decoder regressions for data/date/non-finite-real `$top` values also fail (XML branches executed before their binary branches). ### Expected behavior Reject the archive's scalar `$top` rather than treating generated presentation fields as serialized root names. The graph's existing contract requires a nonempty root dictionary: `projectKeyedArchive` in `src/domain/apple/keyedArchive.ts` already has a `Keyed archive has no $top roots` guard. ### Impact and scope One authored public CLI case reproduced false archive acceptance and fabricated root names. No target execution or mutation. Real-world frequency is unknown. Valid root dictionaries, including dictionaries with literal `$plist_type` fields, must retain their original names. ### REA version Working tree based on 5d6029ea6 with the #4295 scalar-kind fix under development. `$top` admission remains unchanged from main in this probe. ### Runtime and platform Node.js24.18.0, macOS arm64, installed plist5.0.0. No deep provider. ### Target and artifact context Authored XML plist; data payload is one zero byte. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Observed exit0, normalized roots `{"$plist_type":"data","base64":"AA=="}`. The new source regression `tests/boundary/filesystem/keyedArchiveScalarStructure.test.ts` expects the existing missing-root error; all3scalar cases fail before their fix. Binary malformed-top and MCP behavior were not executed at report time. ### Typed outcome or recovery expected A typed malformed/unsupported archive outcome. Keep valid root selection, original indices and explicit unknown values intact. Check XML and binary encodings plus a literal-dictionary control. ### Analysis or suspected cause Source-derived: plist scalars become ordinary JSON wrapper objects. `$top`'s dictionary check then consumes that presentation shape instead of the producer's scalar identity. Apply preserved scalar facts consistently to all dictionary consumers. ### Additional context Found while addressing #4295 (object-kind misclassification). #3913 concerns a genuine scalar root named UID; #4714 concerns XML parser recovery. This is a structurally valid plist with an invalid NSKeyedArchiver root-map type.
bsakaguchi 2026-10-17 21:05:37 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Fixed on main in 2cf9c28f2.

The graph owner now projects decoded plist values and retains generated scalar identity through all dictionary/UID decisions. Literal wrapper-shaped dictionaries remain dictionaries; scalar $top values are rejected as malformed. The intermediate JSON clone is removed. Numeric projection, unknown-value reporting, source bytes and binary prototype-key partial evidence remain covered.

Adapted the relevant scalar proposal from @Jax367 in #4317, with additional scalar-root and prototype controls; unrelated PR changes were excluded. The captured Foundation data-node expectation and its manifest digest were corrected after reviewing the unchanged producer bytes/source; this was not a new Foundation capture.

Validation: 175 distinct focused checks across 23 files passed, one skipped; build, check:fast, docs:check and commit/push hooks passed. Both XML and binary cases were exercised through the built CLI and actual stdio MCP. Baseline controls reproduced the incorrect kinds and scalar root acceptance. 24 tiny completed CLI cost comparisons retained ordinary full replies and Evidence IDs byte for byte; no statistical speed claim. No heavy profiling, investigated-target execution or real deep-provider verification.

<!-- gh-comment-id:6233819800 --> @morluto commented on GitHub (Oct 17, 2026): Fixed on main in 2cf9c28f2. The graph owner now projects decoded plist values and retains generated scalar identity through all dictionary/UID decisions. Literal wrapper-shaped dictionaries remain dictionaries; scalar `$top` values are rejected as malformed. The intermediate JSON clone is removed. Numeric projection, unknown-value reporting, source bytes and binary prototype-key partial evidence remain covered. Adapted the relevant scalar proposal from @Jax367 in #4317, with additional scalar-root and prototype controls; unrelated PR changes were excluded. The captured Foundation data-node expectation and its manifest digest were corrected after reviewing the unchanged producer bytes/source; this was not a new Foundation capture. Validation: 175 distinct focused checks across 23 files passed, one skipped; build, check:fast, docs:check and commit/push hooks passed. Both XML and binary cases were exercised through the built CLI and actual stdio MCP. Baseline controls reproduced the incorrect kinds and scalar root acceptance. 24 tiny completed CLI cost comparisons retained ordinary full replies and Evidence IDs byte for byte; no statistical speed claim. No heavy profiling, investigated-target execution or real deep-provider verification.
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#2488
No description provided.