+1 1ddee1089b feat(dotnet): expose field constant defaults (#8015)
* feat(dotnet): expose field constant defaults

* test(dotnet): guard field default comparison warning

Compare actual ordinary and constant-bearing observations independently of the limitation producer. Preserve warning delivery and unchanged matching assertions while allowing harmless wording changes.

Refs #5150

* test(mcp): retain structured bundle path diagnostics

Keep absolute-path refusal and all rejected parameter facts independent of legacy flat or current structured SDK diagnostic rendering. Retain relative CLI, absolute MCP and no-Evidence controls.

Refs #5150

* Revert "fix(ci): integrate main and repair inherited static checks"

This reverts commit afd4d571f6, reversing
changes made to 317bf3fe23.

* fix(ci): replay reviewed field-default integration

* fix(analysis-view): keep inferred confidence on views of inferred Evidence (#8099)

createAnalysisViewEvidence chose view confidence from the view result kind
only, so every summary, page or item view of an inferred parent
(compare_source_to_bundle, inspect_evm_interface, partially resolved
inspect_il2cpp_metadata) was sealed as derived while keeping the parent's
authority. A projection never makes its facts more certain: an inferred
parent now yields inferred views through CLI and MCP alike. Observed and
derived parents keep their existing view confidence and identities.

Fixes #8098

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): read flattened framework Info.plist from Resources (#8101)

A macOS framework without Versions/ keeps its binary, _CodeSignature/ and
Resources/Info.plist directly in the .framework, as Electron and Gecko apps
ship it. Bundle anatomy looked for a shallow bundle's plist only at its root
and reported info_plist_path: null under complete coverage.

Resolve a shallow bundle's root Info.plist first, then Resources/Info.plist,
the same layout a framework version directory uses. Keep the iOS platform
rule tied to a root-level Info.plist so a Resources/-only application root
is not classified as iOS.

Fixes #8100

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(desktop): charge retained rows at their measured representation (#8105)

* fix(desktop): charge retained rows at their measured representation

The desktop scan reserved 1024 + 24 bytes per path character for every
row, omission and the root against its 32 MiB representation budget. Real
rows cost 800-940 heap and 490-640 JSON bytes, so the charge overstated
them 6-13 times and large macOS bundles stopped at 3,700-7,200 entries,
far below max_entries.

Charge each path at the larger of its UTF-16 heap size and its escaped
JSON size, plus the existing fixed row and omission overheads, in one
helper. The bound still covers the retained heap and the serialized
report, including control-character paths that expand six times in JSON.

Fixes #8104

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(desktop): charge every retained copy of a path

The tighter row charge counted each path once, but rows repeat it in
convention-fact values and the next probes those facts offer, and host
failure issues name the absolute path. Control-character paths under a
convention such as Content/Paks or resources/app.asar therefore serialized
47-80 MiB of reports within the 32 MiB budget.

Reserve each copy through the same text helper where it is stored: path
convention facts with their row, the probes any fact offers (at most two,
pinned by a projection test) with that fact, retained read ranges before
their read, and probe failure issues before they are recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(source-maps): resolve empty sourceRoot sources against the map URL (#8107)

webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since
5f702dd6c the shared join added "/" for any non-null root, so these
sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts
under /npm/rxjs@7.8.1/dist/cjs/internal/ became /src/internal/Observable.ts,
and xterm's webpack://xterm/./src/... became https://host/webpack://xterm/....

Treat an empty root like a null root in sourceMapReference, the single join
used by trace-web-source-location, analyze-javascript-application and the
web source-map fetcher. Chrome, Firefox and trace-mapping skip an empty root,
and tc39/ecma426#183 wrote the current steps to match them. Non-empty roots,
including "/" and repeated slashes (#6726), keep their existing behavior.

Fixes #8106

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): stop reporting self-defaulted receivers as module loads (#8109)

The CommonJS loader search treated any repeated state on its own ancestor
chain as an unknown loader. A self-default such as
`session.messages = session.messages || []` forms that cycle, because a
member read inside the assigned value was attributed to the later call's
read site and selected the same write again. Each `push` on the value was
then reported as an unresolved `require` load, and graph coverage became
partial.

Record a member read inside a flowed value as a depth-1 captured selection
at its own position, as member mutations already do for flowed origins.
Treat a revisited state as adding no source; the cycle's other entries are
still expanded once each. Remove the superseded ancestor tracking.

Fixes #8102

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(captures): admit browser-serialized HAR request URLs (#8112)

* fix(captures): admit browser-serialized HAR request URLs

har-schema@2.0.0 checks request.url with ajv-formats' strict RFC 3986
`uri` format. Browsers record and send URLs as WHATWG serializes them,
which keeps characters such as `|` and `{` unescaped in the query, so one
Google Fonts or GraphQL GET entry made inspect-web-network-capture and
environment-build reject the whole HAR.

HAR 1.2 defines request.url only as the absolute request URL. Own that
correction in the HAR validator: every URL the RFC 3986 check accepts
stays accepted, and a URL that WHATWG URL serialization reproduces
unchanged is accepted too. Spellings WHATWG would rewrite (a backslash
authority, a tab, a space) and relative references still fail.

Fixes #8110

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(captures): state that RFC 3986 request URLs stay admitted

The HAR request URL rule is "passes RFC 3986, or WHATWG serialization
reproduces it unchanged". A spelling WHATWG would rewrite therefore fails
only when it also fails the RFC 3986 check; an uppercase host still
passes. Say so in the docs and the validator comment, and pin the
accepted uppercase-host case in the CLI test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): charge the XML plist decoder's DOM before decoding (#8115)

* fix(apple): charge the XML plist decoder's DOM before decoding

The XML preflight charged 4 bytes per character and 256 bytes per start
or end tag. The decoder builds an @xmldom/xmldom DOM that measured
1,000-1,160 bytes per element and up to 240 bytes per text, comment or
CDATA node, which were not charged at all. A 1.75 MB plist of 230,000
<true/> lines was admitted under the 64 MiB decode budget and peaked at
249 MiB of heap; shipped Apple plists peaked at 80-104 MiB.

Charge each element and each text, comment, CDATA or DOCTYPE node at a
measured upper bound, keeping the character and number-projection terms.
The binary estimator already bounds its decoder and is unchanged.

Fixes #8113

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): charge every XML element, attribute and reference

The XML preflight recognized only element names starting with an ASCII
letter, so names such as é, _, a CJK or astral character escaped the
element charge: 100,000 <é/> elements (0.5 MB) built an 84 MiB DOM before
the plist decoder rejected the tag. Attributes and entity or character
references were also uncharged; 20 attributes per element measured about
210 bytes each, and each reference about 26 bytes beyond its characters.

Classify markup by the character after "<" instead of a name pattern, so
anything other than an end tag, comment, CDATA, PI or declaration counts as
an element (saxes rejects malformed markup before the DOM is built). Charge
each unquoted "=" in a start tag as an attribute node and every "&" as a
reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): charge XML references only where the parser expands them

The previous commit charged every "&" in the document as a reference,
including literal ampersands inside CDATA sections and comments. A valid
plist with one CDATA string of 1,000,000 ampersands was refused although
it is a single node the decoder holds as one string.

Count "&" during the existing token walk, only in text runs and in the
attribute values of start tags, so comments, CDATA, PIs and declarations
add no reference charge and the scan stays a single pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(fit): preserve leading U+FEFF in property strings (#8083)

* fix(environment): retain conflicting scenario MIME uncertainty (#8082)

* fix(environment): retain conflicting scenario MIME uncertainty

* test(mcp): assert structured absolute-path diagnostics

* test(mcp): assert typed selector and stdio diagnostics

* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers (#8120)

* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers

A main handler on an unresolved channel can receive any literal renderer
send of a compatible mode, but pairing ignored it unless it had a scoped
receiver. The send was then reported unpaired, or paired with another
handler. Treat an unresolved-channel handler as a competitor that never
pairs.

The wrapper pass seeds only calls with a literal first argument, so it now
records which invocations of a dynamic site it traced. The syntax finding
gains `invocation_channels: "literal"` only when every invocation is a
traced direct call of an unexported local function supplying a literal
channel. Such a site is already represented by its per-invocation findings
and does not compete.

Fixes #8118

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(electron): keep guarded wrapper registrations competing in IPC pairing

A guarded wrapper registration keeps its predicate on a call path and has
no IPC finding. It still counted toward `invocation_channels: "literal"`,
so pairing lost it: a send on that channel was reported unpaired, or
paired uniquely with a direct handler. The enumerated site now carries
these channels as `guarded_invocation_channels`, and each one competes for
its own channel without pairing.

Index each binding's direct call sites by appending in place. Spreading
the earlier calls on every reference was quadratic in a file's call count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): remove unused exports that fail deep static checks (#8095)

* fix(ci): remove unused exports that fail deep static checks

Knip on main reports three analysis-view hub re-exports, two type
re-exports, an unused artifact-format guard and an alias constant. Drop
the dead re-exports and guard, and give the debugger control-reply budget
its own literal: it is a separate budget from the caller command-output
default, as its comment already states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(config): read the Rizin control budget from its own constant

The Rizin control-reply admission limit still took its value from the
caller command-output default, while GDB used DEBUGGER_CONTROL_REPLY_BYTES.
Read the dedicated constant in both so the control budget has one owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(javascript): keep one member-effect record per stored fact (#8103)

Member-mutation indexing appended a binding write or escape record for
every traversal state that reached it. States that differ only in
coverage fields the record never stores (origin, owner initializer,
getter widening) produced identical records, and each later receiver
query rescanned all of them. Record each stored fact once, alongside the
existing required-module effect deduplication. Propagation is unchanged.

Effect ordering also re-walked the same top-level statement ancestry for
every candidate record. Cache it with the other initializer positions,
and test the selected path before the costlier receiver comparison.

On the #7774 reproducer the semantic IR and the complete
analyze-javascript-application Evidence are byte-identical, while the
CLI run drops from 146 s to 40 s.

Refs #7774

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(native): open extension, framework, XPC and plug-in bundle directories (#8117)

* feat(native): open extension, framework, XPC and plug-in bundle directories

open_binary and the CLI inspection commands admitted only .app
directories and pointed every other Apple bundle at the JavaScript
analyzer. Resolve .appex, .framework, .xpc, .systemextension, .bundle
and .plugin directories through their Info.plist CFBundleExecutable, in
the Contents or flat root layout, plus the versioned framework layout
under Versions/Current. Exactly one layout may declare the bundle and
every resolved path must stay inside it.

Unresolvable bundles fail closed with typed constraints, probed paths
and the Info.plist that was read. Other Apple bundle types are refused as
unsupported instead of being offered the JavaScript route. Resource-root
discovery shares the same layout rule, so inspect_plist reads a
versioned framework's Info.plist. The recognized bundle vocabulary now
has one owner in the domain.

Refs #4310

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): refuse kernel extensions and state bundle error fields

Name the Apple bundle types binary routes recognize but do not open
(.dext, .kext, .qlgenerator, .mdimporter) explicitly, so a kernel
extension is refused as unsupported instead of receiving the JavaScript
route. Document which bundle refusals carry probed_paths and info_plist,
and make success tests throw the typed admission error before asserting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(native): open flattened frameworks through Resources/Info.plist

Electron and Gecko apps ship frameworks without Versions/: the program
file sits at the framework root beside Resources/Info.plist, the layout
#8101 taught bundle anatomy. Read that layout for a framework without
Versions/, and keep the versioned layout for one with it, so a versioned
framework's root Resources link is never read as a second declaration.
The resource root reports it as flattened-framework.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(security): configure dependency and ownership reviews

* perf(javascript): index member-effect paths for projected reads (#8108)

* perf(javascript): index member-effect paths for projected reads

Each projected read scanned every member-write record of its binding and
tested the record path against the selection. On the #7774 reproducer
only 3.7% of scanned records pass that test. Index each binding's
append-only records by path in a lazily built trie that extends itself
with newly appended records, and visit only records whose exact keys a
selected choice names or whose keys are wildcards. Candidates come back
in record order and still pass the exact predicate, now owned next to
the index as semanticEffectPathMatches.

The semantic IR and the complete analyze-javascript-application Evidence
are byte-identical. On the full reproducer each worker phase drops from
about 80 s to 29 s, and to 8 s together with #8103.

Refs #7774

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(javascript): stop escape checks at the first indexed match

The escape check only asks whether any matching escape is unsuperseded,
so it does not need ordered candidates. Share one trie traversal between
the ordered write candidates and an early-exit visitor for this check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(hopper): distinguish an absent demo dialog from publisher rejection (#8074)

* fix(hopper): distinguish an absent demo dialog from publisher rejection

The Linux demo watcher collapsed an admitted launcher with no first-run dialog into unsupported_demo_dialog. Report that post-authentication timeout as demo_dialog_absent, keep the unsupported dialog code for an observed but unrecognized Hopper window, and allow REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS to widen the fixed wait. Closes #8008.

* fix(hopper): tolerate callers without the dialog timeout argument

* fix(hopper): report an invalid demo dialog timeout as its own failure

An invalid REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS exited 73 and was shown as
invalid_launch_command. Exit 82 is invalid_dialog_timeout. The public error
names the setting, shows the rejected value and source, lists the allowed
range, and says to restart REA.

Drive the demo watcher with controlled windows and time. No window exits 81,
a Hopper window without a supported dialog exits 76, and a dialog that arrives
after the old 10 second limit is still accepted.

* fix(hopper): keep startup stderr capture on AdapterFailure

DemoWatchStop was inserted above with_stderr, so launch failures called a
method they no longer had and became unexpected_failure. Restore with_stderr
on AdapterFailure and reject a watcher fixture that cannot capture stderr.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* test(ci): require provider checks and reduce test startup (#8145)

* test(ci): require provider coverage and reuse test sessions

* fix(ci): prepare the runtime before Python producer tests

The fast Python job ran the suite on a fresh checkout, so analysis-efficiency
checks failed without compiled process modules. Install locked dependencies
and prepare the runtime before the suite.

* fix(ci): keep provider test reports inside the checkout

The provider-report reader accepted an arbitrary path from argv. Resolve the
path and reject anything outside the checkout before reading it. The packet
and signal workflows write those reports under .cache.

* fix(ci): require a test module in each named Python bridge family

Discovery still runs every bridge test. The mitmproxy, native, and pwndbg
families must each keep a test module, so deleting one cannot pass through
the others.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* fix(javascript): emit no http request for a target that is not a URL (#8147)

* fix(javascript): emit no http request for a target that is not a URL

`node:http` parses a string target with `new URL(...)` before a request object
exists, so a literal that is not an absolute URL throws `ERR_INVALID_URL`
synchronously. The analysis reported `endpoint` at `resolution: "complete"` for
those calls, and the graph then projected `constructs-request` as `resolved`.

Measured on Node 24.22.1:

    http.get("nope")                     -> THREW ERR_INVALID_URL
    http.get("example.test/api")         -> THREW ERR_INVALID_URL
    http.get("/api/items")               -> THREW ERR_INVALID_URL
    http.get("/api/items", { host })     -> THREW ERR_INVALID_URL
    http.get("")                         -> THREW ERR_INVALID_URL
    http.get("//example.test/x")         -> THREW ERR_INVALID_URL
    http.get("http://")                  -> THREW ERR_INVALID_URL
    http.request("nope")                 -> THREW ERR_INVALID_URL
    https.get("/api/items")              -> THREW ERR_INVALID_URL
    --- accepted ---
    http.get("http://example.test/x")    -> OK ClientRequest
    http.get("HTTP://example.test/x")    -> OK ClientRequest
    http.get("http:/x")                  -> OK ClientRequest
    http.get("http:///x")                -> OK ClientRequest
    http.get("http:x")                   -> OK ClientRequest

`URL.canParse` is the exact predictor of that branch, so a relative spelling such
as `"http:x"` keeps its operation. It already has precedent in this codebase
(`javascriptPackageImportAliases.ts`).

Two boundaries are deliberate. `fetch` and `new WebSocket` are host-supplied
globals: a browser resolves a relative target against the document URL, so their
spellings are not judged by Node's absolute-URL rule. A protocol mismatch such as
`http.get("ftp://...")` is also left alone because an options object can rescue
it, so it is not a provable throw.

The guard is scoped to `method === "get" | "request"`, which only
`requestMethod`'s `http`/`https` builtin branch can produce.

* chore: retrigger title and readiness checks after the title was corrected

* refactor(ci): unify lane ownership and guard Python tests (#8150)

* refactor(ci): unify lane ownership and guard Python tests

* fix(ci): reject entirely skipped bridge subtests

* docs: correct the raw ARM example and document a Cortex-M language override (#7483)

* docs(raw-binary): replace the STM32-style ARM example

The headline raw example opened ecu.bin at 0x8000000, an STM32 flash
address, while the arm lane decodes in ARM state. Map a 32-bit ARM-state
image at 0x8000 instead, and state beside the example that Thumb-only
images, including Cortex-M firmware, are not supported.

* docs(ghidra): document an interim Cortex-M language override

Ghidra imports little-endian ARM ELF files as ARM:LE:32:v8, which
defaults to ARM state and marks Thumb code only from $t mapping symbols
and odd function-symbol addresses. Unpinned arm ELF already admits
REA_GHIDRA_LANGUAGE_ID, so document ARM:LE:32:Cortex (ARMv6-M, ARMv7-M,
ARMv7E-M) and ARM:LE:32:v8-m (ARMv8-M) as an unverified interim.

State the override's full effect while it is set: raw-binary, DOS, DOL,
PSP and language-pinned lanes such as RISC-V refuse it, and REA asks
Ghidra to import every other target in the ARM language, because it does
not check the override's processor against the target.

* fix(ida): admit architecture families by allowlist (#7485)

IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so
any family nobody excluded was reported supported, and the label ternary
printed "Hexagon" for any other metadata family. Admit exactly the
families IDA admits today (x86, x86_64, arm, arm64) and name every other
family from its own architecture id. Families that Ghidra admits keep
the pointer to a Ghidra profile.

Refusal text now uses architecture ids, for example "no verified mips
lane" instead of "no verified MIPS lane".

* ci(ghidra): probe M-profile ARM and Xtensa decoding (#7482)

* ci(ghidra): probe M-profile ARM and Xtensa decoding

The embedded plan relies on ARM:LE:32:Cortex, ARM:LE:32:v8-m and
Xtensa:LE:32:default, but CI only exercised ARM:LE:32:v7. Import
source-owned bytes for each language at 0x1000 with -noanalysis and
check how they decode from the language's default context: Cortex and
v8-m must start in Thumb state, v8-m must decode the ARMv8-M MSPLIM
register, and Xtensa must decode entry and retw.n.

Add the probe scripts the job runs, including the existing ARM context
probe, to the ghidra_bridge change scope.

* ci(ghidra): match decode-probe operands as complete operands

The operand expectation searched the instruction text for a substring,
so mrs/4/msp also accepted mrs r0,msplim. Compare it with each operand
Ghidra reports for the instruction, so only a complete operand matches.
The Cortex (msp, lr), v8-M (msplim, lr) and Xtensa (a1) expectations are
unchanged.

* test: guard target format vocabularies and DOL snapshot saves (#7481)

* test(mcp): save and reopen a GameCube DOL snapshot

#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.

* test(binary): require every target format in each target vocabulary

A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.

This is a guard until target formats have a single owner.

* test(mcp): assert the replayed DOL result and the owned replay limitation

The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.

* test: list Intel HEX and S-record targets in the vocabulary guard

Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.

* test: list UnityFS among the target format fixtures

Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list uImage among the target format fixtures

Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list the ESP image among the target format fixtures

Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* fix: correct stale provider refusal and diagnostic text (#7484)

* fix(ghidra): name every admitted family in the architecture refusal

The refusal listed x86, x86_64, arm, arm64, MIPS, RISC-V, raw 6502 and
raw m68k, but the admission set also holds powerpc and hexagon. Build
the text from SUPPORTED_ARCHITECTURES, marking raw-only families, so a
newly admitted family cannot be left out again, and name the refused
family.

* fix(hopper): stop calling the Ghidra Hexagon lane verified

Hopper declined Hexagon targets with "Select a verified Ghidra Hexagon
profile instead", but REA has no verified Hexagon lane. Record whether
each declined family's Ghidra lane is verified, and say that Ghidra
admits Hexagon through an unverified lane.

Name declined families by architecture id, as the Ghidra and IDA
refusals do.

* fix(ghidra): name the pinned language in profile readback refusals

The session readback said "MIPS" whenever a provider-pinned profile
conflicted with the target or with REA_GHIDRA_LANGUAGE_ID, although the
RISC-V, PowerPC and DOL lanes are pinned too. The readback helper was
MIPS-named and re-parsed the language slot that
ghidraProfileLanguageOverride already owns. Move it beside that owner as
a family-neutral readback and name the pinned language id instead.

* fix(ghidra): list DOL among independently verified load images

GameCube DOL load images are independently verified, but the declined
format reason, the inspect_native_load_image capability note and the
Windows P0 guide listed only DOS MZ, DOS COM and raw-binary.

* fix(ghidra): count RV32E's sixteen integer registers

The EF_RISCV_RVE refusal described a 15-register ABI. RV32E has 16
integer registers, x0-x15, as docs/ghidra-riscv.md already says.

* fix(elf): name the refused e_machine

"unsupported ELF architecture" did not say which machine was refused.
Include the numeric e_machine, for example EM_XTENSA 94.

* test(hopper): assert declined-lane facts and owned refusal reasons

The MIPS case banned the phrase "not verified" instead of checking
facts, so wrong advice would still pass. Expose each declined family's
Ghidra lane status, assert those statuses, check that the advice depends
on the status, and compare Hopper's refusals with
hopperDeclinedArchitectureReason. The RISC-V refusal table now compares
the provider's reason with ghidraRiscvUnsupportedReason and expects the
EF_RISCV_RVE declaration name rather than fixed wording.

* test(ghidra): assert verified load-image formats as owned facts

The declined-format test required display phrases such as "DOS MZ" and
"GameCube DOL", so harmless rewording failed it while wrong guidance
containing those names passed. The format list was also spelled three
times: in the attestation's membership check, its reason and the
inspect_native_load_image capability note. Give the verified formats one
owner with the formatters for both texts, assert the list as a fact, and
compare the delivered reason and capability note with those formatters.

* fix(hopper): stop restating which Ghidra lanes REA has verified

Hopper's refusal for families it declines carried its own table of
which Ghidra lanes REA has verified (mips, riscv and powerpc verified,
hexagon not). That restated the Ghidra provider's facts in Hopper and
goes stale whenever they change, the drift that made the Hexagon refusal
wrong in the first place. Reading Ghidra's facts from Hopper would
couple the providers instead; no module in src/hopper imports from
src/ghidra.

The table and hopperDeclinedGhidraLane are removed.
hopperDeclinedArchitectureReason now states only what Hopper owns: it
does not admit the family, and the Ghidra provider's lane reports
whether REA has verified it. The arm64_32 sentence is unchanged.

The test checks every declined family against the formatter: each
reason names the family and Ghidra, and apart from the name all reasons
are the same text, so none states a per-family Ghidra status.

* fix(hopper): route declined families to Ghidra without promising its lane facts

The refusal said the Ghidra lane reports whether REA has verified it, but
Ghidra admits Hexagon with no such note, so a caller could not find the
promised status. Hopper now only states that it does not admit the family
and routes the caller to the Ghidra provider.

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
Co-authored-by: Kaoru0822 <kanadekitauji@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ShuaixinHuang <1836656514@qq.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Henglie <90338155+Henglie@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: Entrpi <133157614+Entrpi@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-31 00:10:36 +08:00

English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Türkçe · Русский · Tiếng Việt · ไทย · Deutsch · Español · Français · Українська · Polski · Português (Brasil) · العربية · فارسی · Bahasa Indonesia · Italiano · Ελληνικά · മലയാളം · Roman Urdu · Română

REA: Reverse Engineer Anything

One MCP for reverse engineering across binaries, applications, and runtime behavior.

See a feature you like. Understand how it works, down to the binary level.

npm version MCP tool catalog Node.js requirements skills.sh MIT license Discord

morluto%2Frea | Trendshift

Website · Guides · Showcases

Quick start · How REA works · What you can analyze · Showcases · Build RL environments · FAQ · Documentation

npx rea-agents setup

Important

Get REA from morluto/rea, rea.tools, or the rea-agents npm package. Follow the installation and release links from these official sources. A similar repository name or copied README does not establish that a download is official.


REA launching its analysis bridge inside Hopper while inspecting a native binary



Discord
Join the Reverse Engineering Community

Discord · Q&A · Show and Tell


See a feature in an app that you want in your own product? Ask your agent to investigate it with REA. It can inspect the app without its source code, explain how the feature works, show the evidence, and build a version for your project.

REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.

Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper, Ghidra, IDA or Kuna installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs no native analysis engine.

Visit the REA website for setup instructions, illustrated guides, and real case studies.

Quick start

On Linux, Ghidra, JADX, firmware tool launches and static JavaScript/Electron analysis require a procps-compatible ps on REA's PATH for process ownership checks. Recovery steps are in docs/installation.md under "Linux owned-process prerequisite".

Set up your agent

With Node.js and npm installed, run:

npx rea-agents setup

Choose your agents, review the proposed changes, and approve them. Setup adds REA's MCP server and matching workflow instructions, with backups of existing configuration. Restart your agent afterward. Setup records the selected runtime and REA entry script directly. If acquired through a package runner, keep its cache entry available. Run package acquisition from a trusted directory outside the target project. Installation and recovery details are in the setup guide below.

Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and other agents. See installation and setup for provider configuration and manual MCP registration and Claude Code/Codex plugin installation.

MCP uses compact input schemas by default to reduce context overhead. Set REA_MCP_INPUT_SCHEMA_PROFILE=full in your MCP registration when you need the complete advertised field guidance and nested validation details. Both profiles keep the full tool set and canonical server validation. See "Compact input schema profile" in the MCP contracts guide.

Complete output schemas are advertised by default. If your client caps the complete tools/list frame, set REA_MCP_OUTPUT_SCHEMA_PROFILE=compact to bound each advertised output schema, or off to omit output schemas entirely. Both settings keep canonical server-side validation and complete structured results. See "Output schemas" in the MCP contracts guide.

Ask your agent

Understand how search works in the Notes app, show me the evidence, and build a
similar feature for my project.

Replace Notes with your target app and the feature you want to understand.

Use the terminal

Inspect an extracted JavaScript/Electron app directory or ASAR:

npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json

The result includes modules, imports, Electron boundaries and their evidence. Replace the path with your target, such as "D:/apps/example" on Windows.

To install the rea command for regular use:

npm install --global rea-agents
rea --help

For native analysis, configure a provider first. See the CLI and Evidence guide for native commands, provider selection, snapshots and scripting.

Update REA

REA changes quickly, and new releases include frequent bug fixes. Keep your installation up to date.

For a CLI installed with npm install --global or bun add --global:

rea update

rea --update is an alias for this verified workflow and accepts the same global output flags. Use rea update when combining it with other command controls.

Updater commands retain at most 4 MiB of stdout and stderr combined. Oversized metadata replies are rejected after the command exits; verbose installs continue with bounded diagnostics. Human progress output respects terminal backpressure and waits for submitted writes to finish.

Bun global updates require Bun 1.2.17 or later. With an older Bun release, upgrade Bun manually before running rea update.

To refresh your agent registrations and skill, run the setup command printed by the update.

If you use npx, update your agent setup with:

npx rea-agents@latest setup

Review the setup changes and restart your agent. For one-off CLI commands, use npx rea-agents@latest followed by the command. With Bun, use bunx rea-agents@latest in place of npx rea-agents@latest.

How REA works

Your agent calls REA through MCP to inspect the target and trace relevant code. REA returns findings with their evidence. The agent uses them to ask follow-up questions, explain the behavior, or write and test an implementation. CLI commands use the same workflows.

REA investigation flow: your agent asks about a local target, REA inspects and traces it using analysis tools, and the agent uses the returned code, references and unknowns to explain, implement and test.

Open the full-size figure.

What you can analyze

REA requires Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), or 26.12.1+, plus npm. Additional tools and host support depend on the target:

Target What REA returns Requirements and guide
Desktop installations Runtime candidates, supporting files, component markers and next probes Built-in bounded directory/header inspection; desktop applications
Native binaries Pseudocode, assembly, strings, symbols, calls and references Hopper, Ghidra, IDA or Kuna; native analysis
Offline ELF layout Sections, segments, original symbols/relocations and static mitigation candidates Caller-supplied pwntools on Linux x64; binary diagnostics
Go binaries Embedded compiler version, modules, replacements and build settings Built-in static ELF/PE/thin Mach-O inspection; no Go installation required
GPU code objects CUDA/HIP targets, selected kernel parameters and resources, retained PTX text Built-in static ELF/PE/fatbin/CUBIN/PTX inspection; GPU code objects
Model artifacts GGUF / safetensors metadata, tensor declarations and derived totals Built-in header inspection; optional full-file hash; model files
IL2CPP metadata Unity global-metadata.dat assembly images, type and method names and index ranges Built-in static decoding of versions 24, 27, 29, 31 and 39; no Unity required
Godot PCK Archive inventory and extraction, original header/directory facts and selected content identities Built-in static formats 2, 3 and 4; no Godot required
Rust binaries Embedded cargo-auditable crate names, versions, sources and dependency edges Built-in static ELF/PE/thin Mach-O inspection; Rust binaries
PE certificates Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields Built-in static PE inspection; PE certificates
PE imports Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash Built-in static PE inspection; PE imports
ELF notes PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields Built-in static ELF inspection; ELF notes
Apple IPS crashes .ips metadata, threads, images, registers and documented crash classes Built-in static JSON inspection; Apple IPS
Linux kernel modules .modinfo keys, __versions CRCs, this_module presence, and signature trailer Built-in static ELF inspection; kernel modules
Erlang BEAM IFF chunks, atoms, exports, imports, lambdas and the Code header Built-in static BEAM inspection; Erlang BEAM
WASM artifact Validation, sections, exact import/export WAT forms and decoded WAT Caller-supplied WABT 1.0.42; inspect_wasm_artifact
EVM bytecode Dispatch selectors, byte offsets, inferred arguments and mutability Local raw/hex carrier on Linux x64; offline EVM guide
SQLite databases Tables, columns, indexes, views, triggers and selected ordinary-table rows Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits
Recorded Linux crashes Raw notes, every recorded thread's registers/signals and optional mapping candidates Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; recorded crashes
JavaScript / Electron Modules, imports, source maps, routes, IPC and native add-on relationships Node.js and npm; Linux: procps-compatible ps; application analysis
Websites Page structure, scripts, network observations and requested screenshots A Chrome-family browser; browser analysis
Saved network captures Requests, responses, exposed payloads and source locations HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; capture guide
.NET assemblies Metadata, CIL instructions, declared native dependencies and build comparisons None (static only); managed-code guide
Android APKs Manifest declarations, classes, decompiled methods and references Headless JADX and a full JDK on Linux/macOS/Windows x64; Android guide
APK signatures v1/v2/v3/v3.1 signers, certificate fingerprints, key rotation and digest self-consistency None (static only); inspect_apk_signature
Android devices Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers Caller-supplied adb (emulator or hardware); ADB guide
JEB projects Engine-backed project units, artifact digests and type/method pseudo-code A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; JEB guide
Android resources Decoded manifest, string tables, locales and version facts Caller-supplied Apktool launcher; resource guide
Flutter apps Dart snapshot hash, per-ABI library digests and engine build provenance None (static APK parsing); Flutter guide
Firmware Regions, extraction results and native-analysis handoffs Binwalk / Unblob on Linux; firmware guide
Recorded packets Selected fields, repeated values, filters and decode-as rules Linux: TShark 4.x + prlimit; capture guide
Packet summaries Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests Caller-supplied tshark and capinfos (Linux-verified); pcap guide
Recorded logic signals Decoder annotations, sample ranges and diagnostics Linux: sigrok-cli 0.7.2 + prlimit; signal recordings
Packages and resources File inventories, digests, plists, Apple bundle anatomy and extracted resources Artifact and JavaScript guide, Apple applications
Process behavior Terminal output, interactions, exit and filesystem observations, and run comparisons Linux/macOS with a native PTY; process capture
PyInstaller executables CArchive members, Python version, entry scripts and extracted bytecode None (static only); Python packaged applications

Static JavaScript and .NET inspection read the supplied files without running the application. Runtime capture runs or interacts with the selected target using your user permissions; each runtime guide describes its effects.

For offline native-binary inspection, REA also supports caller-supplied GNU objdump and Rizin. See the reverse-engineering tools guide for provider setup, CLI commands, and Evidence limitations. REA does not install or update either provider.

Native formats and host support vary by provider. See Hopper and Ghidra setup, the IDA guide (including an optional native MCP bridge), the Kuna decompiler guide, and experimental Windows Ghidra support. Ghidra also supports 16-bit DOS analysis. For large binaries, raise the provider's startup deadline with REA_GHIDRA_STARTUP_TIMEOUT_MS or REA_HOPPER_STARTUP_TIMEOUT_MS. For provider selection, see the CLI guide. Check release availability for features added since the latest npm release.

Showcases

Illustrations of the DX-Ball sound-pan, Notion clipboard-bridge, and TH04 bullet-ring showcases

DX-Ball: reconstruct a sound-pan calculation

Follow a sound call into its position-to-pan helper, inspect the instructions, and turn incomplete pseudocode into C. The reconstruction passes 3,205 original-x86 cases and reproduces all 63 compiled function bytes.

Read the case study · Reconstruction repository

Notion: trace the Electron clipboard bridge

Find the renderer's clipboard API, follow it through preload and IPC into the main process, and inspect the rich clipboard format.

Read the case study

TH04: recover a DOS bullet-ring calculation

Inspect the original PC-98 game's 16-bit instructions, recover the fixed and aimed angle calculations, and compare the reconstructed C++ with the historical compiler output.

Read the case study · Reconstruction repository

Build RL environments with REA

Record a web app session, turn its API into tools, and export an environment an AI agent can use. Run it with AgentEnv, then add tasks and reward checks for RL training or evaluation.

Read the guide · Watch the video

If you've used REA on something interesting, we'd love to see it. Share your case in an issue or a pull request, including the target, your question, how REA helped, and what you found.

Build RL environments

Give an AI agent a local environment to practice tasks such as finding an issue or creating a folder, without running the original app. Use it for reinforcement learning (RL) or to test how well an agent completes tasks.

REA turns recorded API requests and responses into tools the agent can call. The environment uses the recorded data and supports changes only when the recording confirms their effects.

With REA set up in your agent, replace the example URL and folder tasks below with your own:

Use REA to build an RL environment from my test web app at
http://127.0.0.1:3000.
Cover listing, creating, renaming, and deleting folders. You may create and
remove test folders. Leave existing data unchanged.
Save it in a folder named my-env. Check which operations work and tell me
how to run it.
Use the command line instead

Use these commands if you already have a file containing the app’s recorded browser requests and responses. Running the result also requires AgentEnv and Docker.

rea environment-build ./session.har ./my-env --name my-env --json
agent-env run ./my-env

REA exports a runnable environment with a basic test to check that it starts and responds. Use it with your RL framework to define task goals, add reward checks, and run training.

Build an environment · Environment reference · Watch the demo on X

FAQ

Which agents can use REA?

Any agent that supports local MCP servers. Setup configures the supported agents; other clients can use manual MCP registration.

Do I need a native analysis engine?

Deep native analysis uses Hopper, Ghidra, IDA or Kuna, depending on the target and operation. Static JavaScript and .NET inspection work without a native analysis engine. Setup can install Hopper after approval; Ghidra, IDA and Kuna use your existing installations. See provider setup for the options and their supported targets.

Do I need to start Hopper first?

REA starts Hopper when an operation needs it. On macOS, a first-run dialog may ask you to choose demo mode or activate your license. Complete that prompt in the same user session before your first CLI/MCP operation or unattended job. Open Hopper manually if needed. See Hopper startup and troubleshooting.

What does installing the skill from skills.sh do?

The skill supplies investigation instructions for your agent. Use npx rea-agents setup to register REA's MCP server and install the matching instructions, then restart your agent. See skill-only installation.

What code does REA return?

Native analysis returns pseudocode and assembly. JavaScript/Electron analysis recovers modules and their relationships. Your agent uses these findings to write and test an implementation; the showcases give worked examples.

Does REA upload my app?

REA analyzes targets locally. Your agent receives the tool results, and its model provider has its own data policy.

What should I do if I hit a bug?

Update first; a recent release may already fix it.

For a CLI installed with npm install --global or bun add --global:

rea update

For agent setup through npx:

npx rea-agents@latest setup

If you're using an agent, complete the setup refresh and restart it. Retry the same task. If the problem persists, open an issue with your REA version, target type, steps to reproduce and error output.

Documentation

Start with the website's worked guides. For exact options, prerequisites and result contracts:

Report vulnerabilities through SECURITY.md.

Star history

🎉 400,000 GitHub stars — thank you!

Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.

REA GitHub star history

Disclaimer

See DISCLAIMER.md for responsible-use guidance.

The project's position on illegal or unauthorized use covers all REA versions, distribution channels, and platforms; each copy retains its applicable licenses.

REA is for lawful research, education, interoperability, software preservation, and analysis of software you own or are authorized to examine.

Contributing

We'd love your help with REA! Open an issue to report a bug or suggest a feature, or send a pull request to improve the code or docs.

See CONTRIBUTING.md for development setup and checks, testing for verification lanes, and the architecture map for selected layers and dependency paths.

License

MIT

REA-authored code is licensed under MIT. Third-party material retains its own licenses and copyright notices; see Third-party notices for the dependency inventory, preserved license texts, and external-tool terms.

You must legally obtain and be authorized to use commercial tools, including IDA Pro / Hex-Rays decompilers, Hopper, and JEB, under their applicable licenses. REA grants no tool license, key, or activation entitlement. Trials and demos remain subject to vendor terms, including user, feature, automation, and output sharing restrictions. Cracked copies, stolen keys, unauthorized key sharing, and bypassing license restrictions are not authorized. This guidance does not amend MIT or third-party licenses.

Languages
TypeScript 90.2%
JavaScript 5.7%
HTML 1.4%
Python 1.2%
Java 1%
Other 0.4%