[PR #4788] [CLOSED] fix(electron): report ELF and PE architectures in fuse inspection #6409

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4788
Author: @kaoru0822-kitauji
Created: 10/17/2026
Status: ❌ Closed

Base: main ← Head: fix/electron-fuse-elf-pe-architecture


📝 Commits (1)

  • 659c3ff fix(electron): report ELF and PE architectures in fuse inspection

📊 Changes

4 files changed (+146 additions, -11 deletions)

View changed files

📝 src/domain/binaryTarget.ts (+2 -1)
📝 src/electron/ElectronFuseReader.ts (+55 -10)
📝 tests/acceptance/electronFusesCli.test.ts (+49 -0)
📝 tests/fixtures/electron/fuseImage.ts (+40 -0)

📄 Description

Summary

Fixes #4786

inspect-electron-fuses now reports the architecture of ELF and PE Electron executables in the slice and the Evidence subject, as it already did for thin Mach-O. Before this change both were null for every ELF and PE file. A header REA does not recognize still gives null, and the fuse read still succeeds.

Problem and expected behavior

Trigger: any ELF or PE executable that carries an Electron fuse wire, for example an x86-64 ELF (e_machine 62) or a PE32+ AMD64 image (COFF machine 0x8664).

Root cause: identifyContainer in src/electron/ElectronFuseReader.ts identified ELF and PE by magic bytes alone and hardcoded architecture: null on the single slice. Only the Mach-O branch asked a header parser. ElectronFuseService takes the subject architecture from that one slice, so the subject was null too.

Expected: an ELF or PE header that the executable-header owner can decode reports that architecture (x86, x86_64, arm, arm64, and for ELF also mips, riscv or powerpc). A header the owner rejects (unknown machine, truncated or malformed header) keeps null. Fuse values, sentinel offsets and container admission stay the same.

Change and scope

  • The ELF branch passes a bounded 64-byte prefix to parseElf in src/domain/binaryTarget.ts. That is the same function parseExecutableHeader uses for ELF; it already wraps readElfFixedHeader and the ELF machine mapping. It was module-private, so it is now exported next to the already-exported parsePeRecord.
  • isPe becomes peHeaderOffset, which runs the same admission checks and returns the validated PE\0\0 offset instead of a boolean. The PE branch passes the record at that offset to parsePeRecord, which wraps decodePeMachine. parsePeRecord also rejects a native-OS-override machine on a non-managed image and an optional-header magic that contradicts the machine; calling decodePeMachine directly would not. The record read is bounded at 24 + 0xFFFF bytes, the largest optional header its u16 size field can declare.
  • If either parser returns an error, the slice keeps architecture: null. Container admission does not depend on the parsers, so magic-only ELF and PE images that worked before still work. The existing fixtures elfFuseImage and peFuseImage cover that path and still pass.
  • Not included: no change to Mach-O handling, fuse decoding, limits, schemas or the service-layer subject projection.

Contribution scope: an ordinary fix inside the existing read-only boundary. A maintainer scope decision is not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the domain executable-header parsers (parseElf, parsePeRecord) own architecture. The earliest changed stage is container identification in ElectronFuseReader.

  • Contribution scope fit and linked maintainer decision: fits the existing scope; not applicable.

  • CLI and MCP/tool-catalog contract: the schema is unchanged. slices[].architecture and subject.architecture were already nullable strings. Values for ELF and PE change from always null to the decoded architecture.

  • Provider, bridge, target-format, or platform compatibility: ELF (32/64-bit, either byte order) and PE images with a recognized machine. Other formats are unchanged.

  • Evidence, artifact, provenance, or reconstruction contract: the subject architecture is now filled in for ELF and PE. Digest, paths, offsets and fuses are unchanged.

    Before (x86-64 ELF):

    "subject": { "format": "elf", "architecture": null },
    "slices": [{ "index": 0, "architecture": null, "sentinel_count": 1 }]
    

    After:

    "subject": { "format": "elf", "architecture": "x86_64" },
    "slices": [{ "index": 0, "architecture": "x86_64", "sentinel_count": 1 }]
    
  • Target selection, authority, upstream behavior, and cleanup details: unchanged. Reads still go through the same owned file handle and readAt.

  • 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: tests/acceptance/electronFusesCli.test.ts gains a public-CLI case table: ELF x86-64, ELF AArch64, ELF unknown machine (0xfffe), PE32+ AMD64, PE32+ ARM64, and PE unknown machine (0x01f0). Each case asserts subject.architecture, the slice architecture, the sentinel offset, and that the raw fuse bytes are unchanged. New fixture builders elfHeaderFuseImage and peHeaderFuseImage live in tests/fixtures/electron/fuseImage.ts.
  • Base reproduction (executed): I applied only the new tests to origin/main (first 2cf9c28f2, then again after rebasing onto 78cf6100f). Both runs gave 4 failed, 11 passed, 1 skipped. The four known-architecture cases failed with AssertionError: expected null to be 'x86_64' / expected null to be 'arm64'. The two unknown-machine cases passed because they expect null.
  • User-visible CLI/MCP output: see the before/after JSON above. It shows the asserted fields, not a captured real-app run.
  • Remaining proof gaps: only synthetic minimal headers were tested. I did not run against a real Linux or Windows Electron executable, and MCP output was not checked with the new fixtures (the existing MCP fuse suite was run and passes). Token, time and memory costs were not measured. The change adds one 64-byte read for ELF and one bounded read of up to about 64 KiB for PE, next to the full-file hash the reader already does.

For evidence-bearing changes:

  • 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:focused -- tests/acceptance/electronFusesCli.test.ts on base with only the new tests: 4 failed, 11 passed, 1 skipped (before and after the rebase).
  • npm run test:focused -- tests/acceptance/electronFusesCli.test.ts tests/boundary/mcp/electronFusesMcp.test.ts src/electron/ElectronFuseProvider.test.ts src/electron/ElectronFuseReaderCleanup.test.ts src/application/electron/ElectronFuseService.test.ts src/domain/electron/electronFuses.test.ts src/domain/binaryTarget.test.ts tests/boundary/filesystem/binaryTarget.test.ts with the fix, after the rebase: 8 files passed, 156 tests passed, 1 skipped.
  • npx tsc --noEmit -p .: exit 0.
  • npx oxfmt --check on the 4 changed files: all correctly formatted.
  • npx oxlint on the 4 changed files: 0 errors. Only existing warnings remain (max-lines in binaryTarget.ts, and max-params / max-lines-per-function in untouched functions of ElectronFuseReader.ts).

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Values move from null to a decoded architecture within the existing nullable field.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable. Synthetic fixtures were run on macOS only.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: read-only. It adds bounded reads on the already-owned handle and no new side effects.

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.

🤖 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/4788 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/17/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/electron-fuse-elf-pe-architecture` --- ### 📝 Commits (1) - [`659c3ff`](https://github.com/morluto/rea/commit/659c3ff43a6b77314776761c711e10fe782dbd6a) fix(electron): report ELF and PE architectures in fuse inspection ### 📊 Changes **4 files changed** (+146 additions, -11 deletions) <details> <summary>View changed files</summary> 📝 `src/domain/binaryTarget.ts` (+2 -1) 📝 `src/electron/ElectronFuseReader.ts` (+55 -10) 📝 `tests/acceptance/electronFusesCli.test.ts` (+49 -0) 📝 `tests/fixtures/electron/fuseImage.ts` (+40 -0) </details> ### 📄 Description ## Summary Fixes #4786 `inspect-electron-fuses` now reports the architecture of ELF and PE Electron executables in the slice and the Evidence `subject`, as it already did for thin Mach-O. Before this change both were `null` for every ELF and PE file. A header REA does not recognize still gives `null`, and the fuse read still succeeds. ## Problem and expected behavior Trigger: any ELF or PE executable that carries an Electron fuse wire, for example an x86-64 ELF (`e_machine` 62) or a PE32+ AMD64 image (COFF machine `0x8664`). Root cause: `identifyContainer` in `src/electron/ElectronFuseReader.ts` identified ELF and PE by magic bytes alone and hardcoded `architecture: null` on the single slice. Only the Mach-O branch asked a header parser. `ElectronFuseService` takes the subject architecture from that one slice, so the subject was `null` too. Expected: an ELF or PE header that the executable-header owner can decode reports that architecture (`x86`, `x86_64`, `arm`, `arm64`, and for ELF also `mips`, `riscv` or `powerpc`). A header the owner rejects (unknown machine, truncated or malformed header) keeps `null`. Fuse values, sentinel offsets and container admission stay the same. ## Change and scope - The ELF branch passes a bounded 64-byte prefix to `parseElf` in `src/domain/binaryTarget.ts`. That is the same function `parseExecutableHeader` uses for ELF; it already wraps `readElfFixedHeader` and the ELF machine mapping. It was module-private, so it is now exported next to the already-exported `parsePeRecord`. - `isPe` becomes `peHeaderOffset`, which runs the same admission checks and returns the validated `PE\0\0` offset instead of a boolean. The PE branch passes the record at that offset to `parsePeRecord`, which wraps `decodePeMachine`. `parsePeRecord` also rejects a native-OS-override machine on a non-managed image and an optional-header magic that contradicts the machine; calling `decodePeMachine` directly would not. The record read is bounded at 24 + 0xFFFF bytes, the largest optional header its u16 size field can declare. - If either parser returns an error, the slice keeps `architecture: null`. Container admission does not depend on the parsers, so magic-only ELF and PE images that worked before still work. The existing fixtures `elfFuseImage` and `peFuseImage` cover that path and still pass. - Not included: no change to Mach-O handling, fuse decoding, limits, schemas or the service-layer subject projection. Contribution scope: an ordinary fix inside the existing read-only boundary. A maintainer scope decision is not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: the domain executable-header parsers (`parseElf`, `parsePeRecord`) own architecture. The earliest changed stage is container identification in `ElectronFuseReader`. - Contribution scope fit and linked maintainer decision: fits the existing scope; not applicable. - CLI and MCP/tool-catalog contract: the schema is unchanged. `slices[].architecture` and `subject.architecture` were already nullable strings. Values for ELF and PE change from always `null` to the decoded architecture. - Provider, bridge, target-format, or platform compatibility: ELF (32/64-bit, either byte order) and PE images with a recognized machine. Other formats are unchanged. - Evidence, artifact, provenance, or reconstruction contract: the subject architecture is now filled in for ELF and PE. Digest, paths, offsets and fuses are unchanged. Before (x86-64 ELF): ```json "subject": { "format": "elf", "architecture": null }, "slices": [{ "index": 0, "architecture": null, "sentinel_count": 1 }] ``` After: ```json "subject": { "format": "elf", "architecture": "x86_64" }, "slices": [{ "index": 0, "architecture": "x86_64", "sentinel_count": 1 }] ``` - Target selection, authority, upstream behavior, and cleanup details: unchanged. Reads still go through the same owned file handle and `readAt`. - 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: `tests/acceptance/electronFusesCli.test.ts` gains a public-CLI case table: ELF x86-64, ELF AArch64, ELF unknown machine (`0xfffe`), PE32+ AMD64, PE32+ ARM64, and PE unknown machine (`0x01f0`). Each case asserts `subject.architecture`, the slice architecture, the sentinel offset, and that the raw fuse bytes are unchanged. New fixture builders `elfHeaderFuseImage` and `peHeaderFuseImage` live in `tests/fixtures/electron/fuseImage.ts`. - Base reproduction (executed): I applied only the new tests to `origin/main` (first `2cf9c28f2`, then again after rebasing onto `78cf6100f`). Both runs gave 4 failed, 11 passed, 1 skipped. The four known-architecture cases failed with `AssertionError: expected null to be 'x86_64'` / `expected null to be 'arm64'`. The two unknown-machine cases passed because they expect `null`. - User-visible CLI/MCP output: see the before/after JSON above. It shows the asserted fields, not a captured real-app run. - Remaining proof gaps: only synthetic minimal headers were tested. I did not run against a real Linux or Windows Electron executable, and MCP output was not checked with the new fixtures (the existing MCP fuse suite was run and passes). Token, time and memory costs were not measured. The change adds one 64-byte read for ELF and one bounded read of up to about 64 KiB for PE, next to the full-file hash the reader already does. For evidence-bearing changes: - [ ] 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:focused -- tests/acceptance/electronFusesCli.test.ts` on base with only the new tests: 4 failed, 11 passed, 1 skipped (before and after the rebase). - `npm run test:focused -- tests/acceptance/electronFusesCli.test.ts tests/boundary/mcp/electronFusesMcp.test.ts src/electron/ElectronFuseProvider.test.ts src/electron/ElectronFuseReaderCleanup.test.ts src/application/electron/ElectronFuseService.test.ts src/domain/electron/electronFuses.test.ts src/domain/binaryTarget.test.ts tests/boundary/filesystem/binaryTarget.test.ts` with the fix, after the rebase: 8 files passed, 156 tests passed, 1 skipped. - `npx tsc --noEmit -p .`: exit 0. - `npx oxfmt --check` on the 4 changed files: all correctly formatted. - `npx oxlint` on the 4 changed files: 0 errors. Only existing warnings remain (`max-lines` in `binaryTarget.ts`, and `max-params` / `max-lines-per-function` in untouched functions of `ElectronFuseReader.ts`). ## Compatibility, safety, and release - Breaking changes or migration steps: none. Values move from `null` to a decoded architecture within the existing nullable field. - Real Hopper/Ghidra, browser, or OS coverage: not applicable. Synthetic fixtures were run on macOS only. - Package or release metadata impact: none. - Security, privacy, process, or containment review: read-only. It adds bounded reads on the already-owned handle and no new side effects. ## 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. 🤖 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-31 01:14: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#6409
No description provided.