[PR #4591] feat(ghidra): select the loopback bridge transport on POSIX hosts #6326

Open
opened 2026-10-31 01:13:36 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4591
Author: @CameronBrooks11
Created: 10/16/2026
Status: 🔄 Open

Base: main ← Head: feat/ghidra-posix-loopback-transport


📝 Commits (4)

  • 75a247e feat(ghidra): select the loopback bridge transport on POSIX hosts
  • 1a931ce fix(ghidra): withhold analysis extensions over the loopback transport
  • 3a04240 fix(ghidra): declare the loopback transport network effect
  • a40c193 Merge remote-tracking branch 'upstream/main' into feat/ghidra-posix-loopback-transport

📊 Changes

18 files changed (+675 additions, -31 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md (+1 -1)
📝 SECURITY.md (+4 -0)
📝 docs/ghidra-provider.md (+3 -1)
📝 docs/installation.md (+33 -0)
📝 src/config.test.ts (+35 -0)
📝 src/config/environment.ts (+2 -0)
➕ src/config/ghidraBridgeTransport.ts (+16 -0)
📝 src/config/parseConfig.ts (+3 -0)
📝 src/config/types.ts (+2 -0)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+207 -0)
📝 src/ghidra/GhidraProvider.ts (+34 -4)
📝 src/ghidra/GhidraProviderCapabilities.ts (+55 -0)
📝 src/ghidra/GhidraProviderClient.ts (+42 -8)
📝 src/ghidra/GhidraTransport.ts (+27 -1)
📝 src/ghidra/extensions/GhidraExtensions.test.ts (+85 -13)
📝 src/ghidra/extensions/GhidraExtensions.ts (+15 -3)
📝 tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts (+6 -0)
➕ tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts (+105 -0)

📄 Description

Summary

Fixes #4588.

On Linux and macOS the Ghidra provider always serves its bridge over a Unix socket (GhidraProviderClient.ts selects authenticated-loopback-tcp only for win32). A sandbox that refuses socket(AF_UNIX) with EPERM, such as Claude Code's bubblewrap plus seccomp sandbox, therefore fails every Ghidra operation after import and auto-analysis with Ghidra exited before bridge startup and java.net.SocketException: Operation not permitted from ReaGhidraBridge.serveUnixSocket. Loopback TCP works in the same sandbox, and REA already ships an authenticated loopback transport for Windows P0 whose Node and Java paths have non-Windows branches.

This PR adds REA_GHIDRA_BRIDGE_TRANSPORT with the values unix-socket and authenticated-loopback-tcp. Unset keeps today's host default. On a POSIX host, authenticated-loopback-tcp selects the existing transport; the session is read-only, as on Windows, and says so. A configured analysis extension (NativeAOT) is withheld on that transport rather than committed, so the session still opens read-only instead of the bridge rejecting the profile and failing outright.

Problem and expected behavior

  • Trigger: any Ghidra operation on a POSIX host whose seccomp filter denies AF_UNIX sockets. Reproduced on main 61e688070 and 6.5.0 with Ghidra 12.1.4 and JDK 21 (output in #4588).
  • Violated expectation: a host that can run Ghidra and reach 127.0.0.1 has no way to use the transport REA already has for that situation.
  • Expected: with the setting, the read-only operations succeed over loopback; annotate_native_function, set_address_name and set_addresses_names fail as capability_unavailable with a reason naming the setting; a configured NativeAOT extension is withheld with a reason rather than aborting the session; without the setting nothing changes.

Change and scope

  • src/config/ghidraBridgeTransport.ts (new): the zod enum, with a constraint message that does not echo the rejected value.
  • src/config/environment.ts, types.ts, parseConfig.ts: REA_GHIDRA_BRIDGE_TRANSPORT -> AppConfig.ghidraBridgeTransport, optional like ghidraDisabledAnalyzers.
  • src/ghidra/GhidraTransport.ts: GhidraTransportKind is inferred from that schema, so the literal list lives once. Adds resolveGhidraTransport(platform, configured) (the effective host transport, in one place) and transportAdmitsAnalysisExtensions(transport) (true only for the Unix socket, mirroring the bridge's own rule).
  • src/ghidra/GhidraProviderClient.ts: the transport is win32 ? loopback : config.ghidraBridgeTransport ?? unix-socket. An explicit unix-socket on Windows is refused in the admission gate (admitGhidraClient), before installation and native readiness checks, because Node path-based IPC cannot reach a Java AF_UNIX listener there. A POSIX loopback session adds loopbackTransportLimitations to every result.
  • src/ghidra/extensions/GhidraExtensions.ts: extension resolution and profile validation take the selected transport. Over a transport that cannot serve extensions (loopback), a configured adapter is withheld after its host/target checks rather than committed, and the client guard and validator expect the empty inventory there. This prevents a session-killing state: ReaGhidraBridge rejects analysis extensions on any non-Unix transport before it opens the listener, so committing one under loopback would fail the whole session (including reads), not just the extension.
  • src/ghidra/GhidraProviderCapabilities.ts: loopbackTransportCapabilities() marks the three mutating operations unavailable with LOOPBACK_TRANSPORT_MUTATION_REASON and appends the limitations; no availabilityCode, because the selected transport, not the host, withholds mutation. LOOPBACK_TRANSPORT_EXTENSION_REASON names a withheld configured extension so the omission is explicit. Loopback capabilities also declare mayAccessNetwork, since the bridge opens a 127.0.0.1 TCP listener the client connects to; declareLoopbackNetworkEffect applies it to a POSIX loopback session and to an existing-project session over loopback. windowsP0Capabilities and the Unix socket default are unchanged.
  • src/ghidra/GhidraProvider.ts: capabilities() and inspectReadinessSnapshot() use that projection on POSIX when loopback is selected; profile resolution passes the effective transport to extension resolution.
  • Docs: docs/installation.md (new "Ghidra bridge transport" section and a pointer from the socket paragraph), docs/ghidra-provider.md, SECURITY.md, and the skill reference native-and-artifacts.md.

Contribution scope: a configuration option that selects an existing transport of an existing provider and withholds mutation and configured extensions on it, so no boundary widens; a maintainer scope decision is not applicable. Whether POSIX loopback may carry mutation authority, or whether a FIFO transport is preferred, is the open question in #4588.

Intentionally not included:

  • Mutation over POSIX loopback. The Java gates and GhidraClient.ts key read-only on the transport; changing that is the scope decision in #4588.
  • A FIFO transport (prototype evidence in #4588).
  • Forwarding the setting through rea setup. It is not in SELECTED_REGISTRATION_SETTINGS, like REA_GHIDRA_DISABLED_ANALYZERS; the docs say to set it in the sandboxed process environment.
  • A doctor field. Session diagnostics already report transport.
  • The Hopper bridge, which binds AF_UNIX in bridge/hopper_bridge.py and has the same limitation.
  • The /tmp fallback of ghidraSessionRoot() when TMPDIR has a dot-prefixed element; it is unrelated and reproduces without this change.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/config/environment.ts at configuration parsing; transport selection and the admission refusal in src/ghidra/GhidraProviderClient.ts at client admission/creation.
  • Contribution scope fit and linked maintainer decision: existing provider and transport, read-only on the new path; not applicable. Mutation widening deferred to #4588.
  • CLI and MCP/tool-catalog contract: no tool or schema change. An invalid value fails configuration validation naming REA_GHIDRA_BRIDGE_TRANSPORT and its constraint. Over POSIX loopback the three mutating operations report available: false with the reason and no availability_code; results gain the loopback limitation strings, plus a withheld-extension limitation when a NativeAOT jar is configured.
  • Provider, bridge, target-format, or platform compatibility: the Java bridge is unchanged. Windows keeps loopback and refuses unix-socket. Linux verified against real Ghidra 12.1.4; macOS not run.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged; the transport was already part of session diagnostics.
  • Target selection, authority, upstream behavior, and cleanup details: the loopback endpoint record, .pending atomic move, retry of a dropped pre-authentication connection and runtime cleanup are the existing POSIX code paths; no new lifecycle stage.
  • Generated metadata, package, or installation impact: skill reference text only; generate-skill-metadata.mjs --check passes. No catalog change.

Expected effects:

  • Read existing files or captured data. (existing Ghidra analysis)
  • Launch or attach to a process. (existing headless Ghidra launch; only its bridge transport is selectable)
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services. (the bridge binds and the client connects to 127.0.0.1 on an ephemeral port, as Windows P0 does; nothing leaves the host)
  • 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:

  • src/config.test.ts: accepts both values, keeps the field absent when unset, rejects another value naming the setting and constraint without echoing the value.
  • src/ghidra/GhidraProvider.behavior.test.ts, "Ghidra bridge transport selection": POSIX default stays unix-socket with full authority and no loopback limitation; the setting selects loopback, adds the limitations to results, withholds exactly the three mutating operations (same in inspectReadinessSnapshot) with the reason and no code; a configured NativeAOT jar over loopback commits no analysis_extensions, the session still opens, and the result carries the withheld-extension reason; loopback capabilities declare mayAccessNetwork while the Unix socket default does not, including for an existing-project session over loopback; Windows keeps its P0 capability wording and refuses unix-socket with a ProviderAdapterError before the client factory runs.
  • src/ghidra/extensions/GhidraExtensions.test.ts, "Ghidra extension transport withholding": resolution withholds a configured extension over a non-admitting transport; the profile validator expects the empty inventory there and treats a committed extension as an inventory mismatch.
  • tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts (skipped on win32): the production provider client, private runtime, endpoint record and loopback socket against the fake bridge; list_procedures succeeds, the endpoint is bridge-endpoint.json, and the limitations are present.
  • tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts: updated for the new extension-resolution signature (the selected transport is threaded through).

The transport-withholding regression tests were written red-first: each fails against the unfixed predicate (extensions admitted over loopback) and passes once it is applied.

Validation performed

Re-verified on 3a0424058, rebased onto current upstream main (d48e472fa), Linux x86-64:

  • tsc --noEmit — clean.
  • oxfmt --check src tests, oxlint src tests (0 errors), knip, verify:architecture-guards (2283 files), verify:module-boundaries (3447 files) — all pass.
  • Ghidra unit suites src/ghidra/extensions/GhidraExtensions.test.ts and src/ghidra/GhidraProvider.behavior.test.ts — 201 passed.
  • Boundary suites tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts, ghidraTransport.test.ts, and tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts — 23 passed.

End-to-end against real Ghidra 12.1.4 (Linux x86-64, CLI), captured on the initial implementation; the runtime transport path is unchanged since, for int add_magic(int x){ return x * 3 + 42; }:

  • Unset: code: execution_failure, failure_message: Ghidra exited before bridge startup, transport: unix-socket, script error java.net.SocketException: Operation not permitted ... at ReaGhidraBridge.serveUnixSocket.

  • REA_GHIDRA_BRIDGE_TRANSPORT=authenticated-loopback-tcp: normalized_result is int add_magic(int param_1) { return param_1 * 3 + 0x2a; }, provider ghidra 12.1.4, limitations include the loopback strings.

  • Same setting, rea annotate-native-function: capability_unavailable with reason "The authenticated loopback bridge transport does not admit database mutation; unset REA_GHIDRA_BRIDGE_TRANSPORT to use the Unix socket transport."

  • Observed, derived, and inferred claims remain distinguishable.

  • Artifact identity, source provenance, and failed attempts remain preserved.

  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Remaining proof gaps: the NativeAOT-extension-withholding path is covered by unit and boundary tests but not exercised against real Ghidra with a configured jar over loopback; macOS not run; npm run verify:ghidra not run with the setting; no Windows host; the full-repo npm run check:pr, verify:package, and config.test.ts/docs:check were not re-run in this pass (docs:check fails in this sandbox on EROFS mkdtemp, an environment limitation, not a repo defect).

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Unset keeps the Unix socket on POSIX and loopback on Windows. The only new failures are an invalid value of the new setting, or unix-socket on Windows.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on Linux x86-64 through the CLI, with and without the setting.
  • Package or release metadata impact: skill reference text only.
  • Security, privacy, process, or containment review: over loopback the listener is reachable by every local uid and the per-session 256-bit token is the only authentication; the bridge drops unauthenticated peers after a 1 s handshake budget. The POSIX descriptor keeps its consume-and-delete flow, so the token is not on disk after startup. Mutation and configured extensions stay withheld on this transport. SECURITY.md and the installation guide say so. The value is non-secret and appears in limitations and diagnostics.

Author readiness

Human owner: @CameronBrooks11

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

🔄 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/4591 **Author:** [@CameronBrooks11](https://github.com/CameronBrooks11) **Created:** 10/16/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/ghidra-posix-loopback-transport` --- ### 📝 Commits (4) - [`75a247e`](https://github.com/morluto/rea/commit/75a247e38daa2344df1e956c8a941bce633a1028) feat(ghidra): select the loopback bridge transport on POSIX hosts - [`1a931ce`](https://github.com/morluto/rea/commit/1a931ceba2b9326d4d3fa125379de09052899e2e) fix(ghidra): withhold analysis extensions over the loopback transport - [`3a04240`](https://github.com/morluto/rea/commit/3a04240586defff126b320c774817fba2e707676) fix(ghidra): declare the loopback transport network effect - [`a40c193`](https://github.com/morluto/rea/commit/a40c193f3c8612b5e12e67c3a804f1bd73ab8263) Merge remote-tracking branch 'upstream/main' into feat/ghidra-posix-loopback-transport ### 📊 Changes **18 files changed** (+675 additions, -31 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` (+1 -1) 📝 `SECURITY.md` (+4 -0) 📝 `docs/ghidra-provider.md` (+3 -1) 📝 `docs/installation.md` (+33 -0) 📝 `src/config.test.ts` (+35 -0) 📝 `src/config/environment.ts` (+2 -0) ➕ `src/config/ghidraBridgeTransport.ts` (+16 -0) 📝 `src/config/parseConfig.ts` (+3 -0) 📝 `src/config/types.ts` (+2 -0) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+207 -0) 📝 `src/ghidra/GhidraProvider.ts` (+34 -4) 📝 `src/ghidra/GhidraProviderCapabilities.ts` (+55 -0) 📝 `src/ghidra/GhidraProviderClient.ts` (+42 -8) 📝 `src/ghidra/GhidraTransport.ts` (+27 -1) 📝 `src/ghidra/extensions/GhidraExtensions.test.ts` (+85 -13) 📝 `src/ghidra/extensions/GhidraExtensions.ts` (+15 -3) 📝 `tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts` (+6 -0) ➕ `tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts` (+105 -0) </details> ### 📄 Description ## Summary Fixes #4588. On Linux and macOS the Ghidra provider always serves its bridge over a Unix socket (`GhidraProviderClient.ts` selects `authenticated-loopback-tcp` only for `win32`). A sandbox that refuses `socket(AF_UNIX)` with `EPERM`, such as Claude Code's bubblewrap plus seccomp sandbox, therefore fails every Ghidra operation after import and auto-analysis with `Ghidra exited before bridge startup` and `java.net.SocketException: Operation not permitted` from `ReaGhidraBridge.serveUnixSocket`. Loopback TCP works in the same sandbox, and REA already ships an authenticated loopback transport for Windows P0 whose Node and Java paths have non-Windows branches. This PR adds `REA_GHIDRA_BRIDGE_TRANSPORT` with the values `unix-socket` and `authenticated-loopback-tcp`. Unset keeps today's host default. On a POSIX host, `authenticated-loopback-tcp` selects the existing transport; the session is read-only, as on Windows, and says so. A configured analysis extension (NativeAOT) is withheld on that transport rather than committed, so the session still opens read-only instead of the bridge rejecting the profile and failing outright. ## Problem and expected behavior - Trigger: any Ghidra operation on a POSIX host whose seccomp filter denies `AF_UNIX` sockets. Reproduced on main 61e688070 and 6.5.0 with Ghidra 12.1.4 and JDK 21 (output in #4588). - Violated expectation: a host that can run Ghidra and reach `127.0.0.1` has no way to use the transport REA already has for that situation. - Expected: with the setting, the read-only operations succeed over loopback; `annotate_native_function`, `set_address_name` and `set_addresses_names` fail as `capability_unavailable` with a reason naming the setting; a configured NativeAOT extension is withheld with a reason rather than aborting the session; without the setting nothing changes. ## Change and scope - `src/config/ghidraBridgeTransport.ts` (new): the zod enum, with a constraint message that does not echo the rejected value. - `src/config/environment.ts`, `types.ts`, `parseConfig.ts`: `REA_GHIDRA_BRIDGE_TRANSPORT` -> `AppConfig.ghidraBridgeTransport`, optional like `ghidraDisabledAnalyzers`. - `src/ghidra/GhidraTransport.ts`: `GhidraTransportKind` is inferred from that schema, so the literal list lives once. Adds `resolveGhidraTransport(platform, configured)` (the effective host transport, in one place) and `transportAdmitsAnalysisExtensions(transport)` (true only for the Unix socket, mirroring the bridge's own rule). - `src/ghidra/GhidraProviderClient.ts`: the transport is `win32 ? loopback : config.ghidraBridgeTransport ?? unix-socket`. An explicit `unix-socket` on Windows is refused in the admission gate (`admitGhidraClient`), before installation and native readiness checks, because Node path-based IPC cannot reach a Java AF_UNIX listener there. A POSIX loopback session adds `loopbackTransportLimitations` to every result. - `src/ghidra/extensions/GhidraExtensions.ts`: extension resolution and profile validation take the selected transport. Over a transport that cannot serve extensions (loopback), a configured adapter is withheld after its host/target checks rather than committed, and the client guard and validator expect the empty inventory there. This prevents a session-killing state: `ReaGhidraBridge` rejects analysis extensions on any non-Unix transport before it opens the listener, so committing one under loopback would fail the whole session (including reads), not just the extension. - `src/ghidra/GhidraProviderCapabilities.ts`: `loopbackTransportCapabilities()` marks the three mutating operations unavailable with `LOOPBACK_TRANSPORT_MUTATION_REASON` and appends the limitations; no `availabilityCode`, because the selected transport, not the host, withholds mutation. `LOOPBACK_TRANSPORT_EXTENSION_REASON` names a withheld configured extension so the omission is explicit. Loopback capabilities also declare `mayAccessNetwork`, since the bridge opens a `127.0.0.1` TCP listener the client connects to; `declareLoopbackNetworkEffect` applies it to a POSIX loopback session and to an existing-project session over loopback. `windowsP0Capabilities` and the Unix socket default are unchanged. - `src/ghidra/GhidraProvider.ts`: `capabilities()` and `inspectReadinessSnapshot()` use that projection on POSIX when loopback is selected; profile resolution passes the effective transport to extension resolution. - Docs: `docs/installation.md` (new "Ghidra bridge transport" section and a pointer from the socket paragraph), `docs/ghidra-provider.md`, `SECURITY.md`, and the skill reference `native-and-artifacts.md`. Contribution scope: a configuration option that selects an existing transport of an existing provider and withholds mutation and configured extensions on it, so no boundary widens; a maintainer scope decision is not applicable. Whether POSIX loopback may carry mutation authority, or whether a FIFO transport is preferred, is the open question in #4588. Intentionally not included: - Mutation over POSIX loopback. The Java gates and `GhidraClient.ts` key read-only on the transport; changing that is the scope decision in #4588. - A FIFO transport (prototype evidence in #4588). - Forwarding the setting through `rea setup`. It is not in `SELECTED_REGISTRATION_SETTINGS`, like `REA_GHIDRA_DISABLED_ANALYZERS`; the docs say to set it in the sandboxed process environment. - A doctor field. Session diagnostics already report `transport`. - The Hopper bridge, which binds `AF_UNIX` in `bridge/hopper_bridge.py` and has the same limitation. - The `/tmp` fallback of `ghidraSessionRoot()` when `TMPDIR` has a dot-prefixed element; it is unrelated and reproduces without this change. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/config/environment.ts` at configuration parsing; transport selection and the admission refusal in `src/ghidra/GhidraProviderClient.ts` at client admission/creation. - Contribution scope fit and linked maintainer decision: existing provider and transport, read-only on the new path; not applicable. Mutation widening deferred to #4588. - CLI and MCP/tool-catalog contract: no tool or schema change. An invalid value fails configuration validation naming `REA_GHIDRA_BRIDGE_TRANSPORT` and its constraint. Over POSIX loopback the three mutating operations report `available: false` with the reason and no `availability_code`; results gain the loopback limitation strings, plus a withheld-extension limitation when a NativeAOT jar is configured. - Provider, bridge, target-format, or platform compatibility: the Java bridge is unchanged. Windows keeps loopback and refuses `unix-socket`. Linux verified against real Ghidra 12.1.4; macOS not run. - Evidence, artifact, provenance, or reconstruction contract: unchanged; the transport was already part of session diagnostics. - Target selection, authority, upstream behavior, and cleanup details: the loopback endpoint record, `.pending` atomic move, retry of a dropped pre-authentication connection and runtime cleanup are the existing POSIX code paths; no new lifecycle stage. - Generated metadata, package, or installation impact: skill reference text only; `generate-skill-metadata.mjs --check` passes. No catalog change. Expected effects: - [x] Read existing files or captured data. (existing Ghidra analysis) - [x] Launch or attach to a process. (existing headless Ghidra launch; only its bridge transport is selectable) - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [x] Contact network services. (the bridge binds and the client connects to `127.0.0.1` on an ephemeral port, as Windows P0 does; nothing leaves the host) - [ ] 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: - `src/config.test.ts`: accepts both values, keeps the field absent when unset, rejects another value naming the setting and constraint without echoing the value. - `src/ghidra/GhidraProvider.behavior.test.ts`, "Ghidra bridge transport selection": POSIX default stays `unix-socket` with full authority and no loopback limitation; the setting selects loopback, adds the limitations to results, withholds exactly the three mutating operations (same in `inspectReadinessSnapshot`) with the reason and no code; a configured NativeAOT jar over loopback commits no `analysis_extensions`, the session still opens, and the result carries the withheld-extension reason; loopback capabilities declare `mayAccessNetwork` while the Unix socket default does not, including for an existing-project session over loopback; Windows keeps its P0 capability wording and refuses `unix-socket` with a `ProviderAdapterError` before the client factory runs. - `src/ghidra/extensions/GhidraExtensions.test.ts`, "Ghidra extension transport withholding": resolution withholds a configured extension over a non-admitting transport; the profile validator expects the empty inventory there and treats a committed extension as an inventory mismatch. - `tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts` (skipped on win32): the production provider client, private runtime, endpoint record and loopback socket against the fake bridge; `list_procedures` succeeds, the endpoint is `bridge-endpoint.json`, and the limitations are present. - `tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts`: updated for the new extension-resolution signature (the selected transport is threaded through). The transport-withholding regression tests were written red-first: each fails against the unfixed predicate (extensions admitted over loopback) and passes once it is applied. ## Validation performed Re-verified on `3a0424058`, rebased onto current upstream main (`d48e472fa`), Linux x86-64: - `tsc --noEmit` — clean. - `oxfmt --check src tests`, `oxlint src tests` (0 errors), `knip`, `verify:architecture-guards` (2283 files), `verify:module-boundaries` (3447 files) — all pass. - Ghidra unit suites `src/ghidra/extensions/GhidraExtensions.test.ts` and `src/ghidra/GhidraProvider.behavior.test.ts` — 201 passed. - Boundary suites `tests/boundary/providers/ghidra/ghidraBridgeTransportSelection.test.ts`, `ghidraTransport.test.ts`, and `tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts` — 23 passed. End-to-end against real Ghidra 12.1.4 (Linux x86-64, CLI), captured on the initial implementation; the runtime transport path is unchanged since, for `int add_magic(int x){ return x * 3 + 42; }`: - Unset: `code: execution_failure`, `failure_message: Ghidra exited before bridge startup`, `transport: unix-socket`, script error `java.net.SocketException: Operation not permitted ... at ReaGhidraBridge.serveUnixSocket`. - `REA_GHIDRA_BRIDGE_TRANSPORT=authenticated-loopback-tcp`: `normalized_result` is `int add_magic(int param_1) { return param_1 * 3 + 0x2a; }`, provider `ghidra 12.1.4`, limitations include the loopback strings. - Same setting, `rea annotate-native-function`: `capability_unavailable` with `reason` "The authenticated loopback bridge transport does not admit database mutation; unset REA_GHIDRA_BRIDGE_TRANSPORT to use the Unix socket transport." - [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. Remaining proof gaps: the NativeAOT-extension-withholding path is covered by unit and boundary tests but not exercised against real Ghidra with a configured jar over loopback; macOS not run; `npm run verify:ghidra` not run with the setting; no Windows host; the full-repo `npm run check:pr`, `verify:package`, and `config.test.ts`/`docs:check` were not re-run in this pass (`docs:check` fails in this sandbox on `EROFS mkdtemp`, an environment limitation, not a repo defect). ## Compatibility, safety, and release - Breaking changes or migration steps: none. Unset keeps the Unix socket on POSIX and loopback on Windows. The only new failures are an invalid value of the new setting, or `unix-socket` on Windows. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on Linux x86-64 through the CLI, with and without the setting. - Package or release metadata impact: skill reference text only. - Security, privacy, process, or containment review: over loopback the listener is reachable by every local uid and the per-session 256-bit token is the only authentication; the bridge drops unauthenticated peers after a 1 s handshake budget. The POSIX descriptor keeps its consume-and-delete flow, so the token is not on disk after startup. Mutation and configured extensions stay withheld on this transport. `SECURITY.md` and the installation guide say so. The value is non-secret and appears in limitations and diagnostics. ## Author readiness Human owner: @CameronBrooks11 - [x] 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. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## 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. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [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>
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#6326
No description provided.