[PR #841] [MERGED] fix(native): inspect plists containing data, dates, and non-finite reals #2279

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/841
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/plist-non-json-values


📝 Commits (1)

  • 5ead2cf fix(native): inspect plists containing data, dates, and non-finite reals

📊 Changes

6 files changed (+260 additions, -56 deletions)

View changed files

📝 src/artifacts/KeyedArchiveReader.ts (+6 -21)
➕ src/domain/plistValue.test.ts (+40 -0)
➕ src/domain/plistValue.ts (+50 -0)
📝 src/native/NativeMacOSProvider.ts (+29 -11)
📝 src/native/parsers/plist.ts (+67 -24)
📝 tests/boundary/cli/nativePlistCli.test.ts (+68 -0)

📄 Description

Summary

inspect_plist (rea inspect-plist, MCP inspect_plist) converts every plist with plutil -convert json. Apple's JSON converter rejects any plist containing <data>, <date>, or a non-finite <real> (Invalid object in plist for JSON format), so REA fails with a cause-free error:

{ "code": "execution_failure", "details": { "provider_id": "native-macos", "operation": "inspect_plist" } }

These plists are valid and common. On this macOS host, 233 of 4,001 plists inside installed .app bundles fail JSON conversion. Among app Info.plists, /System/Applications/Stocks.app fails because of UIApplicationShortcutItems/0/UIApplicationShortcutItemUserInfo <data>, so rea inspect-plist /System/Applications/Stocks.app fails on main.

Fix

  • Run the JSON conversion with acceptNonZero. If it succeeds, nothing changes: the same value, provenance, and empty limitations.
  • If it exits nonzero, run plutil -convert xml1 -o - -- $PLIST, which is lossless for every plist type, and decode it with the existing plist dependency.
    • <data> → { "$plist_type": "data", "base64": "…" } and <date> → { "$plist_type": "date", "iso8601": "…" }, matching the representation inspect_keyed_archive already returns.
    • Non-finite <real> → { "$plist_type": "real", "value": null }, an explicit unknown. The plist package parses nan, +infinity, and -infinity all as NaN, so reporting a specific value would be a guess.
    • limitations explains the XML decoding and counts unknown reals.
    • provenance records the file classification, the failed JSON conversion (exit 1), and the XML conversion.
  • If the XML conversion also fails (for example, a malformed plist), the existing provider-failure path is unchanged.
  • The $plist_type projection moves from KeyedArchiveReader into src/domain/plistValue.ts and is shared. The keyed-archive reader still rejects non-finite reals as before; only the location of the code changed.

Contract / behavior changes

  • No schema or catalog changes: value is already jsonValueSchema, and the tool description ("Returns normalized JSON rather than plutil text") stays accurate.
  • Plists that used to fail now succeed. Output for plists that JSON can express is unchanged.

Verification

  • Real plutil CLI regression (tests/boundary/cli/nativePlistCli.test.ts, darwin-only like the existing cases): XML and binary plists containing <data>, <date>, and <real>nan</real> check value, bundle metadata, the three-step provenance, and limitations. Both fail on the original source and pass with the fix.
  • Domain unit test for projectPlistValue (typed values, unknown-real counting, rejection of non-plist values and excessive nesting).
  • Path-equivalence check against real plists on macOS (Darwin 25.6): 475 bundle plists were decoded through both paths with the built parsers. For all 469 that JSON can express, the XML path gives a deep-equal value and bundle. All 6 that JSON rejects decode through XML, and none fail.
  • rea inspect-plist /System/Applications/Stocks.app now succeeds (CFBundleIdentifier: com.apple.stocks).
  • npm run test:focused for nativePlistCli, nativeProvider, nativeTextRobustness, KeyedArchiveReader, and plistValue: 35 tests passed. npm run check:changed and npm run check:fast passed.

MCP uses the same NativeMacOSProvider path, so behavior is equivalent. I didn't run a separate stdio MCP session. No Hopper/Ghidra/IDA involvement.

Not in this PR

Integers beyond ±2^53 are still rounded in both decode paths. For example, <integer>18446744073709551615</integer> is reported as 18446744073709552000 with no limitation. Native plutil reports the exact value, so I'll handle that in a separate PR.

🤖 Generated with Claude Code


🔄 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/841 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/7/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/plist-non-json-values` --- ### 📝 Commits (1) - [`5ead2cf`](https://github.com/morluto/rea/commit/5ead2cfbc47896b43502363d3568330f5d94762e) fix(native): inspect plists containing data, dates, and non-finite reals ### 📊 Changes **6 files changed** (+260 additions, -56 deletions) <details> <summary>View changed files</summary> 📝 `src/artifacts/KeyedArchiveReader.ts` (+6 -21) ➕ `src/domain/plistValue.test.ts` (+40 -0) ➕ `src/domain/plistValue.ts` (+50 -0) 📝 `src/native/NativeMacOSProvider.ts` (+29 -11) 📝 `src/native/parsers/plist.ts` (+67 -24) 📝 `tests/boundary/cli/nativePlistCli.test.ts` (+68 -0) </details> ### 📄 Description ## Summary `inspect_plist` (`rea inspect-plist`, MCP `inspect_plist`) converts every plist with `plutil -convert json`. Apple's JSON converter rejects any plist containing `<data>`, `<date>`, or a non-finite `<real>` (`Invalid object in plist for JSON format`), so REA fails with a cause-free error: ```json { "code": "execution_failure", "details": { "provider_id": "native-macos", "operation": "inspect_plist" } } ``` These plists are valid and common. On this macOS host, **233 of 4,001** plists inside installed `.app` bundles fail JSON conversion. Among app `Info.plist`s, `/System/Applications/Stocks.app` fails because of `UIApplicationShortcutItems/0/UIApplicationShortcutItemUserInfo` `<data>`, so `rea inspect-plist /System/Applications/Stocks.app` fails on `main`. ## Fix - Run the JSON conversion with `acceptNonZero`. If it succeeds, nothing changes: the same value, provenance, and empty limitations. - If it exits nonzero, run `plutil -convert xml1 -o - -- $PLIST`, which is lossless for every plist type, and decode it with the existing `plist` dependency. - `<data>` → `{ "$plist_type": "data", "base64": "…" }` and `<date>` → `{ "$plist_type": "date", "iso8601": "…" }`, matching the representation `inspect_keyed_archive` already returns. - Non-finite `<real>` → `{ "$plist_type": "real", "value": null }`, an explicit unknown. The `plist` package parses `nan`, `+infinity`, and `-infinity` all as `NaN`, so reporting a specific value would be a guess. - `limitations` explains the XML decoding and counts unknown reals. - `provenance` records the `file` classification, the **failed** JSON conversion (exit 1), and the XML conversion. - If the XML conversion also fails (for example, a malformed plist), the existing provider-failure path is unchanged. - The `$plist_type` projection moves from `KeyedArchiveReader` into `src/domain/plistValue.ts` and is shared. The keyed-archive reader still rejects non-finite reals as before; only the location of the code changed. ## Contract / behavior changes - No schema or catalog changes: `value` is already `jsonValueSchema`, and the tool description ("Returns normalized JSON rather than plutil text") stays accurate. - Plists that used to fail now succeed. Output for plists that JSON can express is unchanged. ## Verification - **Real `plutil` CLI regression** (`tests/boundary/cli/nativePlistCli.test.ts`, darwin-only like the existing cases): XML and binary plists containing `<data>`, `<date>`, and `<real>nan</real>` check value, bundle metadata, the three-step provenance, and limitations. **Both fail on the original source and pass with the fix.** - Domain unit test for `projectPlistValue` (typed values, unknown-real counting, rejection of non-plist values and excessive nesting). - Path-equivalence check against real plists on macOS (Darwin 25.6): 475 bundle plists were decoded through both paths with the built parsers. For all **469** that JSON can express, the XML path gives a **deep-equal** `value` and `bundle`. All 6 that JSON rejects decode through XML, and none fail. - `rea inspect-plist /System/Applications/Stocks.app` now succeeds (`CFBundleIdentifier: com.apple.stocks`). - `npm run test:focused` for `nativePlistCli`, `nativeProvider`, `nativeTextRobustness`, `KeyedArchiveReader`, and `plistValue`: 35 tests passed. `npm run check:changed` and `npm run check:fast` passed. MCP uses the same `NativeMacOSProvider` path, so behavior is equivalent. I didn't run a separate stdio MCP session. No Hopper/Ghidra/IDA involvement. ## Not in this PR Integers beyond ±2^53 are still rounded in both decode paths. For example, `<integer>18446744073709551615</integer>` is reported as `18446744073709552000` with no limitation. Native `plutil` reports the exact value, so I'll handle that in a separate PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-16 21:16: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#2279
No description provided.