[GH-ISSUE #5769] [Bug] inspect-keyed-archive reports an invalid binary date marker as an observed timestamp #3506

Closed
opened 2026-10-31 00:41:16 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

  • I searched open and closed issues and open PRs for a related report.
  • The reproduction uses only synthetic archive data and no credentials.

Area

Evidence, artifacts, or reconstruction

Observed behavior

inspect-keyed-archive and inspect_keyed_archive accept a binary date marker 0x30 and report object 1 as an observed date 2001-01-01T00:00:00.100Z. Binary plist dates require marker 0x33. The same bytes are rejected by Python plistlib.loads (InvalidFileException) and macOS plutil -lint; the unmodified 0x33 control is accepted by all three.

Reproduction

From a built checkout, run this Python standard-library fixture generator and CLI replay. It writes disposable files, uses a real binary UID, and runs no target code. The final three fixtures were also verified through open_binary + inspect_keyed_archive over stdio MCP with the selected advertised SDK output schema.

python3 - <<'PYTHON'
import datetime, hashlib, json, pathlib, plistlib, struct, subprocess, tempfile
root=pathlib.Path(tempfile.mkdtemp(prefix='rea-keyed-date-'))
control=plistlib.dumps({'$archiver':'NSKeyedArchiver','$version':100000,'$objects':['$null',datetime.datetime(2001,1,1,0,0,0,100000)],'$top':{'root':plistlib.UID(1)}},fmt=plistlib.FMT_BINARY)
pattern=b'\x33'+struct.pack('>d',.1)
assert control.count(pattern)==1
offset=control.index(pattern)
for name in ('valid','invalid-date-marker','submillisecond'):
    data=bytearray(control)
    if name=='invalid-date-marker': data[offset]=0x30
    if name=='submillisecond': struct.pack_into('>d',data,offset+1,.1005)
    path=root/(name+'.plist');path.write_bytes(data)
    p=subprocess.run(['node','scripts/rea.mjs','inspect-keyed-archive',str(path),'--json'],capture_output=True,text=True,check=True)
    result=json.loads(p.stdout)['normalized_result']
    print(json.dumps({'case':name,'sha256':hashlib.sha256(data).hexdigest(),'object':result['objects'][1],'limitations':result['limitations']}))
PYTHON

Expected behavior

Refuse a malformed reachable scalar with a typed input/format failure, preserving the valid control. Do not project an invalid marker as a known observed timestamp. Apple's binary plist reader accepts the exact date marker and rejects other type-3 markers; this is also observed in the local plutil control.

Impact and scope

Confirmed evidence-integrity error on a reduced synthetic archive. Frequency in real targets and other scalar forms are unmeasured. No target execution, file mutation, or security exploit is claimed.

REA version

6.7.0, built source 3a0e91dd914145ecb9accbdeb68ecf7fe4d5133d.

Runtime and platform

Node.js 24.18.0; macOS 26.3 arm64; pinned plist dependency 5.0.0.

Target and artifact context

Self-authored binary NSKeyedArchiver document with $objects = ["$null", date] and $top.root encoded as a binary UID to object 1. The valid, malformed-marker, and sub-millisecond fixtures differ only in the selected date marker/payload. All bytes were unchanged after CLI/MCP inspection.

Interface

CLI and MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Both interfaces return:

{"id":1,"kind":"scalar","status":"observed","value":{"$plist_type":"date","iso8601":"2001-01-01T00:00:00.100Z"}}

The malformed input has marker 0x30 at offset 77 and SHA-256 ac5696b866c312feb53c2e82edd8fea79dbeb572eb1b07e4291603faa41e8c0e. REA only adds a limitation about incomplete number metadata. That does not identify the invalid date. plutil -lint exits 1 with (Unexpected character b at line 1); it exits 0 on the valid control.

Typed outcome or recovery expected

A typed malformed-input refusal for the invalid date; the valid archive remains inspectable.

Analysis or suspected cause

Source-derived: the shared binary expansion preflight traverses reachable objects but does not validate date/numeric scalar marker contracts. The scalar scanner detects incomplete metadata but parsing continues, and plist 5.0.0's date decoder reads eight bytes without checking the low nibble. This is already present on the keyed-archive path, independently of the unpublished native inspect_plist byte-decoding candidate.

Additional context

Prepared with AI assistance. Related #4852 covers malformed XML scalar text; #5703 concerns valid binary dates losing milliseconds through plutil. Neither covers malformed binary keyed-archive date admission. Keyed-archive/Interface Builder adjacent source paths are being audited; only the keyed-archive public workflows are reproduced here. Last known good: NOT_ENOUGH_INFO; no version range was tested.

Originally created by @morluto on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5769 ### Before submitting - [x] I searched open and closed issues and open PRs for a related report. - [x] The reproduction uses only synthetic archive data and no credentials. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `inspect-keyed-archive` and `inspect_keyed_archive` accept a binary date marker `0x30` and report object 1 as an **observed** date `2001-01-01T00:00:00.100Z`. Binary plist dates require marker `0x33`. The same bytes are rejected by Python `plistlib.loads` (`InvalidFileException`) and macOS `plutil -lint`; the unmodified `0x33` control is accepted by all three. ### Reproduction From a built checkout, run this Python standard-library fixture generator and CLI replay. It writes disposable files, uses a real binary UID, and runs no target code. The final three fixtures were also verified through `open_binary` + `inspect_keyed_archive` over stdio MCP with the selected advertised SDK output schema. ```sh python3 - <<'PYTHON' import datetime, hashlib, json, pathlib, plistlib, struct, subprocess, tempfile root=pathlib.Path(tempfile.mkdtemp(prefix='rea-keyed-date-')) control=plistlib.dumps({'$archiver':'NSKeyedArchiver','$version':100000,'$objects':['$null',datetime.datetime(2001,1,1,0,0,0,100000)],'$top':{'root':plistlib.UID(1)}},fmt=plistlib.FMT_BINARY) pattern=b'\x33'+struct.pack('>d',.1) assert control.count(pattern)==1 offset=control.index(pattern) for name in ('valid','invalid-date-marker','submillisecond'): data=bytearray(control) if name=='invalid-date-marker': data[offset]=0x30 if name=='submillisecond': struct.pack_into('>d',data,offset+1,.1005) path=root/(name+'.plist');path.write_bytes(data) p=subprocess.run(['node','scripts/rea.mjs','inspect-keyed-archive',str(path),'--json'],capture_output=True,text=True,check=True) result=json.loads(p.stdout)['normalized_result'] print(json.dumps({'case':name,'sha256':hashlib.sha256(data).hexdigest(),'object':result['objects'][1],'limitations':result['limitations']})) PYTHON ``` ### Expected behavior Refuse a malformed reachable scalar with a typed input/format failure, preserving the valid control. Do not project an invalid marker as a known observed timestamp. Apple's [binary plist reader](https://github.com/apple-oss-distributions/CF/blob/main/CFBinaryPList.c) accepts the exact date marker and rejects other type-3 markers; this is also observed in the local `plutil` control. ### Impact and scope Confirmed evidence-integrity error on a reduced synthetic archive. Frequency in real targets and other scalar forms are unmeasured. No target execution, file mutation, or security exploit is claimed. ### REA version 6.7.0, built source `3a0e91dd914145ecb9accbdeb68ecf7fe4d5133d`. ### Runtime and platform Node.js 24.18.0; macOS 26.3 arm64; pinned plist dependency 5.0.0. ### Target and artifact context Self-authored binary NSKeyedArchiver document with `$objects = ["$null", date]` and `$top.root` encoded as a binary UID to object 1. The valid, malformed-marker, and sub-millisecond fixtures differ only in the selected date marker/payload. All bytes were unchanged after CLI/MCP inspection. ### Interface CLI and MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Both interfaces return: ```json {"id":1,"kind":"scalar","status":"observed","value":{"$plist_type":"date","iso8601":"2001-01-01T00:00:00.100Z"}} ``` The malformed input has marker `0x30` at offset 77 and SHA-256 `ac5696b866c312feb53c2e82edd8fea79dbeb572eb1b07e4291603faa41e8c0e`. REA only adds a limitation about incomplete **number** metadata. That does not identify the invalid date. `plutil -lint` exits 1 with `(Unexpected character b at line 1)`; it exits 0 on the valid control. ### Typed outcome or recovery expected A typed malformed-input refusal for the invalid date; the valid archive remains inspectable. ### Analysis or suspected cause Source-derived: the shared binary expansion preflight traverses reachable objects but does not validate date/numeric scalar marker contracts. The scalar scanner detects incomplete metadata but parsing continues, and plist 5.0.0's date decoder reads eight bytes without checking the low nibble. This is already present on the keyed-archive path, independently of the unpublished native `inspect_plist` byte-decoding candidate. ### Additional context Prepared with AI assistance. Related #4852 covers malformed **XML** scalar text; #5703 concerns valid binary dates losing milliseconds through `plutil`. Neither covers malformed binary keyed-archive date admission. Keyed-archive/Interface Builder adjacent source paths are being audited; only the keyed-archive public workflows are reproduced here. Last known good: NOT_ENOUGH_INFO; no version range was tested.
bsakaguchi 2026-10-31 00:41:16 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
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#3506
No description provided.