[PR #2458] [MERGED] fix(go): classify JVM class files instead of universal Mach-O for CAFEBABE bytes #5241

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2458
Author: @Joshwong1908
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/go-java-class-fat-macho


📝 Commits (6)

  • c39b6a1 test(go): pin JVM class files as unsupported Go images
  • e53b21c fix(go): classify JVM class files instead of universal Mach-O
  • 10328cd test(go): pin universal Mach-O thin-slice failure with FAT fixtures
  • 1b9b658 test(go): cover little-endian FAT magics in the universal container control
  • 1a14c05 fix(go): require positive universal Mach-O identity
  • 617a4b4 fix(go): retain malformed FAT header diagnostics

📊 Changes

3 files changed (+62 additions, -6 deletions)

View changed files

📝 src/go/GoBinaryImage.ts (+25 -5)
📝 tests/boundary/go/goBinaryImage.test.ts (+35 -0)
📝 tests/fixtures/javaClassFile.ts (+2 -1)

📄 Description

Summary

inspect-go-binary reports JVM class files (CAFEBABE) as "Universal/fat Mach-O containers require selecting a thin slice" — a false format identity. A JVM class file's bytes 4..7 hold minor_version/major_version, not nfat_arch, so the message steers users to a slice-selection dead end that can never succeed. This is the Go-reader sibling of the disambiguation added for the artifact and target lanes in 921bb9575 (fixes #2317); #2405 covers the artifact inventory lane. Contradiction on identical bytes: inspect-artifact already refuses the same class file as unsupported binary format.

Problem and expected behavior

Smallest trigger: rea inspect-go-binary Hello.class (any valid class file; version 65 lands in nfat_arch's plausible 1..128 range).

Violated invariant: docs/go-binaries.md "Supported boundary" — "Unsupported containers receive a separate unsupported-target failure." A class file is not a container of any kind; the unsupported-target reason must not assert a Mach-O FAT identity the bytes do not support.

Expected: the generic Only ELF, PE, and thin Mach-O native images are supported failure, matching the artifact/target lanes.

Change and scope

In src/go/GoBinaryImage.ts readContainer, the four FAT magics now route through the shared readFatContainerHeader + classifyFatContainerIdentity facts. The thin-slice failure is kept only when the container identity is confirmed Mach-O (or the header is truncated/malformed enough that FAT cannot be ruled out, preserving the existing 4-byte behavior). not-fat (class files: table extends past file size) and not-mach-o identities fall through to the generic failure.

Not included: src/hopper/HopperFileOffset.ts fatLayout still classifies CAFEBABE by magic alone; 921bb9575's message leaves Hopper readers their own parsing, so I did not change it here.

Contribution scope: focused bug fix within existing boundaries; no new capability, target execution, injection, or mutation. Not applicable for a maintainer scope decision.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Go build-info reader (readGoBinaryImage container admission); earliest stage is target admission for inspect_go_binary.
  • Contribution scope fit and linked maintainer decision: ordinary fix, not applicable.
  • CLI and MCP/tool-catalog contract: unchanged surface; only the unsupported_target reason text for non-Mach-O CAFEBABE inputs changes from the fat-container message to the generic supported-formats message.
  • Provider, bridge, target-format, or platform compatibility: none; pure static classification before any provider is contacted.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata: none; no tool signatures changed (CI-generated catalogs untouched).

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/boundary/go/goBinaryImage.test.ts — JVM class fixture (both the 57-byte table-overflow form and the 4000-byte parsed-garbage-table form) must hit the generic unsupported failure; confirmed universal containers (repository fatImage/machoImage builders: two real thin Mach-O slices x narrow, FAT64, and both little-endian cigam tables) must keep the thin-slice failure. Commit c39b6a1f3 lands the failing regression first; e53b21ccd turns it green; 10328cd64 and 1b9b65827 harden the positive control.
  • Base reproduction: on main @ 37b7550ec, node scripts/rea.mjs inspect-go-binary Hello.class returns message: "... Universal/fat Mach-O containers require selecting a thin slice"; after the fix it returns "... Only ELF, PE, and thin Mach-O native images are supported".
  • User-visible CLI/MCP output (before → after, executed):
$ rea inspect-go-binary Hello.class            # before (main)
message: "Unsupported target for inspect_go_binary at .../Hello.class: Universal/fat Mach-O containers require selecting a thin slice"

$ rea inspect-go-binary Hello.class            # after (this branch)
message: "Unsupported target for inspect_go_binary at .../Hello.class: Only ELF, PE, and thin Mach-O native images are supported"

$ rea inspect-go-binary fat-real-universal.bin # after (positive control)
message: "... Universal/fat Mach-O containers require selecting a thin slice"
  • Remaining proof gaps: no real macOS-produced universal binary was available on this Linux host; the positive controls are synthesized with the repository's own FAT/Mach-O fixture builders (real thin Mach-O slice headers, all four magics x narrow/wide tables). HopperFileOffset.ts behavior is unchanged and noted above.

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

  • npx vitest run tests/boundary/go/goBinaryImage.test.ts — 81 passed (fails 1/81 on main without the fix)
  • npx vitest run tests/acceptance/artifactInventoryJvmClass.test.ts src/domain/binaryTarget.test.ts tests/boundary/go/ — 141 passed
  • npm run typecheck — clean
  • npm run lint (includes verify:module-boundaries) — 1665 source files checked, clean
  • npm run verify:test-discovery — 1003 test files discovered exactly once
  • npm run build:cached then both CLI invocations above — executed, output quoted
  • Full-repo vitest lanes (src-unit 5490 tests, boundary+process+mcp 4102, cli-boundary 314, acceptance 290, test:python 9) — all green; every observed failure reproduced verbatim on a pristine main checkout (pre-existing) or was environmental/load-flaky passing on rerun
  • Adversarial sweep of 151 crafted inputs (all FAT magics x truncation/count/offset/endian boundaries, double-magic, 50 seeded-random buffers) via the built CLI — zero invariant violations, only the two documented message buckets

Compatibility, safety, and release

  • Breaking changes or migration steps: none; an error message for non-Mach-O CAFEBABE inputs becomes accurate.
  • Real Hopper/Ghidra, browser, or OS coverage: not needed (pure static classification); verified on Linux x64 via CLI.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: not applicable (read-only classification).

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 (family context: #2317; sibling lane: #2405; no Go-lane issue exists).
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed (no doc change required: behavior now matches the documented boundary).
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 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/2458 **Author:** [@Joshwong1908](https://github.com/Joshwong1908) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/go-java-class-fat-macho` --- ### 📝 Commits (6) - [`c39b6a1`](https://github.com/morluto/rea/commit/c39b6a1f35db69a59ecd40a016606d356b60ad9c) test(go): pin JVM class files as unsupported Go images - [`e53b21c`](https://github.com/morluto/rea/commit/e53b21ccd41901a12e7ac00036c4fdfa3fba6b42) fix(go): classify JVM class files instead of universal Mach-O - [`10328cd`](https://github.com/morluto/rea/commit/10328cd64bb60fcf56e51511a43418b0d7ab5524) test(go): pin universal Mach-O thin-slice failure with FAT fixtures - [`1b9b658`](https://github.com/morluto/rea/commit/1b9b65827fa73361d1252a9b5bc39119864d8277) test(go): cover little-endian FAT magics in the universal container control - [`1a14c05`](https://github.com/morluto/rea/commit/1a14c05efa321cded48190bbcf41db50dd1c543c) fix(go): require positive universal Mach-O identity - [`617a4b4`](https://github.com/morluto/rea/commit/617a4b4aeedd273b6b06b28727b0326269cf03e4) fix(go): retain malformed FAT header diagnostics ### 📊 Changes **3 files changed** (+62 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `src/go/GoBinaryImage.ts` (+25 -5) 📝 `tests/boundary/go/goBinaryImage.test.ts` (+35 -0) 📝 `tests/fixtures/javaClassFile.ts` (+2 -1) </details> ### 📄 Description ## Summary `inspect-go-binary` reports JVM class files (CAFEBABE) as "Universal/fat Mach-O containers require selecting a thin slice" — a false format identity. A JVM class file's bytes 4..7 hold `minor_version`/`major_version`, not `nfat_arch`, so the message steers users to a slice-selection dead end that can never succeed. This is the Go-reader sibling of the disambiguation added for the artifact and target lanes in 921bb9575 (fixes #2317); #2405 covers the artifact inventory lane. Contradiction on identical bytes: `inspect-artifact` already refuses the same class file as `unsupported binary format`. ## Problem and expected behavior Smallest trigger: `rea inspect-go-binary Hello.class` (any valid class file; version 65 lands in `nfat_arch`'s plausible 1..128 range). Violated invariant: docs/go-binaries.md "Supported boundary" — "Unsupported containers receive a separate unsupported-target failure." A class file is not a container of any kind; the unsupported-target reason must not assert a Mach-O FAT identity the bytes do not support. Expected: the generic `Only ELF, PE, and thin Mach-O native images are supported` failure, matching the artifact/target lanes. ## Change and scope In `src/go/GoBinaryImage.ts` `readContainer`, the four FAT magics now route through the shared `readFatContainerHeader` + `classifyFatContainerIdentity` facts. The thin-slice failure is kept only when the container identity is confirmed Mach-O (or the header is truncated/malformed enough that FAT cannot be ruled out, preserving the existing 4-byte behavior). `not-fat` (class files: table extends past file size) and `not-mach-o` identities fall through to the generic failure. Not included: `src/hopper/HopperFileOffset.ts` `fatLayout` still classifies CAFEBABE by magic alone; 921bb9575's message leaves Hopper readers their own parsing, so I did not change it here. Contribution scope: focused bug fix within existing boundaries; no new capability, target execution, injection, or mutation. Not applicable for a maintainer scope decision. ## Contract and boundary impact - Semantic owner and earliest changed stage: Go build-info reader (`readGoBinaryImage` container admission); earliest stage is target admission for `inspect_go_binary`. - Contribution scope fit and linked maintainer decision: ordinary fix, not applicable. - CLI and MCP/tool-catalog contract: unchanged surface; only the `unsupported_target` reason text for non-Mach-O CAFEBABE inputs changes from the fat-container message to the generic supported-formats message. - Provider, bridge, target-format, or platform compatibility: none; pure static classification before any provider is contacted. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata: none; no tool signatures changed (CI-generated catalogs untouched). 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/boundary/go/goBinaryImage.test.ts` — JVM class fixture (both the 57-byte table-overflow form and the 4000-byte parsed-garbage-table form) must hit the generic unsupported failure; confirmed universal containers (repository `fatImage`/`machoImage` builders: two real thin Mach-O slices x narrow, FAT64, and both little-endian cigam tables) must keep the thin-slice failure. Commit c39b6a1f3 lands the failing regression first; e53b21ccd turns it green; 10328cd64 and 1b9b65827 harden the positive control. - Base reproduction: on main @ 37b7550ec, `node scripts/rea.mjs inspect-go-binary Hello.class` returns `message: "... Universal/fat Mach-O containers require selecting a thin slice"`; after the fix it returns `"... Only ELF, PE, and thin Mach-O native images are supported"`. - User-visible CLI/MCP output (before → after, executed): ``` $ rea inspect-go-binary Hello.class # before (main) message: "Unsupported target for inspect_go_binary at .../Hello.class: Universal/fat Mach-O containers require selecting a thin slice" $ rea inspect-go-binary Hello.class # after (this branch) message: "Unsupported target for inspect_go_binary at .../Hello.class: Only ELF, PE, and thin Mach-O native images are supported" $ rea inspect-go-binary fat-real-universal.bin # after (positive control) message: "... Universal/fat Mach-O containers require selecting a thin slice" ``` - Remaining proof gaps: no real macOS-produced universal binary was available on this Linux host; the positive controls are synthesized with the repository's own FAT/Mach-O fixture builders (real thin Mach-O slice headers, all four magics x narrow/wide tables). `HopperFileOffset.ts` behavior is unchanged and noted above. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npx vitest run tests/boundary/go/goBinaryImage.test.ts` — 81 passed (fails 1/81 on main without the fix) - `npx vitest run tests/acceptance/artifactInventoryJvmClass.test.ts src/domain/binaryTarget.test.ts tests/boundary/go/` — 141 passed - `npm run typecheck` — clean - `npm run lint` (includes verify:module-boundaries) — 1665 source files checked, clean - `npm run verify:test-discovery` — 1003 test files discovered exactly once - `npm run build:cached` then both CLI invocations above — executed, output quoted - Full-repo vitest lanes (src-unit 5490 tests, boundary+process+mcp 4102, cli-boundary 314, acceptance 290, test:python 9) — all green; every observed failure reproduced verbatim on a pristine main checkout (pre-existing) or was environmental/load-flaky passing on rerun - Adversarial sweep of 151 crafted inputs (all FAT magics x truncation/count/offset/endian boundaries, double-magic, 50 seeded-random buffers) via the built CLI — zero invariant violations, only the two documented message buckets ## Compatibility, safety, and release - Breaking changes or migration steps: none; an error message for non-Mach-O CAFEBABE inputs becomes accurate. - Real Hopper/Ghidra, browser, or OS coverage: not needed (pure static classification); verified on Linux x64 via CLI. - Package or release metadata impact: none. - Security, privacy, process, or containment review: not applicable (read-only classification). ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above (family context: #2317; sibling lane: #2405; no Go-lane issue exists). - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed (no doc change required: behavior now matches the documented boundary). - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:05:05 +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#5241
No description provided.