Skip to content

fix(artifacts): refuse an ASAR header that declares one member name twice - #10552

Merged
morluto merged 1 commit into
mainfrom
fix/asar-duplicate-members
Nov 14, 2026
Merged

morluto merged 1 commit into
mainfrom
fix/asar-duplicate-members

Conversation

@morluto

@morluto morluto commented Nov 14, 2026

Copy link
Copy Markdown
Owner

Summary

An ASAR header that declares the same member name twice in one directory listed only the last declaration. The first member disappeared from the listing while coverage still read as complete. No issue was filed; this was found by a structural audit and reproduced by running the listing on such a header.

Problem and expected behavior

  • Trigger: {"files":{"a.js":{"size":10,"offset":"0"},"a.js":{"size":5,"offset":"10"},"b.js":{...}}}.
  • Before: the listing has two entries (a.js size 5, b.js), no limitation, and complete coverage. The 10-byte declaration is silently dropped.
  • Expected: the archive index is ambiguous, so reading it fails as a malformed (format) archive. This matches how the listing already refuses unsafe path components and how extraction refuses colliding paths.

Change and scope

  • New src/domain/jsonDuplicateMember.ts: findDuplicateJsonMember(text) scans already-validated JSON text and returns the first repeated member name and its offset. The scan is iterative because REA admits ASAR headers nested thousands of levels deep (asarListingAdmission.test.ts uses 3,500), which a recursive parser would refuse or overflow on.
  • readAsarHeader runs it after JSON.parse succeeds and throws ArtifactReaderFailure("format", ...) naming the member and offset.
  • Not included:
    • src/browser/ElectronFileScope.ts has its own header JSON.parse for live Electron file scope. There, last-wins matches what Electron itself loads, so it is left alone.
    • Whether ZIP drops members missing from the central directory has not been checked yet.
    • The audit counts about 7 other JSON duplicate detectors with different policies. This adds a reusable scanner but does not migrate them.
  • Contribution scope: a fix within existing boundaries; not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: readAsarHeader (src/artifacts/AsarHeader.ts), before the listing is built. All ASAR readers that use it are affected.

  • Contribution scope fit and linked maintainer decision: not applicable.

  • CLI and MCP/tool-catalog contract: no schema change. Archives with repeated member names now fail as malformed instead of listing a partial tree.

  • Provider, bridge, target-format, or platform compatibility: Electron's own reader keeps the last value. REA now refuses these archives instead of choosing one.

  • Evidence, artifact, provenance, or reconstruction contract: no partial listing is produced for an ambiguous index.

  • Target selection, authority, upstream behavior, and cleanup details: none.

  • Generated metadata, package, or installation impact: none.

  • Read existing files or captured data.

Evidence and regression coverage

  • Tests added or updated:
    • src/artifacts/AsarHeader.test.ts: "refuses a header that declares one member name twice".
    • src/domain/jsonDuplicateMember.test.ts: escaped names, nested scopes, arrays, string values equal to names, and 200,000 levels of nesting.
  • Base reproduction: on the unfixed source the new header test fails (1 failed, 4 passed).
  • Boundary regressions: malformed input (duplicate entries), and deep nesting kept admissible.
  • Remaining proof gaps: no real-world ASAR with duplicate names was found; the fixture is synthetic.

Validation performed

  • npm run test:local -- src/artifacts/AsarHeader.test.ts on unfixed source: the new test failed (1 failed, 4 passed).
  • npm run test:focused -- on jsonDuplicateMember.test.ts plus all 19 ASAR test files: 20 files and 158 tests passed. An earlier attempt using the recursive lossless JSON parser failed the 3,500-level nesting test, which is why the scan is iterative.
  • npm run check:fast passed.
  • oxfmt passed on the changed files.

Compatibility, safety, and release

  • Breaking changes or migration steps: none to contracts. Archives with repeated member names, which used to be listed incompletely, are now refused.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS only, synthetic archives.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: this removes a way for an archive to hide a member from REA's listing.

Author readiness

Human owner:

  • 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.

…wice

JSON.parse keeps only the last value for a repeated name, so an ASAR header
that declared a path twice listed one member, hid the other and still
reported complete coverage.

Add findDuplicateJsonMember, an iterative scan of validated JSON text that
does not recurse, so the deeply nested headers REA already admits stay
readable. readAsarHeader now refuses a repeated member name as a malformed
index, the same way the listing refuses unsafe path components.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-14T14:38:36.420905Z f0cd632 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable defect was found.

Summary

The PR rejects repeated JSON member names at readAsarHeader, before an ASAR listing can silently lose a declaration.

  • ASAR inventories reject headers that repeat a member name.

Reviews (2) · Last reviewed commit: "fix(artifacts): refuse an ASAR header th..." · Reviewed by Greptile

@morluto
morluto merged commit 9a2aff2 into main Nov 14, 2026
52 checks passed
@morluto
morluto deleted the fix/asar-duplicate-members branch November 14, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant