[GH-ISSUE #5188] [Bug] Short reads suppress artifact signatures and reject ASAR admission #3182

Closed
opened 2026-10-31 00:37:35 +00:00 by bsakaguchi · 1 comment
Owner

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

At 2e5171e391b83f8a467d0861fd452c1f78621fd8, root classification reports file for an official compact ASAR and a synthetic DMG signature fixture when positional reads are deterministically capped at three bytes. Direct admitAsarHeader() also rejects the same ASAR with reason: format and Truncated or invalid ASAR size pickle.

Without that cap, the same baseline reports asar / dmg, and ASAR admission succeeds. Both variants calculate identical complete archive digests. This is a controlled production-boundary reproduction with real descriptors, not a claim that natural OS short reads were observed.

Reproduction

Executed on macOS with Node 24.18.0 and @electron/asar 4.2.0:

  1. Package inert main.mjs and package.json with createPackageWithOptions. Separately write 1,024 zero bytes with koly at offset 512 as a classifier-only DMG fixture.
  2. In an isolated Node process, wrap the real FileHandle prototype's positional read(buffer, offset, length, position) to call the native method with Math.min(length, 3). Restore it after the observation.
  3. Call production classifyAndHashRoot() with the real file metadata and an ArtifactResourceScope, closing the scope in finally. Call admitAsarHeader() through a real ASAR descriptor and close it in finally.
  4. Compare with an effectively unrestricted 65,536-byte cap. Each configuration was run once. All admitted descriptors were closed.
Observation 3-byte reads Unrestricted control
Compact ASAR root file asar
DMG signature fixture file dmg
ASAR header admission format failure succeeds

Expected behavior

A positive short read is partial progress, rather than proof of EOF. Fill the required fixed prefix/trailer or continue bounded classification until zero-byte EOF, a completed observation, cancellation, or the real resource limit.

Impact and scope

Observed in a controlled local reproduction. Natural occurrence frequency is NOT_ENOUGH_INFO. This can silently suppress a container role or reject ASAR admission. The DMG fixture proves signature handling only, not complete DMG decoding. Windows and other operating systems were not verified.

REA version

6.7.0; source revision 2e5171e391b83f8a467d0861fd452c1f78621fd8.

Source-derived cause

src/artifacts/inventory/classify.ts assumes the first prefix read and four-byte DMG trailer read fill their buffers; src/artifacts/AsarHeader.ts assumes one read supplies all 16 fixed Pickle bytes. Found while following #3574 into adjacent signature/admission consumers. Open and closed issue titles were searched for the same short-read report.

Originally created by @morluto on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5188 ### Area Evidence, artifacts, or reconstruction ### Observed behavior At `2e5171e391b83f8a467d0861fd452c1f78621fd8`, root classification reports `file` for an official compact ASAR and a synthetic DMG signature fixture when positional reads are deterministically capped at three bytes. Direct `admitAsarHeader()` also rejects the same ASAR with `reason: format` and `Truncated or invalid ASAR size pickle`. Without that cap, the same baseline reports `asar` / `dmg`, and ASAR admission succeeds. Both variants calculate identical complete archive digests. This is a controlled production-boundary reproduction with real descriptors, not a claim that natural OS short reads were observed. ### Reproduction Executed on macOS with Node 24.18.0 and `@electron/asar` 4.2.0: 1. Package inert `main.mjs` and `package.json` with `createPackageWithOptions`. Separately write 1,024 zero bytes with `koly` at offset 512 as a classifier-only DMG fixture. 2. In an isolated Node process, wrap the real `FileHandle` prototype's positional `read(buffer, offset, length, position)` to call the native method with `Math.min(length, 3)`. Restore it after the observation. 3. Call production `classifyAndHashRoot()` with the real file metadata and an `ArtifactResourceScope`, closing the scope in `finally`. Call `admitAsarHeader()` through a real ASAR descriptor and close it in `finally`. 4. Compare with an effectively unrestricted 65,536-byte cap. Each configuration was run once. All admitted descriptors were closed. | Observation | 3-byte reads | Unrestricted control | | --- | --- | --- | | Compact ASAR root | `file` | `asar` | | DMG signature fixture | `file` | `dmg` | | ASAR header admission | `format` failure | succeeds | ### Expected behavior A positive short read is partial progress, rather than proof of EOF. Fill the required fixed prefix/trailer or continue bounded classification until zero-byte EOF, a completed observation, cancellation, or the real resource limit. ### Impact and scope Observed in a controlled local reproduction. Natural occurrence frequency is `NOT_ENOUGH_INFO`. This can silently suppress a container role or reject ASAR admission. The DMG fixture proves signature handling only, not complete DMG decoding. Windows and other operating systems were not verified. ### REA version 6.7.0; source revision `2e5171e391b83f8a467d0861fd452c1f78621fd8`. ### Source-derived cause `src/artifacts/inventory/classify.ts` assumes the first prefix read and four-byte DMG trailer read fill their buffers; `src/artifacts/AsarHeader.ts` assumes one read supplies all 16 fixed Pickle bytes. Found while following #3574 into adjacent signature/admission consumers. Open and closed issue titles were searched for the same short-read report.
bsakaguchi 2026-10-31 00:37:35 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

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

Fixed on main in e8da7e1edd075feec0e6beedec93d5645fed8125, alongside #3574.

The producing boundaries treated a positive partial read as a complete observation or truncation. Root classification now accumulates bounded bytes through the shared signature probe; ASAR admission fills all 16 fixed bytes; DMG signature observation fills its four-byte trailer. Each loop checks cancellation around awaited reads, recognizes zero-byte EOF, and retains the existing descriptor cleanup owner.

The controlled baseline/fixed comparison used the same real descriptors and three-byte read cap. Compact ASAR root classification changes file → asar, the classifier-only DMG fixture changes file → dmg, and direct ASAR admission changes a format failure → success. Complete digests are unchanged and descriptors are closed. Regression coverage also includes a long-whitespace ASAR under short reads.

Validation: 24 focused files / 170 passing tests, full npm run check, documentation checks, and test discovery passed. These are deterministic descriptor-fault and inert-format fixtures; naturally occurring OS short reads, Windows, complete DMG decoding and real Electron execution remain unverified. No directly related cleanup remains identified.

<!-- gh-comment-id:6261217310 --> @morluto commented on GitHub (Oct 19, 2026): Fixed on `main` in [e8da7e1edd075feec0e6beedec93d5645fed8125](https://github.com/morluto/rea/commit/e8da7e1edd075feec0e6beedec93d5645fed8125), alongside #3574. The producing boundaries treated a positive partial read as a complete observation or truncation. Root classification now accumulates bounded bytes through the shared signature probe; ASAR admission fills all 16 fixed bytes; DMG signature observation fills its four-byte trailer. Each loop checks cancellation around awaited reads, recognizes zero-byte EOF, and retains the existing descriptor cleanup owner. The controlled baseline/fixed comparison used the same real descriptors and three-byte read cap. Compact ASAR root classification changes `file` → `asar`, the classifier-only DMG fixture changes `file` → `dmg`, and direct ASAR admission changes a `format` failure → success. Complete digests are unchanged and descriptors are closed. Regression coverage also includes a long-whitespace ASAR under short reads. Validation: 24 focused files / 170 passing tests, full `npm run check`, documentation checks, and test discovery passed. These are deterministic descriptor-fault and inert-format fixtures; naturally occurring OS short reads, Windows, complete DMG decoding and real Electron execution remain unverified. No directly related cleanup remains identified.
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#3182
No description provided.