[PR #1593] [CLOSED] fix(plist): report binary plist __proto__ keys that results omit #2993

Closed
opened 2026-10-17 21:12:39 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1593
Author: @Adolanium
Created: 10/10/2026
Status: ❌ Closed

Base: main ← Head: fix/binary-plist-prototype-keys


📝 Commits (1)

  • 8339a5c fix(plist): report binary plist proto keys that results omit

📊 Changes

5 files changed (+220 additions, -26 deletions)

View changed files

📝 src/artifacts/apple/InterfaceBuilderAnalysis.ts (+12 -6)
📝 src/artifacts/apple/InterfaceBuilderDecodeBudget.ts (+71 -8)
📝 src/artifacts/apple/KeyedArchiveReader.test.ts (+81 -0)
📝 src/artifacts/apple/KeyedArchiveReader.ts (+16 -12)
📝 tests/boundary/filesystem/interfaceBuilderBundle.test.ts (+40 -0)

📄 Description

Summary

Fixes #1594

Binary property lists can store a dictionary key __proto__. REA omits that key from results. XML plists already report how many such entries were omitted. Binary plists reported a count of zero, so the omission was silent.

Problem and expected behavior

plist.parseBinary assigns each dictionary entry with dict[key] = value. The key __proto__ changes that object's prototype, and Object.entries then drops the entry. projectPlistValue never sees it. Keyed archive decoding and Interface Builder decoding passed omittedPrototypeKeys: 0 after parseBinary.

XML decoding already counts the same key and adds this limitation:

1 dictionary entry keyed __proto__ was omitted because REA results cannot represent that key.

A binary archive with that key should add the same limitation. An ordinary binary archive should stay free of it.

Change and scope

The binary plist walk already visits every dictionary before parseBinary. It now counts key references whose object is the ASCII or UTF-16BE string __proto__, including an extended size field. A key object shared by two dictionaries counts twice. A value equal to that string does not count. Keyed archive decoding and Interface Builder plist decoding pass the count into the existing limitation. XML decoding is unchanged. NIBArchive documents still report no plist prototype-key omission.

Contract and boundary impact

  • Semantic owner and earliest changed stage: keyed archive and Interface Builder plist projection, at result projection.
  • CLI and MCP/tool-catalog contract: inspect_keyed_archive and Interface Builder analysis can include the existing omission limitation for a binary plist. No new fields or parameters.
  • Provider, bridge, target-format, or platform compatibility: binary plist on every supported platform. XML plist results stay the same.
  • Evidence, artifact, provenance, or reconstruction contract: the __proto__ entry stays omitted, and the limitation states the count. Archive bytes and hashes stay the source bytes.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: KeyedArchiveReader.test.ts covers one ASCII __proto__ key, two dictionaries that share that key, a UTF-16BE key with an extended size, and an ordinary binary archive. interfaceBuilderBundle.test.ts covers a binary .nib whose limitation marks archive decoding partial.
  • Base reproduction or other evidence: on revision 02ef2b1c, those binary fixtures decode with omittedPrototypeKeys: 0 and no prototype-key limitation. The tests assert the limitation text, so they fail on that revision.
  • User-visible CLI/MCP output (if applicable): 1 dictionary entry keyed __proto__ was omitted because REA results cannot represent that key. Interface Builder prefixes that sentence with the archive path.
  • Remaining proof gaps: the fixtures are synthetic bplist00 bytes. This run did not round-trip them through macOS plutil or ibtool.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- src/artifacts/apple/KeyedArchiveReader.test.ts — 10 passed.
  • npm run test:focused -- tests/boundary/filesystem/interfaceBuilderBundle.test.ts — 17 passed, 24 skipped.
  • npx tsc --noEmit --pretty false — exit 0.
  • npx oxlint on the five changed files — 0 errors. Pre-existing complexity and length warnings remain on untouched functions.
  • git push pre-push npm run check:fast — typecheck and lint, 2 successful.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. A binary plist that omitted a __proto__ entry now reports the same limitation XML plists already report. Interface Builder coverage for that archive becomes partial with reason dictionary_entries_omitted.
  • Real Hopper/Ghidra, browser, or OS coverage: none required. Decoding uses the pinned plist package and local fixtures.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the omitted key is still absent from the projected result. The assignment changes only that dictionary object's prototype. Object.prototype stays unchanged. The limitation makes the omission visible.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1593 **Author:** [@Adolanium](https://github.com/Adolanium) **Created:** 10/10/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/binary-plist-prototype-keys` --- ### 📝 Commits (1) - [`8339a5c`](https://github.com/morluto/rea/commit/8339a5c74b5cbd7bda50f4f025df317f80f9aadc) fix(plist): report binary plist __proto__ keys that results omit ### 📊 Changes **5 files changed** (+220 additions, -26 deletions) <details> <summary>View changed files</summary> 📝 `src/artifacts/apple/InterfaceBuilderAnalysis.ts` (+12 -6) 📝 `src/artifacts/apple/InterfaceBuilderDecodeBudget.ts` (+71 -8) 📝 `src/artifacts/apple/KeyedArchiveReader.test.ts` (+81 -0) 📝 `src/artifacts/apple/KeyedArchiveReader.ts` (+16 -12) 📝 `tests/boundary/filesystem/interfaceBuilderBundle.test.ts` (+40 -0) </details> ### 📄 Description ## Summary Fixes #1594 Binary property lists can store a dictionary key `__proto__`. REA omits that key from results. XML plists already report how many such entries were omitted. Binary plists reported a count of zero, so the omission was silent. ## Problem and expected behavior `plist.parseBinary` assigns each dictionary entry with `dict[key] = value`. The key `__proto__` changes that object's prototype, and `Object.entries` then drops the entry. `projectPlistValue` never sees it. Keyed archive decoding and Interface Builder decoding passed `omittedPrototypeKeys: 0` after `parseBinary`. XML decoding already counts the same key and adds this limitation: `1 dictionary entry keyed __proto__ was omitted because REA results cannot represent that key.` A binary archive with that key should add the same limitation. An ordinary binary archive should stay free of it. ## Change and scope The binary plist walk already visits every dictionary before `parseBinary`. It now counts key references whose object is the ASCII or UTF-16BE string `__proto__`, including an extended size field. A key object shared by two dictionaries counts twice. A value equal to that string does not count. Keyed archive decoding and Interface Builder plist decoding pass the count into the existing limitation. XML decoding is unchanged. NIBArchive documents still report no plist prototype-key omission. ## Contract and boundary impact - Semantic owner and earliest changed stage: keyed archive and Interface Builder plist projection, at result projection. - CLI and MCP/tool-catalog contract: `inspect_keyed_archive` and Interface Builder analysis can include the existing omission limitation for a binary plist. No new fields or parameters. - Provider, bridge, target-format, or platform compatibility: binary plist on every supported platform. XML plist results stay the same. - Evidence, artifact, provenance, or reconstruction contract: the `__proto__` entry stays omitted, and the limitation states the count. Archive bytes and hashes stay the source bytes. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `KeyedArchiveReader.test.ts` covers one ASCII `__proto__` key, two dictionaries that share that key, a UTF-16BE key with an extended size, and an ordinary binary archive. `interfaceBuilderBundle.test.ts` covers a binary `.nib` whose limitation marks archive decoding partial. - Base reproduction or other evidence: on revision `02ef2b1c`, those binary fixtures decode with `omittedPrototypeKeys: 0` and no prototype-key limitation. The tests assert the limitation text, so they fail on that revision. - User-visible CLI/MCP output (if applicable): `1 dictionary entry keyed __proto__ was omitted because REA results cannot represent that key.` Interface Builder prefixes that sentence with the archive path. - Remaining proof gaps: the fixtures are synthetic `bplist00` bytes. This run did not round-trip them through macOS `plutil` or `ibtool`. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- src/artifacts/apple/KeyedArchiveReader.test.ts` — 10 passed. - `npm run test:focused -- tests/boundary/filesystem/interfaceBuilderBundle.test.ts` — 17 passed, 24 skipped. - `npx tsc --noEmit --pretty false` — exit 0. - `npx oxlint` on the five changed files — 0 errors. Pre-existing complexity and length warnings remain on untouched functions. - `git push` pre-push `npm run check:fast` — typecheck and lint, 2 successful. ## Compatibility, safety, and release - Breaking changes or migration steps: none. A binary plist that omitted a `__proto__` entry now reports the same limitation XML plists already report. Interface Builder coverage for that archive becomes `partial` with reason `dictionary_entries_omitted`. - Real Hopper/Ghidra, browser, or OS coverage: none required. Decoding uses the pinned `plist` package and local fixtures. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the omitted key is still absent from the projected result. The assignment changes only that dictionary object's prototype. `Object.prototype` stays unchanged. The limitation makes the omission visible. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:12:39 +00:00
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#2993
No description provided.