[PR #3475] [CLOSED] fix(plist): decode hexadecimal XML integers as Apple's parser does #5764

Closed
opened 2026-10-31 01:09:15 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3475
Author: @apitoagents
Created: 10/14/2026
Status: ❌ Closed

Base: main ← Head: fix/xml-plist-hexadecimal-integers


📝 Commits (2)

  • c889a42 fix(plist): decode hexadecimal XML integers as Apple's parser does
  • 561bcff refactor(plist): convert only hexadecimal integer text before decoding

📊 Changes

6 files changed (+189 additions, -21 deletions)

View changed files

📝 docs/native-investigation.md (+4 -2)
📝 src/artifacts/apple/PlistNumberLiterals.ts (+6 -3)
📝 src/domain/apple/plistNumbers.ts (+32 -0)
📝 src/domain/propertyListKeys.test.ts (+57 -0)
📝 src/domain/propertyListKeys.ts (+64 -13)
📝 tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts (+26 -3)

📄 Description

Summary

Fixes #3373.

inspect-keyed-archive / inspect_keyed_archive reported the valid XML plist integer <integer>0x2A</integer> as 0. Apple's property-list parser (and plutil -p) reads the same bytes as 42, and both the decimal spelling and a binary conversion of the same archive already returned 42. Every XML plist consumer that goes through parseXmlPropertyList (keyed archives, Info.plist resolution, Interface Builder documents, native plist inspection) now decodes hexadecimal integers to the value Apple's parser reports.

Problem and expected behavior

The pinned plist decoder reads <integer> text with parseInt(text, 10), so a 0x-prefixed literal decodes to 0. The supplemental XML number collector also only recognised decimal digits, so a hexadecimal literal was recorded as "incomplete metadata" instead of being classified. Expected: the same value as the decimal spelling (42), with the exact-integer association still available for literals beyond 2^53.

Change and scope

  • src/domain/apple/plistNumbers.ts: one owner for <integer> literal text, parsePlistIntegerLiteral, which reads an optional sign followed by decimal digits or a 0x/0X hexadecimal literal into an exact bigint and reports the radix.
  • src/domain/propertyListKeys.ts: the existing __proto__ key substitution is generalised into substituteDecoderDivergences. It locates first children the pinned decoder reads (rejected keys and hexadecimal integers) with the decoder's own XML parser, orders them by source position, and prefixes each with a CDATA section holding the text the decoder must read. The original source text is untouched, so character references and line endings keep their meaning exactly as before.
  • src/artifacts/apple/PlistNumberLiterals.ts: the number collector uses the shared literal parser, so hexadecimal literals participate in exact-integer association instead of marking metadata incomplete.
  • docs/native-investigation.md: one sentence in "Keyed archive integer precision".

Intentionally not included: whitespace handling is unchanged (the decoder already tolerates surrounding whitespace for decimal text; Apple rejects it). Mixed content such as <integer>0x2A<string>1</string></integer> keeps the decoder's first-child semantics (reads 0x2A), matching how <key> first children are already treated; the collector still marks it incomplete.

Scope decision: not applicable (ordinary fix within existing read-only boundaries).

Contract and boundary impact

  • Semantic owner and earliest changed stage: parseXmlPropertyList (domain), XML plist decode.
  • Contribution scope fit and linked maintainer decision: routine fix; not applicable.
  • CLI and MCP/tool-catalog contract: unchanged. Only the decoded value of hexadecimal <integer> elements changes (from 0 to the Apple-parsed value).
  • Provider, bridge, target-format, or platform compatibility: XML plist only; binary plists and OpenStep unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: archive digests and object identities unchanged; large hexadecimal literals now reach the existing { "$plist_type": "integer", "decimal": "<exact digits>" } representation.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:

    • src/domain/propertyListKeys.test.ts: hexadecimal spellings (0x2A, 0X2a, +0x2A, -0x2A, entity-encoded, CDATA, trailing comment, surrounding whitespace, 2^53+1, 0xFFFFFFFFFFFFFFFF) interleaved with __proto__ keys before and after, to cover substitution ordering; hexadecimal text outside integers and non-literal integers (0x, 0x1G) stay as decoded; first-child semantics for mixed content.
    • tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts: new hexadecimal-integer (0x2A → 42, no precision/incomplete-metadata limitation) and unsafe-hexadecimal-integer (0x20000000000001 → exact 9007199254740993) cases, through both inspectBundleKeyedArchive and the built CLI.
  • Base reproduction (executed): the issue's sample.plist against the compiled runtime from main (cbc0e97c2) returned normalized_result.objects[1].value: 0. After this change the same command returns value: 42, /usr/bin/plutil -p reports 42, and the decimal control (sed 's/0x2A/42/') returns 42. Both runs exit 0.

  • User-visible CLI/MCP output: {"id":1,"kind":"scalar","class_id":null,"class_name":null,"status":"observed","value":42}.

  • Remaining proof gaps: plutil comparison ran on macOS only. Occurrence of hexadecimal integers in real application archives was not measured (the issue reports the same).

  • 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:local -- src/domain/propertyListKeys.test.ts — 49 passed
  • npm run test:focused -- tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts tests/boundary/filesystem/keyedArchiveIntegerAmbiguity.test.ts tests/boundary/providers/native/keyedArchiveEncoding.test.ts tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts — 5 files, 33 passed
  • npm run test:local -- src/artifacts/apple/KeyedArchiveReader.test.ts src/native/parsers/plist.test.ts src/domain/propertyListXmlText.test.ts — 32 passed
  • npm run check:fast — typecheck and lint successful
  • npm run verify:module-boundaries — 1878 source files checked, no violations
  • node scripts/check-doc-facts.mjs — no output (pass)
  • node scripts/rea.mjs inspect-keyed-archive sample.plist --json — value: 42 after the change (0 on the base revision)

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Hexadecimal integers previously decoded to 0; they now decode to their value.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable; plutil cross-check on macOS 13 (Darwin 22.3.0).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: read-only parsing; no new inputs, processes, or network access.

Author readiness

Human owner: @apitoagents

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • 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/3475 **Author:** [@apitoagents](https://github.com/apitoagents) **Created:** 10/14/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/xml-plist-hexadecimal-integers` --- ### 📝 Commits (2) - [`c889a42`](https://github.com/morluto/rea/commit/c889a424043db5b316f379e607f9eda5ce846a1e) fix(plist): decode hexadecimal XML integers as Apple's parser does - [`561bcff`](https://github.com/morluto/rea/commit/561bcff130662a99370e9cef3243b92566d08698) refactor(plist): convert only hexadecimal integer text before decoding ### 📊 Changes **6 files changed** (+189 additions, -21 deletions) <details> <summary>View changed files</summary> 📝 `docs/native-investigation.md` (+4 -2) 📝 `src/artifacts/apple/PlistNumberLiterals.ts` (+6 -3) 📝 `src/domain/apple/plistNumbers.ts` (+32 -0) 📝 `src/domain/propertyListKeys.test.ts` (+57 -0) 📝 `src/domain/propertyListKeys.ts` (+64 -13) 📝 `tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts` (+26 -3) </details> ### 📄 Description ## Summary Fixes #3373. `inspect-keyed-archive` / `inspect_keyed_archive` reported the valid XML plist integer `<integer>0x2A</integer>` as `0`. Apple's property-list parser (and `plutil -p`) reads the same bytes as `42`, and both the decimal spelling and a binary conversion of the same archive already returned `42`. Every XML plist consumer that goes through `parseXmlPropertyList` (keyed archives, Info.plist resolution, Interface Builder documents, native plist inspection) now decodes hexadecimal integers to the value Apple's parser reports. ## Problem and expected behavior The pinned `plist` decoder reads `<integer>` text with `parseInt(text, 10)`, so a `0x`-prefixed literal decodes to `0`. The supplemental XML number collector also only recognised decimal digits, so a hexadecimal literal was recorded as "incomplete metadata" instead of being classified. Expected: the same value as the decimal spelling (`42`), with the exact-integer association still available for literals beyond 2^53. ## Change and scope - `src/domain/apple/plistNumbers.ts`: one owner for `<integer>` literal text, `parsePlistIntegerLiteral`, which reads an optional sign followed by decimal digits or a `0x`/`0X` hexadecimal literal into an exact `bigint` and reports the radix. - `src/domain/propertyListKeys.ts`: the existing `__proto__` key substitution is generalised into `substituteDecoderDivergences`. It locates first children the pinned decoder reads (rejected keys and hexadecimal integers) with the decoder's own XML parser, orders them by source position, and prefixes each with a CDATA section holding the text the decoder must read. The original source text is untouched, so character references and line endings keep their meaning exactly as before. - `src/artifacts/apple/PlistNumberLiterals.ts`: the number collector uses the shared literal parser, so hexadecimal literals participate in exact-integer association instead of marking metadata incomplete. - `docs/native-investigation.md`: one sentence in "Keyed archive integer precision". Intentionally not included: whitespace handling is unchanged (the decoder already tolerates surrounding whitespace for decimal text; Apple rejects it). Mixed content such as `<integer>0x2A<string>1</string></integer>` keeps the decoder's first-child semantics (reads `0x2A`), matching how `<key>` first children are already treated; the collector still marks it incomplete. Scope decision: not applicable (ordinary fix within existing read-only boundaries). ## Contract and boundary impact - Semantic owner and earliest changed stage: `parseXmlPropertyList` (domain), XML plist decode. - Contribution scope fit and linked maintainer decision: routine fix; not applicable. - CLI and MCP/tool-catalog contract: unchanged. Only the decoded value of hexadecimal `<integer>` elements changes (from `0` to the Apple-parsed value). - Provider, bridge, target-format, or platform compatibility: XML plist only; binary plists and OpenStep unchanged. - Evidence, artifact, provenance, or reconstruction contract: archive digests and object identities unchanged; large hexadecimal literals now reach the existing `{ "$plist_type": "integer", "decimal": "<exact digits>" }` representation. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `src/domain/propertyListKeys.test.ts`: hexadecimal spellings (`0x2A`, `0X2a`, `+0x2A`, `-0x2A`, entity-encoded, CDATA, trailing comment, surrounding whitespace, 2^53+1, `0xFFFFFFFFFFFFFFFF`) interleaved with `__proto__` keys before and after, to cover substitution ordering; hexadecimal text outside integers and non-literal integers (`0x`, `0x1G`) stay as decoded; first-child semantics for mixed content. - `tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts`: new `hexadecimal-integer` (`0x2A` → `42`, no precision/incomplete-metadata limitation) and `unsafe-hexadecimal-integer` (`0x20000000000001` → exact `9007199254740993`) cases, through both `inspectBundleKeyedArchive` and the built CLI. - Base reproduction (executed): the issue's `sample.plist` against the compiled runtime from `main` (`cbc0e97c2`) returned `normalized_result.objects[1].value: 0`. After this change the same command returns `value: 42`, `/usr/bin/plutil -p` reports `42`, and the decimal control (`sed 's/0x2A/42/'`) returns `42`. Both runs exit 0. - User-visible CLI/MCP output: `{"id":1,"kind":"scalar","class_id":null,"class_name":null,"status":"observed","value":42}`. - Remaining proof gaps: `plutil` comparison ran on macOS only. Occurrence of hexadecimal integers in real application archives was not measured (the issue reports the same). - [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:local -- src/domain/propertyListKeys.test.ts` — 49 passed - `npm run test:focused -- tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts tests/boundary/filesystem/keyedArchiveIntegerAmbiguity.test.ts tests/boundary/providers/native/keyedArchiveEncoding.test.ts tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts` — 5 files, 33 passed - `npm run test:local -- src/artifacts/apple/KeyedArchiveReader.test.ts src/native/parsers/plist.test.ts src/domain/propertyListXmlText.test.ts` — 32 passed - `npm run check:fast` — typecheck and lint successful - `npm run verify:module-boundaries` — 1878 source files checked, no violations - `node scripts/check-doc-facts.mjs` — no output (pass) - `node scripts/rea.mjs inspect-keyed-archive sample.plist --json` — `value: 42` after the change (`0` on the base revision) ## Compatibility, safety, and release - Breaking changes or migration steps: none. Hexadecimal integers previously decoded to `0`; they now decode to their value. - Real Hopper/Ghidra, browser, or OS coverage: not applicable; `plutil` cross-check on macOS 13 (Darwin 22.3.0). - Package or release metadata impact: none. - Security, privacy, process, or containment review: read-only parsing; no new inputs, processes, or network access. ## Author readiness Human owner: @apitoagents - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [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-31 01:09:15 +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#5764
No description provided.