Compare commits

...
Author SHA1 Message Date
morluto ff5af6a7cf fix(plugins): resolve review findings and integrate main 2026-10-14 07:51:46 +08:00
20e4f83097 fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory (#2792)
* fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory

probeJava now also reads the JVM os.arch property. When it maps to a
different native decompiler directory than the one validated from the
host architecture (for example an x86_64 JDK under Rosetta on Apple
silicon selecting os/mac_x86_64 while only mac_arm_64 was built), the
native_decompiler check fails with a remediation pointing at a
matching-arch JDK instead of reporting a healthy installation that
fails every analysis. Unknown os.arch values keep the previous
behavior.

* fix(ghidra): tighten JVM arch remediation and cover os.arch extraction

Address review findings on the JVM os.arch decompiler-directory check:
- The remediation no longer suggests building the decompiler for the
  JVM-selected directory, which could never clear the check; it only
  advises pointing JAVA_HOME at a matching-architecture JDK.
- Add a test running the real Java probe against captured
  -XshowSettings:properties output, asserting os.arch reaches the
  installation diagnostics.
- Document the JDK-architecture prerequisite in the Ghidra setup guide.

* fix(ghidra): refuse unrecognized JVM native architectures

* docs(ghidra): align doctor prerequisites with JVM architecture admission

* fix(ghidra): reject unreported JVM architecture

* test(ghidra): separate JVM and executable admission cases

* fix(ghidra): defer decompiler arch validation when Java is absent

* fix(ghidra): reject JVM native architecture override

* fix(ghidra): validate JVM platform overrides

* fix(ghidra): validate JVM os.name and helper-environment platform overrides

* test(ci): isolate report fixtures and declare PowerPC Java bounds

* test(ghidra): provide observed JVM platform in PowerPC fixture

* fix(ghidra): parse quoted JVM environment options before admission

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:31:11 +08:00
97aff28201 fix(electron): reject non-executable paths and contain launch rejections (#2705)
* fix(electron): reject non-executable paths and contain launch rejections

An executable_path that runs but never prints the inspector line made
Playwright's electron.launch() leave three waits unawaited. They rejected
unhandled and Node ended the CLI or the MCP server (#2269).

Check the resolved executable_path is a regular file, and on non-Windows
hosts that it passes X_OK, before any launch. Wrap only the launch call
in a scoped unhandledRejection listener that absorbs rejections whose
stack is in playwright-core/lib and re-throws anything else.

Tested on Linux with Node 22.23.1, 24.18.0 and 26.11.1.

Refs #2269

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* fix(electron): absorb only this launch's orphaned rejections

The listener around the launch dropped every rejection with a
playwright-core stack, so an unrelated Playwright failure during the
launch window would vanish. Hold those rejections instead. When the
launch settles, drop only the ones whose message is part of the launch's
own error (the orphans of a failed launch). Raise every other one again
through the unhandledRejection path, so the process still fails. A launch
that succeeds raises all of them. Rejections without a Playwright stack
still fail at once.

One setImmediate turn before the decision lets rejections queued by the
settling tick arrive first. It is a single turn, not a timer. A shared
set of absorbed errors stops overlapping launches from raising each
other's orphans. The listener is removed on every path.

Add child-process tests that drive the built provider through its launch
seam, since Vitest's worker has its own unhandledRejection listener.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* test(electron): distinguish launch success from capture ownership

* test(electron): assert cleanup ownership failures around overlapping launches

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(browser): avoid replaying observed launch rejections

* style: format comparison and IDA sources

* test(electron): allow cold macOS ownership setup

* test(electron): allow cold macOS setup in the early-exit path test

The test that runs an executable which exits at once builds the Swift
ownership helper twice on macOS: in the CLI process, and again in the
MCP server process on its first tool call that prepares. A cold Swift
cache can spend up to 120 seconds on a build. The test had only the
30 second default.

Give the test 180 seconds, as e1579ec31 did for
electronLaunchRejections.test.ts and 65a809a0d did for
webSourceLocationCli.test.ts. cli.run has no deadline of its own, so
it needs no timeoutMs.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* test(ci): isolate report fixtures and declare PowerPC Java bounds

---------

Co-authored-by: Claude Opus 5.5 <opus@fox.invalid>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 07:30:14 +08:00
641a8395ee fix(cli): refuse a JavaScript integrity policy the binary analyze lane cannot honor (#2808)
* fix(cli): refuse an integrity policy the binary analyze lane cannot honor

`analyze` declares --integrity-policy for its JavaScript lane and forwards it
only there. On a binary target the flag was accepted and then dropped:
inspect_artifact-style integrity handling does not exist on this path, and
binary_overview is a strictObject with no input at all, so the request could
never take effect and the run proceeded as if the caller had not asked.

The same branch already refuses source_paths on a binary target with a typed
AnalysisInputError, and the sibling `inspect` command refuses the flag outright,
so the refusal is the established handling here. The policy option carries a
default, so the check keys off a value that differs from the owner schema's own
default rather than the flag's presence; otherwise every binary analyze would be
refused. The default is read from the schema instead of being restated.

Verified with 5 integrity policy CLI cases, check:fast and
verify:test-discovery (1023 files) on macOS arm64, and by running the built CLI
for the no-flag, explicit-default and record-and-continue cases. The regression
case failed with the command reaching the binary lane before the change.

* fix(cli): reject explicit default integrity policies for binaries

* fix(cli): keep an omitted integrity policy absent on the binary lane

`javascriptApplicationOptions.shape.integrityPolicy` fills an omission with its
default, and `.optional()` keeps reporting that parsed value, so
`options.integrityPolicy !== undefined` was true for every `analyze <target>`
call: ordinary binary analysis was rejected before the target was even read.

List `z.undefined()` first in a union so an omitted option stays absent while
the owner still validates a supplied value, and reapply the owner's metadata so
help and `--schema` keep describing the option.

Validation: tests/boundary/cli/javascriptIntegrityPolicyCli.test.ts reports
1 failed | 5 passed without this change (the case "still routes a binary
analyze target without an integrity policy" returned invalid_request for
integrity_policy instead of reaching the target) and 6 passed with it;
`npm run check:fast` passes on macOS 26.5.1 arm64 with Node.js 24.18.0.

* fix(cli): omit integrity defaults from binary option schema

* test(ci): isolate report fixtures and declare PowerPC Java bounds

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:28:00 +08:00
1ae9000587 fix(javascript): exclude TypeScript this annotations from runtime parameters (#2761)
* fix(javascript): exclude TypeScript this annotations from runtime parameters

* test(javascript): assert exact typed argument bindings

* test(apple): keep dyld slice regression portable

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* fix(ci): restore descriptor and fixture validation

* test(ci): retain verification coverage within lint limits

* fix(test): share admitted Ghidra profile installation fixtures

* fix(ci): isolate timing report fixtures from workflow state

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:18:50 +08:00
morluto 02987d8e8c test(hopper): bind string assertions to observed source extents
Remove OS-based assumptions about long and non-ASCII string recognition. Require the dedicated fragmented fixture, independently verify full source bytes, and reject gaps, overlaps, incomplete reads and corrupted decoding. Report unrecognized native strings explicitly and retain the actual procedure count.

Verified through 14 verifier regressions and the complete macOS arm64 Hopper 6.6.0-demo CLI/MCP lane with cleanup.
2026-10-14 07:12:10 +08:00
morluto b1b0211c28 fix(native): preserve observed function dossier coverage
Declare observed Ghidra and Hopper facets, preserve unavailable pseudocode, and attach dossier call-set coverage. Version the analysis profiles to invalidate older snapshots and remove the retired include_assembly fixture branch.

Add MCP comparison and snapshot-replay regressions plus real-provider checks for empty and unavailable facets. Fixes #2980.
2026-10-14 07:11:55 +08:00
80592b82a6 fix(javascript): keep CommonJS percent bytes literal (#3001)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:50:29 +08:00
b9b3a9098b fix(gemini): compare consolidated allowlists through one normalizer (#2495)
* fix(gemini): compare consolidated allowlists through one normalizer

geminiServerPolicyBlock consolidates allowlists by normalized intersection but
tested the consolidated list against the raw server key. Because the
intersection keeps the spelling of whichever policy came first, the same pair of
policies blocked the server in one arrangement and allowed it in the other, and
the excluded test had the mirror-image asymmetry.

Normalize the comparison key once and use the same normalizer for the exclusion
test, the intersection and the membership test. The messages keep the
caller-facing server key, so lookup and display stay separate.

Verified with 88 client profile, lifecycle and registration tests, check:fast
and verify:test-discovery on macOS arm64. The regression test drives setup twice
with the same policies in both arrangements; before the change one arrangement
failed and the other passed.

* test(gemini): cover normalized exclusions and padded allowlist entries

The regression case added with the fix drove the allowlist intersection in
both spellings but left the other two comparisons the change touches
uncovered: the exclusion membership test and the trimming applied to padded
names. Reverting either behaviour kept the suite green, so neither was
pinned. Greptile raised exactly this on the PR.

An exclusion is now exercised as " REA " against the selected server, and a
padded allowlist entry is exercised as the only policy, so a padded entry has
to permit the same server its exact spelling permits rather than fall to the
membership test.

Verified with `npm run test:focused -- tests/boundary/cli/clientProfiles.test.ts`:
14 passed with the change; reverting only ClientConfigurationDocument.ts to
main fails both cases, the arrangement case with `rea+REA: expected 1 to be +0`
and the new one on both the excluded and the padded-allowlist assertions.

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 06:43:03 +08:00
Yann RobertoandGitHub 1f122c02b7 fix(javascript): exclude HTML URL suffixes from path admission (#2833) 2026-10-14 06:41:22 +08:00
bcb60c179e fix(browser): read expression-free template literals as module specifiers (#2842)
Captured-bundle chunk edges and source-map original module edges only
accepted quoted string literals as import()/require() specifiers. Bundlers
such as rolldown emit plain constants as expression-free template literals
(import(`./chunk.js`)), so statically known dependencies were dropped while
completeness stayed complete. Read both sites through the shared exact-literal
reader (stringValue); template literals with substitutions stay unrecognized.

On OWASP Juice Shop v20.3.0 this recovers 8 lazy chunk edges (previously 0).

Fixes #2841
Fixes #2599

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:40:25 +08:00
c6166d4b56 fix(javascript): retain bridge conditions on IPC joins (#2747)
* fix(javascript): retain bridge conditions on IPC joins

* fix(javascript): retain IPC condition ownership

* style(javascript): format bridge path regression

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* fix(javascript): preserve Electron join condition coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codex <codex@users.noreply.github.com>
2026-10-14 06:39:27 +08:00
3a4d49406a fix(javascript): preserve Electron runtime entrypoint paths (#2742)
* fix(javascript): preserve Electron runtime entrypoint paths

* fix(javascript): resolve runtime entrypoints before modules

* fix(javascript): preserve Electron runtime path semantics

* fix(javascript): resolve Electron renderers exactly

* fix(javascript): retain runtime package aliases

* fix(javascript): retain missing renderer URL paths

* fix(javascript): resolve Electron URL and utility entries correctly

* fix(javascript): reject suffix-only worker URLs

* fix(javascript): respect Electron URL and utility path semantics

* fix(javascript): separate Node runtime entrypoints from source imports

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e12184d56a9ba80a4427b3e6082ddac1ec)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc569a1cfb34b6efedcb6f2e3aaaa3357)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 06:39:17 +08:00
7072f87baf fix(artifacts): preserve Mach-O load-command string identity (#2110)
* fix(artifacts): preserve Mach-O load-command string identity

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74cc84a45dcb186422a0f51aa61bc427)

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* style: format identity comparison and IDA runner

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 06:38:32 +08:00
1ebbc3b371 fix(managed): pair members by declared identity before name-free remaps (#2116)
* fix(managed): pair members by declared identity before name-free remaps

The first matching round keyed methods on signature and normalized CIL,
and fields on signature, without the declared type or name. Identical
members of different types fell into one ambiguous group, so a file
compared with itself reported unknowns, and a lone match across types
was reported as an unchanged exact pair while the member's own
counterpart went unpaired.

Pair by declared type, name, and exact CIL/signature first, then by
declared type, name, and raw signature. The name-free CIL/signature,
field-signature, and structural rounds now run last, as remaps, and
skip a pair across declared identities while either member's own type
and name remain unpaired on the other side.

Nested types that share a bare name still share one declared identity,
so their identical members stay ambiguous in a self-comparison.

* fix(managed): keep members with kept identities out of remap rounds

The cross-identity check applied only to one-to-one groups. A member
whose own declared type and name were still unpaired on the other side
could fall into a larger remap group and be reported ambiguous, and a
blocked candidate could make an otherwise unique remap ambiguous.

A remap round now drops such members before grouping, along with
members whose declaring type is unresolved. Same-identity structural
pairs get their own round before the remaps. Each member's declared
identity is computed once.

Ambiguity limitations now name the evidence a group shares, which
stays true whether the round keyed on names or not.

* fix(managed): keep names in unresolved identities and hold ambiguous ones

A member of an unresolved declaring type had no declared identity, so
the declared-identity rounds keyed it by CIL or signature alone. Two
unresolved members with different names paired as an exact match, and
a file compared with itself reported unresolved members ambiguous.

The declared identity now always includes the name, with a null type
for an unresolved one. Remap rounds still leave such members out.

A remap round now also leaves out a member whose own identity sits in
an ambiguous group on the other side, not only one left one-sided.

The undecoded-counterpart check uses the same declared identity.

* fix(managed): keep remap groups with unresolved owners ambiguous

Remap rounds dropped members of an unresolved declaring type before
grouping. Such a member can be another member's own counterpart, so a
resolved member could remap to a copy in another type while its
unresolved counterpart was reported added.

Unresolved members now join remap grouping, and a group that contains
one stays ambiguous instead of pairing.

The undecoded-counterpart set reuses each member's computed identity.

* fix(managed): keep ownerless members out of declared rounds when shared

A member whose declaring type did not resolve has a declared identity
of a null type and its name. The declared rounds paired two such
same-named members even when the other side also held a resolved
member with the same CIL and signature, which could be the owner. The
old name-free first round reported that group as ambiguous.

An ownerless member now enters the declared rounds only when no other
member on either side shares its exact CIL/signature key, and never
the declared structural round. Otherwise it reaches the remap rounds,
which keep any group with an unresolved member ambiguous.

* fix(managed): keep competitors of an ownerless member out of the declared round

When an ownerless member shares its exact CIL/signature key with another
member on the same side, its owner may be that member. The resolved
members holding the key now stay out of the declared round too, so they
cannot pair and consume the candidate the ownerless member may own, and
the remap round keeps the whole group ambiguous as main does. A lone
ownerless member still pairs with its lone counterpart.

* test(managed): split the ownerless comparison tests under the function length limit

The ownerless describe block reached 181 lines, past oxlint's
max-lines-per-function limit of 150. Move the structural-competitor
case into its own block.

* fix(managed): contest signatures and remap only open candidates

* fix(managed): retain blocked raw-signature choices as unknown

* fix(managed): keep member names in signature contention

* fix(managed): scope residual signature contention by name once

ownerlessContested applies competitionIdentityKey itself, but
unresolvedSignatureAmbiguities passed it keys that were already
name-scoped, so the contested set never matched the groups and the
same-signature, different-body case reported additions and removals
instead of one unknown group. Pass the raw signature key.

* fix(managed): constrain matchObserved to declared members

matchObserved passes its items to matchByKeys, which needs Declared, so
its looser { declaring_type } bound failed typecheck (TS2344, TS2322).

* fix(managed): retain held ownership contention for residual matches

* fix(test): bind workspace fixture in Hopper launch cases

* fix(ci): prepare worker tests and preserve Hopper cleanup ordering

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:36:43 +08:00
c86b09a109 feat(ghidra): admit 32-bit big-endian PowerPC ELF (#1747)
* feat(ghidra): admit 32-bit big-endian PowerPC ELF

Recognize EM_PPC as a provider-neutral `powerpc` family that keeps its ELF
class, byte order, type and flags, and admit ELF32 big-endian ET_EXEC through
a bounded Ghidra lane. ELF64, little-endian, relocatable/shared objects and
EF_PPC_EMB are refused with a specific reason; Hopper and IDA do not gain
PowerPC support, and Windows Ghidra P0 is unchanged.

Ghidra's ELF loader selects PowerPC:BE:32:e500:default for a plain EM_PPC
header, which misreads 750-family code. The lane binds
PowerPC:BE:32:default/default in the profile (language_selection
"admitted-lane-v1"), imports with it and checks the reported language. A
language set through REA_GHIDRA_LANGUAGE_ID still replaces the lane default.

Add a source-owned fixture, fixed-encoding probe, GNU readelf cross-check and
the optional `verify:ghidra:ppc` real-provider lane, plus docs/ghidra-ppc.md.

Refs #1741

* style(ghidra): format shared PowerPC profile integration

* fix(ghidra): preserve caller-selected PowerPC languages

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:35:55 +08:00
morluto 7e9fb6f1bb test(javascript): run native type bindings with compiled boundary prerequisites 2026-10-14 06:32:39 +08:00
morluto cd740a6764 test(hopper): fix fixture context and cleanup assumptions 2026-10-14 06:23:22 +08:00
morluto 22552525d5 fix(ci): preserve per-job findings and unavailable blob evidence 2026-10-14 06:23:22 +08:00
morluto 0161a593e5 style(hopper): format startup diagnostic expectations 2026-10-14 06:20:35 +08:00
morluto 70c238e376 test(ci): verify resolved cache inputs instead of glob spelling 2026-10-14 06:20:22 +08:00
morluto f0e86997aa test(providers): correct executable and retained-owner expectations 2026-10-14 06:20:22 +08:00
morluto 3ae02f6a45 fix(verification): parse browser endpoints before provider calls 2026-10-14 06:20:22 +08:00
morluto 4acda94b76 fix(hopper): recover provenance-verified Spotlight exits
Verify the native helper's sealed identifier and pinned Apple signing team in BridgeLauncher, then carry that provenance to HopperClient. Only a verified native helper's complete not-found exit confirms no document handoff, allowing runtime and target-lease cleanup and retry; wrappers retain unknown document state.

Share publisher identity with setup through the domain contract. Correct fixture teardown ordering and parameterized MCP coverage. Validation: 54 focused tests passed across boundary, CLI, MCP acceptance, diagnostics and installer coverage (one Linux-only case skipped); check:fast passed. Real codesign verification accepted the installed official Hopper helper; Spotlight failures and bridge recovery used process fixtures.
2026-10-14 06:19:47 +08:00
4d98123279 fix(hopper): recover from a Spotlight not-found launch (#2795)
* fix(hopper): recover from a Spotlight not-found launch

Hopper's native launcher locates the application through a fixed Spotlight
query before any Apple event. With indexing disabled it exits 3 with a
not-found report, but REA treated the external document as unconfirmed,
retained the runtime and failed every later open_binary until the server
restarted. Recognize the complete report as a pre-handoff failure, confirm
no document exists, and tell the caller to enable Spotlight indexing.

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

* fix(hopper): require empty stdout for native launch signatures

* fix(hopper): retain ownership after unverified Spotlight reports

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:08:31 +08:00
morlutoandGitHub 6510d0d57f perf(ci): start source feedback early and diagnose stalled runs (#3013)
* perf(ci): start source feedback early and diagnose stalled runs

* fix(ci): verify workflow identities before main comparison limits

* fix(ci): preserve diagnostics across passing checks and install failures

* fix(ci): preserve ambiguous workflow run ordering

* fix(ci): preserve failure-report candidate and request budgets

* fix(ci): retain candidate errors while checking later main evidence

* fix(ci): retain partial reports and require complete health scans

* test(ci): prune stale fixtures and duplicated assumptions
2026-10-14 06:07:08 +08:00
aa6f74af04 fix(setup): configure a comment-only Hermes config.yaml (#2096)
* fix(setup): configure a comment-only Hermes config.yaml

comments, or only a `---` marker, also parses to a null YAML document.
Setup still rejected it as malformed ("expected record, received null")
and reported Hermes as invalid.

Treat a null Hermes document as holding no settings, as #1983 does for
an empty file. The serializer already edits a comment-only document in
place. A bare `---` document holds a null scalar that takes no keys, so
replace it with a map and keep the comments written after the marker.

* fix(setup): keep rejecting an explicit null Hermes root

Only a document with no root, or a bare --- marker whose null scalar has
no source text, holds no settings. An explicit null or ~ is a value, so
setup and uninstall reject it as before, the same as any other non-map
root.

* test(setup): split the Hermes empty-configuration tests under the function length limit

The Hermes YAML registration block reached 159 lines, past oxlint's
max-lines-per-function limit of 150. Move the empty-document cases and
the multiple-document rejection into their own block.

* fix(setup): type the Hermes edit document as a plain Document

parseDocument with options returns a Document.Parsed, whose contents
accept only parsed nodes, so assigning the new map for a bare --- root
failed typecheck (TS2322). Annotate the edited document as Document, as
the code did before it moved into HermesConfigurationYaml.ts.

* fix(ci): isolate Linux Hopper process fixture inventory

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:48:05 +08:00
1342eddd03 test(analysis-view): order the message-cutoff fixture lexicographically (#3018)
boundedViewMessages returns uniqueSorted output, so the 100-message fixture
came back in lexicographic order ("message 59", "message 6", "message 60")
while the assertion compared against generation order. Pad the index to a
fixed width so both orders coincide and the test checks what it means to:
that no count-based cutoff applies below the byte budget.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-14 05:45:41 +08:00
YsopwnableGitHubKaoru0822factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>morluto
14fef73882 docs(readme): restore Greek guide and official-source links (#3021)
* docs(readme): restore Greek guide and official-source links

Copy the canonical English official-source notice, Rust analysis row, and GNU
objdump/Rizin guide paragraph into the newly added Greek README. Restore the
documentation target set required by docs:check.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* docs(i18n): translate restored Greek README guidance

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:45:07 +08:00
06739aeaf2 fix(javascript): ignore erased TypeScript names in native-addon requests (#2766)
* fix(javascript): ignore erased TypeScript names in native-addon requests

Skip import type / export type declarations and per-binding type
specifiers when collecting .node addon members so only runtime value
imports request native exports (fixes #2667).

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

* test(javascript): cover erased TypeScript re-export forms

* test(javascript): allow Inspector observation events to arrive

* test(inspector): emit events before enable responses

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:39:09 +08:00
morluto bee73fcb67 docs(readme): move setup command above official-source notice 2026-10-14 05:27:03 +08:00
wseellyGitHubmorlutoKaoru0822factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
3620074b31 docs(android): link the JADX provider download
Document the pinned JADX release download and digest verification for npm users and repository checkouts. Keep caller-selected local paths; identify the fixture helper as checkout-only. Include shell and PowerShell examples, with the POSIX path set only after successful download and verification.

Validation: actual pinned JAR/APK fetch verification; real curl download and SHA-256 checks using sha256sum and the shasum fallback; controlled corrupt-file and failed-download cases; POSIX syntax; docs:check (5 conformance tests), typecheck/lint, formatting and diff checks. Provider execution and native macOS/Windows command runs were not repeated.

Co-authored-by: Seelly <93262000+Seelly@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 05:25:39 +08:00
Pirro ZaniandGitHub ddcdb3c378 docs(i18n): add Greek translations (#2985)
* docs(i18n): add Greek translations

Give Greek-speaking readers localized onboarding guidance and begin website coverage while keeping incomplete pages on the explicit English fallback.

- Add the reviewed Greek README and place Greek last in every language selector.
- Register Greek website localization and include 206 translated messages.
- Keep incomplete routes nonindexable and replace fixed translation counts with inventory-based coverage.

Refs #2984

* docs(website): improve Greek homepage wording
2026-10-14 05:24:04 +08:00
4aa51bfe19 fix(reverse): reconcile cleanup ownership and mutation diagnostics (#2799)
* feat(process): add owned debugger shutdown lifecycle

* feat(reverse): add local inspection commands

* feat(gdb): add persistent debugger integration

* feat(cutter): add Cutter bridge integration

* test(reverse): enforce complete integration coverage

* fix(process): preserve debugger shutdown ownership

* fix(reverse): retain evidence across interrupted commands

* fix(gdb): honor captured provider environment

* test(process): cover debugger shutdown retries

* fix(reverse): retain evidence for interrupted providers

* test(process): cover debugger shutdown retries

* fix(reverse): retain evidence for interrupted providers

* fix(server): snapshot provider environment at creation

* fix(reverse): close review gaps with complete coverage

* fix(reverse): address inspection review findings

* fix(gdb): bound sessions and retain cleanup ownership

* fix(ci): require reverse-tools quality lane

* fix(process): unref inherited debugger output pipes

* fix(reverse): address remaining PR 1960 findings

* fix(gdb): address remaining PR 1961 findings

* fix(reverse): account for unrestricted provider execution

* fix(process): retain debugger ownership until safe shutdown

* fix(process): preserve replacement debugger sessions

* fix(reverse): preserve admission and output provenance

* fix(process): retain exited debugger until output settles

* fix(mcp): bound provider admission and debugger cleanup

* fix(reverse): preserve failure provenance and classify E2BIG

* fix(gdb): preserve startup cleanup and detach safety

* fix(process): retain debugger ownership after detach failure

* fix(reverse): preserve mutation and failure provenance

* fix(gdb): address debugger review findings

* fix(reverse): rebind sessions after external mutation

* fix(process): cap debugger output after exit

* fix(gdb): protect one-shot detach verification

* feat(debugger): add reverse tool coverage gate

* test(debugger): cover integrated failure paths

* fix(gdb): bound and preserve session observations

* fix(gdb): address review regressions

* style: format merged integration files

* fix(reverse): preserve session and output evidence

* refactor(gdb): share repeated test and partial result setup

* fix(gdb): invalidate TTY proof for Python aliases

* fix(reverse): retain failure output byte counts

* test(process): synchronize late debugger output

* fix(gdb): invalidate TTY proof for wrapped commands

* fix(gdb): recognize wit command abbreviation

* test(reverse): wait for cancelled descendant readiness

* test(reverse): bound descendant cancellation cleanup

* style(reverse): format integration tests and adapters

* style: format reverse engineering integration files

* test(reverse): remove duplicate environment harness

* test(gdb): share session startup assertions

* fix(gdb): invalidate output proof for command abbreviations

* test(reverse): share composition edge case scenarios

* fix(ci): recognize verification and mutation tool entries

* test(rizin): cover successful fake inferior detach

* test(cli): align full output failure envelope

* test(cli): isolate reverse command lifecycle service

* test(process): share debugger output fixture setup

* fix(binary): preserve high-bit Windows signature bytes

* style(binary): format Windows signature parser

* test(mcp): preserve call coverage in unchanged comparison

* fix(ci): include reverse tools Vitest configs in lint cache

* fix(mcp): preserve target content on delivery errors

* ci: align duplication ceiling with expanded suite

* test(reverse): allow signal admission mutants

* fix(reverse): bound retained cleanup failures to process owners

* fix(test): replace drifting mutation ranges with owned selectors

* fix(reverse): admit persistent commands and retain cleanup ownership

* fix(cutter): recover timed-out commands from exact receipts

* fix(cutter): reject unsupported bridge request kinds

* fix(ci): repair client format and shared verification fixtures

* fix(native): preserve debugger cleanup ownership and bridge authentication

* test(native): retain diagnostics without perfect coverage requirements

* chore(licenses): refresh locked dependency inventory and notices

* fix(test): exclude transient command locks from mutation sandboxes

* fix(gdb): admit only platforms with safe detach verification

* fix(cutter): retain bounded completion receipts until acknowledgement

* fix(cutter): acknowledge commands rejected before UI dispatch

* test(reverse): fail cancellation checks before fixture teardown

* test(gdb): clean up the retained broken-stdin fixture owner

* docs(test): clarify focused provider verification scope

* fix(rizin): retain close cleanup failures

* fix(gdb): wait for inferior stop and bound MI buffering

* fix(cutter): retain failed receipt acknowledgements

* fix(rizin): return retained session identity on rebind failure

* fix(gdb): preserve startup cancellation and lossy status

* fix(rizin): retain launch provenance and assemble frames once

* fix(cutter): recover receipts across callers and bound discovery

* docs: describe debugger evidence and bridge recovery

* fix(process): keep cleanup incomplete while writers remain

Stopping a debugger no longer reports success when inherited output
streams stay open, so BinarySession cannot reopen a binary a child is
still changing. Concurrent Cutter cleanup retries share one receipt
acknowledgement instead of treating a consumed receipt as failure.

* fix(debuggers): recover cleanup with captured process identities

* fix(analysis): retire sessions after unrestricted commands

* fix(cli): preserve completed evidence after cleanup recovery

* fix(cutter): bound frame collection and coalesce cleanup

* fix(windows): resolve selected provider paths absolutely

* fix(gdb): report decoding loss in failed startup evidence

* fix(rizin): scope decoding loss to collected command frames

* test(mcp): cover snapshot retirement in reverse tools CI

---------

Co-authored-by: Daniel Gonçalves Araujo <47118053+dandgabr@users.noreply.github.com>
Co-authored-by: REA Maintainers <noreply@github.com>
Co-authored-by: OpenAI Codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-14 04:47:34 +08:00
morluto 2ad2a044d4 chore(package): add analysis target and provider keywords 2026-10-14 04:45:49 +08:00
morluto 49e73fc03b chore(package): replace disassembler keyword with rea 2026-10-14 04:37:46 +08:00
morlutoandGitHub 0bf01dfa59 perf(ci): share trusted caches and fail fast on PR test failures (#3007)
* fix(ci): restore inherited validation failures

* perf(ci): share trusted caches and fail fast on PR test failures

* fix(ci): bound static cache and hash vitest config for knip

- Bound the rolling .turbo/cache snapshot with 7d/500MB eviction so each
  trusted main save cannot accumulate history toward the GitHub limit.
- Hash vitest.config.ts in knip inputs so removing the CiTestSequencer
  import invalidates the cached passing check.
- Cover both in cache-policy and task-hash regressions.
2026-10-14 04:25:57 +08:00
9630c8ff46 fix(adb): remove false exclusivity from the push-file description (#2990)
push_adb_file claimed to be the only device-mutating ADB tool while
install, uninstall, start-app, start-activity and stop-app are also
device-mutating. Drop the exclusivity claim from the contract
description and the push limitations, keeping the device-mutation fact.

Fixes #2602.

Co-authored-by: me <me@medz.org>
2026-10-14 04:00:39 +08:00
Nianze WuandGitHub dd36b4a8a0 feat(analysis-view): report observed query parameter names (#2982) 2026-10-14 04:00:21 +08:00
87a8c8c923 test(signals): verify native decoder discovery coverage (#2818)
* test(signals): verify native decoder discovery coverage

Compare catalog and help pages against independent native sigrok output,
including full coverage counts, empty matches and boundary offsets.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* style(test): format the JVM class fixture

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 03:54:14 +08:00
3cb9d61ec1 fix(artifacts): describe ZIP non-entry coverage accurately (#2059)
* fix(artifacts): describe ZIP non-entry coverage accurately

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* style: format comparison and IDA sources

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 03:37:35 +08:00
2320ac23ee test(process): preserve terminal deadline expiry regression (#2055)
* fix(cli): preserve import-map option descriptions

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* test(process): keep terminal deadline expiry sticky

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 03:29:34 +08:00
4c83846947 docs: identify official REA installation sources (#2429)
* docs: identify official REA installation sources

Assisted-by: OpenAI GPT-6 <noreply@openai.com>

* docs: show official sources before setup shortcut

Move the unchanged official-source notice before the first setup command in all 19 README editions.

Assisted-by: OpenAI GPT-6 <noreply@openai.com>

---------

Co-authored-by: Aurantium-47 <280805072+Aurantium-47@users.noreply.github.com>
2026-10-14 03:23:30 +08:00
cddf31d55e fix(wasm): preserve original glue source locations (#2114)
* fix(wasm): preserve original glue source locations

* fix(wasm): retain BOM-prefixed hashbang glue references

* test(wasm): parse the artifact contract before reading glue evidence

* test(wasm): give glue fixtures and assertions a single owner

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e12184d56a9ba80a4427b3e6082ddac1ec)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc569a1cfb34b6efedcb6f2e3aaaa3357)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 03:20:48 +08:00
e1897c3c17 fix(verify-package): read Hopper installer applicability from its owner (#2973)
* fix(verify-package): read Hopper installer applicability from its owner

packageSetupSupport derived hopperSetupSupported from the general doctor
host check plus a non-Windows condition, so a Debian-family host REA
supports but the Hopper package never tested reported an automated
installer the packaged setup never offers. verifySetupApply then expected
needs_confirmation for the explicit --install-hopper request and failed
at verify-package-setup.mjs:403.

Applicability now comes from readLinuxDistribution() in the built
application, so the verifier follows the predicate the package ships, and
it stays unknown when no distribution can be read instead of collapsing
into a known-unsupported host. Generic agent configuration and idempotence
assertions stay independent of installer applicability, and the expected
status is still derived from the host rather than from the request being
asserted.

Verified on Kali 2026.3, a Debian-family host with an installer-untested
distribution and the simulated Hopper package environment: before the
change npm run verify:package fails at verify-package-setup.mjs:403 with
the packaged setup correctly returning needs_human; after it the same run
passes every stage. Both applicability branches are covered by
tests/evaluation/package/packageSetupSupport.test.ts, which runs in the
source lane without a build. No real Hopper installation was exercised,
so no engine compatibility claim follows.

Refs #2939

* fix(ci): preserve Hopper doctor readiness checks

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:19:45 +08:00
ed24604ee1 fix(javascript): preserve empty ESM import names in provenance (#2797)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-14 03:12:04 +08:00
Bruno Costa CandiaGitHubClaude Opus 5.5morlutogithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
20b802f165 fix(analysis-view): bound limitations and unknowns per view (#2794)
* fix(analysis-view): bound limitations and unknowns per view

Views of a JavaScript application or binary layout copied every parent
limitation into both limitations and unknowns, so a parent with tens of
thousands of limitations produced views as large as the complete record
and exceeded the MCP response budget even for a one-item page. Each view
now carries at most 64 distinct messages per field plus one line naming
the omitted count and the parent Evidence that retains the full list.

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

* test(analysis-view): assert exact bounded message notices

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

* fix(analysis-view): preserve coverage facts in bounded messages

Reserve JavaScript completeness and unknown-count facts before bulk limitations. Direct omission recovery to the source parent without assuming session retention. Preserve exact threshold assertions and add unrun exhausted-page/item and unretained-inline MCP regressions. Source inspection and targeted formatting only; tests/checks/builds/provider workloads/CI reruns skipped by explicit user request.

* fix(analysis-view): bound repeated message serialization bytes

* fix(analysis-view): let byte budget bound messages

* test(analysis-view): exercise byte-based message omissions

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-14 03:11:48 +08:00
23dc8fbf83 fix(application): accept alternative document selection in binary_ove… (#2680)
* fix(application): accept alternative document selection in binary_overview (#2460)

Update binary_overview capability negotiation so that either current_document or list_documents satisfies the document identity requirement, matching resolveAnalysisDocument runtime semantics. When a provider declares neither document capability, resolveAnalysisDocument reports AnalysisCapabilityUnavailableError for current_document rather than leaking the internal list_documents fallback. Preserve list_segments as an independently mandatory requirement.

* fix(application): retain document fallback provider errors

Return the fallback producer error unchanged, retaining operation identity, retry advice, diagnostics and cleanup instead of fabricating missing capabilities.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

* test(application): retain document fallback error expectations

Composition and MCP controls now expect the original list_documents producer error and reason.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

* fix(ida): assert attached-mode routing failure

---------

Co-authored-by: yashas1624 <yashas1624@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:11:02 +08:00
0c6c571a01 perf(javascript): reuse semantic member-mutation lookups (#2941)
The semantic member-mutation collector revisits the same nodes while it closes
alias chains, and each visit re-derived results that cannot change within the
phase:

- resolveSemanticBindingState re-walked the scope chain for the same identifier
  node. Definition collection completes before this phase, and addBinding is
  reachable only from it, so a scope's bindings no longer change.
- directInitializerPosition, directMutationPosition and
  referenceInitializerPosition are pure functions of a node and the analysis
  parent map, but were recomputed for every mutation and initializer.
- markValue re-encoded the same capture tuple with JSON.stringify and built a
  fresh long key for every Set/Map lookup, so each lookup re-hashed the whole
  key. The capture key is now cached per binding, origin and owner, and the
  lifetime key is derived from it.

On pdfjs-dist 5.3.93 wasm/openjpeg_nowasm_fallback.js (462316 bytes, sha256
53c228428a8861035a34738966af263fc9d4cee6df25a0ad942af947469a0bd9) the semantic
phase takes 177.8 s instead of 272.9 s in-process, with a byte-identical
semantic IR. With --max-heap-mb 3072 and the default heap the base revision
exhausts the worker; with the default 300 s per-source deadline raised, the
completed CLI run takes 240.6 s instead of 396.7 s and its output is identical
apart from volatile identifiers.

Refs #2498

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-14 03:09:34 +08:00
Devulapalli Naga Sri VaishnaviandGitHub a9195c3a98 fix(managed): retain raw CIL changes in member comparison (#2912) 2026-10-14 03:07:12 +08:00
4758ba30b6 fix(package-verifier): reject overview responses without a procedure count (#2936)
* fix(package-verifier): reject overview responses without a procedure count

The analyze and inspect checks compared procedure_count with `< 1`, and an absent
or null normalized_result makes that comparison false, so a binary_overview
response without its required result passed the platform smoke check. Require an
integer procedure count before the positive-count comparison.

* test(package-verifier): cover malformed overview receipts

---------

Co-authored-by: REA Maintenance <76467478+morluto@users.noreply.github.com>
2026-10-14 03:06:53 +08:00
AadityaandGitHub 64041e3072 fix(session): reject an unknown configured provider before unbound routing (#2976)
When every registered deep provider rejected a target, select() returned
an intentionally unbound success for any concrete environment selector,
including one that names no registered provider. A misconfigured
REA_ANALYSIS_PROVIDER therefore opened the target unbound, and the later
deep-operation diagnostic blamed target support instead of the unknown
configuration. The same identifier requested explicitly already failed
with unknown_provider.

Take the unbound shortcut only for a registered environment preference.
An unknown configured identifier now reaches concrete selection and fails
with unknown_provider, matching the explicit request. Registered
preferences and auto still leave artifact-only targets unbound.

Fixes #2592
2026-10-14 03:05:57 +08:00
39f96d7188 fix(qlog): list a hidden stream's records without spreading call arguments (#2860)
* fix(qlog): list a hidden stream's records without spreading call arguments

When an explicit declaration matched a stream's derived total, the
decoder pushed every contribution ordinal with a spread call. A stream
with more than ~125k STREAM frames exceeded the engine's argument limit,
so a valid capture within the input budget failed with RangeError
instead of listing the records as incomplete. Push the ordinals in a
loop.

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(android): make DEX descriptor pattern portable

---------

Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:05:34 +08:00
morluto 3f17e75b4b test(release): split plugin conformance groups 2026-10-14 03:02:59 +08:00
xingzhouandGitHub 02095cd74a fix(browser): report complete storage key inventory changes (#2861)
* fix(browser): report complete storage key inventory changes

* fix(browser): preserve unavailable storage inventory coverage

Reuse the key-coverage propagation and independent fingerprint mapping proposed by chAng-L19 in morluto/rea#2491. Propagate failed or malformed inventories into the canonical completeness ledger before comparing membership; preserve independently proven value changes. Add producer-to-comparison regressions and document recapture requirements.
2026-10-14 03:02:57 +08:00
26525706fc feat(managed): decode ldstr user strings in member inspection (#2162)
* feat(managed): decode ldstr user strings in member inspection

inspect_managed_members reported every ldstr operand as a bare 0x70 token,
so an analyst could see that a method loads a literal but not which one.
Answering "where is this path or key used" required a second tool.

Read the optional #US stream and add a top-level user_strings inventory:
each distinct token loaded by an inspected method body, once, in token
order, with its #US file offset and decoded UTF-16 value. Instruction
anchors and normalized CIL digests keep their build-local tokens. A missing
#US stream or malformed item is reported against its token as partial
coverage. Evidence recorded before this field reads user_strings as null.

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

* fix(managed): collect user strings only from ldstr

A malformed ldtoken can carry a 0x70 token, which the operand decoder
classifies as a string operand. Only ldstr loads a literal, so collect
user strings by opcode.

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

* fix(managed): admit user-string tokens and failures before collecting them

Charge each distinct ldstr token to the decode budget before adding it to
the collection set, and admit each #US failure before retaining it. A
missing #US stream is now one stream-level issue rather than one per
token.

Document why the #US terminal flag is not validated: Mono and Roslyn
assemblies disagree with ECMA-335 II.24.2.4 and the runtime ignores it.

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

* docs(managed): describe missing #US reporting and the unvalidated flag byte

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

* test(managed): cover #US items that run past the stream end

An odd length whose payload crosses the end of #US, and a two-byte
length prefix that starts on the last #US byte, are both rejected as
"User string leaves #US" beside a readable literal. The test fails if
that bounds check is removed.

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

* fix(managed): report reserved #US length prefixes as string failures

The compressed-length reader shared by #Blob and #US hard-coded
invalid-blob and "Metadata blob" for a reserved prefix, so a malformed
user string was published as a blob failure. The caller now names the
item it reads: #Blob keeps its code and message, and #US reports
invalid-string.

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

* fix(managed): append member inspection issues without argument spreading

inspectManagedMembersBytes gathered every producer's issues with
issues.push(...list), passing one argument per issue. A large malformed
input, such as hundreds of thousands of distinct bad ldstr operands or
MemberRef rows, overflows the stack with an untyped RangeError instead of
returning partial coverage. All six sites now use
appendManagedParseIssues, which appends one issue at a time.

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

* fix(managed): classify truncated #US and #Blob length prefixes by item

A two- or four-byte compressed-length prefix cut off by the end of the
artifact went through the generic range check and was reported as
invalid-row. The prefix reader now uses the item's own code for both
reserved and truncated prefixes: invalid-string for #US, invalid-blob for
#Blob. The unused generic range helper is removed.

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

* fix(managed): retain user-string terminal flag conformance issues

* test(managed): write ECMA-335 #US terminal flags and cover flag issues

d5b7b0704 reports a #US terminal flag that differs from ECMA-335
II.24.2.4. The fixture heap always wrote 0, so its non-Latin-1 literals
now read as nonconforming. The fixture writes the spec flag, and a new
test flips one flag each way ("ok" set to 1, "it's" set to 0) and checks
that both literals remain beside one issue each.

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

* fix(managed): classify ldstr operands before collapsing a missing #US

With no #US stream, every collected ldstr operand was counted as a #US
reference and reported only as missing-stream, while the same non-0x70
operand got invalid-heap-index when the stream existed. parseUserStrings
now owns which operands are user-string tokens: any other operand is
invalid-heap-index either way, and only 0x70 tokens count toward the
missing-stream issue. The heap reader drops its duplicate kind check.

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

* fix(managed): state user-string limits in member comparisons

compare_managed_members ignored user_strings, so a method whose ldstr
literal changed at the same token, or legacy Evidence that never examined
literals, could read as unchanged with no caveat. The comparison now adds
an unknown limitation when either side predates user strings and a
not-compared limitation when literals were observed. ExportedType and
user-string inventories share one uncomparedInventory rule.

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

* docs(managed): advertise ldstr literals in member inspection help

The MCP tool description, the type_names input, and the CLI command and
--type help now say that inspect_managed_members returns the #US string
literals that ldstr loads, and that a type selection limits them to the
literals the selected types' method bodies load.

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

* fix(managed): read the empty #US entry at offset 0

ECMA-335 II.24.2.4 makes the first #US entry the empty blob: a zero
length with no terminal flag byte. ldstr 0x70000000 loads it, but the
odd-length check rejected it as invalid-string, dropping the literal and
making coverage partial. Offset 0 with length 0 now reads as "".

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

* fix(managed): reserve #US offset 0 and admit literals before scanning

Offset 0 is the reserved empty #US entry, so a nonempty item there is now
invalid-string instead of a published literal. The literal's projection
is reserved before the terminal-flag scan, so an inadmissible item fails
at the budget without reading every code unit. The scan's loop index no
longer shadows the token's heap index.

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

* fix(managed): report #US length prefixes wider than ECMA-335 assigns

compressedLength accepted an overlong encoding, such as 0x80 0x05 for a
5-byte item, without comment. It now reports whether the width is the
one ECMA-335 II.23.2 assigns, and the #US reader reports a non-minimal
prefix as an invalid-string conformance issue beside the readable
literal, like the terminal flag. readMetadataUserString returns every
issue it observes. #Blob behavior is unchanged.

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

* fix(managed): check the #US length width before the empty entry

The offset-0 empty entry returned "" before the length-width check, so an
overlong empty entry such as 0x80 0x00 read as complete. The width issue
is now collected first and returned with the empty literal.

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

* fix(managed): admit #US width issues only for returned literals

The overlong-prefix issue was reserved before the fatal shape checks, so
an item that then failed, such as 0x80 0x00 away from offset 0, leaked
512 bytes of budget per token. The width issue is now built and admitted
only on paths that return the literal.

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

* fix(managed): keep #US issues out of member metadata status

User-string issues, including terminal-flag and length-prefix
conformance differences, marked metadata.status partial, so
compare_managed_members turned one-sided methods and fields into unknown
even though every member row was examined. #US issues now make coverage
partial while metadata.status describes the member inventory.

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

* fix(managed): give user strings their own coverage

#US issues entered the member coverage, so every consumer that reads it
as member coverage (application graph member facts, comparison status)
reported examined members as partial over a literal conformance
difference. user_strings is now { state, literals, issues }: #US issues
live there and never change the member coverage or metadata.status.
parseUserStrings returns that inventory directly.

compare_managed_members inspects with user strings skipped, since it
never reads literals, so a large literal cannot exhaust the decode budget
of a comparison. Its limitations state once that literals are not
compared, and the ExportedType limitations return to their main form.

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

* fix(managed): mark user strings partial when a body is not fully decoded

With user strings owning their coverage, an inspected method body that
was partial, malformed, or truncated no longer reached their state, so a
literal after the decode stopped was missing from a "complete" inventory.
parseUserStrings now marks the inventory partial unless every inspected
body was fully decoded, keeping the literals it did observe.

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

* fix(managed): mark user strings partial when a method row is omitted

A MethodDef row that failed before its body was read was recorded as an
issue and omitted, so its ldstr instructions were never scanned, yet the
user-string inventory could still read as complete. parseUserStrings now
takes the parseMethods result and stays partial whenever a method row
failed, as well as when an inspected body was not fully decoded.

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

* fix(managed): count only method rows that may hide a selected body

Any parseMethods issue marked user strings partial, including an
unreadable name on a method outside the type selection, so a fully
scanned selection read as incomplete. parseMethods now reads each row's
owner first and reports unscannedRows: rows that produced no method while
their owner is selected or unknown. parseUserStrings uses that count.

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

* fix(managed): treat unreadably named types as possibly selected

Under a type_names selection, a type whose name could not be read may be
a requested type, but its methods were skipped without counting toward
unscannedRows, so an unexamined body left user strings complete. A
method whose owner's name is unreadable now counts as possibly selected.

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

* fix(managed): judge user-string scans by CIL decoding, not body status

body.status is malformed for an invalid exception section even when
every instruction decoded, so user strings read as partial although every
ldstr was examined. The body reader now reports whether its CIL was fully
scanned, independent of exception-region validity, and parseMethods folds
unscanned bodies and unproduced rows into one unscannedMethods count that
parseUserStrings uses instead of reinterpreting body.status.

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

* fix(managed): stop deduplicating user-string issues

The inventory passed its issues through uniqueManagedParseIssues, which
serializes every issue into an unbudgeted map: about 42 MB of heap and
113 ms for 125,000 issues on Node 24. The issues are unique by
construction (one failure or per-offset conformance issues per distinct
token, one missing-stream issue), so the inventory now returns them, and
its literals, without copies.

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

* fix(managed): preserve selected string coverage unknowns

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:01:23 +08:00
morluto 7cb6c30039 fix(deps): keep package lockfiles aligned 2026-10-14 03:01:07 +08:00
45a3509db3 fix(artifacts): compare inventories using declared coverage (#2214)
* fix(artifacts): preserve complete root inventory comparisons

* fix(artifacts): declare inventory coverage before comparison

* fix(artifacts): preserve traversal omissions and materialization limits

* fix(artifacts): preserve coverage declarations and projection scope

Remove the duplicate inventory-schema import. Compare original coverage declarations, including field omission, before adapting legacy pages. Require explicit scope for completeness and container-entry coverage for application projections.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

* fix(artifacts): require entry coverage for child changes

* fix(artifacts): accept source-valid inventory limitations

* style(artifacts): format mobile graph projection

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: OpenAI Codex <openai-codex@users.noreply.github.com>
2026-10-14 03:00:27 +08:00
e4186af31b fix(process): expose Darwin swiftc compile deadline via env (#2240)
* fix(process): expose Darwin swiftc compile deadline via env

Busy hosts can exceed the hard-coded 60s ownership-helper compile
timeout. Honor REA_SWIFTC_COMPILE_TIMEOUT_MS like the other provider
startup deadlines, keeping 60000 as the default.

Fixes #2184

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

* fix(process): keep the Swift compile deadline with its runtime owner

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e12184d56a9ba80a4427b3e6082ddac1ec)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc569a1cfb34b6efedcb6f2e3aaaa3357)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 02:59:54 +08:00
morluto 9544c80eba fix(release): align shrinkwrap and release lock checks 2026-10-14 02:59:41 +08:00
42667fc1a0 fix(wasm): report WABT output past its budget as a resource constraint (#2117)
* fix(wasm): report WABT output past its budget as a resource constraint

A module whose WAT exceeds the 32 MiB output budget failed as
unreadable_output, advised `rea doctor`, and echoed the full 32 MiB
truncated WAT prefix in details.captured_output. Map the owned-command
output-limit outcome during inspection to AnalysisResourceConstraintError
naming the budget, as the apktool and native macOS providers do, with a
remediation that says the fixed budget will not change on retry.

Captured output for an output-limit failure keeps only the first 4,096
characters of each stream, including on the cleanup-failure path. An
oversized --version probe keeps its configuration-phase mapping.

* fix(wasm): keep observed sizes and whole code points in output-limit excerpts

Cut each stream's output-limit excerpt with boundedUtf8Text at 4,096
UTF-8 bytes, so an astral character at the cut no longer leaves a lone
surrogate. Mark the output truncated when either excerpt was cut, and
report each stream's observed byte count.

Word the remediation as a fact about the fixed budget. A flood of
diagnostics from any WABT tool reaches this path, so it no longer says
the module is too large.

Replace the 32 MiB end-to-end test with a launcher seam that raises a
small output-limit failure.

* fix(wasm): match sibling captured-output fields for WABT output limits

Report retained byte counts in stdout_bytes and stderr_bytes, as apktool
and the firmware command do, and record exit_code and signal. Mark the
excerpt truncated outright, since the command raises output-limit only
after retention was truncated.

When cleanup also fails, record the output limit and its budget as the
previous error instead of the generic command message. Document that
the two streams share the budget, so the excerpt holds up to 4,096 of
the bytes each stream retained.

* docs(wasm): scope the output-budget error to inspection commands

* docs(wasm): distinguish reported warm costs from unmeasured gaps

* docs(wasm): clarify the scope of reported warm measurements

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:58:41 +08:00
729ca2ab6a fix(ghidra): report unapplied PE CodeView PDB limitation (#2283)
* fix(ghidra): report unapplied PE CodeView PDB limitation (#2259)

When Ghidra analyzes a PE target that references companion debug symbols
in its CodeView (IMAGE_DEBUG_TYPE_CODEVIEW / RSDS or NB10) debug directory,
Ghidra's headless analyzer cannot automatically locate or apply the companion
PDB from isolated runtime directories without companion admission. Procedure
names remain analysis-generated (FUN_<address>).

Parse CodeView debug metadata (RSDS GUID/age and NB10 age) during PE target
resolution, and report an Evidence limitation informing callers and agents
that referenced debug symbols were not applied and procedure names are
analysis-generated.

Fixes #2259

* test(pe): group CodeView header coverage separately

* fix(ghidra): avoid contradictory PDB limitations

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* style: format identity comparison and IDA runner

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 02:56:26 +08:00
4481f86188 fix(frida): drain admitted operations and honor queued cancellation (#2305)
* fix(frida): drain admitted operations and honor queued cancellation

* fix(frida): settle cancelled loads before queued resume

* test(frida): share lifecycle fixtures without duplicating assertions

* fix(frida): preserve operation admission before session close

* test(apple): bound cold archive fixture compilation

* fix(frida): retain cancelled script creation evidence

* test(release): scope preflight schema to selected jobs

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e12184d56a9ba80a4427b3e6082ddac1ec)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc569a1cfb34b6efedcb6f2e3aaaa3357)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-10-14 02:56:00 +08:00
72d5bbc96d fix(process): keep provider deadlines independent of wall-clock changes (#2311)
* fix(process): keep provider deadlines independent of wall-clock changes

* fix(process): retain monotonic targets after early timers

Adapt the stronger timer/poll owner from @nosmile99 PR #2793 as integrated by 21aa877e5 and captured main 94072364c7. Retain all contributor reliability coverage. Tests and workloads skipped by explicit request.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:55:46 +08:00
97268f40ea fix(dotnet): name the truncated instruction when an operand leaves the method body (#2704)
* fix(dotnet): name the truncated instruction when an operand leaves the method body

The extent guard ran after the operand read, so a truncated operand threw a raw Buffer RangeError that replaced the intended diagnostic with the runtime message and lost the instruction identity.

Bound the operand extent before reading its value and carry the width beside each fixed reader so the two facts cannot drift.

Fixes #2677

* test(dotnet): consolidate truncated operand coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:52:47 +08:00
Shenrui MaandGitHub 388b907c92 perf(javascript): defer ambiguity expansion in version comparisons (#2962)
perf(javascript): avoid quadratic temporary matching state

Repeated source groups can allocate candidates that later identity matches
discard. Retain the original ambiguous groups until all tiers finish, expand
only unmatched survivors, and append to locally owned key buckets in place.

Constraint: Preserve matching priority, full ambiguity, and public output.
Directive: Do not re-match residual groups that become unique after later tiers.
Tested: 59 relevant tests, npm run check, and test discovery on current main.
Tested: CLI benchmarks and full-suite failure comparison on the recorded base.
Not-tested: Full suite on the updated main; native Linux and Windows workflows.
2026-10-14 02:52:13 +08:00
0e6eca4085 feat(native): read embedded cargo-auditable Rust dependency lists (#2456)
* feat(native): read embedded cargo-auditable Rust dependency lists

* fix(native): trim PE section padding and compute dependency cycles iteratively

* fix(native): ignore SHT_NOBITS sections and validate dependency edges linearly

* fix(native): bound compiler path scanning and retained hashes

* fix(rust): pass package arrays as named parameterized test cases

* fix(rust): admit graph expansion and preserve resource units

* fix(rust): reject malformed packages, false cycles, and hidden compiler paths

Fail the first malformed cargo-auditable package before Zod collects an
issue per field. Require cyclic dependency indices to match the cycles in
package dependencies. Keep read-only ELF ranges when a section name does
not fit the bounded name window, so a rustc path in that range is still
reported.

* fix(ci): repair integrated checks and Hopper test baseline

* fix(go): verify Mach-O sections against segments

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-14 02:50:30 +08:00
c7ceaadb70 fix(browser): preserve unknown storage inventory and fingerprint coverage (#2491)
* fix(browser): preserve unknown storage inventory and fingerprint coverage

* fix(browser): compare known storage edits with incomplete inventories

* test(browser): consolidate storage completeness regression workflows

* test(ida): admit aligned FAT thinning fixtures on every platform

---------

Co-authored-by: chAngE <172472630+chAng-L19@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:50:03 +08:00
090b454350 fix(javascript-semantics): keep emitted data out of dispatch listener fields (#2952)
* fix(javascript-semantics): keep emitted data out of dispatch listener fields

The EventEmitter operation collector read argument position 1 as the
listener for every event method, so a dispatch such as
bus.emit("ready", data) reported the data binding and its range as the
listener. Only registration and removal name a listener there; emit
passes its remaining arguments to listeners that already exist. Read the
listener argument only for register and remove operations.

Fixes #2574

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(javascript-semantics): read listener arguments only for listener-taking methods

removeAllListeners accepts only an optional event name, so gating the
listener argument on operation kind still reported an ignored second
argument as a listener. Gate on the methods whose argument position 1
names a listener instead: the registration methods, off, and
removeListener.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 02:48:54 +08:00
morlutoandGitHub f45cb4b92a Merge branch 'main' into codex/review-fixes-pr-1977 2026-10-14 02:45:55 +08:00
0bc422d7be fix(wasm): attribute invalid paths to their selected fields (#2796)
* fix(wasm): attribute invalid paths to their selected fields

* test(wasm): assert the exact rejected selection issues

---------

Co-authored-by: maxazure <405711+maxazure@users.noreply.github.com>
2026-10-14 02:39:03 +08:00
morlutoandGitHub 2895a18ca9 fix(ci): skip portable report merge when shards are skipped (#2943)
* fix(ci): skip portable report merge when shards are skipped

The portable consumer can still run after an upstream build failure, even though the test-shard matrix is skipped and no Vitest blob artifacts exist. Guard artifact download and both report merge variants on successful shards; retain the required-shard gate so skipped or failed shards remain failures.

This applies the report-consumer condition from ee0256c740c9be18baae6c56d90285df8069f5b4 to current pinned main without changing action pins or required gates.

* fix(ci): preserve failed shard diagnostics

* docs: clarify current revision CI policy
2026-10-14 02:30:44 +08:00
OpenAI Codex bc6eb789ce fix(plugins): publish npm source dependencies 2026-10-14 02:23:13 +08:00
morlutoandGitHub 7a6defab18 test(reconstruction): declare complete comparison fixture facets (#2964) 2026-10-14 02:17:57 +08:00
morlutoandGitHub 8fef2aa64e fix(ci): enforce merge verification and unblock superseded runs (#2938)
* fix(ci): repair inherited formatting failures

* fix(ci): release superseded PR concurrency slots

* docs: require current-base checks before maintainer merges

* docs: preserve administrator merge override
2026-10-14 01:52:57 +08:00
morluto 9de80b4deb fix(plugins): separate source and package launcher modules 2026-10-14 01:34:42 +08:00
morluto a85bfed8dc fix(plugins): validate Codex companion launch metadata 2026-10-14 01:23:15 +08:00
ebaaa79ca0 test(http3): add real MITM capture verification lab (#1997)
* test(http3): add real MITM capture verification lab

* test(http3): register the optional verification command

* fix(http3): preserve the latest verification attempt owner

* test(cli): admit the dyld slice fixture on x64 hosts

* test(http3): verify exact producer TLS connection facts

* test(http3): verify readable capture fields against observed bytes

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(http3): retain MCP startup diagnostics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 01:09:19 +08:00
1d5f95dc4a fix(native): parse renamed dyld_info re-exports (#2364)
* fix(native): parse renamed dyld_info re-exports

dyld_info prints a re-export whose name differs from the source library's
symbol as `[re-export] _foo_renamed (_foo from libfoo)`. The export parser
only accepted `(from libfoo)`, so inspect_macho reported a nonexistent
export named `_foo_renamed (_foo from libfoo)` with no source, and the nm
row for `_foo_renamed` survived the name-based merge as a second export
with reexport null.

Accept the optional source symbol, trying the plain `(from lib)` form
first so a library name containing ` from ` keeps its text. The row now
reports `_foo_renamed` re-exported from libfoo and replaces the nm
duplicate. The export schema has no field for the source symbol name, so
exports coverage records a limitation when a renamed re-export is present.
The golden fixture is a dyld_info capture of a synthetic dylib linked with
-Wl,-alias,_foo,_foo_renamed.

* fix(native): split dyld re-export suffix without nested quantifiers

Replace the (?:(.+?)\s+)??from lazy-optional group with a linear string
scan so malformed lines cannot backtrack and a library path containing
" from " still keeps its whole text via the plain-form-first check.

* test(native): cover nested re-export library names

* fix(native): retain reexports missing from nm inventory

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
2026-10-14 01:06:55 +08:00
f652709cac fix(cli): keep file URLs out of operator-relative path resolution (#2882)
CLI path normalization resolved every nonblank selected string against
the working directory, so executable_path "file:///opt/electron" became
an invented local path and failed with ENOENT for that location instead
of the field's local-path requirement. Leave `file:` and `scheme://`
references unchanged so the shared schema rejects them as non-local
paths. One-letter prefixes stay paths, so Windows drive paths and
relative names containing a colon resolve as before.

Fixes #2577

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:05:14 +08:00
Burak KalaycıandGitHub c8ef1f916c docs(readme): sync Turkish translation with current README (#2777)
docs(readme): sync Turkish translation
2026-10-14 01:04:55 +08:00
RandGitHub f1045001f0 fix(browser): retain optional source-map requires (#2886)
Carry the require callee from dependency extraction into lexical scope
validation so optional calls remain visible without admitting shadowed
loaders.

Fixes #2883
2026-10-14 00:57:42 +08:00
morlutoandGitHub 69bd81f7f2 fix: isolate Linux inventory and repair DEX schema regex (#2910)
* fix(ci): isolate Linux Hopper process fixture inventory

* fix(android): make DEX descriptor pattern portable
2026-10-14 00:56:43 +08:00
1b6c899b03 docs: update Chinese README guides (#2119)
Update Chinese README docs

Refresh the Chinese README guidance to mention IDA as a supported native analysis backend, clarify that static JavaScript analysis does not require native engines, and document global CLI installation/update steps for Bun (`bun add --global`, `bunx` usage, minimum version 1.2.17). The capability table also now includes Go binary and WASM artifact support.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:42:03 +08:00
To3akaRinandGitHub 93e29d242a feat(cli): summarize committed reference-source inventories (#2768) 2026-10-14 00:37:54 +08:00
c6631814e0 docs(website): document Apple Silicon Cairo setup for local preview (#1930)
* docs(website): document Apple Silicon Cairo setup for local preview

* docs(website): scope Apple Silicon Cairo fix to preview process

* docs(website): avoid shared-prefix rewrite and clobbering backup

* docs(website): keep shared Intel libs intact with same-shell Cairo lookup

* docs(website): enter ARM shell before exporting Cairo lookup

* docs(website): use ARM brew path and unprotected Python for Cairo

* docs(website): reinstall requirements after ARM venv rebuild

* docs(website): document DejaVu font dependency

* docs(website): show ARM Python selection for venv rebuild

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:37:39 +08:00
all-black-493andGitHub 62c8b3da1b feat(browser): attach passive tools to browsers without HTTP discovery (#2346)
* feat(browser): attach passive tools to browsers without HTTP discovery

Accept a literal loopback browser WebSocket,
ws://127.0.0.1:PORT/devtools/browser/ID, as the cdp_endpoint of the six
passive browser tools. Discovery then reads Browser.getVersion and
Target.getTargets over that socket, and the target session adopts the
same socket, so a browser that asks for consent per connection prompts
once per call.

The socket identifier gates access, so Evidence records the endpoint as
ws://127.0.0.1:PORT/devtools/browser/[REDACTED], and runtime
reconciliation accepts that recorded form. HTTP endpoints and their
Evidence are unchanged. Electron, web runtime, scenario and Inspector
endpoints stay HTTP-only.

Refs #2288

* test(browser): match browser-socket captures against HTTP discovery

Check in the real Chrome lane that DevToolsActivePort names the same
browser socket as /json/version, and that inspecting the selected page
through that socket leaves DOM structure and scripts unchanged compared
with an inspection through HTTP discovery.

Refs #2288

* fix(browser): bound browser-socket handshake and report discovery timeouts

Run the browser-socket WebSocket handshake and both discovery commands
under one discovery deadline, so a port that accepts the connection but
never completes the upgrade times out instead of waiting forever.

Report a reached deadline as a timeout without carrying the internal
abort as its cause, so CLI and MCP project provider_timeout rather than
cancelled. Caller cancellation is still reported as cancelled.

Refs #2288
2026-10-14 00:36:37 +08:00
ShitKandGitHub 0c9ee56c2f fix(native): accept declared facts in dispatch name inventory (#2906) 2026-10-14 00:34:06 +08:00
ShitKandGitHub 501c5d45fe fix(managed): share native verification input diagnostics (#2901) 2026-10-14 00:33:14 +08:00
df3d96da80 fix: repair the static-check failures on the default branch (#2895)
* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:31:27 +08:00
7e3a198638 fix(javascript): keep logical assignment results uncertain for unknown values (#2788)
* fix(javascript): keep logical assignment results uncertain for unknown values

* test(javascript): cover exact uninitialized logical assignments

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

---------

Co-authored-by: Ganesh2325 <184471511+Ganesh2325@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:06:45 +08:00
72b9ab4af1 fix(javascript): retain configuration reads before member access (#2756)
Squash merge of https://github.com/morluto/rea/pull/2756.

Original pull request submitted by @xu91102.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 23:59:49 +08:00
d4f0c5e664 fix(cli): start on Windows when USERPROFILE is empty (#2345)
* fix(cli): start on Windows when USERPROFILE is empty

Node's Windows home lookup throws ERR_SYSTEM_ERROR (uv_os_homedir ENOENT)
for an empty USERPROFILE instead of consulting the account profile. Incur
resolves the home while it is imported, so every command, including
`rea --version`, stopped with a raw stack trace before REA ran.

Treat the empty selection as unset in the launcher, before any runtime
import, and cover startup with a compiled-CLI regression.

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

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 23:53:35 +08:00
Riley GramlichandGitHub 3f80b75708 fix(source-maps): keep sources when sourcesContent length differs (#2873)
ECMA-426 DecodeSourceMapSources reads a content slot only while its index
is within sourcesContent, so a shorter array leaves trailing sources
without content and a longer array adds no sources. Local application
analysis and browser collection rejected any length mismatch and dropped
every original from the map.

The content helper now checks only entry types. Browser collection drops
its separate check because the leaf schema already validates every
supplied entry, and the raw records are still budgeted before parsing.

Fixes #2821
2026-10-13 23:52:47 +08:00
855ed9689e fix(application): preserve a cancellation that races a completed provider run (#2710)
Squash merge of https://github.com/morluto/rea/pull/2710.

Original pull request submitted by @XiaoBinGan.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 23:51:44 +08:00
59298923a5 fix(apktool): read the resource locale grammar from its owner (#2738)
Squash merge of https://github.com/morluto/rea/pull/2738.

Original pull request submitted by @XiaoBinGan.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 23:51:30 +08:00
af782dd5f5 fix(setup): treat empty DSH_HOME as unset (#2686)
* fix(setup): treat empty DSH_HOME as unset (#2666)

The released `@deepseek-ai/dsh-home-paths` package explicitly treats an
empty or whitespace-only `DSH_HOME` as unset and selects the default
Harness home patch. `dshHomeDirectory` previously used nullish coalescing
(`env.DSH_HOME ?? join(home, ".dsh")`), so an empty override flowed
straight through `join(..., "cordis.patch.yml")` and produced a
cwd-relative `cordis.patch.yml` outside the client-selected home.

Treat empty and whitespace-only overrides as unset, matching the sibling
OpenCode override resolver and the released DSH resolver. The patch now
lands at `$DSH_HOME/cordis.patch.yml` for explicit overrides, the default
`~/.dsh/cordis.patch.yml` for empty/unset, and trimmed whitespace for
padded overrides.

Adds `src/application/SupportedClients.test.ts` covering the empty,
whitespace-only, unset, padded, and explicit cases via the public
`supportedClients(...)` entrypoint. Fixes #2666.

* test(setup): remove superseded DSH path unit coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:51:28 +08:00
d86d29c411 fix(mcp): keep advertised inputs within client reference budgets (#1749)
Squash merge of https://github.com/morluto/rea/pull/1749.

Co-authored-by: akushonkamen <akushonkamen@163.com>
Co-authored-by: GLM <noreply@z.ai>
2026-10-13 23:49:53 +08:00
77e71d55ec fix(native): name numeric Mach-O build-version platforms and tools (#2065)
Squash merge of https://github.com/morluto/rea/pull/2065.

Co-authored-by: TonyWu <2038838159@qq.com>
2026-10-13 23:49:37 +08:00
168bfe5060 fix(target): probe WASM before inferred database routing (#2007)
* fix(target): probe WASM before inferred database routing

* test(wasm): execute recovery arguments from native refusal

* test(ci): repair fixture alignment and share MCP assertions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:49:33 +08:00
554ea7611e fix(adb): keep the device's refusal when a package change exits nonzero (#2344)
Squash merge of https://github.com/morluto/rea/pull/2344.

Co-authored-by: Hui <a3239737781@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:49:20 +08:00
911894d758 feat(wasm): compare retained core-module interfaces (#2464)
Squash merge of https://github.com/morluto/rea/pull/2464.

Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>
2026-10-13 23:49:04 +08:00
75e7965dc3 docs(nativeaot): document the verified Windows-host parser route (#2497)
Squash merge of https://github.com/morluto/rea/pull/2497.

Co-authored-by: pablodiazjorge <pablodiazjorgegarcia@gmail.com>
2026-10-13 23:48:41 +08:00
522b9927c8 feat(recorded-crash): decode AArch64 core register notes (#2505)
Squash merge of https://github.com/morluto/rea/pull/2505.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:48:24 +08:00
2b98fc5f51 fix(cli): reject empty analyze and artifact target paths (#2687)
Squash merge of https://github.com/morluto/rea/pull/2687.

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 23:48:09 +08:00
8b1549a264 test: share repeated boundary preambles to restore the jscpd gate (#2691)
Squash merge of https://github.com/morluto/rea/pull/2691.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:47:55 +08:00
782cc3b060 refactor(process): give the prlimit producer budget one owner (#2698)
Squash merge of https://github.com/morluto/rea/pull/2698.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:47:41 +08:00
f825c869d6 fix(artifacts): bound expanded ASAR listing before allocation (#2724)
Squash merge of https://github.com/morluto/rea/pull/2724.

Co-authored-by: chAngE <172472630+chAng-L19@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 23:47:18 +08:00
9240a4e9ac fix(javascript): preserve asterisks in block source-map URLs (#2728)
Squash merge of https://github.com/morluto/rea/pull/2728.

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-13 23:47:03 +08:00
Jeremy LevartovskyandGitHub ba06d9ec52 fix(setup): keep a Cordis operation's own fields when uninstall empties it (#2730)
Squash merge of https://github.com/morluto/rea/pull/2730.

Co-authored-by: JayOfTheKeyboard <140487034+JayOfTheKeyboard@users.noreply.github.com>
2026-10-13 23:46:46 +08:00
FengandGitHub af7638fa9d fix(session): preserve investigation records after failed target opens (#2754)
Squash merge of https://github.com/morluto/rea/pull/2754.

Co-authored-by: Adkid-Zephyr <laufs0310@163.com>
2026-10-13 23:46:23 +08:00
48b90fbaa4 fix(native): preserve unresolved calls in untyped route fallback (#2757)
Squash merge of https://github.com/morluto/rea/pull/2757.

Co-authored-by: IvenKooLab <276572622@qq.com>
2026-10-13 23:46:02 +08:00
c580ba940a fix(browser): treat an unusable cache list as incomplete storage coverage (#2762)
Squash merge of https://github.com/morluto/rea/pull/2762.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:43:11 +08:00
2a78763e20 feat(native): search analyzed memory for masked byte patterns (#2341)
* feat(native): search analyzed memory for masked byte patterns

The diff adds search_bytes to the CLI, the shared MCP contract, Ghidra
inventory, and the headless bridge, and advertises the same operation on
Hopper as unavailable until that provider is verified. Patterns are
hexadecimal byte pairs with optional ?? wildcards and an optional
equal-length bit mask; the TypeScript boundary and the bridge both
require a nonempty pattern, equal lengths, and at least one constrained
bit. Ghidra walks initialized blocks in address order within inclusive
bounds, keeps overlapping matches inside a single block, and returns
each hit with its segment, unique source file offset, and containing
procedure, continuing through a cursor bound to the target, profile,
pattern, mask, and bounds. Each page reports the inclusive ranges
examined and the skipped uninitialized blocks; cancellation returns a
typed error and discards the active Ghidra database.
address_to_file_offset now uses the same unique-offset helper, which
rejects a mapping that is missing, ambiguous, or larger than a safe
integer.

* fix(native): limit unbounded byte search to loaded memory

Without start or end, search_bytes walked every Ghidra block, including
non-loaded file parts such as .symtab and overlay blocks, which the docs
call out of scope. Unbounded searches now skip those blocks and list them
in searched.skipped_not_loaded; explicit bounds in their address space
still search them. Document that a match must end at or before end.

* fix(native): preserve implicit address spaces in range consumers

* fix(native): reject present malformed byte search state

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:41:45 +08:00
70611713e1 fix(targets): compare the Info.plist bplist prefix without ASCII folding (#2100)
* fix(targets): compare the Info.plist bplist prefix without ASCII folding

The six-byte prefix check used an encoding that maps high-bit bytes onto bplist, so a non-binary property list could be sent to the binary decoder.

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

* fix(targets): satisfy the strict index check in the plist prefix test

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:37:45 +08:00
43e33cd49e fix(artifacts): compare plist and NIB signatures without ASCII folding (#2098)
* fix(artifacts): compare plist and NIB signatures without ASCII folding

Buffer.toString("ascii") drops the high bit, so a non-plist prefix could be decoded as bplist00 or NIBArchive and sent to the binary decoder.

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

* fix(artifacts): satisfy the strict index check in the signature test

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

* fix(artifacts): remove superseded signature regression causing CI failure

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:37:31 +08:00
efc491ab45 feat(setup): add Kimi Code client support (#2744)
* feat(setup): add Kimi Code client support

Registers rea in ~/.kimi-code/mcp.json (or $KIMI_CODE_HOME/mcp.json), preserving unrelated settings with a .rea.backup; registration is command-based stdio with no type field, startupTimeoutMs 30000 and toolTimeoutMs 120000 from MCP_STARTUP_POLICY.
Strict JSON parsing: empty or whitespace-only files initialize; malformed JSON is rejected untouched with no backup; explicit transport other than stdio reports invalid.
Bundled skill uses the shared ~/.agents/skills destination that Kimi Code discovers, installed and verified through the shared-skill flow.
Validated by 19 Kimi-specific tests, 260 adjacent-client regressions passing, check:fast and docs:check, and real-client verification on Kimi Code 0.40.1 (173 tools discovered, real mcp__rea__analyze_javascript_application call succeeded).

* fix(setup): preserve Kimi tool filters and strict JSON coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:36:54 +08:00
b19f9e4d2d fix(javascript): keep spread child argv counts unknown (#2748)
* fix(javascript): keep spread child argv counts unknown

* test(javascript): own runtime controls and tighten evidence assertions

* test(apple): keep dyld slice regression portable

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 23:36:35 +08:00
morluto b7dabdbc4f chore(licenses): refresh npm dependency inventory 2026-10-13 23:36:26 +08:00
af142f656c fix(mcp): report references a provider truncated in trace_feature (#2348)
* test(mcp): expect trace_feature to report references the provider truncated

A provider that reports a capped xrefs read makes the trace truncated with a residual unknown for that target. An explicit complete report and a provider that reports nothing leave the trace as before.

* fix(mcp): report references a provider truncated in trace_feature

Executions gain an optional flag for a capped xrefs read, set by the IDA adapter from the upstream cap. trace_feature marks the trace truncated with a residual unknown for that target only when the provider reports a truncation; a provider that reports nothing keeps its previous result. The trace_feature workflow profile is revised so earlier results are not replayed.

* refactor(ida): report reference truncation whenever the runner observed an xrefs cap

* test(mcp): cover session forwarding, per-target state, native reports and failures of the trace coverage

* test(mcp): format the session forwarding test

* test(process): format processCaptureLifecycle.test.ts after the merge

* test(ida): declare the alignment each synthetic universal slice offset satisfies

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74cc84a45dcb186422a0f51aa61bc427)

* fix(process): keep monotonic deadline ownership after merge

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:35:21 +08:00
ffd8c9c03d test(evaluation): grade managed agent fixture facts (#1924)
* test(evaluation): grade managed agent fixture facts

* test(evaluation): cover distinct managed artifact deliveries

* fix(evaluation): require unfiltered managed type inventories

* fix(evaluation): preserve selected entry point grounding

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:34:17 +08:00
7dcd23c005 fix(setup): separate host support and stop skill checks after drift (#2073)
* fix(host): separate REA host support from Hopper installer targets

LinuxDistribution.supported carried two meanings at once: that the host runs
REA, and that the automated Hopper installer is tested on it. The doctor read
the first meaning from the second, so Debian-derived hosts such as Kali
reported unsupported_host while their providers worked normally.

Rename the field to hopperSetupSupported, which only the Hopper installer and
setup planning consume, and derive host support from the package family
through supportsReaHost(). Any distribution with a recognized apt, dnf, or
pacman family is now a REA host. The tested Hopper installer list is
unchanged, so Debian and Kali keep falling back to configuring an existing
provider instead of downloading an untested package.

Verified on Kali 2026.3 (Debian testing, x86_64): rea doctor reports a healthy
environment with Ghidra 12.1.4, and analyze_function decompiles an x86_64 ELF
through the MCP server. npm run check, npm run docs:check, and the application
and Ghidra suites pass. No Hopper or IDA host workflow was exercised.

* fix(setup): stop skill checks after first drift

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:33:54 +08:00
OpenAI Codex 9d22eea7d2 fix(plugins): stop fallback server process trees 2026-10-13 23:33:15 +08:00
ed93abed02 fix(cli): label each uninstall skill root by its client (#2197)
* fix(cli): label each uninstall skill root by its client

rea uninstall reported every managed skill destination under the
shared "skill" label (with only Claude Code special-cased), so a
retained or failed directory could not be told apart from the others.

Use the destination's client identifier so the aggregated detail names
each root: "shared: retained (...)", "claude_code: removed (...)",
"antigravity: retained (...)". Retained and failed entries now point to
the specific directory that needs attention.

Fixes #2111

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(cli): align skill summary assertions with client and path labels

---------

Co-authored-by: wjl8636 <wjl8636@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:32:31 +08:00
d005bd5dcd fix(javascript): preserve CommonJS lookup and analysis provenance (#2306)
* fix(javascript): preserve CommonJS directory and ancestor lookup

* test(apple): bound cold archive fixture compilation

* test(release): scope preflight schema to selected jobs

* fix(javascript): retain requested analysis limits in Evidence

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:31:30 +08:00
688defca0f fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:30:31 +08:00
3c39a3f536 test(adb): isolate cross-platform activity launch regressions (#2226)
* fix(adb): reject timed-out activity launches and retain observations

* test(adb): isolate cross-platform activity launch regressions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:30:18 +08:00
xingzhouandGitHub e6c6fdd21a fix(browser): preserve script-specific parser uncertainty (#2858) 2026-10-13 23:22:59 +08:00
xingzhouandGitHub 40e94aeb9d fix(browser): preserve distinct WebMCP declaration metadata (#2854)
* fix(browser): preserve distinct WebMCP declaration metadata

* test(browser): retain the first equivalent declaration location
2026-10-13 23:19:37 +08:00
morluto 45bae55787 fix(plugins): load Codex MCP server from root config 2026-10-13 23:13:34 +08:00
morluto 987cb3b96b chore: sync captured review base for PR 2815 2026-10-13 23:09:50 +08:00
3e7c630223 fix(mcp): count the error flag in error result budgets (#2848)
Error results reach the wire as text plus `isError: true`, but the
text-only envelope omitted the flag. An error up to 15 bytes above the
result budget passed both the delivery and transport checks and was sent
as the original diagnostic instead of taking the oversized-error recovery
path.

Rename the error representation to "error-text" and size its envelope
with the flag, so ToolResultDelivery and the error transport share the
complete delivered shape. Successful structured results are unchanged.

Fixes #2557

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:06:55 +08:00
204a6f3f20 fix(ghidra): import HashMap in the bridge script (#2844)
8823c55b0 added a HashMap use without its import, so Ghidra could not
compile ReaGhidraBridge.java and every Ghidra-backed operation exited
before bridge startup.

Co-authored-by: SeokMyungSeop <SeokMyungSeop@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:57:45 +08:00
e13da084e7 fix(apple): exclude AppleDouble sidecars from IPA components (#2838)
* fix(apple): exclude AppleDouble sidecars from IPA components

IPA projection returned every inventoried component before the AppleDouble
sidecar filter applied to directory, ZIP and DMG roots, so ._* and
__MACOSX/ metadata appeared as native libraries and frameworks while the
result reported that sidecars are excluded. Apply the sidecar exclusion
first for every root format; IPA still keeps non-sidecar components
outside Payload/.

Fixes #2546

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

* test(apple): exercise the __MACOSX exclusion with a non-dotfile name

The IPA fixture's __MACOSX entry also started with "._", so the AppleDouble
filename rule excluded it and a broken __MACOSX check would go unnoticed.
Use a library name that only the __MACOSX rule excludes; the native
library assertion now fails if that rule regresses.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:55:21 +08:00
DragoloneandGitHub 94072364c7 fix(sqlite): keep triggers whose declaration spells the table in another case (#2807)
* fix(sqlite): keep triggers whose declaration spells the table in another case

SQLite stores a trigger's target in sqlite_schema.tbl_name exactly as
written in CREATE TRIGGER and resolves it by ASCII case folding, while an
index stores the table's own name. The selected_table scope filtered
tbl_name with COLLATE BINARY, so a trigger declared ON users for table
Users was omitted while the schema still reported complete. Match
dependents with COLLATE NOCASE, and report each trigger's table as the
stored table or view name so the selected-table contract still holds.

* fix(sqlite): refuse an inexact table before the case-folded schema scan

With the NOCASE expansion, a selection such as users for table Users
expanded the stored table's declarations before readRows reported the
missing exact name, so large trigger definitions could surface as an
output-limit failure instead of the selection error. Check the exact
table name against PRAGMA table_list first and share the selection
failure with readRows.

* fix(sqlite): fold dependent names only for a row-selectable table

An exact view, virtual or possible shadow table passed the exact-name
guard, so the NOCASE scan could still expand a differently cased trigger
past the reply budget before readRows refused the selection. Fold case
only when the selection is an ordinary table that is not a possible
shadow table, and keep the exact filter otherwise so every refused
selection fails as it did before the case-folding change.

* fix(sqlite): resolve trigger tables without folding unrelated names

The stored-name lookup case-folded every PRAGMA table_list name up front,
so a selected-table inspection of a tiny table paid for large unrelated
identifiers. A selected-table scan only returns the selected table's
dependents, so report the selected name directly; other scopes use an
exact stored name when one matches and fold the stored names once, only
when a trigger spells its target differently.

* perf(sqlite): fold each table name once when matching shadow parents

The extracted shadow-parent helper folded the table name inside the find
callback, once per unresolved virtual table, and the selected table was
checked both before and during the schema scan. Fold the name once per
call as before, and reuse the selected table's result in the scan.
2026-10-13 22:37:47 +08:00
e103a89889 fix(artifacts): preserve NIB key and class name identity (#2109)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:36:06 +08:00
edd9939879 fix(browser): give the DevToolsActivePort wait one deadline owner (#2265)
* Single owner for the Chrome DevToolsActivePort startup deadline

Cold Chrome startup tolerance was spelled three ways for one wait: the helper
fell back to 15,000 ms, the runtime fixture lifecycle passed a literal 60,000,
and the real-browser verifier kept a local 60,000 constant. A per-caller literal
quietly changes how long a lane tolerates a cold browser before it fails, and
the failure reported only elapsed_ms, so a reader could not see which ceiling
was hit.

Export BROWSER_DEVTOOLS_STARTUP_TIMEOUT_MS as the owner, use it for the helper
fallback and both call sites, and report startup_timeout_ms in
BrowserStartupError. A source guard keeps callers from restating the number.

* Pin the DevToolsActivePort deadline guard to the startup call argument

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:35:45 +08:00
1e38a038c6 fix(process): release handles and close worker on incomplete cleanup (#2321)
* fix(process): unreference handles on incomplete provider process stop

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* fix(javascript): close worker and release handles on incomplete cleanup

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* fix(javascript): defer worker stdin close until incomplete cleanup

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(javascript): assert cleanup error instance on inspection uncertainty

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

---------

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:34:54 +08:00
morluto 6e25cc3c76 fix(tests): clear lint and typecheck errors on main
Split two over-long test functions into module-level helpers
(max-lines-per-function): IdaSessionClient transport cases, IDA provider
composition, and function comparison identity cases keep their names and
assertions.

Also fix three typecheck errors: default omitted
direct_invokes_exhaustive to null so the result schema stays JsonValue
compatible, parse the ghidra dossier before spreading it, and delete the
optional beforeCall hook instead of assigning undefined under
exactOptionalPropertyTypes.
2026-10-13 22:32:01 +08:00
7369b5ee63 fix(ida): retain cleanup facts and let one-shot commands exit (#2814)
* fix(ida): let one-shot commands exit after retaining cleanup

When a headless IDA request outlives timeoutMs (for example idb_open on
a large binary still running auto-analysis), REA cannot prove the
worker stopped and correctly retains the upstream connection and
private workspace. The retained stdio child and its pipes stayed
referenced, so a one-shot CLI printed its cleanup_incomplete result and
then never exited, keeping the supervisor, worker and database alive.

Detach the retained connection's child process and pipes from the event
loop when cleanup fails. A long-lived MCP server can still retry
through the retained connection; a one-shot CLI exits with status 1,
and the supervisor observes end of input.

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

* fix(ida): report an unverified upstream worker after failed cleanup

Upstream ida-pro-mcp spawns headless workers in their own session and
deliberately leaves them running when its supervisor exits, so neither
a retained connection nor REA exiting proves that a timed-out worker
stopped. When cleanup fails before the owned worker's release is
confirmed, the cleanup diagnostics now name its private session and
state that it may still be running until it finishes and reaches its
idle timeout. Failures after a confirmed release, or before any open
was attempted, do not report a worker.

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

* fix(ida): name the inventory sessions that block worker release

When idb_close confirmed the requested worker gone but the follow-up
inventory still found a different peer on the private input, the
upstream_worker diagnostic named the released session and could
misdirect manual cleanup. Record the session ids of the inventory
entries that actually blocked release and report those; fall back to
the requested private session only when no inventory evidence exists
(an incomplete open or an unanswered request). The field becomes
session_ids, since more than one entry can block release.

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

* fix(ida): keep blocker evidence and backend in cleanup diagnostics

The release diagnostic reset its observed blockers on every close, so a
retry whose inventory was unavailable fell back to the already-released
session; and it kept only session ids, describing a blocking GUI
session with detached-worker guidance.

Blocker evidence is now replaced only by a successful inventory, and
the diagnostic becomes upstream_sessions with each session's id and
backend, with guidance that distinguishes workers from GUI sessions.

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

* fix(ida): stop naming a worker after its close was confirmed

When idb_close confirmed the requested worker's release but the
following inventory failed, no blocker evidence existed yet and the
diagnostic fell back to the confirmed-absent worker. After a confirmed
close the diagnostic now reports an empty session list, meaning any
remaining same-input sessions are unknown.

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

* test(ida): group cleanup diagnostic cases in their own suite

The added cases pushed the reaped-worker describe block past the
150-line limit enforced by lint:built.

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

* fix(ida): keep ownership and pre-close peers in cleanup diagnostics

Blocking sessions dropped the parsed ownership fact, so an external
unowned worker looked the same as REA's own; and recovery inventory
recorded same-input peers only when the requested worker was missing,
so a peer seen beside it was lost if the following close failed.

Each reported session now carries owned (null when no inventory has
reported it), and recovery records every same-input session until a
later successful inventory replaces that evidence.

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

* fix(ida): drop a confirmed worker from retained cleanup blockers

Recovery inventory now records the requested worker beside any peers,
so once idb_close confirmed its release a failing follow-up inventory
still reported it as unreleased. Remove the confirmed session from the
retained blockers at close confirmation and keep the remaining peers.

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

* test(ida): retry cleanup through a detached stdio connection

The retry coverage used a recording connection whose detach() only
counts calls, so it could not show that detaching keeps a retained
connection usable. Run a real stdio supervisor in a long-lived host,
fail the first post-close inventory, and require the retry through the
same detached connection to confirm release and remove the workspace.
The test fails if detach() destroys the pipes instead of unref-ing them.

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

* fix(ida): close the stdio supervisor in failed retry tests

Merge main and give its new IdaNativeProtocol connection double the
required detach() method. The real-stdio retry test now closes its
connection in an unconditional teardown, so a failing assertion cannot
leave the supervisor waiting on stdin.

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

* test(ida): share the cleanup diagnostic expectation in session tests

Repeated upstream_sessions assertions and the unrelated live peer
literal pushed repository duplication over the jscpd threshold. Build
the expected diagnostic with one helper and reuse one peer constant.

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

* test(ida): distinguish observed ownership from unreported input bindings

* fix(ida): keep requested-ID conflicts in recovery cleanup

Recovery inventory kept only same-input entries, unlike the startup and
post-close filters. After an explicitly rejected open, an entry under
the requested session ID on another input was dropped, so cleanup could
close the connection, remove the private workspace and report success
while that session stayed registered. Match the requested ID as well so
the entry is retained as a blocker and cleanup stays incomplete.

Also split the cleanup diagnostics suite under the lint:built function
length limit, and make two startup-inventory fixtures pass a defined
session id so the test file typechecks.

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

* fix(ida): preserve retained session inventory facts

---------

Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 22:27:26 +08:00
morluto c85abacdb2 fix(ida): mock legacy list_functions in attached transport tests
The attached fixture negotiates the legacy protocol, so list_procedures
calls list_functions, never list_funcs. The list_funcs mocks added in
#2449 never fired and both tests failed on arrival. No product change.
2026-10-13 22:26:36 +08:00
Aimal KhanGitHubKaoru0822factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
cbe769b504 docs: move translated READMEs under docs/readme (#2165)
Move all 18 translated READMEs into docs/readme/ and keep the English entry at the repository root. Update navigation, guide/image paths, website discovery and documentation checks together.

Preserve the contributor's work and current main content. Cover every translated command and HTML destination, and repair the verified Persian heading fragments.

Validation: 203 focused tests on the synchronized head, plus 119 tests and documentation checks on the integration with main fc0dbe7f9; 1,501 local references and 399 authored fragments verified. The complete CI workflow passes. The upstream x64 dylib fixture correction is included; an unchanged-checkout rerun cleared the earlier deadline timing assertion.

Closes #2141

Co-authored-by: Aimal Khan <94559553+aimalexe@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 22:25:32 +08:00
865c35e25a fix(ghidra): disclose missing PDB symbol coverage (#2275)
* fix(ghidra): disclose missing PDB symbol coverage

* style: format reviewed source changes

* fix(ghidra): bound PDB handshake and retain coverage uncertainty

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
2026-10-13 22:17:27 +08:00
Yann RobertoandGitHub 1016c96e6b fix(source-maps): accept omitted names during browser collection (#2831) 2026-10-13 22:17:09 +08:00
f0b8ae1fc8 fix(javascript): trace nested Electron bridge members (#2745)
* fix(javascript): trace nested Electron bridge members

* fix(javascript): preserve shallow Electron bridge paths

* fix(javascript): preserve exact Electron bridge member paths

* fix(javascript): preserve exact Electron bridge paths

* fix(javascript): preserve bridge entry ownership

* fix(javascript): preserve bridge path identity

* fix(javascript): normalize typed feature seeds before tracing

* test(javascript): normalize direct selector seed inputs

* refactor(javascript): consume normalized feature seed defaults

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:44 +08:00
c9982ad8bf fix(browser): release scenario listeners and allow bounded Chrome close (#2441)
* fix(browser): release scenario event listeners on finish

* fix(browser): allow Chrome cleanup to complete within bounded deadline

* style(browser): format cleanup deadline regression

* test(browser): expect popup cleanup listener

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:25 +08:00
9b08c2b1cb fix(native/windows): share FILE_SHARE_DELETE so owned objects can be marked delete-pending (#2445)
* fix(native/windows): share FILE_SHARE_DELETE so owned objects can be marked delete-pending

The Windows native authority self-test (`binding.call("inspect", [])`) fails
closed on stock Windows x64 with a non-admin user:

    Delete owned object by handle failed (Win32 5): Access is denied.

Reproduced deterministically (15/15 failures) on Windows 11 x64, Node 22.22.2,
non-elevated account. Because `inspect` is a hard gate, every provider-backed
operation is rejected and the whole native authority is unusable.

Root cause: Windows requires that every handle currently open on an object
grants FILE_SHARE_DELETE before that object can be marked delete-pending via
SetFileInformationByHandle(FileDispositionInfo).

`createRelative()` creates the runtime object with FILE_SHARE_READ only, and the
returned handle is retained for the lifetime of the runtime
(`Runtime::directory`). `closeRuntime()` later needs to mark that same object
delete-pending, but the retained handle never shared DELETE, so the transition
is denied (Win32 5).

The child handles opened by `closeRuntime()` for recursive cleanup have the same
defect: they request DELETE access while sharing only FILE_SHARE_READ, so the
subsequent `dispose()` call on those very handles is denied too.

Fix: add FILE_SHARE_DELETE to both share masks, keeping FILE_SHARE_READ and
deliberately not adding FILE_SHARE_WRITE, so the existing write-exclusion
behaviour of the retained leases is preserved while the delete-pending
transition the cleanup path depends on becomes possible.

Verified end-to-end after the change: `inspect` passes, the full runtime
lifecycle (`runtime_create` / `runtime_mkdir` / `runtime_write` / `runtime_open`
/ `runtime_close`) completes with no residual directories, and a real 925 MB
application analysis (4396 files) runs through all stages.

* fix(windows): anchor private runtime cleanup to directory handles

* test(windows): assert cleanup sharing failure by code

---------

Co-authored-by: maodns <311719211+maodns@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:05 +08:00
21aa877e5a fix(process): honor startup deadlines and add a CI audit skill (#2692)
* docs(skills): add CI and test correctness auditing

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

* fix(process): retain the monotonic startup deadline budget

Recheck the absolute monotonic deadline when an early timer fires, and keep
polling waits pending until their own target or the shared deadline arrives.
Preserve caller cancellation, disposal and the unchanged real-timer assertion.
This replaces the earlier remainingMs workaround: an early callback must not
classify an unexpired deadline as a timeout.

Adapt the production fix and regression cases from nosmile99 PR #2793 at
546001693636f13700d86c6e93b6086abba2b7d6, including the independently reported
both-timers-early reproduction. Preserve the prior contributor's commit.

Fixes #2760

Co-authored-by: nosmile99 <147114285+nosmile99@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* style(tests): format the Java class fixture signature

The captured main baseline has an unformatted helper signature. Apply the
repository formatter so the required style gate can run to completion.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* test(ghidra): group expanded suites by their contracts

Split result-envelope and completeness checks, and Windows invocation and
runtime checks, into named describe groups within the existing lint limit.
Preserve every test, fixture, assertion and platform prerequisite.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: nosmile99 <147114285+nosmile99@users.noreply.github.com>
2026-10-13 22:15:43 +08:00
b17487f482 fix(comparison): keep unsupported function facets unknown (#2450)
* fix(comparison): keep unsupported function facets unknown

* docs(comparison): explain function facet availability

* fix(analysis): preserve unavailable facets across Evidence consumers

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:15:01 +08:00
30e9b83a68 fix(ida): retain bounded stderr on upstream transport failures (#2449)
* fix(ida): retain bounded stderr on upstream transport failures

* fix(ida): preserve stderr through operation and cleanup boundaries

Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request.

* fix(ida): preserve cancellation and single-owner stderr diagnostics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:14:39 +08:00
DragoloneandGitHub 5278835b53 fix(flutter): find snapshot headers that a read chunk boundary splits (#2804)
* fix(flutter): find snapshot headers that a read chunk boundary splits

Within one chunk the snapshot hash scanner resumes four bytes after each
magic, but across chunks it resumed after the whole 52-byte record and
dropped the carried bytes. A magic starting 49-51 bytes after a rejected
(non-hex) record was therefore lost when its four bytes straddled a read
boundary, so the result depended on chunking and a conflicting hash could
be omitted while coverage stayed complete. Resume after the magic in both
cases.

* refactor(flutter): drop the snapshot scanner cursor the fix superseded

After a complete record the carried tail is shorter than a magic, and a
pending magic has not been processed, so every push can rescan its carried
bytes from the start without counting a record twice. Remove the absolute
cursor and base offset, and cover sections 49-51 bytes after a rejected
record with two-chunk and byte-at-a-time feeds.
2026-10-13 22:14:07 +08:00
e3a012d6c9 fix(flutter): reject ambiguous ELF mappings and bound note reads (#2334)
* fix(flutter): reject ambiguous mappings and bound ELF notes

Resolve PT_LOAD interpretations once for symbols and dynamic tables, including partial overlaps. Preserve equivalent mappings and leave conflicting or invalid alternate definitions unresolved.

Bound distinct nonempty ELF note ranges before retention and reads, deduplicate shared ranges, preserve observed build-ID candidates and report partial coverage on exhaustion. Correct shared verifier workspace ownership and structured Windows path assertions.

Addresses the two post-merge review findings on PR #1582. Validation: 147 focused cases across Linux, macOS Sonoma 14.8.9 and non-administrator Windows 11 build 26300.9457; real Gallery CLI/MCP checks on all three hosts; six compiler-produced ELF32/ELF64 hash-layout cases; static, docs, discovery and duplication checks. Full string-pool projections remain explicitly bounded.

* test(flutter): parse complete facts through named result schemas

---------

Co-authored-by: me <me@medz.org>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:13:22 +08:00
fd65e278be fix(setup): resolve the configuration MSIX Claude Desktop reads (#2426)
* fix(setup): resolve the configuration MSIX Claude Desktop reads

On Windows, setup, doctor and uninstall always used
%APPDATA%\Claude\claude_desktop_config.json. The MSIX build of Claude
Desktop (package family Claude_pzs8sxrjxfjjc) virtualizes AppData writes
and reads its package-private copy under
%LOCALAPPDATA%\Packages\<family>\LocalCache\Roaming\Claude once it exists.
Setup from an ordinary process therefore wrote a file the app never reads,
and doctor reported that file healthy.

Resolve the Claude Desktop location once, in supportedClients(): the
private copy when it exists, else the AppData file the package still
reads, else a new private copy. An unrecognized Claude_* publisher or
unreadable package storage is reported instead of falling back to AppData.

Compare one of the package's private files with its AppData path through
realpath.native to learn whether the running package started this
process. From that redirected view, or when no file can be compared,
setup and uninstall refuse to change Claude Desktop's configuration,
because the running app rewrites it from memory. From a redirected view
they also refuse writes under %APPDATA% and %LOCALAPPDATA% for other
clients and skills, which would land in the package's private storage.

Setup refuses to register a launcher stored in any MSIX package's
private storage, discloses which file the package reads, and tells the
user to quit Claude Desktop fully on every platform. Configuration fails
with target_changed if the package switched to its private copy after
the plan was shown. Uninstall removes REA from both files.

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

* docs(setup): record the verified MSIX Claude Desktop registration

On the Windows 11 host with the MSIX build 2.31226.1.0, setup from an
ordinary terminal with the app quit wrote the package-private copy. After
the app restarted, the entry persisted, doctor reported it aligned, and
Claude Desktop's MCP log showed REA starting and answering tools/list.
Uninstall against the real app remains unverified.

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

* style(setup): format MSIX review corrections

* fix(setup): carry the admitted legacy path through uninstall removal

clientRegistrationRemoval returned a removeLegacy boolean, and removeClient
recomputed the legacy path separately, so TypeScript could not prove the
path existed when removeLegacy was true and `tsc --noEmit` failed in
Uninstall.ts. The decision now returns the admitted legacy path itself;
preflight and removal consume that one value.

The alias regression matched new-destination children with a "/"
separator while building paths with path.join, so it failed on Windows.
It now uses path.sep.

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

* test(setup): split MSIX Claude Desktop lifecycle cases under the function length limit

The uninstall cases pushed the registration lifecycle describe past the 150-line lint limit. Group them separately, with the AppData registration case kept among the registration cases.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:04:22 +08:00
5996d8160f fix(native): bound captured output carried by native command failures (#2764)
* fix(native): bound captured output carried by native command failures

The native command runner retains up to 64 MiB of output because
successful analysis needs it complete, and failureCapture copied all of
it into the failure. An inspect_plist whose plutil output exceeded the
budget therefore returned a 67 MB resource_constraint error whose
details.captured_output.stdout repeated the whole retained output.

Keep a 64 KiB UTF-8-safe excerpt per stream in failure diagnostics,
mark them truncated, and keep the observed stdout/stderr byte totals.
Successful captures are unchanged.

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

* fix(native): bound codesign failures and cover excerpt cuts

translateCodeSignExitFailure wrapped an accepted nonzero codesign capture
into a NativeCommandFailure by copying both complete streams, so
inspect_signature failures still bypassed the diagnostic bound. Route it
through the shared failureCapture builder; accepted output stays
complete until the provider decides it is a failure.

Add a focused runner test for an ordinary nonzero exit with stdout and
stderr just above the excerpt size and a two-byte character straddling
the cut, and a provider test for the codesign path.

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

* test(native): build excerpt test output inside the child process

The output strings embedded in one node -e argument totalled about 133
KB, above Linux's 128 KiB per-argument limit, so spawn failed with E2BIG
on the CI runner. Generate the repeated output in the child instead.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:03:19 -07:00
c938f97fb2 fix(android): clean Windows workspaces after owner termination (#2440)
* fix(android): clean Windows workspaces after owner termination

* fix(android): retain Windows workspace guardian on cleanup failure

* fix(android): preserve released guardian pipe state on cleanup retry

* fix(android): normalize extended local guardian parent paths

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:03:16 +08:00
c45d912c91 feat(android): expose exact DEX descriptors and direct invoke sites (#2439)
* feat(android): expose precise method references and DEX invoke sites

* fix(android): bound invoke evidence without losing reference inventory

* perf(android): skip invoke admission for class references

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:02:47 +08:00
ce748be2f3 fix(process): order interaction differences by their dispatch time (#2739)
* fix(process): order interaction differences by their dispatch time

first_divergence ranks candidate differences by the earliest recorded
time of the differing observations, but eventTime read only `at_ms`.
Interaction events record their time as `dispatched_at_ms`
(and `scheduled_at_ms`), so a changed interaction always ranked as
untimed. An interaction outcome that differed at 6 ms therefore lost to
a process sample that differed at 500 ms.

Give each comparison dimension its recorded time field. Interactions use
the dispatch time, when their outcome was observed; the other
dimensions keep `at_ms`. An earlier process difference still wins.

Fixes #2645

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

* fix(process): require ordered interaction dispatch times in captures

Comparison ranks a changed interaction by its dispatch time, while
firstCollectionDivergence returns the first differing interaction by
sequence. The capture parser checked only that interaction sequences are
contiguous, so an imported capture whose dispatch times decrease could
make that first-by-sequence difference later than another differing
interaction.

Interactions are dispatched in sequence and their times come from the
monotonic capture clock, so require non-decreasing dispatch times at
parsing, as frames, rendered frames, process samples and filesystem
checkpoints already require for their timestamps.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:02:28 -07:00
173da755cd fix(reconstruction): reject contradictory function comparison status (#2798)
* fix(reconstruction): reject contradictory function comparison status

* test(reconstruction): cover imported function match precedence

---------

Co-authored-by: maxazure <405711+maxazure@users.noreply.github.com>
2026-10-13 22:01:41 +08:00
1eca600515 fix(apktool): resolve Windows PATH launchers before invocation (#2802)
* fix(apktool): resolve Windows PATH launchers before invocation

Resolve the default launcher through the selected PATH/PATHEXT before choosing native process creation or the existing conservative cmd.exe wrapper. Reuse the resulting selection and one canonical environment for the version probe and decode.

Follow-up to the release #1693 review of #1777. Source inspected and formatted; tests, builds, check reruns and real Windows Apktool execution were skipped at maintainer request.

* fix(apktool): bound lookup and retain only selected environment

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

* fix(apktool): use supported timeout diagnostics

Retain lookup phase through AnalysisErrorOptions diagnostics while preserving the operation and timeout identity. Source inspection and targeted formatting only; tests, checks, builds and provider workloads were skipped at the user request.

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 22:00:47 +08:00
ae9a7f34e1 fix(process): report no remaining time after a provider deadline fires (#2720)
Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:59:57 -07:00
96d1f0f3fb fix(cli): refuse scenario steps on passive native UI observation (#2712)
observe-native-ui and capture-native-ui-scenario were registered in one
loop with a shared option schema and description. The passive command
therefore advertised `--steps` ("Observe or run a scenario"), accepted
any value, and then dispatched observe_native_ui without it, so a
caller who selected the wrong command never learned that no scenario ran.

Register each command against its own contract. Observation declares
only the passive scope options, so `--steps` is rejected as an unknown
flag; the scenario command keeps its JSON step decoding and validation.
Each command now has its own description.

Fixes #2674

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:59:33 -07:00
beca34f522 fix(ida): report missing tools when a compatibility profile mismatches (#2307)
A profile mismatch reached the caller as a bare AnalysisProtocolError,
which projects to `unreadable_output` with a generic message. The error
already named every missing tool, but none of that detail survived, so
the only way to identify the cause was to log the JSON-RPC traffic and
notice the exchange stopping after `tools/list`.

Throw a ProviderAdapterError instead, carrying the mode, the expected
profile and the missing tool names through the diagnostics channel that
already surfaces in `details`. Document which upstream versions each
profile accepts, since upstream `main` now serves neither.

Refs #2301

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:58:36 +08:00
48a683ac8c feat(evm): inspect lossless Solidity terminal CBOR metadata (#2790)
feat(evm): retain lossless terminal CBOR metadata

Refs morluto/rea#2388

Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
2026-10-13 21:55:30 +08:00
a3daf8a07b fix(ci): run the Windows native lane with its process boundary fixture (#2336)
* fix(ci): run the Windows native lane with its process boundary fixture

`verify-windows-native.mjs` takes the boundary executable as its second
argument and skips every observation that needs it when the argument is
absent. CI passed no argument, so the breakaway denial, caller and child
token comparison, Windows environment semantics and in-place ancestor
reparse attempt have never run there; they only ran for an operator who
built the fixture by hand, which `native/windows/README.md` described as
optional.

The fixture is an ordinary Windows executable independent of the addon,
so the lane that already installs MinGW to cross-build the artifact
cross-builds it too and uploads it beside the existing inspection
fixture. The Windows job downloads it and passes its path, matching how
that inspection fixture already crosses the same job boundary.

No observation, assertion or threshold changes; this only stops four of
them from being silently skipped.

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

* fix(ci): pass the process boundary fixture in the real Ghidra Windows lane

`real-ghidra-windows.yml` also runs the native lane without the boundary
executable, so its proof run skipped the same breakaway, caller and child
token, environment semantics and ancestor reparse observations. Its
artifact job already installs MinGW, so it cross-builds and uploads the
fixture the same way, and the self-hosted Windows job downloads it and
passes its path.

This makes the README's claim about both Windows workflows true rather
than narrowing it to one lane.

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

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74cc84a45dcb186422a0f51aa61bc427)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 21:55:17 +08:00
morluto 961f410094 chore(plugins): sync the captured final review base 2026-10-13 21:50:53 +08:00
9680da76c8 perf(evidence): reuse sealed bundle reads in compare and export (#2715)
* perf(evidence): reuse sealed bundle reads in compare and export

`rea compare`, the compare_bundles MCP tool and `rea evidence-export`
read bundles through the mutable reader, so each consumer
re-authenticated every record again: compareBundles parsed both bundles
a second time and bundleDigest's serializeEvidenceBundle a third time;
export's writer parsed the source once more before serializing.

These consumers only read the bundle, so read it through the existing
sealed reader (readImmutableEvidenceBundle). Later parses hit the
immutable bundle cache. Ordinary mutable reads, kept deliberately by
#1721, are unchanged. Output is byte-identical.

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

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:50:25 +08:00
46f51ee6cc fix(flutter): name a missing or non-file APK selection (#2727)
* fix(flutter): name a missing or non-file APK selection

identify_flutter_build and inspect_dart_aot reported a missing path or a
directory as "The APK could not be read as a package: <path>: ENOENT ...",
which blames the package bytes for a selection mistake. Report
"Selected APK does not exist" for ENOENT/ENOTDIR and "Selected APK is not
a regular file" for a non-regular selection, matching the SQLite
inspector's wording. The issue path and invalid_format reason are
unchanged; package format failures keep their existing message.

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

* fix(filesystem): classify unopenable non-regular selections at admission

A Unix socket cannot be opened at all: open() fails with ENXIO on Linux
and EOPNOTSUPP on macOS (surfaced by libuv as "Unknown system error
-102") before openRegularFile can stat the descriptor, so such a
selection escaped NonRegularFileReadError and the Flutter provider still
blamed the package bytes.

When open fails, classify the path by its own metadata (lstat when
symlinks are rejected, stat otherwise): an existing non-regular file
becomes NonRegularFileReadError. Missing paths, permission failures,
regular files, and the O_NOFOLLOW ELOOP rejection keep their original
errors. The Flutter selection test now covers a real Unix socket.

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

* fix(filesystem): reclassify only unopenable file-type errors at admission

Checking the path's metadata after any open failure turned EACCES on an
unreadable directory into NonRegularFileReadError, so callers reported
invalid input instead of access_denied. Reclassify only errors that mean
the file type cannot be opened (ENXIO, EOPNOTSUPP, ENOTSUP, matched by
errno because libuv has no name for macOS EOPNOTSUPP 102); permission,
symlink-rejection and resource failures keep their original errors.

Run the Unix socket selection check in its own test, skipped on Windows
where listen() takes named pipes, and keep the missing-path and
directory checks cross-platform.

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

* fix(filesystem): keep cancellation that arrives during the non-regular probe

A caller abort while the post-open metadata probe ran was replaced by
NonRegularFileReadError, so callers such as parseBinaryTarget reported an
input-format error instead of cancellation. Recheck the signal before
substituting the non-regular classification.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:49:23 +08:00
c22d7cd756 fix(browser): keep malformed Content-Length declarations unknown (#2773)
* fix(browser): keep malformed Content-Length declarations unknown

Response metadata normalized Content-Length with Number(), truncation and
clamping, so "-5", "0x10", "1e3" and "+12" became observed lengths 0, 16,
1000 and 12. RFC 9110 section 8.6 defines the field as 1*DIGIT; any other
spelling declares no length. Parse only decimal digits within the safe
integer range and keep every other value null.

Fixes #2596

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

* test(browser): cover the largest accepted Content-Length

Pin the safe-integer boundary beside the overflow case so a future change
cannot reject the largest representable declared length unnoticed.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:48:55 +08:00
4c79127fcb fix(sqlite): classify a missing database path as an invalid value (#2775)
* fix(sqlite): classify a missing database path as an invalid value

The snapshot reported an absent selected database as an artifact path
failure, which the adapter projects as invalid_format alongside malformed
paths and content. The snapshot owns the absence observation, so it now
returns an invalid_value input issue on path directly, matching the
adapter's existing ENOENT/ENOTDIR classification.

Fixes #2614

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

* fix(sqlite): classify a missing database path as an invalid value

The snapshot reported an absent selected database as an artifact path
failure, which the adapter projects as invalid_format alongside malformed
paths and content. The snapshot owns the absence observation, so it now
returns an invalid_value input issue on path directly, matching the
adapter's existing ENOENT/ENOTDIR classification.

Fixes #2614

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:48:39 +08:00
morluto 802a1b0b75 test(plugins): guard packaged launcher ownership and checkout identity 2026-10-13 21:47:29 +08:00
morluto 42ce2c75ab fix(plugin): keep the shared launcher owner outside shipped source trees 2026-10-13 21:47:15 +08:00
c8e85b962a fix(javascript): recover child-process options without args (#2781)
* docs: celebrate 180,000 GitHub stars 🎉

* fix(javascript): recover child-process options without args

* fix(javascript): preserve execFile callback overload options

Choose execFile options by the second argument object shape rather than total argument count, preserving callback overloads.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:46:27 +08:00
jbernousandGitHub fc0dbe7f9d fix(server): mark retained partial-observation errors as target content (#2398)
Pass the tool contract's `returnsTargetContent` to
`toRecordedPartialErrorToolResult` in the shared Evidence handler, matching
the Electron call site.

The call site that records a retained partial observation was added without
the argument required by the untrusted target-content framing, so the
delivery neither marked the result nor counted the provenance marker in its
wire budget. The type gate failed on main with
`contractToolHandler.ts(69,38): error TS2554`, which also blocked every
compiled boundary test lane.
2026-10-13 21:46:14 +08:00
23f72141d5 fix(javascript): keep configuration setting reads out of endpoints (#2402)
* fix(javascript): keep configuration setting reads out of endpoints

Static endpoint extraction reads the first literal argument of any call
named get as an HTTP request path unless the receiver is a provable keyed
collection. VS Code extension bundles read settings through
`workspace.getConfiguration(section).get(key)`, so every setting key
became a network endpoint: the bundled Git extension's dist/main.js
reported 173 network endpoints, nearly all of them setting names such as
`autofetchPeriod` and `blame.statusBarItem.enabled`.

The lexical receiver classifier now records a `configuration` fact for a
`.get` call whose receiver is a `getConfiguration(...)` call, inline or
through a binding whose only visible write is one unconditional
initializer. Minifiers emit `let` for these bindings, so the proof relies
on the recorded writes rather than the declaration kind, as tRPC client
root resolution already does. Endpoint extraction skips non-URL literals
for that fact in both application and web bundle analysis. Reassigned,
conditional, and shadowed receivers, URL or path literals, and other
clients such as `api.get("users")` keep their endpoints.

The receiver fact union was duplicated in three modules; consumers now
import the classifier's type.

On the Git extension bundle, network endpoints drop from 173 to 7 with
unchanged analysis time (2.1 s).

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

* fix(javascript): require the workspace host for configuration receivers

A bare or differently scoped `getConfiguration(...)` may be an
application's own HTTP client factory, so `.get("users")` on its result
lost its endpoint. Only `workspace.getConfiguration(...)` and
`<x>.workspace.getConfiguration(...)` now produce the configuration fact.
Minifiers keep property names, so the bundled VS Code Git extension still
reports 7 network endpoints instead of 173.

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

* fix(javascript): recognize default VS Code namespace imports

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:45:07 +08:00
morluto 3206274e0b fix(plugins): reuse the portable Node package-runner bootstrap 2026-10-13 21:45:00 +08:00
5d12a0b4b6 test(web): cover nullable HTTP captures through CDP and CLI (#1834)
* fix(browser): preserve unknown HTTP response comparison fields

* fix(browser): retain proven request inventory changes

* fix(javascript): preserve Electron identity and avoid quadratic property scans

Share visible Electron mutations with wrapper expansion, index static object
literal properties once, and align MCP assertions with current contracts.

* fix(javascript): track writes through symbolic Electron aliases

Resolve mutation targets through the same factories, module tables, containers,
and conditional origins as call analysis. Share mutation coverage between
direct and wrapper scans while isolating origin caches and preserving exact
property paths, local rebinding semantics, and analysis resource limits.

* fix(javascript): preserve container slots and imported mutation origins

* perf(javascript): defer container mutation replay until property reads

* fix(javascript): preserve captured object identities and write order

* fix(javascript): preserve ordered mutation origins

* fix(javascript): honor property mutation constraints

* test(web): narrow PR to HTTP and CLI comparison regressions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:44:08 +08:00
9958754f6c fix(process): preserve discovery ownership across cleanup retries (#2643)
Bind retained descriptors and runtime resources to their admitting operation until cleanup succeeds, then rebind newly admitted work. Verify failed retries, successful recovery, shutdown and CLI projection.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 13:43:06 +00:00
a4b8e9ccee feat(ghidra): admit recordless legacy MIPS32 ELFs with a pinned 32-bit language (#1898)
* feat(ghidra): admit recordless legacy MIPS32 ELFs with a pinned 32-bit language

Firmware built with pre-ABI-flags GNU toolchains declares EF_MIPS_ARCH_32
with an unspecified ABI field and no PT_MIPS_ABIFLAGS record. The lane
refused them before provider selection, and Ghidra's own header
auto-selection picks a 64-bit R6 language for exactly these files.

Admit ELF32 ET_EXEC headers with EF_MIPS_ARCH_32 or EF_MIPS_ARCH_32R2,
an unspecified ABI field, EF_MIPS_ABI2 clear and no observed ABI flags
record. o32 is recorded as an inferred analysis assumption
(mips_abi_basis), floating-point ABI semantics stay unverified, and the
existing refusals (FP64, NAN2008, ASEs, machine variants, ELF64) are
unchanged. The profile pins -processor MIPS:<LE|BE>:32:default through a
provider-pinned-v1 selection the bridge-reported language_id is checked
against after import; a caller-selected REA_GHIDRA_LANGUAGE_ID still
wins. The lane identity is versioned
elf32-exec-arch32-legacy-pinned-v1.

verify:ghidra:mips gains a recordless legacy round for both byte orders
that strips the ABI record declarations from a real mips32 fixture,
checks REA's interpretation against GNU readelf, asserts the pinned
language after import and the retained assumption limitations through a
snapshot round-trip.

Fixes #1620

* fix(ghidra): require an inspected absence for the recordless MIPS lane

Address review: undefined ABI flags means the record was not inspected,
not that none exists. Only an inspected absence (null) establishes the
recordless legacy admission; a merely unobserved record keeps its
specific refusal. Also document the legacy admission and its pinned
language in the MIPS provider guide.

* fix(ghidra): tighten the recordless MIPS admission boundaries

Address review: a recordless-looking header whose ABI-record inspection
left declaration sources unexamined (carved ELF with a partial section
table and no program-header record) no longer establishes the recordless
admission; it is refused with the inspection limitation retained. An
EF_MIPS_ARCH_32 header that declares an ABI is refused rather than
relabeled through the 32R2 declared-ABI lane.

* fix(ghidra): reject EF_MIPS_ABI_ON32 from the recordless MIPS lane

Address review: bit 0x40 declares the old n32 ABI. A recordless header
carrying it was admitted with the inferred-o32 assumption even though
the header itself declares n32; the recordless shape now requires the
bit clear and refuses it with a specific constraint.

* fix(ghidra): keep recordless relocatable MIPS objects off the legacy lane

The recordless admission now requires ET_EXEC: the lane and its
verification cover executable firmware images, and a recordless ET_REL
object must stay on the declared-ABI path instead of inheriting the
inferred-o32 assumption under an executable lane id. The ABI-record
requirement paragraph is scoped to the declared-ABI lane so it no longer
contradicts the recordless admission documented above it.

* fix(ghidra): name the ET_EXEC scope for recordless objects and probe the legacy lane

A recordless relocatable object is not an incomplete inspection, so its
refusal now names the ET_EXEC scope of the legacy admission instead of
claiming unavailable ABI headers remain unknown. The recordless legacy
verifier round reads the probe pair and applies the full assertMipsProbe
oracle like the standard round, and its report keeps the independent
GNU readelf receipt beside the fixture facts.

* fix(ghidra): retain the concrete ELF32 legacy compiler pin

* fix(ghidra): bind legacy profiles to fully admitted targets

---------

Co-authored-by: BaaRai <baarai@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:42:38 +08:00
morluto 3457e65be5 docs(plugins): preserve contributor readiness and recovery guidance 2026-10-13 21:41:20 +08:00
morluto 11404c05f4 fix(plugins): distribute canonical skills from the exact npm release 2026-10-13 21:41:20 +08:00
c6503edfc4 ci: pin GitHub Actions to full commit SHAs (#1901)
* ci: pin GitHub Actions to full commit SHAs

Replace every third-party `uses: owner/repo@vN` reference in
.github/workflows with the 40-character commit SHA that the tag
currently resolves to, keeping the exact release as a trailing comment.
Each pin was checked with `git ls-remote --tags`: the floating major tag
and the commented release dereference to the same commit. Local
reusable workflows are unchanged.

Re-pin reactivecircus/android-emulator-runner from the SHA of the
annotated `v2` tag object (4c44018e) to the commit it points to
(a421e438, v2.38.0); Actions resolves both, but only the commit SHA is
an immutable reference that tooling can compare with the release.

Update the Windows Ghidra workflow trust-boundary test to require
SHA-pinned setup-node and upload-artifact references instead of the
previous floating tags.

Validation: actionlint v1.7.7 (-shellcheck= -pyflakes=, as in CI), YAML
parse of all workflows, no remaining unpinned third-party `uses:`, and
the workflow boundary/conformance tests.

* style(ci): format action identity assertion

Retain formatting edits produced before the user stopped validation.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:38:46 +08:00
5dd2ce3125 fix(ida): let one-shot commands exit after retaining cleanup (#1841)
* fix(ida): let one-shot commands exit after retaining cleanup

When a headless IDA request outlives timeoutMs (for example idb_open on
a large binary still running auto-analysis), REA cannot prove the
worker stopped and correctly retains the upstream connection and
private workspace. The retained stdio child and its pipes stayed
referenced, so a one-shot CLI printed its cleanup_incomplete result and
then never exited, keeping the supervisor, worker and database alive.

Detach the retained connection's child process and pipes from the event
loop when cleanup fails. A long-lived MCP server can still retry
through the retained connection; a one-shot CLI exits with status 1,
and the supervisor observes end of input.

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

* fix(ida): report an unverified upstream worker after failed cleanup

Upstream ida-pro-mcp spawns headless workers in their own session and
deliberately leaves them running when its supervisor exits, so neither
a retained connection nor REA exiting proves that a timed-out worker
stopped. When cleanup fails before the owned worker's release is
confirmed, the cleanup diagnostics now name its private session and
state that it may still be running until it finishes and reaches its
idle timeout. Failures after a confirmed release, or before any open
was attempted, do not report a worker.

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

* fix(ida): name the inventory sessions that block worker release

When idb_close confirmed the requested worker gone but the follow-up
inventory still found a different peer on the private input, the
upstream_worker diagnostic named the released session and could
misdirect manual cleanup. Record the session ids of the inventory
entries that actually blocked release and report those; fall back to
the requested private session only when no inventory evidence exists
(an incomplete open or an unanswered request). The field becomes
session_ids, since more than one entry can block release.

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

* fix(ida): keep blocker evidence and backend in cleanup diagnostics

The release diagnostic reset its observed blockers on every close, so a
retry whose inventory was unavailable fell back to the already-released
session; and it kept only session ids, describing a blocking GUI
session with detached-worker guidance.

Blocker evidence is now replaced only by a successful inventory, and
the diagnostic becomes upstream_sessions with each session's id and
backend, with guidance that distinguishes workers from GUI sessions.

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

* fix(ida): stop naming a worker after its close was confirmed

When idb_close confirmed the requested worker's release but the
following inventory failed, no blocker evidence existed yet and the
diagnostic fell back to the confirmed-absent worker. After a confirmed
close the diagnostic now reports an empty session list, meaning any
remaining same-input sessions are unknown.

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

* test(ida): group cleanup diagnostic cases in their own suite

The added cases pushed the reaped-worker describe block past the
150-line limit enforced by lint:built.

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

* fix(ida): keep ownership and pre-close peers in cleanup diagnostics

Blocking sessions dropped the parsed ownership fact, so an external
unowned worker looked the same as REA's own; and recovery inventory
recorded same-input peers only when the requested worker was missing,
so a peer seen beside it was lost if the following close failed.

Each reported session now carries owned (null when no inventory has
reported it), and recovery records every same-input session until a
later successful inventory replaces that evidence.

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

* fix(ida): drop a confirmed worker from retained cleanup blockers

Recovery inventory now records the requested worker beside any peers,
so once idb_close confirmed its release a failing follow-up inventory
still reported it as unreleased. Remove the confirmed session from the
retained blockers at close confirmation and keep the remaining peers.

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

* test(ida): retry cleanup through a detached stdio connection

The retry coverage used a recording connection whose detach() only
counts calls, so it could not show that detaching keeps a retained
connection usable. Run a real stdio supervisor in a long-lived host,
fail the first post-close inventory, and require the retry through the
same detached connection to confirm release and remove the workspace.
The test fails if detach() destroys the pipes instead of unref-ing them.

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

* fix(ida): close the stdio supervisor in failed retry tests

Merge main and give its new IdaNativeProtocol connection double the
required detach() method. The real-stdio retry test now closes its
connection in an unconditional teardown, so a failing assertion cannot
leave the supervisor waiting on stdin.

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

* test(ida): share the cleanup diagnostic expectation in session tests

Repeated upstream_sessions assertions and the unrelated live peer
literal pushed repository duplication over the jscpd threshold. Build
the expected diagnostic with one helper and reuse one peer constant.

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

* test(ida): distinguish observed ownership from unreported input bindings

* fix(ida): keep requested-ID conflicts in recovery cleanup

Recovery inventory kept only same-input entries, unlike the startup and
post-close filters. After an explicitly rejected open, an entry under
the requested session ID on another input was dropped, so cleanup could
close the connection, remove the private workspace and report success
while that session stayed registered. Match the requested ID as well so
the entry is retained as a blocker and cleanup stays incomplete.

Also split the cleanup diagnostics suite under the lint:built function
length limit, and make two startup-inventory fixtures pass a defined
session id so the test file typechecks.

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

---------

Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 21:36:52 +08:00
Jose CamargoandGitHub 811bd9f635 docs(contributing): explain Windows native prerequisites for source checkouts (#2295) 2026-10-13 21:34:59 +08:00
Danish Rafie EkaputraandGitHub 9e019ad27d feat(clients): register Factory Droid during setup (#2309) 2026-10-13 21:34:38 +08:00
117bd23dd5 test(clients): verify Qwen Code on native Windows (#2103)
Co-authored-by: AICodeLion <190163814+AICodeLion@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:27:45 +08:00
Saim ShafiqueandGitHub 6637161e02 fix(ghidra): warn when callee/caller reads stop at a truncated function body (#2360)
procedure_callees and procedure_callers returned a bare address list, so an
inferred no-return call that truncates Ghidra's function body silently dropped
real edges (fixes #2326). Wrap both views in the function-observation envelope
with noReturnCallLimitations, matching procedure_info, procedure_assembly,
procedure_pseudo_code, read_function_instructions, and analyze_function
(precedent: 69a8b3a95). Version the boundary-observation contract to
ghidra-terminal-call-limitations-v2 so older snapshots cannot replay
unqualified results.

Validation: red->green unit test for the envelope requirement, MCP boundary
evidence test extended for both views (limitations retained through Evidence
and snapshot replay), 8 harness test files + ghidra behavior tests green,
check:fast, docs:check, verify:test-discovery green. Real Ghidra host lane
unavailable here (no Ghidra installed); Java change mirrors the existing
procedureInfo call pattern.
2026-10-13 21:26:57 +08:00
dea770d5d9 fix(javascript): retain getter receivers through local aliases (#2778)
* fix(javascript): retain getter receivers through local aliases

* fix(javascript): resolve projected private getter receivers

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:26:13 +08:00
48b92e1be1 fix(go): classify JVM class files instead of universal Mach-O for CAFEBABE bytes (#2458)
* test(go): pin JVM class files as unsupported Go images

The regression fails on main: readGoBinaryImage(javaClassFile()) reports
"Universal/fat Mach-O containers require selecting a thin slice" for a
JVM class file whose bytes 4..7 are minor/major version, not nfat_arch.

* fix(go): classify JVM class files instead of universal Mach-O

Route the four FAT magics through the shared readFatContainerHeader and
classifyFatContainerIdentity facts added for the same disambiguation in
the artifact and target lanes (921bb9575). Keep the thin-slice failure
only when the container identity is confirmed Mach-O; a JVM class file
now falls through to the generic only-ELF/PE/thin-Mach-O failure.

* test(go): pin universal Mach-O thin-slice failure with FAT fixtures

Cover the kept behavior with the repository's own fatImage/machoImage
builders (narrow and FAT64 tables) so the regression also proves real
universal containers keep the slice-selection failure.

* test(go): cover little-endian FAT magics in the universal container control

* fix(go): require positive universal Mach-O identity

Require positive Mach-O identity before reporting a universal thin-slice remedy; malformed FAT-shaped bytes are not proof. Add the JVM constant-pool CPU lookalike to the existing fixture control.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

* fix(go): retain malformed FAT header diagnostics

Preserve the owning FAT header parser reason for malformed headers while requiring positive Mach-O proof before the universal-slice remedy.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:25:32 +08:00
003707f712 fix(verification): align Linux Hopper setup lane with current contracts (#1942)
* fix(verification): align Linux Hopper setup lane with current contracts

* fix(verification): retain orphan checks on clean Linux runners

* fix(verification): detect Hopper through alternate launcher paths

* fix(verification): report identified Hopper leaks before proc errors

* fix(verification): distinguish Hopper string recognition from byte fidelity

* fix(verification): report full literal decoding coverage explicitly

* test(verification): require a clean host before orphan checks

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:24:40 +08:00
dd4bfed614 fix(javascript): keep ASAR-named files without an ASAR header opaque (#2347)
Inventory expands an ASAR-named filesystem file only when its bytes are
an ASAR. JavaScript reconstruction expanded it on the suffix alone, so
a directory holding any other file named *.asar failed the whole
analysis with an invalid ASAR header.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:23:58 +08:00
xingzhouandGitHub 84ec40f1af fix(browser): distinguish SVG links from HTML XLink attributes (#2480)
* fix(browser): respect SVG desc and title HTML integration points

* fix(browser): preserve moved SVG XLink destinations

* fix(browser): bound concurrent SVG identity reads
2026-10-13 21:23:33 +08:00
MasonGitHubDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com>morluto
5eb296649d fix(native): refuse inspect_signature on non-Mach-O targets (#2262)
* fix(native): refuse inspect_signature on non-Mach-O targets

This provider's signature inspection covers Mach-O code signatures only.
On a xar pkg, codesign's "code object is not signed" verdict cannot see
the archive-level signature layer, so a signed or invalidly-signed package
was reported as signed:false with "Artifact is not signed." Match the
existing inspect_macho/list_architectures gate and report
capability_unavailable instead of an unsigned claim for a layer that was
never evaluated.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(native): scope the signature-gate comment to this provider

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(native): align signature capability discovery with Mach-O support

Bind inspect_signature availability to Mach-O targets in capability discovery and align the public tool description.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:22:42 +08:00
cae7620b77 fix(apple): report a recognized connector that cannot name its owner (#2484)
* fix(apple): report a connector that cannot name its owner as an omission

A connector is recognized by class name and then dropped by an early
`continue` when its owner cannot be resolved. Nothing recorded the drop, so
connection coverage derived `complete` with `examined: 0` and `omitted: 0`
for a connector that was present, recognized and discarded.

Both producers of that coverage contract are affected and are changed
together, because they share the facet and the reason value:

- the keyed-archive parser records the drop as `unresolvedConnections` and
  reports it through the `connections:<path>` facet, the document
  `connection_count` and the graph omission total;
- the NIBArchive projection in the artifact adapter recorded nothing at all,
  and the projected dictionary carried no trace of the dropped connector for
  a downstream consumer to find. Its count is taken where the connector is
  discarded and plumbed through the same per-document omission channel that
  already carries incomplete hierarchies, so the reason follows the cause.

An unresolved source cannot be represented as a connection at all, because
connection identity requires a non-empty source; that is unlike an unresolved
destination, which the graph already retains as an unresolved edge.
`connection_limit_reached` stays reserved for connections dropped by the hard
cap and `serialized_view_hierarchy_incomplete` for hierarchy omissions.

`connection_count` also includes the unresolved connectors. That field already
counts connectors the parser recognized but could not emit, because
connections dropped by the hard cap are counted there too, so unresolved
connectors follow the existing rule rather than a new one.

Verified with 98 focused Interface Builder and catalog tests, check:fast and
verify:test-discovery on macOS arm64, and through the public CLI: a rooted
archive holding one ownerless UIRuntimeOutletConnection reported complete with
examined 0 and omitted 0 before this change and reports partial with the reason
connection_unresolved and omitted 1 after it.

* fix(apple): count ownerless NIBArchive connectors and keep the limit reason

Greptile found two defects in the NIBArchive half of this change.

The document summary was left unchanged, so the same recognized connector
counted as one in a keyed archive and zero in a NIBArchive. The dictionary
parser only sees connectors that were projected, so the omission recorded
where the connector was dropped is added to the matching document here.

The facet reason was overwritten unconditionally, so a NIBArchive holding both
a connection dropped by the limit and an ownerless connector reported
connection_unresolved and lost the cause a higher limit could recover. The
existing reason is kept when the graph already named one, which is the
precedence the keyed-archive producer applies.

Verified with `npm run test:focused` over the bundle suite (42 passed, 1
skipped), `npm run lint` (0 errors) and `npm run check:fast` on macOS arm64.
Reverting the reason precedence alone fails only the limit case; reverting the
document count fails the count assertion in both cases.

The new case moved into its own describe block because the enclosing callback
crossed the repository's max-lines-per-function bound otherwise.

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 21:19:17 +08:00
c44d17fd3c fix(pwntools): let callers widen the artifact input bound (#2486)
* fix(pwntools): let callers widen the artifact input bound

The pwntools adapters rejected every artifact over 32 MiB before any
analysis ran, and nothing in configuration, environment, CLI or MCP
could raise that bound, so real recorded core dumps stayed unanalyzable.

Resolve the bound from REA_PWNTOOLS_MAX_INPUT_BYTES in front of the
shipped default. An absent value keeps 32 MiB, and a value outside
1..1073741824 bytes is refused as unusable provider configuration before
the artifact is read instead of falling back silently.

* fix(pwntools): name the bound variable in its own refusal

An unusable REA_PWNTOOLS_MAX_INPUT_BYTES travelled through the shared
engine-absence helper, which always stamped configuration_key
REA_PWNTOOLS_PYTHON, so a caller following that field was pointed at the
Python executable instead of the bound it had just set. The guide also
described the refusal as an invalid request while the decoder reports
provider_unavailable.

Let the failure context carry the key under review and pass the bound's
own name at that call site, then state the code callers actually receive
in the guide. Assert the key on the projected rejection so the engine
default and the bound override each stay attributed to the right setting.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:18:12 +08:00
FengCiandGitHub 59d33a2733 fix(native): keep lipo path echoes out of FAT records (#2605)
* fix(native): keep lipo path echoes out of FAT records

Parse FAT records only after separating the exact echoed operand path. Pass the same lipo operand at all three consumers and cover multiline paths through parser, slice-reader and compiled CLI regressions.

* fix(native): use parsed lipo framing for container offsets

* test(native): use explicit FAT fixtures for CLI regressions
2026-10-13 21:16:49 +08:00
GamerDubzandClaude Opus 5.5 a74d0502b9 docs(skill): use the plugin's pinned version for package-runner checks
The plugin recovery section left the package-runner version to the skill's
generic @latest fallback, so a doctor run could describe a newer release
than the server an installed plugin starts. Tell the agent to use the
version pinned in the plugin manifest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:16:31 +02:00
11268fe241 perf(artifacts): share sealed inventory and report in inspect_artifact (#2690)
inspect_artifact parsed a fresh inventory and inspection report but left
both mutable, so each later Evidence boundary cloned and re-digested the
complete payload: createArtifactInspection re-authenticated the nested
inventory Evidence, createAnalysisExecution re-parsed the report, and
recordEvidence re-authenticated the outer Evidence.

Seal both exclusively owned values with freezeJsonSnapshot so those
boundaries take the existing immutable-snapshot path. Output is
byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:16:29 +08:00
e46bed73f1 fix(managed): decode the CIL no. prefix (#2241)
The CIL opcode table held five of the six ECMA-335 prefix opcodes and
omitted no. (FE 19 <unsigned int8>, III.2.2). A method body using it was
reported as malformed with "Unsupported CIL opcode 0xfe19", losing its
instruction counts and normalized IL digest.

Decode it with its one-byte check mask and treat it as a prefix, so it
shares an instruction boundary with the opcode it modifies.


Claude-Session: https://claude.ai/code/session_012pRGskbPGjT444CgJfuhhj

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 21:16:00 +08:00
ece15ed6a1 fix(electron): identify each unresolved scenario path (#2279)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:15:33 +08:00
97dc6581f4 fix(dotnet): name the instruction when a CIL operand leaves the method body (#2697)
The decoder already reports "Instruction X at IL offset N leaves method
body" when an operand's computed end passes the IL body, but only after
the operand was read. Operand readers use Buffer reads, so an operand
cut off by the body end threw a RangeError first. The catch-all stored
Node's message ("Attempt to access memory outside buffer bounds" or an
offset RangeError) as body.issue, dropping the instruction and offset.
A body ending in a lone 0xFE two-byte opcode prefix failed the same way.

Treat a RangeError from the operand read as the operand leaving the body
so the existing diagnostic applies, and check the second opcode byte
before reading it. Malformed status, partial coverage and earlier
decoded instructions are unchanged.

Fixes #2677

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:14:31 -07:00
cdc41fc86c fix(managed): classify ReadyToRun images without ILONLY as modern .NET (#2075)
* fix(managed): classify ReadyToRun images without ILONLY as modern .NET

crossgen2 clears COMIMAGE_FLAGS_ILONLY and sets IL_LIBRARY on ReadyToRun output. inspect_managed_artifact checked the clear ILONLY flag before the ReadyToRun signature, so real ReadyToRun assemblies were reported as runtime_family mixed-clr-native and implementation cpp-cli-mixed, and cil-and-ready-to-run was unreachable for crossgen2 output. Check the ReadyToRun signature first; a clear ILONLY flag without it still reports mixed CLR/native code.

* test(managed): exhaust capacity through inspected assembly references

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:13:18 +08:00
记得吃中饭andGitHub 9c14999efd feat(ghidra): support raw NMOS 6502 analysis (#2079)
* feat(ghidra): admit raw NMOS 6502 analysis

* test(ghidra): require verified POSIX verifier cleanup

* test(ghidra): verify 6502 low-memory raw imports

* docs(skill): document raw NMOS 6502 support

* fix(ghidra): preserve requested Windows raw source paths

* docs(ghidra): include raw 6502 in Windows installation scope

* test(ghidra): verify Windows raw junction rejection through CLI and MCP

* fix(validation): make fixtures portable and preserve explicit Codex home

* fix(ghidra): attest raw mapping state and address space

* fix(ghidra): align diagnostics and regression groups

* fix(ghidra): bind raw attestation to language address space

* chore(ghidra): avoid verifier insertion conflict

* docs(ghidra): align linked raw admission guides

* fix(ghidra): retain 6502 verifier workspace on failure

* test(ghidra): isolate Windows authority doctor fixture

* fix(binary): re-admit Windows raw paths on session reopen

* test(binary): require fresh provider execution after path change

* Constrain 6502 to raw lifecycle and snapshot targets

* Bypass saved reads for unadmitted Windows paths

* Guard direct CLI snapshot replay on Windows paths

* fix(snapshot): gate Windows Ghidra replay on native admission

* fix(snapshot): constrain nested 6502 subjects to raw images

* test: make dyld slice fixtures portable
2026-10-13 21:13:01 +08:00
352c46aa75 fix(javascript): keep CommonJS require file URLs unresolved (#2077)
* fix(javascript): keep CommonJS require file URLs unresolved

require("file:///dep.cjs") was reported as a resolved dependency of
the inventoried dep.cjs although Node rejects the string with
MODULE_NOT_FOUND. The shared module-specifier resolver decoded file URLs
before the declared CommonJS loader semantics.

File-URL decoding now applies only to non-require module specifiers, so a
CommonJS file URL reports external with its declared string and source
evidence preserved. ESM file URLs and ordinary relative require paths
keep their established lookup.

* test(javascript): consolidate CommonJS file URL public workflow coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:12:50 +08:00
91830f1e0e feat(managed): name the declaring type of call and field targets (#2163)
* feat(managed): name the declaring type of call and field targets

Call edges named external targets by member name alone (Join, ToJson,
WriteAllText), and method-spec edges had no name at all, so an analyst could
not tell which library a method calls or find every use of a type without a
decompiler. Field accesses through MemberRefs had no name either.

Name each TypeDef, TypeRef, and generic-instantiation TypeSpec once, in the
namespace-qualified form TypeDef full_name uses, and add member_refs
parent_name, call_edges target_declaring_type, and field_accesses
field_declaring_type. A method-spec target reports the generic method it
instantiates. Fields, member references, methods, and method specs now
resolve through one token-keyed MemberCore lookup instead of three maps.
Evidence recorded before these fields reads them as null.

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

* fix(managed): validate spec blobs and member kinds before naming targets

Address review findings on declaring-type naming:

- A generic instantiation must name a TypeDef or TypeRef, so a TypeSpec
  base is reported as malformed instead of followed. Naming no longer
  recurses.
- TypeSpec and MethodSpec instantiation blobs are decoded with the
  member-signature grammar. Malformed blobs and out-of-range TypeDef rows
  are reported against their owning row and make coverage partial.
- A MethodSpec reads both columns. An invalid Method coded index is
  reported; a readable Method keeps its name beside a malformed
  instantiation.
- MemberCore carries its signature kind, so a field operand is named only
  by a field or field-signature MemberRef. This replaces the token-table
  guard.

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

* fix(managed): validate MethodSpec arity and report unsupported spec blobs

- A MethodSpec now checks that its method signature is generic and that
  the instantiation supplies as many type arguments as it declares. A
  non-generic target, a field reference, or a count mismatch is reported
  as invalid-row; the readable method keeps its name.
- Unsupported TypeSpec and MethodSpec forms raise an owner-scoped
  unsupported-signature issue instead of reading as an intentional
  unknown.
- MethodSpec reading moves to ManagedMethodSpecs.ts, and its tests get
  their own describe block.

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

* fix(managed): reject non-method signature headers and invalid TypeRef scopes

The method signature header reader now owns the calling-convention rule
that only FNPTR applied: FIELD, LOCAL_SIG, PROPERTY, and GENERICINST
headers, the reserved bit, and EXPLICITTHIS without HASTHIS are malformed.
A MethodSpec whose method has a PROPERTY header is no longer accepted as a
generic method, and member signatures with such headers no longer decode
as methods.

The type-name lookup validates a TypeRef's ResolutionScope coded index and
reports an out-of-range scope as invalid-row, keeping the readable name.

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

* fix(managed): hold member rows to MethodDefSig and FieldSig headers

Method signature headers are now checked in context. MethodDef and
MemberRef rows accept only the MethodDefSig/MethodRefSig headers: DEFAULT,
VARARG, or GENERIC with DEFAULT. FNPTR keeps the unmanaged conventions
compilers emit for function pointers. A field signature must be exactly
FIELD, so a flagged FIELD header is malformed and never names a field.

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

* fix(managed): accept MethodSpec arity only from a fully decoded signature

The arity check read only the target's signature header, so a truncated
generic method signature passed. Under type selection a MethodDef target
outside the selection is never decoded elsewhere, so coverage stayed
complete. The arity now comes from a transient full decode, and only a
decoded method signature supplies it. signature() keeps its raw length
and digest around the shared decodeSignature.

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

* perf(managed): decode each MethodSpec target signature once

edgeTargets caches the generic arity, including a failed decode, by
target method token, so many instantiations of one generic method share
a single signature decode. Unity.InputSystem has 472 distinct MethodSpecs
over about 133 target methods.

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

* fix(managed): validate spec type operands and transitive reference scopes

* fix(managed): retain readable names when a scope row is unreadable

* test(managed): pin owner-scoped spec operand and scope-chain checks

e59217c83 validates spec type operands inside specSignature, so a
malformed generic TypeSpec base is now invalid-row under its owning
TypeSpec row; the three stale expectations follow that. New cases cover
a TypeSpec argument and a MethodSpec argument that name a missing TypeRef,
and a TypeRef whose ResolutionScope names itself. The scope cases move to
their own describe block.

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

* fix(managed): keep field targets from naming MethodSpecs

A MethodSpec over a field-signature MemberRef inherited kind "field", so
an ldsfld on that MethodSpec published a field name and declaring type
for malformed IL. A MethodSpec now borrows its target's name only when
the target is not a field; the arity check still reports it.

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

* fix(managed): reuse owner-decoded signatures for MethodSpec arity

MemberRef signatures, and MethodDef signatures in the type selection, are
already fully decoded, but the MethodSpec arity check decoded them again
and admitted a second transient allocation, which could exhaust the
budget. MemberCore now carries the owner's genericArity, and only a target
nothing decoded, such as an unselected MethodDef, falls back to the raw
signature. signatureGenericArity is the one rule for both paths.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:12:15 +08:00
df04f99df0 fix(readiness): deduplicate stage-level missing-evidence findings (#2203)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:10:56 +08:00
GamerDubzandClaude Opus 5.5 351ad8282a docs(plugin): check engine readiness through the connected plugin server
The plugin guidance said a package-runner doctor report describes the
server the plugin starts. Doctor reads the invoking shell's environment,
while the plugin server inherits the client's, so the two can disagree
about settings such as GHIDRA_INSTALL_DIR. Point plugin users and the
bundled recovery guide at binary_session tool availability, and say what
the terminal check does and does not cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:09:04 +02:00
d2e16b2a49 fix(sqlite): attribute unknown column selections to columns (#2774)
Every worker "selection" rejection was projected onto the table argument,
including an exact column name that does not exist in an existing table.
Give column rejections their own worker reason and map each input reason
to the caller argument it requires correcting. Table rejections keep the
table path.

Fixes #2613

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:03:14 +08:00
928b4d5fe4 fix(plist): decode formatted XML dictionary keys (#1843)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:59:02 +08:00
jhuang-twandGitHub 4ea24f9767 fix(ida): retain private input after ambiguous open (#1845)
fix(ida): retain private input after unverified open
2026-10-13 20:58:51 +08:00
2a61deafc6 fix(adb): report the device state when a push cannot probe the path (#2354)
A push without overwrite read any unclassified failure of its existence
probe as an existing path, so an offline or unauthorized device was
reported as "the device path already exists". With overwrite it pushed
after a probe that had not established anything. The probe's own
refusal is now returned in both cases.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:56:41 +08:00
Mykola DementievandGitHub 5f8af4ee5f feat(ida): resolve containing procedures and report the admitted program as the document (#2374)
* test(mcp): expect trace_feature to report references the provider truncated

A provider that reports a capped xrefs read makes the trace truncated with a residual unknown for that target. An explicit complete report and a provider that reports nothing leave the trace as before.

* fix(mcp): report references a provider truncated in trace_feature

Executions gain an optional flag for a capped xrefs read, set by the IDA adapter from the upstream cap. trace_feature marks the trace truncated with a residual unknown for that target only when the provider reports a truncation; a provider that reports nothing keeps its previous result. The trace_feature workflow profile is revised so earlier results are not replayed.

* refactor(ida): report reference truncation whenever the runner observed an xrefs cap

* test(mcp): cover session forwarding, per-target state, native reports and failures of the trace coverage

* test(mcp): format the session forwarding test

* test(ida): expect headless IDA to resolve containing procedures from lookups and segment facts

Cover positive and negative containment, segment boundaries including addresses above 2^53, failures of the lookup and of the segment survey, cancellation, input validation and the admitted program as the only document.

* test(ida): expect feature tracing and address context to work on headless IDA

Run trace_feature, including a capped reference read, and inspect_address_context with and without an explicit document through the IDA session client.

* test(mcp): expect trace_feature availability to follow the search operations it calls

* fix(mcp): derive trace_feature availability from the search operations it calls

* feat(ida): resolve containing procedures and report the admitted program as the document

The modern headless profile resolves an address through the function lookup and, when no function contains it, tells a mapped address from an unmapped one using the database-scoped minimal survey. A survey that is missing, truncated or inconsistent fails the read. list_documents reports the single admitted program. The attached profiles do not declare either operation.

* docs(ida): describe containing procedures, the admitted document and truncated feature traces

* test(ida): verify containing procedures, the admitted document and feature tracing on real IDA

* docs(testing): list the headless IDA workflows the real lane verifies

* test(ida): make each survey failure case isolate a single defect

* test(ida): pin large addresses, segment order, document identity, attached refusals and each trace_feature prerequisite

* test(mcp): expect a repeated reference source to be resolved once in trace_feature

* fix(mcp): resolve the containing procedure of a repeated reference source once

* test(ida): trace the fixture procedure and derive probe addresses from its facts in the real lane

* test(process): format processCaptureLifecycle.test.ts after the merge

* test(ida): declare the alignment each synthetic universal slice offset satisfies

* refactor(ida): move the real lane's feature trace assertions into a helper

* test(ida): expect the real lane's feature trace check to accept references to every matched address

* fix(ida): check each trace reference against its own matched address in the real lane

* test(ida): pin that the new operations reject a document other than the admitted program

* refactor(ida): move the real lane's mapped non-function probe into a helper

* test(ida): expect the real lane's mapped data probe to skip strings inside functions

* fix(ida): try the target's strings in order for the mapped non-function probe

* test(ida): declare the real lane helper types for the verifier tests

* test(ida): expect the real lane to reject a listed string that resolves as unmapped

* fix(ida): fail the real lane when a listed string resolves as unmapped
2026-10-13 20:56:20 +08:00
morluto 07680f66e2 fix(release): preserve contributor version-block conformance checks 2026-10-13 20:56:14 +08:00
e0bd31c18c fix(ghidra): bound the Java version probe output (#2361)
The Java version probe captured its output with maxBuffer set to Infinity, so a
chatty or hostile JVM selected as the Ghidra launcher could grow the captured
stdout/stderr without limit and exhaust REA's memory during installation
discovery. Bound the capture to 1 MiB, matching the budget already used for
other Java listings. A probe that exceeds the budget yields no observation and
is reported through the existing failed-probe path, the same one the Java check
already uses whenever the probe cannot produce an observation.

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-10-13 20:55:40 +08:00
morluto eaa9fe5cd5 test(release): validate every owned documentation version block 2026-10-13 20:55:14 +08:00
8e6f37e2a0 fix(formats): preserve high bits in remaining magic-byte checks (#2384)
* fix(formats): preserve high bits in remaining magic-byte checks

Node's "ascii" decoder masks the high bit, so byte-for-byte signature
comparisons accepted lookalikes such as CD DA ("MZ" | 0x80) or a
high-bit "bplist00"/"NIBArchive" header. ac57c88ef fixed this for the
shared DOS/MZ reader; apply the same latin1 decoding to the remaining
signature checks: NativeAOT and Windows native loader MZ/PE, binary
plist and NIBArchive detection, and PNG IHDR checks.

Display-name decodes (section and metadata stream names) are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* style(windows): format signature admission regressions

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:54:37 +08:00
GamerDubzandClaude Opus 5.5 9d658d767a test(release): check every versioned example block in the installation guide
The conformance test read only the first Release Please version block, so
adding the plugin readiness block ahead of the MCP Registry example left
the registry example unchecked. Check every block, and require each known
example to sit inside exactly one, so a stale version or a lost marker in
any of them fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:52:24 +02:00
43738bec57 fix(targets): retain unsupported PE machine in diagnostics (#2448)
Assisted-by: OpenAI GPT-6 <noreply@openai.com>

Co-authored-by: Aurantium-47 <280805072+Aurantium-47@users.noreply.github.com>
2026-10-13 20:52:17 +08:00
03ab521db3 fix(process): resolve POSIX backslash basenames through PATH (#2471)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:51:44 +08:00
0ca70e121c fix(ghidra): pre-create the launch-support config directory (#2470)
* fix(ghidra): pre-create the launch-support config directory

Ghidra's launch support resolves its JDK before the headless run starts and
persists that choice under `<config>/ghidra/<release directory>`. Inside REA's
private runtime a child JVM cannot create those directories, so the first
Windows Ghidra query aborted at ghidra.launch.AppConfig.saveJavaHome with:

    java.io.IOException: Failed to create directory:
    ...\config\ghidra\ghidra_12.1.4_PUBLIC
        at ghidra.launch.AppConfig.saveJavaHome(AppConfig.java:145)
        at LaunchSupport.findJavaHome(LaunchSupport.java:210)

LaunchSupport then fell back to its interactive prompt, read EOF, and reported
"JDK 21+ (64-bit) could not be found and must be manually chosen!", so the
provider failed with "Ghidra exited before bridge startup" even though
`rea doctor` reported a valid Ghidra installation and JDK.

Create the release directory while this process still owns the runtime, leaving
Ghidra only a file write inside it. The release coordinate comes from the
installation's Ghidra/application.properties (application.version plus
application.release.name) with the extraction directory name as a fallback. On
Windows each level goes through the native private-runtime owner, which opens
every existing component and creates only the final one.

Verified on Windows x64 with Ghidra 12.1.4, a 64-bit Temurin JDK 21, and REA's
packaged native addon: `rea inspect <x64 PE DLL> --provider ghidra` and the
equivalent MCP `binary_overview` call both complete and report 21 segments,
1436 procedures, and 2761 strings. Windows Ghidra remains read-only P0; this
change does not extend that boundary.

* style(ghidra): format launch configuration regressions

* fix(ghidra): reuse Java Properties parsing for launch release identity

---------

Co-authored-by: AbdulAzizShanShi <441650742@QQ.COM>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:51:18 +08:00
f63674cac0 docs(browser): clarify attach coverage and Chromium setup (#2287)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:44:41 +08:00
ChaoqiandGitHub d3a55720fc fix(setup): keep Hermes config numbers as written (#2368)
Setup and uninstall re-printed every number in an existing Hermes
config.yaml from a JavaScript number. Hermes reads the file as YAML 1.1
with exact integers, so a Discord channel ID lost its low digits, an
octal mode such as 0600 became 600, and 0.0000001 became 1e-7, which
YAML 1.1 does not define as a float. Two IDs that round to the same
double were also rejected as duplicate keys, so setup could not
register at all.

Hermes YAML parsing and editing move to HermesConfigurationYaml. A parsed
number is written with its source spelling while that spelling still
reads as the node's value, the rule yaml's own boolean tag follows, and
two keys are duplicates only when both their value and their spelling
match. Parsed values stay JavaScript numbers.
2026-10-13 20:44:17 +08:00
36132d3e17 fix(managed): classify 32-bit-preferred assemblies as platform neutral (#2342)
The CLI header's 32BITREQUIRED and 32BITPREFERRED bits are one two-bit
field. Reading 32BITREQUIRED on its own reported Prefer32Bit assemblies
(flags 0x00020003) as x86-only with requires_32bit true, and left
anycpu-prefer-32 reachable only through the reserved combination.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:43:39 +08:00
1ca5fd38b4 fix(cli)!: keep a selected JSON null in --filter-output output (#2333)
createCliResultOutput rendered an explicit null selected by
--filter-output as the empty document {}, the same output it uses when
the selection has no value at all. On the streamed JSON/JSONL path,
a consumer could not distinguish a selected null from a missing field.

Write a selected null as null in streamed JSON and JSONL. This also
preserves an unfiltered top-level null. The {} fallback stays for
undefined, functions and symbols. The --full-output envelope already
carried data: null for a selected null. The --token-count path still
hands results that fit the formatted-string limit back to Incur and
prints 0 for a selected null. Other formats and Incur-owned result
paths are unchanged.

Consumers must accept JSON null for a present null and distinguish it
from a missing selection. Use --full-output if an object envelope is
required. Keeping the old shared {} representation would continue
hiding this distinction.


Claude-Session: https://claude.ai/code/session_019TTGAv6fe859ebbCLLfLHP

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:43:20 +08:00
Saim ShafiqueandGitHub 84cf7791a8 fix(call-path): keep caller-only unknown coverage from blocking not_found (#2371)
* fix(call-path): keep caller-only unknown coverage from blocking not_found

build_call_path expands only callee edges, so relationship coverage that
is unknown solely because callers are unobservable (the modern headless
IDA profile) no longer marks the searched callee closure incomplete.
The caller caveat stays visible in limitations, and callee-side unknowns
still yield unknown. Fixes #2322.

* test(call-path): cover IDA-capped callees beside unknown callers

Greptile review noted the poll-timing coverage only exercised unknown
callees, leaving the partial callees IDA returns at its 500-result cap
untested. Add the combined unknown-callers/partial-callees case:
isCallerOnlyUnknownCoverage must stay false and buildCallPath must keep
search_scope.exhaustive false, so a future change cannot silently report
not_found when IDA has omitted callees.
2026-10-13 20:42:40 +08:00
ec4f4ddc58 fix(ghidra): read UTF-8 signed language definitions (#2772)
Buffer#toString kept a leading UTF-8 byte order mark as U+FEFF, so the
strict XML parser rejected a valid .ldefs file as content outside the root
element and aborted the whole language catalog read. XML 1.0 section 4.3.3
defines the mark as an encoding signature; decode with TextDecoder, which
consumes it. Malformed XML is still rejected.

Fixes #2533

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:41:28 +08:00
8823c55b0c feat(ghidra): decode Swift String literals in function dossiers (#2049)
* feat(ghidra): decode Swift String literals in function dossiers

Swift passes a String as two words. Small literals keep their UTF-8 bytes
inside those words, and immortal large literals keep a count plus a pointer
to their bytes biased by 32. Ghidra creates no data reference for either, so
analyze_function reported no strings for Swift code.

On AArch64 programs that contain Swift metadata, the Ghidra bridge now runs
constant propagation over the function and decodes register pairs where
Swift consumes a String: consecutive argument registers at calls and tail
calls, x0 and x1 at returns, and both registers of one STP. Large literals
must resolve to terminated bytes inside one initialized block that is not
executable. A pair whose registers both come from conditional selects is
skipped, because Ghidra forks each select without correlating the
condition and could otherwise pair words from different cases.

Decoded values are a derivation from the ABI layout rather than observed
references, so they appear in a new optional swift_string_literals field
with their source instruction, registers and raw words, and in a matching
inspect_analysis_view facet. referenced_strings is unchanged.

Refs #2030

* fix(ghidra): bound Swift literal reads and compare decoded strings

Large immortal literals are measured against the remaining heap before
allocation, and a failed fit stays visible as a limitation. The canonical
empty string is kept. Small and large literals now require matching
address nullability, and function comparison treats decoded literals as
string evidence without equating a missing record to an empty one.

* fix(ghidra): budget Swift literal decoding and tighten use sites

Large literals now draw on one heap budget per dossier, measured once
before decoding, instead of a separate check per literal. A literal that
does not fit is listed in a new swift_string_literal_omissions field
with its address, byte length and reason, so consumers read omissions
from structured data rather than from limitation text.

strings_names comparison checks referenced strings and names first and
reports a change there even when Swift literal coverage differs; it
returns unknown only when those facts match and literal coverage is
absent on one side or partial.

A computed jump counts as a tail call only when it has no resolved
target inside the function, so jump tables no longer qualify, and a
return requires a RETURN p-code operation, so traps no longer qualify.

Decoded literals keep control characters, which Swift strings may
contain and JSON escapes. The Swift verifier builds the literal fixture
for arm64 on any Mac host and now also covers the empty string and a
literal containing a tab.

* fix(ghidra): charge escaped JSON and follow copied selects

The Swift literal budget now charges the worst case per UTF-8 byte: UTF-16
text, six escaped JSON characters held in Gson's growing buffer and in the
returned String, and the UTF-8 bytes written to the socket. Literals made
of control characters can no longer exceed the reserved heap.

A jump counts as a tail call only when every resolved target lies outside
the function. The conditional select check follows register copies back
to their definition, so selects copied into argument registers are
recognized like selects written there directly.

strings_names reports a change when both dossiers decoded different
literals at the same use site, even when either side has omissions; it
returns unknown only when no such use site disagrees.

* fix(ghidra): bound literal records and trace selects across branches

Every Swift literal record now pays a fixed cost against the dossier
budget in addition to its text, so many empty or small literals are
bounded as well. The first record that does not fit is kept as an
omission and decoding stops there, which keeps the omission record
itself bounded; omissions now describe small or large literals.

The conditional select check searches every predecessor path for the
reaching definitions, following register copies, so selects made
before a branch into the use block are recognized.

A dossier records Swift literals and their omissions together. Coverage
reads omissions first, and strings_names compares every use site that
neither side omitted: a decoded literal is known where it was recorded,
and an absence is known only in a dossier whose decoding examined every
use site.

* fix(ghidra): stop every flow at the literal budget and route omissions

Ending one flow from the evaluator leaves other queued flows running, so
the budget now records a shared stop that every flow checks before it
examines another instruction. A record that exceeds the budget is routed
by that capacity result rather than by whether text was decoded, so a
small literal over budget becomes an omission instead of a literal.

When coverage is partial, the per site comparison keeps each literal's
representation as well as its value, matching the full strings_names
projection.

* fix(ghidra): keep unresolved Swift tail-call sites unknown

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:41:10 +08:00
YsopwnableandGitHub 2f3dee3fcf test(javascript): verify Windows path case and load scope (#2635)
Select the Windows curated lane for edits to the shared runtime path matcher and its workflow suite. Strengthen the existing POSIX/Windows mapping controls with matching basis, coherent target/script coordinates, caller spelling and static load-state assertions.

Follow-up to #2422; thanks to @Aurantium-47 for identifying the recorded-path grammar issue.

Verified with actual Windows 11 x64, official Node 24.18.0 and production CLI/MCP workflows using published npm 11.16.0 module Evidence. Four exact/mismatched implicit/explicit mapping cases pass with unchanged normalized hashes. The capture provider was Node Inspector; no IDA, Ghidra or Electron GUI verification is claimed.

230 focused tests, all eight CI static tasks, architecture guards and documentation checks pass after rebasing onto main fb28ec153. The unrelated release-checkpoint and dyld fixture corrections were integrated from upstream; no patches to those files are included here. Final Windows MCP replay at e6e86946d preserves all four normalized hashes, with no owned Node processes remaining. Resource probes ran serially at BelowNormal priority with one CPU and 512 MiB Node heaps; calls took 251–620 ms and the sampled process-family working-set sum peaked at 499,380,224 bytes. Source pins, provenance and measurement limits are recorded in experiments/analysis-efficiency/windows-posix-reconciliation.md.

All 34 jobs in current-head CI run 38391921571 passed, including the Windows capability/package lane, all six portable test shards, JavaScript recovery and the required aggregate. Current-head Codex and Greptile reviews are clean; both prior scope-selection findings were fixed and resolved.
2026-10-13 20:37:48 +08:00
morluto 22de1ff010 chore(plugin): reconcile main while retaining release decisions 2026-10-13 20:31:51 +08:00
d853744969 fix: preserve process identities in pwntools cleanup errors (#2284)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:28:31 +08:00
fcfa5b7e88 fix(artifacts): distinguish JVM classes from universal Mach-O files (#2327)
* test(artifacts): reproduce JVM class inventory misclassification

* fix(artifacts): validate FAT declarations before classifying executables

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:27:38 +08:00
a8b50ea147 refactor(firmware): split verification into staged functions (#2713)
Refactor firmware verifier into staged functions

Restructure scripts/verify/firmware/analysis.mjs for readability without changing behavior.

- Move prerequisite checks into checkPrerequisites()
- Split the main flow into stageCli, stageMcp, stageExt4 and stageGhidra
- Extract timeouts and size limits into named constants
- Rename cli/call helpers to runCli/callTool

Checks, order of operations, error messages and env vars are unchanged.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:25:58 +08:00
To3akaRinandGitHub eeeb4ca379 fix(javascript): preserve container ownership for getter mutations (#2765) 2026-10-13 20:10:39 +08:00
ChaoqiandGitHub f974353c32 fix(javascript): keep paths after __dirname inside the source directory (#2366)
Node's path.join does not reset at an absolute segment, so
path.join(__dirname, '/preload.js') is <dir>/preload.js. The static path
projection skipped the __dirname anchor and joined the remaining parts,
which turned that common spelling into the root path /preload.js. In an
app whose main script lives in src/, the BrowserWindow preload was then
reported not-found, or resolved to an unrelated preload.js at the artifact
root, and the utilityProcess.fork target was never resolved. String
concatenation had the same defect: path.dirname(__filename) +
'/preload.js' projected to /preload.js, and __dirname + '/preload.js'
stayed unknown.

A source directory now contributes "." to a join, so path.join(__dirname)
projects to "." like path.resolve(__dirname). A concatenation that starts
with __dirname or the source file's dirname keeps text that begins with a
separator inside that directory; other text extends the directory's own
name and stays unknown. The empty-string projection of
path.dirname(__filename), which produced the wrong concatenation, is
removed, so a bare path.dirname(__filename) is unknown like a bare
__dirname.
2026-10-13 20:10:10 +08:00
cd2ad1f240 fix(artifacts): report malformed ZIP archives as format failures (#2683)
* fix(artifacts): report malformed ZIP archives as format failures

zip.js signals archive faults (missing end-of-central-directory record,
unrecognized format, overlapping members, corrupt member data,
unsupported compression) as plain Errors. ZipArtifactReader passed them
through untyped, so the artifact provider's fallback reported every one
as `io` with no detail and told the caller to "check file access".

Type these faults at the ZIP reader boundary: structural and data
corruption become `format` failures and unsupported archive features
become `unavailable`, each carrying the zip.js reason as detail. This
covers enumeration, the getData rejection, and the WritableStream abort
path that delivers corrupt-data errors first.

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

* test(artifacts): share the ZIP inventory failure assertion

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:07:28 +08:00
Iven KooandGitHub f90382756d fix(native): keep bounded trace edges and depth consistent (#2758) 2026-10-13 20:06:50 +08:00
Shenrui MaandGitHub 68ecdcd7a1 fix(reference): preserve directory intent when resolving source imports (#2770)
Keep directory requests from becoming unresolved or pointing at sibling files
when constructing reference-source dependency graphs. Canonicalize index
candidates and preserve directory intent while retaining ordinary lookup rules.

Constraint: Preserve current TypeScript candidate objects and exact-before-Unicode lookup
Rejected: Reorder all file probes before index probes | Changes ordinary CommonJS candidate priority
Confidence: high
Scope-risk: narrow
Tested: Public CLI regression fails on base; focused reference and resolver suites pass; npm run check
Not-tested: Full suite on refreshed base; earlier full run had 12 baseline-reproduced failures
2026-10-13 20:06:19 +08:00
Guangqi ZhangandGitHub 1dd6c2dff9 fix(setup): resolve DeepSeek Harness home overrides (#2461) 2026-10-13 20:05:31 +08:00
Liu JieandGitHub c95315d2ce fix(cli): reject unterminated output filter slices (#2315) 2026-10-13 20:04:33 +08:00
Kaoru0822andGitHub fb28ec1537 test(apple): keep the same-named slice dyld fixture openable on x64 hosts (#2755)
Add a plain x86_64 slice to the dyld fixture while retaining both same-named arm64e slices and their independent environment assertions.

The original fixture was rejected by real x64 CLI admission before tracing. The contributing PR's complete Linux CI run passes all 30 jobs, including the portable suite. Codex and Greptile reviews completed without findings; no unresolved threads remain.

This restores the main test baseline needed for #2635. The change is limited to test data and preserves the original author's contribution.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 19:31:18 +08:00
morluto 490a285070 ci: reuse current-run builds across provider lanes 2026-10-13 19:30:50 +08:00
morluto fa0a9b2d32 ci: observe affected-owner test plans against full results 2026-10-13 19:21:09 +08:00
morluto c8d861f976 ci: scope timing helpers to portable verification 2026-10-13 19:12:26 +08:00
morluto bd2c202dd9 fix(ci): reject incomplete verification plans 2026-10-13 19:06:31 +08:00
morluto 9f3f392eb8 ci: bound core job execution time
Limit classification to five minutes, static/build/docs/report jobs to ten minutes, and each portable shard to thirty minutes. The saved successful-job sample peaked at 392 seconds per shard and 61 seconds for other core jobs, leaving generous setup and coverage headroom. Runner queue waiting is unaffected.
2026-10-13 19:00:47 +08:00
morluto f0f09950f3 ci: finish main validation and balance complete test costs 2026-10-13 18:54:05 +08:00
morluto 3af8f847f0 ci: fix PR metadata triggers and defer expensive runner work 2026-10-13 18:38:46 +08:00
morluto 747e729914 test(release): validate only the selected preflight job
Scope the steps schema to the release job whose preflight is executed, allowing unrelated reusable-workflow jobs such as qualification. Preserve all four valid and invalid metadata execution checks.

Reproduced on main ec3db3745: 35 passed, 4 failed. After the fix: 104 release and CI boundary tests pass; check:fast and format:check pass.
2026-10-13 18:24:40 +08:00
记得吃中饭andGitHub 2995ec6910 fix(ghidra): resolve file offsets in decompile (#2101)
* fix(native): expose loaded image bases in overviews

* test(ghidra): retain resources when verifier cleanup fails

* docs(ghidra): spell out file-offset address mapping

* fix(ci): separate and retry NativeAOT dependency restoration

* fix(test): resolve CI script file URLs on Windows

* fix(native): retain conflict observations and actionable CLI guidance

* fix(fixtures): keep NativeAOT retry logs credential-safe

* fix(fixtures): redact authenticated URLs at diagnostic creation

* fix(ghidra): restrict source-coordinate observations to decompilation

* fix(cli): identify conflicting file-offset selectors accurately

* fix(ghidra): bind file offsets to admitted target sources

* fix(ghidra): preserve resolved file-offset failure context

* test(ghidra): verify ELF address and file-offset round trips

* test(nativeaot): align diagnostic regressions with fixture host support
2026-10-13 18:23:17 +08:00
Yijie LuandGitHub ec3db37457 fix(native): preserve literal line breaks in signing identifiers (#2107)
* fix(native): preserve literal line breaks in signing identifiers

* fix(native): infer codesign framing without a matching path
2026-10-13 18:22:34 +08:00
N0zoM1z0 f1f5582ce8 fix(website): clarify translated evidence and runtime observations 2026-10-13 18:21:09 +08:00
N0zoM1z0 3d78a57043 docs(website): publish model essay in all supported languages 2026-10-13 18:21:09 +08:00
N0zoM1z0 b06751b703 docs(website): simplify reconstruction evidence in model essay
Explain each case through the obstacle, useful hypothesis and verified result. Remove byte totals, relocation counts and trial counts from the main narrative; preserve commit references and the constant-value example that explains the oracle defect. Make the experiment table and compiler terminology easier to follow.

Validation: website.py check, formatting, preserved source links, and eight article layout checks across both preview paths and light/dark themes.
2026-10-13 18:21:09 +08:00
N0zoM1z0 806ff57567 docs(website): use straight quotes in model essay
Normalize the three quoted phrases to straight English quotation marks. Website checks and formatting pass; the local preview serves the revised punctuation.
2026-10-13 18:21:09 +08:00
N0zoM1z0 ca07373e44 docs(website): improve model essay transitions and narrative flow
Connect the reconstruction cases to the questions about specialization, retrieval and feedback-driven search. Replace fragmented sentence sequences with explicit causal links and smooth the progression into oracle design and the proposed comparison.

Validation: website.py check, formatting, preserved source links and evidence figures, and eight desktop/mobile light/dark layout checks at both preview paths.
2026-10-13 18:21:09 +08:00
N0zoM1z0 8583a190c0 fix(website): keep omitted language selectors free of whitespace
Indent the optional language selector with its value so a nonindexable draft does not generate a line containing only spaces. Indexed-page markup is unchanged.

Validation: website.py check passes all 12 maintenance regressions; synchronization changes only the draft header blank line.
2026-10-13 18:21:09 +08:00
N0zoM1z0 fe496bd1cf docs(website): add essay on reverse engineering models
Add an English review draft connecting Touhou reconstruction evidence with model specialization, repository knowledge and feedback-driven learning. Preserve model attribution and historical verification scope in supporting notes, and translate the new Blog listing for all existing editions.

Validation: website.py check, focused formatting checks, and 32 Chromium cases covering both preview paths, light/dark themes, responsive layouts, translated indexes and no-JavaScript reading.
2026-10-13 18:21:09 +08:00
Yijie LuandGitHub f8e95799f9 fix(artifacts): preserve NIB field and class identities (#2106) 2026-10-13 18:20:54 +08:00
HarlyandGitHub 6c816af119 fix(managed): preserve negative zero in CIL operands (#2437) 2026-10-13 18:19:44 +08:00
morluto ab02f3c8d6 ci: reduce reporter fan-out and preserve shard history 2026-10-13 18:16:10 +08:00
ChaoqiandGitHub 9928f357fb fix(artifacts): read ASCII control bytes in legacy ZIP names as ASCII (#2365)
* fix(artifacts): read ASCII control bytes in legacy ZIP names as ASCII

ZIP names without the UTF-8 flag were decoded with zip.js's CP437 table,
which maps bytes 0x01-0x1F and 0x7F to IBM PC display glyphs. A folder
zipped on macOS with a custom icon therefore listed and extracted
`Folder/Icon\r` as `Folder/Icon♪`, a name that is not in the archive and
differs from REA's directory inventory of the same folder. Tabs became `○`
and DEL became `⌂`.

Decode legacy names in REA's existing decodeText hook with the Unicode
CP437 mapping that Python's zipfile also uses: bytes below 0x80 as ASCII,
and the upper half with the same table zip.js uses.

Windows file names cannot contain U+0001-U+001F, so a Windows extraction
destination now refuses such a path with a typed path failure before
creating anything, as it already does for ':'.

* test(artifacts): check extracted legacy ZIP control names on disk

The control-name cases checked only the logical path that inventory
reports. Extraction writes through SafeOutputTree, so a change that
renamed the extracted files would still pass those cases.

Extract `Icon\r` and `é\t\u007F.a`, then compare the reported
relative_path, the directory listing, and the file contents. The new
cases skip on Windows, which refuses these names.
safeOutputTree.test.ts covers that refusal.
2026-10-13 18:05:20 +08:00
morluto d13c9f840d ci: reduce redundant compatibility and qualification work
Select SQLite compatibility by explicit source, fixture, lifecycle, and script ownership while retaining every version in full qualification. Share the canonical scope inventory between planning and the required gate.

Reuse recent exact-SHA full qualification only for hourly schedules, with trusted successful-run metadata and receipts. Force a daily refresh, reject stale original runs even after retries, and never renew proof from reused validation.

Report attempt-specific queue, execution, and matched step timing in a separate completion workflow using trusted default-branch code and read-only API access. Preserve raw metadata and unknown timings without delaying validation or publication.

Validation: 177 focused tests; typecheck/lint; knip; actionlint 1.7.7; all 1046 tests discovered; live 37-job API report smoke. Remote workflow scheduling and real-provider matrices remain CI verification.
2026-10-13 17:46:04 +08:00
0cf224e762 fix(javascript): retain distinct chunk registration callback metadata (#2737)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-13 17:42:41 +08:00
52d4ce6ced fix(apple): derive dyld environment findings from each traced slice (#2733)
deriveFindings looked up each traced root's slice again by architecture
name. A universal executable with arm64e and arm64e.x1 slices reports
both as "arm64e", so both roots resolved to the first slice: a later
slice's LC_DYLD_ENVIRONMENT produced no finding, or the first slice's
command was reported twice. The per-slice dyld_environment arrays were
already correct.

traceDylibLoading already has the exact slice when it records a root, so
pass it to deriveFindings instead of re-deriving it from the name. The
public roots and findings shapes are unchanged.

Fixes #2661

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:42:09 +08:00
morluto e05d3399f1 test(javascript): expect unknown parameter provenance 2026-10-13 17:23:36 +08:00
morluto db3c9f084b fix(ci): keep cancellation ownership in the calling workflow
Remove redundant ref-based concurrency from reusable provider and website workflows. Their old groups could cancel qualification from another CI tier even though the callers used separate groups. Disposable hosted jobs now inherit caller scheduling; direct manual dispatches remain independent.

Validate every called CI workflow has no independent cancellation group. Validation: 100 focused scope/gate tests and actionlint 1.7.7.
2026-10-13 17:15:42 +08:00
morluto 0c7f4dfbd9 ci: separate fast feedback from full qualification
Scope main pushes from a successful ancestor so canceled runs cannot hide changes. Keep the complete portable suite in fast feedback and move coverage plus Intel macOS to hourly, manual, explicit-full, and release qualification. Let qualification finish independently of superseded feedback.

Bind publication to full qualification at its exact revision and publish the verified tarball after checking workflow identity, version, and SHA-256. Keep trusted cache writers and reject incomplete merged test inventories.

Validation: 151 focused CI tests; typecheck/lint; actionlint 1.7.7; knip; all 1044 files discovered; bounded blob/merge smoke; macOS package verifier with retention and cleanup; npm pack dry run. Native Linux/ARM/Windows and remote workflow execution remain CI verification.
2026-10-13 17:09:53 +08:00
morluto c02fb0ea80 fix(native): rank Mach-O slices by parsed CPU subtype 2026-10-13 16:43:12 +08:00
96fc84f1fb fix(native): select one slice when universal Mach-O slices share a CPU type (#2040)
* fix(native): select one slice when universal Mach-O slices share a CPU type

On macOS 27 most system binaries are universal files whose slices share
CPU_TYPE_ARM64: arm64e + arm64e.x1 (Calculator, TextEdit), x86_64 + arm64e +
arm64e.x1 (/bin/ls), or arm64 + arm64e.x1 (ShortcutDroplet). Target admission
names slices by CPU family, so these targets have availableArchitectures
["arm64"] while the file holds two arm64 slices.

inspect_macho read that family count as a slice count and ran otool, nm,
dyld_info, dwarfdump and vtool without -arch. ShortcutDroplet failed with
"Malformed otool load-command output" (40 load commands against ncmds 20).
Calculator reported both slices' imports (4138 instead of 2069), nm's
"(for architecture ...)" header lines as exports, and no selected slice.
selectArchitecture now also filters when lipo lists more than one slice. Its
preference is unchanged: the family name, then arm64e, then the first
arm64e.<variant>.

inspect_native_dispatch_metadata matched FAT slices by CPU type only and
refused /bin/ls and TextEdit as "Ambiguous FAT architecture slice".
selectMachoSlice now ranks slices of the requested CPU type by base subtype in
the same order: arm64 (0, then 1) or x86_64 (3), then arm64e (2), then other
variants such as arm64e.x1 in FAT table order. It reports ambiguity only when
the top-ranked slices share a subtype.

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

* fix(native): share Mach-O slice preference across inspections

* chore: preserve upstream process capture test during merge

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 16:36:23 +08:00
b69d20a300 fix(captures): name the violated HAR schema constraint (#2708)
HAR validation reported only the AJV keyword, so a capture missing
`log.version` failed with "HAR schema validation failed: required." and
the caller could not tell which property to supply. Include AJV's
constraint message, which names the missing property, expected type, or
pattern without restating captured values.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:22:24 +08:00
AvocadoandGitHub 11f8b76257 fix(apktool): accept --no-strings CLI option (#2707) 2026-10-13 16:20:03 +08:00
morluto 11f1d952e7 ci: raise duplication threshold to 3.5 percent 2026-10-13 16:18:53 +08:00
morluto a0317cf732 fix(ci): align FAT fixtures and consolidate duplicated workflow helpers
Declare the synthetic 4 KiB Mach-O slice alignment accurately while preserving the IDA thinning scenario. Share web cleanup aggregation and process-trace fixture facts instead of duplicating them across consumers and tests; keep the 3% duplication threshold unchanged.

Validation: npm run check:ci, test discovery, and 42 focused IDA, process-trace, web workflow and cleanup boundary tests. Duplication falls from 3.01% to 2.99%.
2026-10-13 16:16:31 +08:00
morluto 67c7a24e60 Merge remote-tracking branch 'origin/main' 2026-10-13 16:08:58 +08:00
morluto c7a890d129 fix(javascript): preserve unknown module-alias provenance
Replace the implicit local-provenance fallback with explicit locally constructed values. Keep opaque results, parameters and caught values unknown; follow assignment and sequence results while retaining exact module aliases. Clarify that alias provenance does not represent transitive data taint.

Validation: 26 focused provenance/module-link tests, 135 semantic and graph-builder tests, six application module-relationship boundary tests, and the fast typecheck/lint gate.
2026-10-13 16:08:41 +08:00
morluto b955b825fc fix(artifacts): retain read cleanup ownership through CLI and MCP shutdown
Keep failed stable-read handles and WABT runtime roots under retryable resource scopes. Preserve cancellation causes and primary errors, close the same service instances bound to MCP tools, and consolidate CLI operation/cleanup projection.

Validation: fast typecheck and lint, cached runtime build, docs checks, SDK schema validation, focused service and real-file cleanup tests, CLI cleanup regressions, and MCP lifecycle/shutdown workflows. Provider process fixtures simulate engine output; real WABT and Linux EVM execution are not established by this pass.
2026-10-13 16:08:41 +08:00
9cf49c7a78 fix(snapshot): classify non-object roots as invalid content (#2685)
Co-authored-by: jiangjiang248 <237556902+jiangjiang248@users.noreply.github.com>
2026-10-13 16:04:44 +08:00
morluto c923fb0dc9 fix(javascript): retain compiler-dependent module resolution facts
Compare import and require outcomes for static TypeScript references when compiler options are unknown. Preserve branch facts through worker IR and both graph projections; retain declaration fallbacks as candidates without changing explicitly selected paths.

Validation: 210 focused resolver and semantic tests; 21 production-boundary and source-extension tests. A bounded 2,000-reference probe adds about 16–17 ms wall time for dual resolution; full task token costs and real compiled application behavior remain unmeasured.
2026-10-13 15:58:25 +08:00
morluto ded199abcd Merge remote-tracking branch 'origin/main' 2026-10-13 15:25:24 +08:00
morluto ed881c6d2e fix(progress): measure delivery intervals with a monotonic clock 2026-10-13 15:23:56 +08:00
morluto eaa9616585 fix(javascript): preserve cancellation through recovery publication cleanup 2026-10-13 15:19:18 +08:00
bf34d0d2fc perf(native): add opt-in Mach-O summary delivery and retained pages (#2634)
* perf(native): add opt-in Mach-O summary delivery and retained pages

inspect_macho returns every import and export inline. For VS Code's
Electron Framework one MCP call carries 2.5 MB of text (about 828k o200k
tokens), and the same again as structuredContent; the MCP response
budget admits it. Agents asking for slice identity, UUID or dependency
counts paid that cost on every first call.

Add the established opt-in detail selection: omitted or complete keeps
the existing complete result and Evidence identity, while summary
retains the complete Evidence and returns slice identity with
per-collection returned/total/exhaustive counts (956 tokens).
inspect_analysis_view kind macho pages the original rows of any
collection or the command provenance, repeating the producer's own
total and exhaustive flag so page exhaustion is never read as absence.

Delivery detail never reaches the native tools or the source Evidence
ID: registerEvidenceTools gains a parametersForEvidence hook, so the
summary's parent is the same Evidence as a complete call.

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

* fix(mcp): refuse unretainable summary delivery before analysis

A server without Evidence retention (a fixed analysis source) ran the
complete inspect_macho or inspect_artifact analysis for detail summary
and only then refused it in summarizeRetainedAnalysis, paying the very
cost summary delivery exists to avoid. registerEvidenceTools gains a
preflight hook; both registrations check retention there, as the WASM,
EVM, managed, pcap and capture handlers already do before dispatch.

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

* fix(native): keep whole rows in Mach-O architecture and segment pages

The page item union listed plain-object row arrays, and zod returns the
first union member that parses while dropping unknown keys. Entrypoint
rows declare only file_offset, so every architecture and segment row
(which also carry a numeric file_offset) matched it and lost its name,
CPU fields, permissions and sections. Rows now use exact key sets, and
imports/exports share one member. The MCP test pages every facet of a
real /bin/ls and compares it with the unparsed complete rows.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:13:12 -07:00
morluto e802441bce Merge remote-tracking branch 'origin/main' 2026-10-13 15:12:34 +08:00
morluto e7882b34f4 fix(artifacts): honor cancellation at the output publication boundary 2026-10-13 15:12:17 +08:00
morluto 9049dbaf0c fix(process): preserve monotonic capture timing and observed wall dates 2026-10-13 15:12:08 +08:00
morluto 7fc0b5ec9c fix(mcp): join owned doctor shutdown without signaling saved PIDs 2026-10-13 15:12:08 +08:00
6a05248d46 perf(frida): evict captured messages without copying the whole queue (#2628)
Once a Frida session reached its 16 MiB capture budget, every new message
evicted the oldest one with Array#shift, re-serialized it to recover its
byte charge, and searched and spliced its per-script array. Each of
those is linear in the retained messages, so with about 188k small
messages retained the cost per message rose from 0.14 µs to 32 µs and
capped the main thread at roughly 31k messages per second, which a
hooked hot function can exceed.

Keep both queues as head-offset FIFOs that compact once half their slots
are evicted, and record each message's charge when it is admitted.
Arrival order already makes the globally oldest message the oldest one
of its script, so per-script eviction becomes a head check. Eviction is
now amortized O(1) at about 0.14 µs per message; retained messages,
their order and the truncation flag are unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:11:38 -07:00
20f51d7286 fix(native): name the ownership helper prerequisite in command failures (#2506)
* fix(native): name the ownership helper prerequisite in command failures

Every supervised native command launch needs the Darwin process-ownership
helper. When that helper cannot be compiled (relative REA_DARWIN_SWIFTC_PATH,
missing SDKROOT, a missing compiler, or an SDK/compiler mismatch), the
runner reduced the typed DarwinProcessOwnershipInspectionError to a bare
`io` failure. inspect_macho then reported `execution_failure` with no hint,
and inspect_artifact on any universal Mach-O said only "lipo could not
enumerate universal Mach-O slices".

The runner now keeps the inspection message as the failure's `detail` at
the launch boundary and carries it through xcrun resolution. The native
provider adds it to its diagnostics and the slice reader appends it to its
message, matching what the SQLite and capture providers already report.

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

* test(native): cover the provider's ownership helper diagnostics

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:10:58 -07:00
morluto 6bd2b28161 fix(browser): keep elapsed capture timing independent of wall-clock changes 2026-10-13 15:02:54 +08:00
morluto d9e9fb2273 fix(process): use monotonic time for provider execution budgets 2026-10-13 15:01:59 +08:00
morluto b953f96c0a fix(ghidra): share Java properties semantics with PSP extension admission 2026-10-13 15:00:14 +08:00
OlorinandGitHub 4276aaf18b feat(signals): discover installed decoders and option help (#2487)
* feat(signals): discover installed decoders and selected option help

* fix(signals): bound retained discovery data and preserve resource failures

* fix(signals): complete CLI help and portable decoder patterns

* test(signals): verify case-insensitive decoder help queries

* fix(signals): retain discovery operation across cleanup retries
2026-10-13 15:58:19 +09:00
morluto fe8d0bdd36 fix(android): reject malformed UTF-8 at the JADX protocol boundary 2026-10-13 14:56:04 +08:00
9c34c091b3 feat(captures): discover installed protocol and field definitions (#2496)
* feat(captures): discover installed protocol and field definitions

* test(captures): isolate the direct glossary oracle profile

* test(captures): accept padded kernel limit reports

* fix(captures): detach every retained glossary column

* fix(captures): describe every field discovery CLI option

* fix(captures): retain discovery operation across cleanup retries

---------

Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-13 15:55:25 +09:00
Rudy Mizrahi CelekliandGitHub b9ee921560 fix(process): preserve nested path identities during normalization (#2468) 2026-10-13 15:54:18 +09:00
15463bdbe6 fix(adb): count requested permissions that the device marks restricted (#2359)
Android 10 and 11 print restricted entries of the requested-permissions
list as "NAME: restricted=true". The parser took the first such line as
the end of the list, so the count stopped there while coverage still
read complete.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:52:48 +08:00
morluto 55e21a2eec Merge remote-tracking branch 'origin/main' 2026-10-13 14:51:59 +08:00
GamerDubzandClaude Opus 5.5 2fb79a99c8 fix(release): keep the launched package name when comparing plugin manifests
The version-only comparison blanked the whole launch argument, so a release
that swapped rea-agents for another package passed the notes check as a
version bump. Mask only the version after the last "@" and test a changed
package name.

The plugin readiness command in the installation guide now uses the pinned
release version, so its report describes the server the plugin starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:51:56 +02:00
morluto f6775a3669 fix(setup): preserve current recovery snapshots and decoding failures 2026-10-13 14:51:55 +08:00
Rudy Mizrahi CelekliandGitHub a07ceaec36 fix(config): reject NUL bytes in Hopper loader arguments (#2474) 2026-10-13 14:51:29 +08:00
Rudy Mizrahi CelekliandGitHub 6a04f922e1 fix(javascript): parse explicit CommonJS wrapper syntax (#2475) 2026-10-13 14:51:23 +08:00
Rudy Mizrahi CelekliandGitHub e74e4e951b fix(screenshot): reject invalid PNG header dimensions (#2472) 2026-10-13 15:50:44 +09:00
68ecf3df8c fix(browser): exclude sensitive binary lengths from historical captures (#2477)
Co-authored-by: chAngE <172472630+chAng-L19@users.noreply.github.com>
2026-10-13 14:49:47 +08:00
ab62ee9dcf fix(windows): preserve caller affinity only within one processor group (#2202)
* fix(windows): preserve caller affinity only within one processor group

`spawnProcess` read the caller's affinity mask and applied it to the
suspended child unconditionally. A mask names processors relative to one
processor group, and Windows assigns each new process to a group of its
own choosing, so on hosts with more than 64 logical processors the child
is often placed in another group. `SetProcessAffinityMask` then rejects
the mask with ERROR_INVALID_PARAMETER (87) and the spawn fails, making
every owned provider process start intermittently on such hosts.

Affinity is a nicety, not an authority or isolation control, so it is now
applied only when the caller and the suspended child both occupy the same
single processor group. A child placed in another group keeps that group's
default affinity and the spawn proceeds. Job Object membership,
kill-on-owner-close policy, owner normalization and priority preservation
are unchanged, and the mask failure remains fatal within a shared group,
where it is not expected.

Single-group hosts, including every host with 64 or fewer logical
processors, are unaffected: `GetProcessGroupAffinity` reports one group
for both processes and the same mask is applied as before.

The optional process-boundary fixture gains an `affinity` observation,
and the native verification lane asserts that an owned spawn succeeds and
that the child's mask matches the caller's whenever both share a single
group.

Fixes #2181

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

* fix(windows): refuse to widen a pinned caller's affinity across groups

Addresses review feedback on the first commit.

An unconstrained caller loses nothing when Windows places an owned child
in another processor group, so that spawn proceeds with the group's
default. A caller deliberately pinned to fewer processors than its group
holds is a different case: silently accepting the default would hand the
provider tree the whole group, which `docs/installation.md` tells
operators to rely on affinity to prevent. Such a spawn now fails naming
the constraint instead. `GetProcessAffinityMask` already reports the
group's active set, which distinguishes the two without another query.

Selecting the caller's group at creation through
PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY was tried first and rejected:
`CreateProcessW` fails with ERROR_INVALID_PARAMETER on a host with a
single processor group, both alongside PROC_THREAD_ATTRIBUTE_JOB_LIST and
on its own, so it cannot carry the ordinary path.

The boundary fixture's affinity observation now reports a process id's
own mask rather than its own, because a separate probe process is
assigned its own group and cannot stand in for the process that spawns
the child. Masks and the group's active set cross as fixed-width
hexadecimal strings: a mask can set bits above the safe-integer range,
where `JSON.parse` would round two different masks to one value.

The verification lane asserts the mask is inherited exactly when caller
and child share a group, and otherwise that the caller was unconstrained.

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

* docs(windows): record what owned-process affinity does not preserve

A mask covering its group's whole active set does not distinguish a
caller the system placed in that group from one that selected the group
deliberately, so an owned child can still be placed elsewhere with its
processor count bounded and its group placement lost. Treating that case
as constrained instead is not available: on a multi-group host an
ordinary process is assigned a group by the system and its mask is that
group's active set, so refusing it would fail every owned spawn there.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 14:49:34 +08:00
2c6a4fac4f fix(process): accept a trace identifier the comparison result can represent (#2488)
The trace specification owns the declared identifier vocabulary and the
comparison result echoes those identifiers verbatim into raw_trace[].event_id,
matched_variant and diagnostic event_ids. The result module restated the shared
IDENTIFIER_PATTERN by hand with a 64-character cap, so a specification with a
longer identifier validated and then its own comparison result failed to parse:
compareProcessTraces threw a raw ZodError at the caller instead of returning a
comparison.

Export the identifier schema from the specification as the single owner and
reuse it in the evaluation module, removing the capped copy. IDENTIFIER_PATTERN
has no length bound and every other consumer uses it unbounded, so the
duplicate was the outlier. Removing the bound rather than adding one keeps the
advertised trace_spec input contract unchanged; identifiers of 64 characters or
fewer behave exactly as before.

Verified with 48 process tests, 24 boundary CLI/MCP trace tests, check:fast and
verify:test-discovery on macOS arm64. The regression test failed with the
ZodError before the change and passes after it.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 14:48:26 +08:00
Mykola DementievandGitHub c6acd19b6d fix(ida): stop the private input of a universal Mach-O at the end of the selected slice (#2291)
* test(ida): expect the private input of a universal Mach-O to end at the selected slice

Cover a universal file larger than one read chunk with another slice after the selected one, for both a middle slice and the first slice.

* fix(ida): stop the private input of a universal Mach-O at the end of the selected slice

A read chunk that starts after the slice produced a negative end offset, and Buffer.subarray counts a negative bound from the end, so the head of the following slice was written to the private input and its digest.
2026-10-13 14:48:17 +08:00
1a1b67c888 fix(javascript): preserve POSIX runtime path case on Windows (#2422)
Use POSIX path semantics for recorded POSIX roots when reconciling runtime Evidence on Windows. Preserve Windows mapping behavior and cover implicit and explicit file roots through the reconciliation service.

Assisted-by: OpenAI GPT-6 <noreply@openai.com>

Co-authored-by: Aurantium-47 <280805072+Aurantium-47@users.noreply.github.com>
2026-10-13 14:46:28 +08:00
Rohit GhumareandGitHub 12303812a8 fix(evidence): admit bundle imports across session transitions (#2303) 2026-10-13 14:45:03 +08:00
f6556ad2bc fix(pcap): order evidence ties by code point instead of host locale (#2509)
The pcap summary sorted equal-count endpoints and protocol stacks with
String#localeCompare, and Flutter build inspection sorted APK ABI
directory names the same way. localeCompare follows the host's ICU
locale, so the same capture produced differently ordered Evidence on
different hosts: under da_DK "aarp" sorts after "zz", under lt_LT
"syslog" sorts before "ssdp", and every locale puts "fe80::1" before
"fe80:1::1". Use the repository's compareUnicodeCodePoints, as the
other Evidence producers already do.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:44:09 +08:00
morluto 5f0f25ad61 Merge remote-tracking branch 'origin/main' 2026-10-13 14:43:23 +08:00
d73ed6fce2 fix(domain): preserve retryable capability failures through redaction (#2494)
redactExplicitFailure rebuilds each typed failure field by field so explicit
literals are removed while the projected code stays the same. The rebuild for
AnalysisCapabilityUnavailableError carried providerId, operation, reason and
userMessage but not retryable, and the constructor defaults that field to false.
Redacting a transient capability failure therefore made it permanent and changed
the remediation shown to the caller.

Pass the field through with the same conditional-spread shape used for
userMessage. The constructor recomputes retryable from cleanupIncomplete, and
cleanup is already part of the reconstructed context, so a value that was true
before redaction stays true and a false value stays absent.

Verified with 66 error projection, access-denied, redaction and capture-failure
tests, check:fast and verify:test-discovery on macOS arm64. The regression test
failed with "expected false to be true" before the change.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 14:43:10 +08:00
Vinícius AndradeandGitHub 60f771a11e fix(apple): limit already-loaded reuse to rpath dependencies (#2292)
* fix(apple): limit already-loaded reuse to rpath dependencies

dyld reuses an already-loaded image by install name only for @rpath/
dependencies (Loader.cpp:802-816) and evaluates other dependency forms
against their expanded path for each loader. When multiple images in
different directories depend on @loader_path/ libraries sharing an
install name, checking loaded images before candidate expansion caused
subsequent loaders to bind to the first loaded image's copy or appear
resolved when their own copy was absent.

Limit byInstallName registration and lookup to install names that start
with @rpath/, leaving caller-relative and literal dependencies to expand
and evaluate their own candidate paths.

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(apple): verify loader_path dependencies across isolated directories

Assert that multiple images depending on @loader_path/ libraries with
the same install name do not reuse already-loaded images across loaders.
When the second loader's helper is absent, its candidate path is absent
and dyld reports required-load-unresolved; when both helpers exist, each
loader resolves to its own directory.

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

---------

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-13 14:40:43 +08:00
The one -NiuandGitHub 650237efb0 fix(setup): pin Hopper's macOS publisher identity before install (#2481)
The macOS installer accepted any bundle that passed `codesign --verify`
and whose `Info.plist` said `com.cryptic-apps.hopper-web-4`. An ad-hoc
signed bundle with that identifier satisfies both, so a substituted DMG
could be copied to ~/Applications and launched.

Verify a pinned code requirement instead, covering the sealed bundle
identifier and the signing team together, and report a distinct
`publisher_verification_failed` code so a vendor key change is not
confused with a corrupt package.
2026-10-13 14:40:04 +08:00
morluto 26bffb94bf perf(evidence): reuse authenticated comparisons through aggregation 2026-10-13 14:39:49 +08:00
7017c4d68c fix(artifacts): read every ZIP package family extraction is asked to open (#2493)
extract_artifact carried its own copy of the ZIP package format list with five
of the eight members the domain owner declares, and used it both as the guard
that refuses a format without an extraction reader and as the selector for the
reader itself. A .hap, .hsp or App Pack target was therefore refused with
"Artifact format has no extraction reader" even though the next branch builds a
ZipArtifactReader for exactly those formats, while inventory_artifact classifies
the same path through the owner and reads it.

Import isZipPackageFormat from the domain owner and delete the duplicate list
and its predicate, removing the second representation instead of reconciling it.
The reader branch now narrows to ZipPackageFormat, which is what the
ZipArtifactReader constructor already requires.

Verified with 41 boundary extraction, ZIP integrity and detection tests,
check:fast and verify:test-discovery on macOS arm64. Reverting only the source
change fails exactly the three added families and leaves the zip control green.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 14:39:29 +08:00
141d2095f5 test(hopper): repair Linux demo setup and native verification (#2638)
* fix(verification): align Linux Hopper setup lane with current contracts

* fix(verification): retain orphan checks on clean Linux runners

* fix(verification): detect Hopper through alternate launcher paths

* fix(verification): report identified Hopper leaks before proc errors

* fix(verification): distinguish Hopper string recognition from byte fidelity

* fix(verification): report full literal decoding coverage explicitly

---------

Co-authored-by: Luis Taffelli <86675342+LuisTaffelli@users.noreply.github.com>
2026-10-13 14:39:18 +08:00
morluto 1b953021ea fix(javascript): centralize module syntax and separate erased type dependencies 2026-10-13 14:39:06 +08:00
morluto e1cd292aa6 fix(adb): admit canonical device states and preserve malformed output 2026-10-13 14:38:39 +08:00
YsopwnableandGitHub 18831491cf fix(ci): ignore generated website translation staging (#2511)
Ignore generator-owned website translation staging output while preserving authored lint errors and complete diagnostics.

Verified with a real checker regression, a production translation build, complete coverage, developer/CI regression checks and native lanes.

Fixes #2508
2026-10-13 14:32:22 +08:00
morluto 18d56f5321 test(ghidra): restore complete installation fixtures across CI boundaries
Declare application.java.min=21 at the four synthetic installation producers so profile, PSP, lifecycle and MCP workflows exercise their intended paths under fail-closed Java admission. Keep missing-bound refusal coverage intact and format the two Ghidra tests rejected by the CI static lane.

Validation: all 82 tests across the 11 failing CI files passed; 66 Ghidra admission, doctor and snapshot tests passed, including missing Java bounds. No production admission policy changed. Full suite and real Ghidra lanes were not run.
2026-10-13 14:05:32 +08:00
YsopwnableandGitHub 91886982fc docs: celebrate 170,000 GitHub stars 🎉 (#2462) 2026-10-13 13:58:56 +08:00
morluto 3ae826ceba fix(javascript): share source-map resolution and preserve path uncertainty 2026-10-13 13:48:24 +08:00
morluto 73dd8d6ed5 Merge remote-tracking branch 'origin/main' 2026-10-13 13:40:50 +08:00
morluto ec0b081371 fix(filesystem): enforce symlink and decoded-text admission 2026-10-13 13:39:57 +08:00
morluto 2187e16f14 fix(ghidra): derive installation admission from Java property semantics 2026-10-13 13:39:48 +08:00
bb96afdbe3 fix(pcap): name the host tool to configure in capability refusals (#2507)
* fix(pcap): name the host tool to configure in capability refusals

Missing or misconfigured tshark/capinfos, the unsupported macOS capture
boundary and an unusable Linux prlimit were projected with the generic
"unavailable for the current target; choose another target" advice, even
for inspect_pcap_client, which has no target. Route tool refusals through
one pcapToolUnavailable owner that names REA_TSHARK_COMMAND and
REA_CAPINFOS_COMMAND, give host-boundary refusals their own reason, and
keep the private workspace refusal's retryable flag like WABT and source
maps do.

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

* test(pcap): cover the Linux prlimit refusal guidance

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:29:09 +08:00
1991542e59 test(pcap): run the missing-path refusal only where captures are admitted (#2510)
On hosts without an enforceable native bound (macOS), the provider refuses
inspect_pcap_capture with capability_unavailable before it touches the
path, which the unsupported-boundary test in the same file pins. The
missing-path test expected invalid_request on every host, so it always
failed on macOS while Linux and Windows CI stayed green.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:26:48 +08:00
morluto 1f7f808c4f fix(reconstruction): resolve latest verifier observations without input-order bias
Group observations by exact normalized instant, retaining arbitrary fractional precision and offset equivalence. Coalesce equivalent assessments and all supporting Evidence; report conflicting latest assessments as unknown with verifier-result-ambiguous. Use the same instant comparison for freshness and future checks.

Replace first-input-wins coverage with permutation, provenance, offset, sub-millisecond ordering, and future-clock regressions, including the public MCP workflow. Validation: focused reconstruction/offset tests and MCP workflow passed; converted JSON Schema validation, typecheck/lint, and docs checks passed. Full suite and real-provider lanes were not run.
2026-10-13 13:23:38 +08:00
morluto 3c57e1a8b2 fix(artifacts): authenticate file subjects across provider and view boundaries
Narrow WASM and EVM execution ports to file subjects and preserve supplied subject types at the shared execution factory. Authenticate format with path and digest before recording or projecting Evidence, using the existing WASM identity owner in both consumers.

Validation: focused WASM/EVM service and public MCP workflows passed, including malformed provider subjects and self-contained retained Evidence with contradictory format. Global typecheck/lint and documentation checks passed. Real WABT/Evmole execution and the full suite were not run.
2026-10-13 13:23:03 +08:00
morluto 63e796d50e fix(process): bound retained trace ordering state
Prove total orders with Kahn traversal and use ranks; stream forward and reverse reachability for general partial orders instead of retaining every closure. Aggregate unordered conflicts by declaration while preserving missing-pair counts and independent malformed-input diagnostics.

Validation: 33 focused trace domain and CLI/MCP boundary tests passed. Six bounded Flameox executions preserved answer digests and cycle rejection; combined median peak RSS fell from 391.13 to 127.72 MiB. General partial-order traversal costs more CPU; the experiment records that tradeoff. Full suite and real-provider lanes were not run.
2026-10-13 13:17:44 +08:00
morluto 16f5ded446 test(cli): consolidate development runner workflow coverage 2026-10-13 13:12:17 +08:00
morluto 1447fd2f99 fix(evaluation): share completed transcript facts across graders 2026-10-13 13:12:12 +08:00
morluto 7a2cd1ea0a fix(domain): preserve derived byte counts and uncertain identity 2026-10-13 13:12:06 +08:00
morluto 8b181716ac fix(install): honor redirected setup diagnostics
Require terminal stderr before launching the automatic setup wizard, matching the CLI's interactive admission rule. Print the manual setup command when diagnostics are redirected.

Exercise a real PTY with stderr redirected to a fixture file and verify the installed command is not launched for setup. Installer lane: 33 tests passed; targeted regression, typecheck/lint and npm pack dry-run passed. Full package verifier was not run under the requested focused-check resource policy.
2026-10-13 13:00:39 +08:00
morluto 10935b7b11 fix(process): reject deep trace cycles without recursive expansion
Use iterative cycle detection, memoized reachability and compact unordered-group membership. Preserve declaration diagnostics on cyclic input while skipping checks requiring a valid partial order. Report duplicate group declarations once instead of repeating pair errors.

Replace redundant source cycle/missing-pair assertions with public CLI/MCP coverage including 6000 events, dense not-before constraints and a large unordered group. Validate acyclic branching and mixed relation semantics. Document bounded Flameox CPU/latency gains and higher observed peak memory; agent/model costs remain unmeasured.
2026-10-13 13:00:39 +08:00
morluto acf75e6700 fix(evidence): reject unsupported metadata before authentication
Close persisted Evidence, bundle, profile and unknown record shapes so parsing cannot silently remove caller metadata before checking semantic identity. Keep constructor provider projection explicit and JSON payload fields open.

Exercise actual MCP export/import with independent record and manifest mutations, unchanged state after rejection, and a valid round trip. Prune duplicate source assertions. Validation: focused domain/CLI/MCP/filesystem checks, converted JSON Schemas, docs and typecheck/lint.
2026-10-13 13:00:39 +08:00
YsopwnableandGitHub c105391db5 fix(hopper): stop Linux demo children when the REA owner exits (#2499)
Bind the owned demo helper to REA's private stdin stream and unwind child
cleanup on EOF during startup or analysis. Verify graceful and SIGKILL owner
exit through real CLI/MCP workflows and preserve unrelated processes.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-13 12:59:22 +08:00
morluto d867136cec chore: sync main after artifact ownership audit 2026-10-13 12:56:07 +08:00
morluto a73a55f53c fix(artifacts): preserve decode coverage and cleanup ownership 2026-10-13 12:54:22 +08:00
9581e067ad feat(pcap): add bounded capture summaries through CLI and MCP
Summarize saved Wireshark4.x captures with enforced native resource limits, serialized admission, stable snapshot verification and retained fact/text pages. Preserve complete inventories within resource budgets and source uncertainty; report cleanup failure with partial Evidence and retained ownership.

Verified six recorded corpora /13026 packets with independent Scapy/Python oracles, real CLI/MCP/schema parity,60 SNI names,132 DNS addresses and1000 URIs. Real TShark CI now exercises summary/paging. Actual Windows/Bun Wireshark remains unverified; macOS capture requires a separate enforceable-bound decision.

Co-authored-by: 3QL <85309073+3QL@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-13 04:53:03 +00:00
morluto c74142e6bd chore: sync main for ownership audit fixes 2026-10-13 12:46:42 +08:00
morluto 042dcaa1ee perf(evidence): reuse authenticated unknown mutation snapshots 2026-10-13 12:46:06 +08:00
morluto b045df100e fix(update): share parsed client activation with maintenance 2026-10-13 12:46:05 +08:00
YsopwnableandGitHub fa4b828865 fix(cli): normalize empty Windows profile before imports (#2482)
Follow-up to #2345; thanks to @gefrezim for the import-time home lookup diagnosis.

Normalize only an empty Windows USERPROFILE before runtime dependency imports, preserving all nonempty caller selections. Reuse the existing public setup dry-run regression in the Windows curated CI lane. No additional filesystem query, engine call, cache or retry.

Verified real Windows Node 24.18.0 CLI unset/empty/selected-profile controls, setup dry-run home selection and unchanged configuration, and baseline/candidate SDK stdio MCP catalogue parity. Nine related tests and static/docs checks passed. No IDA or Ghidra engine was used. Scoped resource observations and measurement limits are recorded in experiments/analysis-efficiency/windows-empty-profile.md.

Fixed the review finding and original CI package-scope ownership failure. Current-head CI is green; the unrelated MCP identity timeout passed on a failed-job rerun without changing its code or timeout. Current-head review completed with no further findings.
2026-10-13 12:43:52 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
45984a4319 test(perf): verify existing SQLite column selection on QGIS data (#2489)
Verify existing SQLite column selection on pinned QGIS application data. Preserve complete requested schema and exact typed sample facts, all failed/repeated trials and mixed timing results; add reproducible corpus/question/grader and an isolated per-tool driver approval entry.

Existing controls reduce model input in both corrected pairs; no production/API/default/cache change is justified. Real CLI/MCP parity, independent SQLite binding, current-main SDK comparison and relevant checks pass. Ref #2016.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 12:38:12 +08:00
morluto bb540826d6 fix(artifacts): retry transient DMG verification refusals 2026-10-13 12:18:39 +08:00
2c7240cf9d perf(native): run independent Mach-O slice commands concurrently (#2466)
inspect_macho launched otool, nm, two dyld_info reads, dwarfdump and
vtool one at a time. Each launch also resolves the tool through
`xcrun --find`, so a warm call spent about 350 ms in eight serial
supervised launches. The slice commands only read the selected
artifact, so launch them together. Every launch still settles before
the result, and declared order still fixes provenance and which
failure is reported.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:12:05 -07:00
4303dcdcf6 fix(native): name the retained pwntools process group in cleanup resources (#2281)
When a pwntools or pwndbg stage cannot confirm that its owned process
group stopped, the cleanup_incomplete result listed only the run ID and
runtime directory, although a descendant can still be live. Add the
retained supervisor's pid and process-group coordinates, as
OwnedCommand reports for a non-transferred cleanup failure.

Fixes #2274

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:11:53 -07:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
a86deecac5 fix: align MCP archive contracts and documentation (#2478)
Reuse the domain ZIP format list for session schemas, verify full Harmony
CLI/MCP lifecycles, and correct native view guidance and audited docs drift.
Refresh locked dependency notices and gate inventory/provenance consistency,
including notice inputs in the cached documentation gate.

Fixes #2454

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 12:06:41 +08:00
YsopwnableandGitHub a6efa4fcef test(mcp): reuse advertised schema in inventory check (#2469)
Reuse the current advertised binary_session definition for real SDK reply
validation while preserving all inventory assertions and the 10-second deadline.
Avoid initializing validators for unrelated tools in this correctness check.

Closes #2453.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-13 12:00:04 +08:00
YsopwnableandGitHub 172407787f fix(windows): preserve transient runtime admission failures (#2467)
Fixes #2257. Thanks to @chqcjq for the native self-test retry diagnosis in #2266.

Preserve retry disposition through analysis workflows. Native warm admission requires exact sharing/lock codes and affirmative pre-allocation evidence; cleanup ownership takes precedence. No automatic retry or new analysis cache.

Verified with real Windows 11 CLI, same-connection SDK MCP normal/conflict/recovery, the Windows native verifier, 99 focused tests and static/docs gates. Official npm 11.16.0 launcher sources were the application input. No IDA or Ghidra engine was used for this change. Win32 5 was not reproduced; no resolution claim for #1701/#2196. Performance measurements and limits are recorded in experiments/analysis-efficiency/windows-runtime-retry.md.
2026-10-13 11:42:54 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
23de4f77c7 perf(flutter): add opt-in retained AOT summary and string pages
Add explicit MCP Dart AOT summary delivery and exact retained string-pool pages while preserving complete defaults, CLI behavior, ABI selection, original Evidence identity, partial coverage and lower bounds.

Real LocalSend SDK replies shrink 92.05% with approximately unchanged SDK time. Two actual Codex pairs preserve all selected facts and reduce total input 48.75% / 42.38%, uncached input 33.63% / 42.52%, and task time 23.95% / 30.30%; all client errors/repeated calls remain counted. Preserve old portable Evidence and the real CLI complete result after rebasing onto SAZ/qlog main. Curated results keep measured and integration runtime pins separate; raw artifacts stay outside Git.

Refs #2016.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 11:39:30 +08:00
bb154d1067 feat(browser): inspect retained aioquic qlog JSON captures
Inspect bounded offline aioquic1.5.0 JSON qlog0.3 and page retained event/transport facts with explicit trace and stream selectors. Preserve producer coordinates, exact numeric lexemes, global pre-selection fact scope and endpoint declaration uncertainty.

Verified four real HTTP/3 recordings with an independent interval oracle, public CLI/MCP/schema parity, auth exclusions, source-deletion views and actual decoder heap exhaustion. Unverified producer platforms and qlog-specific lifecycle induction remain documented.

Co-authored-by: LuisTaffelli <86675342+LuisTaffelli@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-13 03:24:05 +00:00
a2d8b94dd5 feat(fiddler): inspect native SAZ captures
Inspect bounded recorded raw HTTP/XML SAZ archives and page retained capture Evidence. Preserve exact stored bytes and provenance, validate ZIP/XML integrity, and require an enforceable decoder memory bound.

Verified 356 real recorded sessions against independent byte and metadata oracles, public CLI/MCP parity, retained pages, and related interface checks. Current Fiddler Everywhere GUI and non-Linux producer workflows remain unverified.

Co-authored-by: zahhak <1792039+zahhak@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-13 02:51:14 +00:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
f254f5ac2b feat(android): add opt-in package summaries and retained manifest views
Add explicit package summary delivery and exact retained manifest pages while preserving complete MCP defaults, one-shot CLI output, provider requests, source identity and session lifetime. Return distinct linked Evidence and preserve partial/null facts; reject unavailable retention before engine work.

Validation: 27 relevant SDK/domain/Android boundary checks, focused test-only correction rerun, full supported compiler/lint and docs checks, real NewPipe SDK pairs, authentic old Evidence continuation, four actual Codex trials with failures/regressions retained, and all 39 exact-head CI checks. Package replies shrink 119,527 to 8,659 bytes; no model-token or time gain is established.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 10:21:53 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
ca2d15b3ee test(perf): observe owned counters at payload completion
Add opt-in, owned payload-completion cgroup witnesses without changing default receipts, terminal-counter semantics, resource limits, or REA public APIs. Preserve unknown and nonzero outcomes; verify ownership, observer capacity, cleanup, and actual SDK/model workflows.

Validation: eleven Python semantic tests, supported full-project typecheck and lint, controlled Linux failure/cancellation/capacity probes, real managed/WASM/Android SDK and Codex runs, and all 39 exact-head CI checks. Curated evidence retains mixed timings and whole-scope uncertainty.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 09:44:41 +08:00
N0zoM1z0 4773c152bc feat(website): add a persistent light and dark theme switch 2026-10-13 09:27:49 +08:00
YsopwnableandGitHub eeba61826c test(reference): scope cleanup faults to the owned source file (#2430)
Scope source-file cleanup fault injection without poisoning enclosing Git
metadata reads. Cover both environments and verify that the retained native
handle stays live through retry, then closes successfully.

Closes #2428.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-13 09:24:04 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
4f4f6cd7b5 test(evm): verify static descriptor bindings before use (#2427)
Verify the static EVM descriptor operation, CLI command, factory and input/output schema bindings with compiler-backed negative assignability checks. Prove SDK duplicate registration and invalid target input fail before provider invocation, preserving named bindings and separate lazy owners.

Validated complete supported single-thread typecheck, focused12 SDK/composition cases plus a targeted2-case provider-spy rerun, static checks, checked pre-push, manual review and all exact-head CI checks. Production behavior/defaults and provider identity remain unchanged. Dynamic third-party module admission and ABI negotiation remain deferred.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 09:07:40 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
ee87111009 test(perf): distinguish Codex tool context and token accounting (#2416)
Add optional owned Codex tool/usage capture and exact-name discovery experiments while preserving default ephemeral trials and all REA public APIs. Retain scoped managed and actual Android results, mixed outcomes, source identities and resource unknowns.

Six semantic Python tests, full checked pre-push, byte-identical own-file rebase verification, manual review and all 39 exact-head CI checks pass. Historical main fixture failures and their independent repair remain documented; measured source/runtime pins are unchanged.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 09:01:17 +08:00
YsopwnableandGitHub 37b7550ecc fix(ida): preserve native coverage and verify returned values (#2324)
Reject native callee successes without an explicit truncation marker while preserving provider failure reasons. Verify positive procedure inventory/search and function-result parity in the real-provider lane; support independent string/xref oracles and label their absence as unverified.

Verified with Windows IDA Pro 9.3: eight normal production CLI/MCP checks passed, and six real-provider response-fault cases reproduced old false positives and validated the correction. Windows IDA Free was not used. No personal paths or license registration information are included.

Repair inconsistent complete-file Ghidra test probe sizes after the shared ELF-header validation change, deriving sizes from the fixture buffers. Reproduced the three failures before the correction; 115 related installation/doctor and ELF tests pass afterward. Runtime parsing and installation behavior are unchanged; no real-Ghidra verification is claimed.

207 focused IDA tests and static/documentation checks passed. Automated review completed on d1e0b1095 without findings. CI run 38327862093 passed all 36 jobs, including Windows, all six test shards, coverage/timing, and real Apple CLI/MCP workflows.

Follow-up to #1544.
2026-10-13 08:44:18 +08:00
Aurelio BernalandGitHub c462a4ebd3 fix(scripts): pass MCP callTool timeouts as request options (#2387)
@modelcontextprotocol/client 2.3.1 declares callTool(params, options).
Three verifiers still used the v1 shape callTool(params, undefined,
{ timeout }), so the third argument was dropped and those calls ran
under the SDK's 60 s default instead of the 150-180 s they request.
Pass the options second.
2026-10-13 09:16:41 +09:00
morluto ebc22d99d6 fix(native): retain signature snapshot cleanup ownership
Use the shared resource scope and private runtime root for signature snapshots. Retain failed file-handle/root cleanup, preserve primary failures alongside cleanup diagnostics, and wait for binding operations during client close before retrying owned resources.

Validate root cleanup failure, blocked retry, repeated client close and in-flight acquisition. Existing provider/MCP/CLI tests and the real Darwin codesign ancestor-swap case pass. Dedicated native file-handle close-failure injection remains unverified.
2026-10-13 08:08:40 +08:00
morluto 9a18251426 feat(mcp): add opt-in paging for retained unknown heads
Keep complete default delivery and exact filters. Select stable ID pages at the ledger owner before cloning, return filtered totals and continuation offsets, and preserve detached records through the shared session port.

Validate public MCP paging, safe-integer edges, snapshot replay and converted JSON Schemas. Document the bounded Flameox workflow comparison and its token/CPU/latency measurement limits; no real model performance claim.
2026-10-13 08:08:24 +08:00
morluto 34a4bb6508 fix(elf): centralize fixed header validation across consumers
Validate ELF identification/header versions and declared header bounds at the shared owner. Reuse parsed facts in target selection, Go, Dart and MIPS readers while preserving their stricter format policies and x32 support.

Repair synthetic ELF fixtures and remove a superseded Go error-text assertion. Validation: 74 source tests, 202 filesystem/Go/MIPS boundary tests, typecheck and lint. No new real-provider claims.
2026-10-13 08:07:58 +08:00
morluto f09bb8a997 fix(browser): redact encoded declared secrets consistently 2026-10-13 07:52:55 +08:00
morluto 9435c7aae2 fix(artifacts): reject malformed serialized keys and integer values 2026-10-13 07:52:55 +08:00
morluto d8ceb3f276 fix(artifacts): preserve inventory uncertainty in application projections 2026-10-13 07:52:54 +08:00
morluto 951b095e11 fix(browser): bound CDP discovery response size and lifetime 2026-10-13 07:52:54 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
82cfcee19f feat(managed): add retained metadata overview and pages (#2413)
Add caller-selected managed metadata summaries and original collection pages while preserving complete CLI/MCP defaults and previous-runtime Evidence. Real SDK replies shrink substantially; actual Codex token/latency results remain mixed and are documented without a token-saving claim.

Validated focused SDK/domain tests, full static/docs checks, real two-assembly repeated workflows and portable old Evidence, real SQLite WASM, and all exact-head CI checks. Raw experimental artifacts remain private.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 07:41:20 +08:00
morluto f43fc02475 fix(javascript): distinguish file URL paths from query identity 2026-10-13 07:35:12 +08:00
morluto dc038d16bb fix(apple): preserve archive signatures and metadata byte identity 2026-10-13 07:34:52 +08:00
653067d799 fix(javascript): resolve module-relative Worker URL references (#2104)
* fix(javascript): resolve module-relative Worker URL references

* fix(javascript): validate Worker URL paths independently of suffixes

* fix(javascript): preserve directory-form Worker URL paths

* fix(javascript): unwrap type assertions in Worker URL references

* fix(javascript): validate Worker bindings before URL resolution

* fix(javascript): leave drive-letter Worker URLs unresolved

Use WHATWG file URL parsing before pathname decoding to detect drive-letter
forms whose resource identity depends on the unmodeled source URL scheme.
Preserve encoded and ordinary relative pipe paths, and cover false matches
through the compiled CLI and resolver boundary tests.

* fix(javascript): preserve Worker URL identity boundaries

Recognize known Worker bindings before filtering callee spellings so ESM
aliases and transparent constructor wrappers reach URL analysis. Keep
root-relative module URLs unknown without a modeled runtime base root.

Index direct source-local global writes with the shared mutation collector
before promoting URL and browser Worker constructors. Preserve lexical
shadowing and unrelated property identities, and exercise all three review
regressions through the compiled CLI.

* fix(javascript): invalidate mutated import.meta URL bases

Track source-local import.meta writes alongside ambient global writes so
Worker URL expressions keep an unknown base after a possible URL mutation.
Tag the identity roots independently and cover direct mutation and unrelated
property cases through the compiled CLI.

* fix(javascript): close Worker URL identity and pathname gaps

Resolve ambient object identities before appending written properties, and
retain possible source-local aliases across rebinding. Reuse intrinsic
invocation handling for aliased and bound mutators, array spreads, and
transparent TypeScript wrappers while preserving Electron write semantics.

Keep malformed-percent nested Worker URL pathnames unknown because the base
scheme is unavailable and Node rejects these file URLs. Preserve tolerant
HTML URL handling, valid escapes, and query/fragment evidence.

Cover the new cases and unrelated writes through the compiled CLI with
existing-file decoys. All 591 targeted tests and npm run check pass.

* fix(javascript): bound alias expansion and classify Worker paths

Join ambient identity projections instead of enumerating alias histories.
Memoize repeated value and provenance queries while preserving capture,
depth, and cycle-dependent uncertainty.

Keep Node Worker string arguments unresolved without filesystem or cwd
context, and admit qualified global URL constructors through identity checks.

Validate three 40-level alias branch shapes through the compiled CLI,
925 source tests, 25 CLI/boundary tests, and the full npm run check gate.

* fix(server): preserve partial Evidence target-content provenance

Pass the exact named contract returnsTargetContent flag through the shared partial-Evidence error delivery path introduced by the shortlist integration.

Exercise successful and failed recording through in-memory MCP for marked and unmarked contracts, preserving source payload and original failures. Observe both shared-handler emission and wire delivery so the SDK error-marker fallback cannot hide omissions. Suppressing the flag fails exactly the two marked cases.

Validation: check:fast, check, 736 semantic tests, 29 MCP transport/error tests and six compiled static Worker CLI journeys pass. No live target/provider execution or hosted publication.

* fix(javascript): structurally share semantic dependency proofs

---------

Co-authored-by: baron <richstyles@gmail.com>
2026-10-13 07:24:45 +09:00
3b0ea590fc fix(native): honor Mach-O chained pointer locations and library ordinals (#1709)
* fix(native): honor Mach-O chained pointer locations and library ordinals

Walk declared page starts and next links before decoding chained pointers,
preserving plain pointers between fixups, zero-offset rebases, and known
facts in malformed chains. Interpret positive dylib ordinals with dyld's
special-ordinal cutoff so valid dependency provenance is retained.

Give the Swift verification fixture an explicit module name so link-mode
filenames do not produce invalid identifiers.

Validation: 46 focused regressions; pre-fix reproductions in an isolated
copy; real macOS CLI/MCP verification of 10 compiled Objective-C/Swift,
arm64/arm64e, chained/legacy and symbols/stripped fixture variants.

* perf(native): reuse parsed Mach-O chain membership

* test(native): validate parsed-chain repair against local gates

* test(native): pin chained fixup page failure provenance

* fix(native): bound parsed chained page facts and metadata

* test(server): pin shared partial Evidence provenance

---------

Co-authored-by: baron <richstyles@gmail.com>
2026-10-13 07:11:26 +09:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
ae8051302b test(evm): verify real worker cancellation and cleanup recovery (#2404)
Verify unchanged real EVMole worker replies through compiled CLI and stdio MCP, including late SDK cancellation, permission-induced retained cleanup failure/retry, preserved malformed-input diagnostics and a healthy real SQLite WASM peer. Add launcher receipts to prove blocked retries do not start another EVM worker.

Preserve production contracts/defaults and explicit frozen-runtime/platform/measurement boundaries. Final head cf558a0661eb29bda932242abc3687b1860428e7 passes all 36 CI checks (run38313631216); manual final review and related real Factory rerun pass. Raw artifacts and receipts remain outside Git.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 06:05:20 +08:00
morluto 8fc2043eb8 fix(mcp): preserve target provenance on retained partial errors 2026-10-13 05:25:15 +08:00
fdd8f82602 fix(browser): compare PNG IHDR bytes without ASCII folding (#2099)
* fix(browser): compare PNG IHDR bytes without ASCII folding

Screenshot checks already compare the PNG signature as raw bytes, then decoded the chunk type with an encoding that maps high-bit bytes onto IHDR.

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

* fix(browser): satisfy the strict index check in the IHDR test

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

* refactor(browser): share byte-faithful PNG header facts

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: REA preparation lane <rea-preparation@localhost>
2026-10-13 06:16:18 +09:00
c59fbf5e9c fix(artifacts): bound archive decompression by source size (#2144)
* fix(artifacts): bound archive decompression by source size

A ZIP/APK/IPA/MSIX member is attacker-controlled and can expand far
beyond the archive that carries it. Inventory hashed every member to EOF
and extraction wrote every member with no ceiling, so a ~2 MiB archive
could drive ~30 s of CPU during inventory and ~150 s plus 2 GiB of disk
writes during extraction.

A fixed per-entry byte cap is the wrong shape: it rejects legitimately
large members inside large packages. The ceiling instead scales with the
archive's own size (500x, with a 256 MiB floor), so large packages keep a
proportionally large budget while a small archive cannot amplify into
unbounded work. The cumulative declared size is charged from the central
directory before any entry is decompressed, and zip.js already verifies
streamed output against each declaration, so the check is exact and
covers both inventory and extraction through the shared reader.

* fix(artifacts): own decompression accounting per traversal

* test(artifacts): verify declared ZIP output enforcement

---------

Co-authored-by: baron <richstyles@gmail.com>
2026-10-13 06:15:34 +09:00
The one -NiuandGitHub 81cca2e0e3 fix(analysis): expose provider image base (#2083) 2026-10-13 06:10:21 +09:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1dd20ce1ff test(analysis): verify real preset snapshot and cleanup boundaries (#2349)
Verify existing caller-selected Ghidra profiles and immutable JSON snapshot boundaries through real CLI/MCP workflows on GNU true and shipped jq. Preserve exact task facts, profile/source identity, original snapshot records and existing defaults; require actual stored query entries and scoped headless launch evidence.

Exercise pending startup cancellation with production token/native start-identity inspection, recovery under the selected target, controlled real permission cleanup failure, retained ownership and restored-permission close retry. Independently confirm recovered socket/root cleanup. Preserve failed observer assumptions, source fingerprints, measured scope and final-resource counter unknowns. No general fast preset, product API change or new performance claim.

Refs #2016

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 05:08:42 +08:00
TonyWuandGitHub 7bc57e1e64 test(apple): skip ibtool-compiled NIB tests when Xcode ibtool is unavailable (#2070)
Command Line Tools install the /usr/bin/ibtool and xcrun shims without ibtool itself, so the existing /usr/bin/ibtool existence guard never skipped the storyboard test, and the native NIB hierarchy suite had no guard. On a Command Line Tools-only macOS host these seven tests failed at fixture compilation. Probe xcrun --find ibtool once and use it for both suites.
2026-10-13 06:07:16 +09:00
5c2956587a fix(apple): preserve slice identity and macOS boundary diagnostics (#1772)
* fix(artifacts): name CPU_SUBTYPE_ARM64E_X1 Mach-O slices arm64e

macOS 27 system binaries such as /bin/ls carry a third FAT slice with
cpusubtype 0x8000000c. The SDK defines subtype 12 as
CPU_SUBTYPE_ARM64E_X1, and CPU_SUBTYPE_HAS_ARM64E puts it in the arm64e
family. architectureName knew only subtype 2, so it named the slice
arm64, lipoNameMatches rejected lipo's arm64e.x1 name, and
inspect-artifact /bin/ls failed with integrity_mismatch.

The arm64e.x1 test fixture now uses the real subtype. It fails without
this change.

Refs #1168, #1765

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

* fix(artifacts): classify en-GB hdiutil verify refusals as format failures

hdiutil follows AppleLanguages. With en-GB it writes "image not
recognised", and verifyFailureReason matched only the US spelling, so a
file that is not a disk image got the reason unavailable. Both spellings
now give format. Other languages still give unavailable.

Refs #1765

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

* test(process): read the full process table in the lifecycle cleanup check

The check ran ps -axo command= with the Node default maxBuffer of 1 MiB.
On a host with 1905 processes, the output is 1.52 MB, and the test failed
with "stdout maxBuffer length exceeded".

Refs #1765

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

* test(process): bound cleanup observations to sampled descendants

* fix(apple): retain CPU subtype identity during dylib resolution

* test(process): retain OS PIDs for descendant observations

---------

Co-authored-by: David Lee <davidlee@Dav3Book-Pro-M4-Max.local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 06:06:37 +09:00
Lluís AriñoandGitHub 85e124253a feat(server): frame analysis-target content as untrusted data (#1908)
* feat(server): frame analysis-target content as untrusted data

Strings, decompiled code, page text, scripts, captures and logs returned
by REA originate from the analysed target, which can embed text aimed at
the agent. Nothing marked that content, so a prompt injection inside a
binary or page could steer the agent toward tools that run code.

- Server instructions now state that target-derived tool output is data,
  never instructions, and that any request in it to run, install,
  download or send something is a prompt-injection attempt to report.
- Tool contracts declare returnsTargetContent. It is on by default; only
  seven tools whose results hold REA session state or caller-supplied
  values (binary_session, close_binary, export_evidence_bundle,
  record_unknown, update_unknown, inspect_adb_client,
  inspect_apktool_client) are audited exceptions, so new tools start
  marked.
- Successful results of marked tools carry
  _meta.provenance = { untrusted_content: true, source: "analysis_target" }.
  content and structuredContent are unchanged, and the response budget
  accounts for the fixed marker bytes.

Tests cover the encoding and budget accounting, the audited exception
list against the contract catalog, and an in-memory MCP client that sees
the instructions and the marker on a marked tool but not on
binary_session.

* fix(server): mark failed target-content results as untrusted too

The untrusted provenance marker was attached only on the success branch
of ToolResultDelivery, but failures can carry target-derived text as
well: error messages, captured output, and partial observations such as
the terminal frames a failed capture_process_scenario retains in
partial_observation. A host gating later process or device actions on the
marker would have treated an adversarial error payload as trusted.

Every error path in src/server now carries the failing tool's provenance:
- toErrorToolResult and toRecordedPartialErrorToolResult take the tool's
  target-content flag explicitly, so each caller is audited by the type
  checker; toCallToolResult, toEvidenceToolResult (recording failure), the
  response-budget error that replaces an oversized success, and the
  Electron partial-evidence path pass contract.returnsTargetContent.
- Admission failures in runAdmittedToolOperation resolve the flag from the
  tool name through the new toolReturnsTargetContent, the same predicate
  the contract metadata uses; unknown names stay marked.
- The transport's oversized-error replacement keeps the original's marker
  and counts it in its budget check, matching the projected error.
- Errors the MCP SDK builds itself (input validation, thrown handlers) are
  marked in the transport for target-content tools only.

Error encoding accounts for the marker bytes exactly as success encoding
does. docs/mcp-contracts.md now states that error results carry the
marker. Tests cover marked and unmarked error results, the
capture_process_scenario partial-observation errors in-process and over
the SDK transport, the recorded partial-evidence path, the budget
replacement, the oversized-error replacement, and SDK validation errors
for a marked and a content-free tool.

* fix(contracts): mark session status, close and client probes as target content

The content-free exception set was audited against successful results
only. Now that failures carry the marker too, every exception was
re-audited against its error paths with one question: can the result
include text produced by an external process, provider or device, or
stored content REA did not author?

Removed from TARGET_CONTENT_FREE_TOOLS:
- close_binary: a failed provider cleanup (GhidraClient cleanup error via
  createGhidraDiagnostics) projects the launcher's captured stdout and
  stderr, which can contain target-derived log text.
- binary_session: capability status reports runtime unavailability as the
  provider error message (ProviderAdapterError and similar), and the
  provider candidate and Android availability diagnostics include probe
  output such as the apktool stderr excerpt.
- inspect_adb_client: the success result carries the version and
  installed path parsed from `adb version` stdout, and a failed probe
  keeps a stderr_excerpt.
- inspect_apktool_client: the success result carries the first line of
  the launcher's `--version` output, and a failed probe keeps a
  stderr_excerpt.
- update_unknown: the new revision carries forward the stored question
  and domain, which an imported evidence bundle can supply; list_unknowns
  and get_evidence_bundle already return that data marked.

The set now holds only export_evidence_bundle (path, byte and record
counts; failures are filesystem, cancellation, or a fixed integrity
message) and record_unknown (the caller's input plus REA-computed ids
and digests; registry errors project only a reason code).

The conformance test pins the reduced set and asserts each removed tool
is marked. Tests that used binary_session as the unmarked example now use
record_unknown. docs/mcp-contracts.md lists the remaining exceptions and
why the removed tools stay marked.

* fix(contracts): include target-content policy in the catalog identity

createToolCatalog() and CATALOG_IDENTITY.tools projected every
client-visible contract field except returnsTargetContent. Because the
untrusted-content framing left schemas, effects, prompts, CLI names and
the package version unchanged, a same-version MCP process started before
the update digested equal to the new server and binary_session reported
it aligned although it never emits _meta.provenance.

Project the flag as returns_target_content in both the digested tool
catalog and CATALOG_IDENTITY.tools, and export createCatalogDigests so a
boundary test proves that toggling the flag on one tool changes the
tools and combined digests.
2026-10-13 06:06:02 +09:00
af29247a78 fix: honor empty URL query and fragment markers (#2091)
URL.search and URL.hash are empty for both a missing component and an explicit empty one, so endpoint and origin checks were accepting URLs the callers meant to reject.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 06:05:29 +09:00
Naema Mohmed FarahandGitHub d59ab6d977 fix(javascript): bound value evaluation through nested closure aliases (#1941)
* fix(javascript): bound value evaluation through nested closure aliases

A closure that aliases a binding from its enclosing scope evaluates that
binding once live and once at its own capture point. The capture cannot
order a binding declared in another function, so it fell back to a second
live evaluation. Only primitive results were kept, so every level of
nesting doubled the work: 20 nested closures over a non-constant value
took about 14 s, and a 443 KB generated wrapper reached the 300 s
analysis timeout.

Keep a live binding value when no caller could change it: no alias cycle
closes on or above the binding and the expression depth budget was not
reached. Bindings on a cycle are still re-evaluated, so their values and
reasons are unchanged.

Found while building a source-owned reproduction for #1847; that report's
worker heap exhaustion is a separate, already closed defect.

* fix(javascript): keep the expression depth budget for retained values

Reusing a retained composite value from any depth let a chain of wrappers
(s1 = {x: s0}, s2 = {x: s1}, ...) nest past the expression depth budget:
300 wrappers produced a 299-level value where the budget stops at 86, the
serialized IR grew quadratically, and 3,000 wrappers overflowed the stack
in JSON.stringify.

Record the expression depth at which each caller-independent value was
derived and reuse it only at that depth or shallower. A deeper reader
derives the value again and reaches the budget exactly as before. Closure
aliases read their outer binding at one depth on both paths, so nested
closures stay linear.
2026-10-13 06:04:58 +09:00
86fab11fbe fix(javascript): preserve import-equals declaration and export evidence (#2062)
* fix(javascript): retain TypeScript import-equals dependencies

* test(mcp): align completion checks with warm provider state

* fix(javascript): propagate require origins through exported aliases

* fix(javascript): normalize equivalent CTS binding origins

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 06:04:21 +09:00
6ce5903e7a test(javascript): verify CommonJS file URL boundaries via CLI and MCP (#2061)
* fix(javascript): keep CommonJS file URL strings unresolved

* test(mcp): align completion checks with warm provider state

* test(javascript): remove superseded file URL resolver coverage

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 06:03:45 +09:00
91b8f587cd feat(signals): decode recordings with bounded sigrok output (#2339)
Decode caller-selected protocols from existing recordings through shared
CLI/MCP Evidence. Page literal output lines after complete decoding;
preserve explicit full output, source identity and failed cleanup ownership.

Verified real sigrok-cli 0.7.2 workflows, published UART/SPI/I2C recordings,
independent UART values, selector limits, SDK schemas and cleanup regressions.

Co-authored-by: Olorinm <59321404+Olorinm@users.noreply.github.com>
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-12 20:59:02 +00:00
5f79c852af docs(ghidra): verified macOS arm64 native decompiler build recipe (#2112)
docs(ghidra): verify and document the macOS arm64 native decompiler build

Co-authored-by: hub <arestheagent@gmail.com>
2026-10-13 05:58:28 +09:00
Hill PatelandGitHub ea2f94d4de fix(wasm): stop appending WABT remediation to unrelated runtime-root failures (#2084)
inspect_wasm_artifact's configuration-phase error handler assumed every
non-OwnedCommandFailure thrown before the artifact phase was about WABT
tool/directory resolution, and unconditionally appended "Supply WABT 1.0.42
via absolute REA_WABT_BIN_DIRECTORY; REA does not install tools." to the
reason. WabtArtifactProvider.inspect() also allocates its PrivateRuntimeRoot
during the configuration phase, so a PrivateRuntimeRootUnavailableError (the
Windows native-authority capability, unrelated to WABT) got the same false
remediation glued onto its own already-correct message, misdirecting anyone
troubleshooting a correctly-configured WABT install toward re-checking WABT
instead of the real blocker.

Classify PrivateRuntimeRootUnavailableError before the generic
configuration-phase branch, matching the existing convention already used
in JavaScriptAnalysisWorker.ts and SourceMapCommand.ts: surface it as its
own AnalysisCapabilityUnavailableError with no userMessage override, letting
its own message stand.

Verified against a real WABT 1.0.42 Windows release (hash-checked against
GitHub release metadata) and a real compiled .wasm module: before the fix,
`reason` read "REA Windows native controls are unavailable: ENOENT: ...
Supply WABT 1.0.42 ..." even with a fully correct REA_WABT_BIN_DIRECTORY;
after, `reason` reports only the real cause.
2026-10-13 05:56:02 +09:00
Martin KleinandGitHub 7b15762d09 fix(reference): report sloppy scripts, PHP, C# and enclosing Git facts (#1770)
* fix(reference): report sloppy scripts, PHP, C# and enclosing Git facts

Importing the source/ subtree of a legacy PHP and C# checkout produced
four wrong or missing facts:

- Babel's unambiguous mode parses a module first and keeps its
  strict-mode recovery errors after falling back to a script, so a valid
  sloppy-mode `delete messages;` was reported as a parse failure and the
  semantic IR marked the file as parser-recovered. Parsing now goes
  through one owner that re-parses such a script as a script.
- .php and .cs files had no language; they are now detected as PHP and
  C# (detection only, no parsing).
- A root without its own .git was reported as having no version
  control even inside a checkout. The adapter now resolves the nearest
  enclosing repository, as Git does, and reports `none` only when no
  ancestor has one.
- The CLI recorded importer_version as null although the package
  version is known.

* fix(reference): keep module strictness and classify C# and PHP as source

Review follow-up. `.mjs` and `.mts` files are modules by extension, but
the script fallback re-parsed them in sloppy mode and erased real
strict-mode errors. The parser boundary now owns the extension rule,
parses explicit modules as modules and applies the fallback only when
the mode is ambiguous; the require-binding analysis uses the same rule.

`.cs` and `.php` received a language but stayed classified as unknown,
so source-to-bundle comparison dropped them. Both are now source
extensions, like the other detected languages.

* fix(reference): honor package module scope and keep unknown-mode strict errors

Review follow-up. A `.js` file under a package.json with `"type":
"module"` is an ES module, but it went through the script fallback that
erases strict-mode errors. The import now reads each in-inventory
package.json and parses `.js` files in a module scope as modules. Other
`.js` and `.ts` sources keep an unknown mode, because bundlers treat them
as modules without that field: they are no longer forced to script, and
errors that only the module parse reports are kept as an entry limitation.

* fix(reference): bound package scopes and keep application parsing unchanged

Review follow-up.
- `.cjs` files parse as scripts, so module-only strict errors no longer
  become limitations on CommonJS trees. `.cts` keeps an unknown mode
  because TypeScript compiles ES import syntax there.
- An unreadable or linked package.json now bounds the module scope with
  an unknown type instead of letting an outer `"type": "module"` apply.
- The application pipeline parses as on main again. Its module-only
  strict errors keep a source partial; package-mode handling stays in the
  reference importer.

* refactor(reference): keep unmarked module modes unknown instead of resolving package scope

Review follow-up. Resolving Node package scope for `.js` kept pulling in
more of Node's lookup semantics (require bindings, excluded or
directory-named manifests). Only extensions now select a mode: `.mjs`
and `.mts` parse as modules and `.cjs` as scripts. Every other source
keeps an unknown mode, so module-only strict errors stay visible as an
entry limitation instead of being dropped or asserted. This matches the
require-binding analysis, which also treats unmarked paths as unknown.
2026-10-13 05:52:27 +09:00
morluto 3ee6ff43c1 test(mcp): preserve coverage in unchanged function fixtures
Carry the source relationship coverage into the synthetic counterpart used by public function comparison and reconstruction verification. Keep missing coverage unresolved rather than weakening comparison completeness.
2026-10-13 04:36:45 +08:00
morluto 4dd44ddf16 test(ida): expect direction in combined call coverage
Keep the ARM64 dossier boundary assertion aligned with caller/callee coverage ownership while preserving unknown coverage, diagnostics, and observed callee assertions.

Validation: recorded IDA protocol and callee relationship boundary tests (22 passed). Live IDA was not exercised.
2026-10-13 04:24:39 +08:00
morluto 8224aeba9d test(cli): align reference import failure envelopes
Expect ok:false and the preserved error payload in full-output mode, including Windows unsupported-host failures. Retain plain JSON, selected-path, remediation, permission, logging, and source-preservation coverage.

Validation: focused compiled CLI output and reference-import tests; Windows-only cases await CI.
2026-10-13 04:24:34 +08:00
morluto ac57c88efe fix(formats): preserve high bits in Windows signature checks
Decode MZ and Windows signature bytes without ASCII high-bit masking. Move malformed signature cases into the shared Go diagnostics fixtures so provider, compiled CLI, and MCP workflows cover NE, LE, LX, and PE lookalikes; remove the superseded provider-only cases.

Validation: runtime build, check:ci, and focused DOS/Go/target/CLI/MCP tests. Windows-only reference-import cases await CI.
2026-10-13 04:24:29 +08:00
morluto fd9588089d fix(dev): preserve command arguments in exclusive runners
Use the existing pinned cross-spawn dependency at the command-spawn boundary instead of forcing every Windows executable and argument vector through cmd.exe. Preserve native executable arguments while retaining PATH/PATHEXT and command-shim support.

Validation: public runner argv and lock/cancellation tests passed on POSIX. Windows-only tests cover an executable path with spaces, bare node, npm, and the local turbo shim; native Windows execution remains unverified.

Fixes #2319
2026-10-13 04:12:45 +08:00
morluto b7169c8b74 fix(electron): attribute unresolved paths to selected inputs
Resolve each caller-selected Electron path once at the application boundary, collect field-specific failures with original causes, and pass canonical paths directly to the provider. Update direct provider consumers and route the real verifier through the shared application workflow.

Validation: public CLI/MCP invalid-path acceptance, canonicalization boundary, provider tests, and cancellation/cleanup checks. Live Electron launch was not verified. This does not address the separate Playwright launch-rejection crash in #2269.

Fixes #2272
2026-10-13 04:12:40 +08:00
morluto 049132ab43 fix(evidence): preserve call coverage in function comparisons
Treat missing, partial, unknown, and unresolved call coverage as insufficient for call-set equality. Combine IDA caller/callee coverage without losing either explanation and recheck source coverage when verifying historical comparisons.

Scope generated function-comparison unknowns with optional typed comparison/dimension facts, preserving legacy IDs and revision digests when absent. Keep unrelated dimensions conclusive, explicit dependencies applicable, and ambiguous function identity unknown. A confirmed change for matched functions remains a failure even if calls are incomplete.

Validation: function/reconstruction/residual-unknown and bundle tests, recorded IDA protocol regression, public MCP comparison/verification, and advertised SDK JSON Schema checks. Real IDA provider execution was not verified.

Fixes #2222
2026-10-13 04:12:34 +08:00
morluto c89972c877 fix(adb): evaluate the full activity start report
Evaluate stdout and stderr together, giving hard errors and non-ok wait statuses precedence over an attempted Starting line. Retain raw streams and typed partial launch facts when Activity Manager refuses or times out; retain successful raw output too.

Validation: parser/provider tests and public MCP app/activity starts through a fake executable at the real process boundary. Real Android device behavior was not verified; device-reported timeout remains distinct from a host deadline.

Fixes #2225
2026-10-13 04:12:20 +08:00
morluto 5ab0cbb48e fix(javascript): resolve bundled exports by binding identity
Identify the factory exports parameter through the existing semantic binding owner, including renamed and minified Webpack parameters. Reject nested targets, bare alias rebinding, and shadowed identifiers. Prune the duplicated source happy path after validating the public CLI workflow.

Validation: public analyze-javascript-application CLI acceptance and focused semantic traversal/module-index tests.

Fixes #2171
2026-10-13 04:12:14 +08:00
morluto 921bb95755 fix(artifacts): distinguish Java classes from universal Mach-O
Classify FAT containers using shared architecture, range, and observed slice-header facts rather than the CAFEBABE signature alone. Preserve typed unsupported and malformed reasons, reject malformed sibling slices consistently, and remove repeated range parsing in consumers.

Validation: valid small and large JVM classes in public JAR/ZIP inspection, four FAT width/byte-order controls, and focused executable/Mach-O boundary tests. The class fixtures were checked with JDK javap; no new JVM analysis capability is advertised.

The Apple selection and Hopper readers retain their existing parsing because they also own oversized-count diagnostics, reserved fields, streaming final-size validation, or source freshness that the shared header result does not represent.

Representative public mixed-ZIP inventory probe, four fresh processes with an already warm filesystem cache: first process elapsed 626.8 -> 548.3 ms, CPU 740 -> 740 ms, sampled family peak RSS 375.5 -> 345.4 MiB. Subsequent three-process medians: elapsed 767.2 -> 454.0 ms, CPU 890 -> 640 ms, peak RSS 368.7 -> 346.2 MiB. Estimated output tokens 2310 -> 2292. Concurrent load was uncontrolled, so these observations do not establish a speed improvement. Whole-agent task tokens and schema/call overhead were not measured.

Fixes #2317
2026-10-13 04:12:04 +08:00
morluto c339f9ebe7 fix(setup): ignore empty home directory selections
Skip empty home environment values while preserving nonempty caller selections and platform precedence. Setup, doctor, and uninstall now use the same fallback home instead of writing relative paths in the current directory.

Validation: focused home tests and the public setup/doctor/uninstall lifecycle under Node 24.18. Package verification and pack dry run are included in the integrated gate.

Fixes #2325
2026-10-13 04:11:55 +08:00
morluto 1c37914306 fix(javascript): retain wrapped isolated-world bridge exposures
Share contextBridge argument ownership between direct and wrapped analysis, seed intact isolated-world calls, and preserve dynamic world IDs and conditional metadata as unknown. Keep callee evaluation scoped to each invocation.

Validation: 99 focused source tests and bundled isolated-world acceptance through dedicated and generic CLI routes. Removed duplicate literal source regression in favor of public workflow coverage. Fixes #2174.
2026-10-13 03:47:45 +08:00
morluto ed485a3dc8 fix(artifacts): compare leaf inventories using explicit coverage
Classify coverage once at inventory production and require it in current outputs. Normalize older retained Evidence at the inventory parser, reject inconsistent page statuses, and remove comparison heuristics over limitation prose.

Validation: public CLI-to-MCP plist comparison, changed-behavior and unknowns workflow; boundary checks for legacy Evidence, partial coverage, page disagreement, content and executable changes; generated docs checks. Fixes #2173.
2026-10-13 03:47:45 +08:00
morluto 8d4d631606 fix(apple): decode UIKit keyed-archive outlet connections
Select authoritative connection fields by runtime class in a shared owner used by keyed archives and NIBArchive projection. Preserve archive identities, endpoint relationships and coverage, and replace duplicate synthetic outlet assertions with a rooted public CLI fixture.

Validation: compiled CLI acceptance against the issue-sourced rooted UIKit fixture and 41 filesystem tests. Native UIKit framework validation was not rerun. Fixes #2177.
2026-10-13 03:47:45 +08:00
morluto 7fb097d72c fix(go): accept file-backed unmapped Mach-O debug segments
Validate unmapped segment file ranges without adding virtual mappings or fallback metadata addresses. Keep mapped-segment bounds and reject build-info sections declared in unmapped segments.

Validation: real unstripped Go 1.26.5 Darwin arm64 and amd64 artifacts passed CLI/MCP oracle, Evidence, source-range and unchanged-byte checks. Retained malformed-input regressions; removed the weaker synthetic acceptance test. Full cross-platform verifier not run. Fixes #2167.
2026-10-13 03:47:45 +08:00
morluto c9fed50691 fix(targets): recognize Windows headers after short DOS stubs
Observe MZ header declarations and signature uncertainty at the domain boundary, then reuse those facts in bounded target resolution, inventory, and Go PE parsing. Preserve DOS relocation ownership and fail closed when the relevant bytes remain unexamined.

Validation: 59 domain tests, 130 target/Go boundary tests, and public CLI inspection of a clang/lld-link PE DLL with a short DOS header. Fixes #2172.
2026-10-13 03:47:45 +08:00
f611d8ce72 feat(captures): inspect recorded packets with bounded TShark delivery (#2318)
Inspect selected fields in existing captures through shared CLI/MCP Evidence.
Page matching frames without changing dissection or two-pass read coverage;
retain failed descriptor/process cleanup ownership and reject excess admission.

Verified with real TShark 4.0.17 and 4.2.2, public Wireshark recordings,
owned HTTP application traffic, real CLI/MCP workflows and schema checks.

Co-authored-by: Olorinm <59321404+Olorinm@users.noreply.github.com>
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-12 19:35:35 +00:00
morluto 570c40cde5 fix(cli): preserve failure semantics across full-output formats 2026-10-13 03:33:27 +08:00
morluto 221c856481 fix(browser): preserve attached service-worker policies and ownership 2026-10-13 03:29:28 +08:00
morluto 02cedc3796 fix(readiness): validate stage evidence references once
Collect stage and check references at their owning stage boundary before evaluating required checks. Emit one missing-reference finding per stage and Evidence ID, including stages with no supplied checks, while preserving independent failed checks and cross-stage identity. The 21 readiness tests pass.
2026-10-13 03:26:16 +08:00
morluto 2397137615 fix(setup): honor config roots and display terminal dry-run plans
Normalize empty XDG config homes once for configuration writers and registration readers. Render the resolved plan for terminal dry runs while preserving structured output. Cover registration placement and a real PTY dry run, including absence of configuration writes.
2026-10-13 03:22:53 +08:00
morluto cecc6ecbff fix(process): preserve invalid runtime path selections as input errors
Classify unresolvable and unusable executable or working-directory selections before launch, retaining field paths, attempted candidates and OS diagnostics. Preserve tagged analysis errors through capture lifecycle handling. Replace duplicate missing-path source assertions with public CLI/MCP regression coverage and retain distinct permission, file-kind and PATH checks.
2026-10-13 03:22:46 +08:00
morluto b123b2e51c perf(evidence): authenticate and seal imported bundles in one pass
Reuse the immutable domain parser at the file boundary instead of rebuilding and revalidating an already authenticated bundle. Existing filesystem and integrity regressions pass (27 tests); a 4 MiB parsing probe preserves immutable output and reduces repeated validation work.
2026-10-13 03:22:38 +08:00
a97b713f74 test(mcp): fix three MCP boundary suites on native Windows (#2312)
* test(mcp): fix three MCP boundary suites on native Windows

* test(mcp): report unavailable root symlink coverage as a skip

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 03:13:35 +08:00
d1aaeee400 feat(ida): support optional native IDA Free MCP in attached mode (#1544)
Add an explicit attached native IDA MCP compatibility profile while preserving the legacy and headless workflows. Keep recorded input identity, complete source observations and the user's open GUI database. Batch unique callee lookups, conservatively prefilter literal strings, preserve Unicode matching and unavailable caller coverage, and require distinct-function switching in native verification.

Verified with Windows IDA Pro 9.3 and the native MCP plugin built from c9d15d98e8a143fc74f6e14037c48f63889b6d6c with a loopback listener. Production CLI/MCP and an unchanged Windows whoami.exe passed. Windows IDA Free was not used. No personal paths or license registration information are included.

Validation: 200 focused tests, static and documentation checks, architecture guards and discovery passed. Historical same-main warm task measurements improved from 675 to 317 ms; response token estimates fell from 16618 to 11509. Fresh-start, overall CPU and memory improvement were not established. Detailed scope is in experiments/analysis-efficiency/ida-native-windows.md.

Co-authored-by: imissapixel <140405963+imissapixel@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-13 03:10:21 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
f76163c246 feat(evm): add retained interface summary delivery (#2308)
Add opt-in EVM MCP summary delivery and retained/portable function and text views. Keep existing complete defaults, provider arguments and original full Evidence identities and records usable. Require retention before selected analysis; preserve inferred candidates, nulls, diagnostics, provenance and explicit unknowns.

Verify old schemas, historical Evidence migration, 64 focused feature checks, 30 affected MCP checks after latest-main rebase, 59 real EVM CLI/MCP cases and six shared-domain artifact workflows. Repeated SDK delivery is smaller; all real model trials and mixed token/time outcomes, limitation failures and resource qualification gaps remain documented without a uniform performance claim.

Refs #2016

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 03:09:32 +08:00
bb26622670 fix(cli): retain correction details across adjacent browser commands (#2294)
* fix(cli): retain correction details across browser commands

Fold the complementary implementation from #2247 into the earlier #2230 proposal, retaining the original contribution history.

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>

* fix(browser): retain diagnostics from the selected input branch

* fix(cli): recognize incomplete browser scenarios by their owned fields

---------

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-13 02:55:59 +08:00
morluto 0365454a57 docs(agents): require brittleness review before resolving threads 2026-10-13 02:46:42 +08:00
e20994a8ea fix(ghidra): keep JVM warnings out of LaunchSupport output (#1698)
* fix(ghidra): keep JVM warnings out of LaunchSupport output

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

* fix(ghidra): retain LaunchSupport warnings in session diagnostics

* fix(ghidra): select helper logging flags by observed JVM identity

* fix(ghidra): preserve diagnostics through typed failure projection

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 02:46:00 +08:00
0043d9b134 fix(browser): report mismatched event family selection as unknown in capture comparison (#2239)
* fix(browser): report mismatched event family selection as unknown in capture comparison

* fix(browser): retain selected event families and unknown coverage

Fold the complementary implementation from #2253 into the earlier #2239 proposal, retaining the original contribution history.

Co-authored-by: itsF4LCON <107556180+itsF4LCON@users.noreply.github.com>

---------

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: itsF4LCON <107556180+itsF4LCON@users.noreply.github.com>
2026-10-13 02:36:45 +08:00
aeee5074d1 feat(sqlite): inspect offline snapshots with selective evidence (#2271)
Add offline SQLite database/WAL inspection through the shared CLI/MCP.

Use compact inventory, selected-table schema, exact ordered columns and
bounded row samples while preserving source identity and lossless values.
Retain completed Evidence through late cancellation and private-root cleanup
failures, bound concurrent inspections, and recheck journal validity after
acquisition. Reuse prepared CI runtime artifacts across supported Node lines.

Co-authored-by: Y0UW1SH <29498139+Y0UW1SH@users.noreply.github.com>
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-12 18:33:23 +00:00
No BS AIandGitHub 916ac97bf9 fix(cli): retain browser and Inspector input correction details (#2230)
Reuse the shared safe issue projection for all six CLI validation boundaries
and cover malformed inputs, credential safety, and parser diagnostics.

Fixes morluto/rea#2180

Developed with assistance from OpenAI Codex.
2026-10-13 02:29:07 +08:00
fd985dbc5b fix(apple): join recognized keys from NIBArchive field maps (#2248)
* fix(apple): join recognized keys from NIBArchive field maps

inspect-asset-catalog returned no resource-key matches for a decoded
NIBArchive resource whose recognized image key is present in the archive. The
NIBArchive projection groups an object's decoded fields under the node's
nibValues attribute beside the class, label and ID it derives from the same
archive, while the AppKit keyed-archive parser flattens the same fields onto
the node. The catalog join read only the top level, so a recognized NSImageName
on a decoded resource produced no row, and the empty-catalog limitation still
claimed resource keys were reported as unmatched.

Read an archive object's fields from both shapes, and give the namespace one
owner by exporting NIB_VALUES_ATTRIBUTE from the projection that writes it.
The recognized-field set stays in the catalog analysis, which owns that
contract.

Verified with 5 focused catalog tests, check:fast and verify:test-discovery on
macOS arm64, and through the public CLI against the reported NIBArchive
fixture. The keyed-archive control keeps its existing row, and a NIBArchive
with only an unrecognized title still produces none. The ibtool-dependent
native hierarchy lane requires full Xcode and was unavailable here.

* fix(apple): normalize resource keys at the NIBArchive producer

Fold the complementary implementation from #2256 into the earlier #2248 proposal, retaining the original contribution history.

Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>

* fix(apple): restrict catalog joins to parsed resource nodes

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>
2026-10-13 02:26:34 +08:00
47a51b08f4 fix(process): add an opt-in Swift module cache (#2252)
* fix(process): add an opt-in Swift module cache

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>

* fix(process): preserve caller-owned Swift module caches

Fold the complementary implementation from #2260 into the earlier #2252 proposal, retaining the original contribution history.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>

* fix(process): bypass xcrun lookup for explicit Swift toolchains

---------

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 02:26:16 +08:00
morluto f941968eb1 fix(ci): allow cold Chrome startup in runtime fixtures
Let the fixture lifecycle own CDP readiness with the same 60-second startup deadline as the broader browser verifier. Return the discovered port to both consumers and clean up failed startup before returning a lease.

Validation: five startup/cleanup boundary tests, npm run check, and real Chrome 154 runtime and source-map CLI/MCP verifiers passed on macOS arm64. Hosted Linux verification follows the push.
2026-10-13 02:17:14 +08:00
Saim ShafiqueandGitHub ec2d251c0a fix(adb): count UTF-8 bytes in logcat total_bytes (#2212)
* fix(adb): count UTF-8 bytes in logcat total_bytes

read_adb_logcat assigned the decoded stdout string's UTF-16 code-unit
length to total_bytes, undercounting non-ASCII dumps (a 37-byte CJK line
reported 33). Measure the UTF-8 byte length instead, matching the field's
byte-unit contract and the repo's Buffer.byteLength convention.

Fixes #2175

* fix(adb): count raw captured bytes in logcat total_bytes

Carry the pre-decode stdout byte length through runAdb so malformed
UTF-8 (replaced by U+FFFD on decode) still reports the true captured
byte count instead of the re-encoded replacement size.
2026-10-13 01:56:12 +08:00
morluto 2e45b4d7a5 fix(ci): stop aggregate jobs when verification is cancelled 2026-10-13 01:45:11 +08:00
11fe5a13f8 fix(native): retain failed pwntools process and runtime cleanup (#1708)
* fix(native): retain failed pwntools process and runtime cleanup

Keep failed worker/debugger cleanup owners and their private files until
verified process shutdown and successful directory removal. Retry cleanup
before another inspection and expose shutdown through the shared services,
CLI and MCP server. Serialize acquisition without delaying cancellation of
queued callers, and preserve primary diagnostics when CLI cleanup fails.

The original decoder reproduction left a live owned Node child with its
runtime deleted and no retained cleanup owner; the regression now verifies
retention, admission blocking, retries and eventual process/file cleanup.

Validation: 10 lifecycle/public CLI/MCP tests, 20 service tests, test
discovery 814/814, build:cached and full check including architecture guards.
Actual POSIX process/filesystem boundaries used injected Node workers on
macOS; real Linux pwntools/GDB provider parsing was not exercised.

* test(native): verify retained recorded-crash cleanup lifecycle

* fix(native): finalize diagnostic cleanup before publishing results

* fix(native): retire decoder stages before reading runtime evidence

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 01:37:47 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
ad08510a11 refactor(evm): bind offline analysis through a static descriptor
Bind existing EVM CLI/MCP metadata and the legacy service factory through one static descriptor, and register the same selected service that owns cleanup. Preserve original names, schemas, inputs/full defaults, configuration, Evidence and injection identity.

Verified exact SDK catalog/fact parity across six real EVM pairs, 59 actual EVM CLI/MCP cases, six real cross-domain workflows, six actual Codex CLI fact/reference/lifecycle trials and relevant local checks. CI run 38283026233 succeeds for exact head d999d9874bf59c3c8486bac9b2c867a4a6a9121d. No token reduction from wiring or fully qualified resource claim; final counters remain unknown. Continues #2016.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 01:26:43 +08:00
YsopwnableandGitHub d96550f2f6 ci: summarize static checks and retain complete reports (#2242)
Keep static feedback concise while retaining complete diagnostic reports,
visible lint errors, module-boundary checks, and the 3% duplication gate.
Upload complete reports after static failures and restore their outputs
from Turbo caches. Preserve unambiguous, checkout-relative clone locations.

Bind both lint caches to the website, documentation configuration and
experiment sources actually scanned by oxlint, including ignore policy.
Guard this ownership by comparing live checker discovery with resolved
Turbo inputs. A real website syntax error now invalidates a cached pass.

Validation: full current-head CI and the separate Go lane passed. Seven
real-checker regressions passed; failure-status and stale-report mutations
were rejected, and restored source passed. Actual Turbo restored both
reports byte-for-byte; full static/docs/workflow checks passed.

Closes #2238.
Refs #2190.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-13 01:02:35 +08:00
N0zoM1z0 8cbd57cafa fix(website): prioritize getting started across language editions 2026-10-13 00:59:21 +08:00
59be866719 fix(ghidra): encode Java probe output as UTF-8 (#2206)
* fix(ghidra): encode Java probe output as UTF-8

* fix(ghidra): preserve Unicode Java homes across supported JDKs

* test(ghidra): verify snapshot rejection without matching prose

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 00:45:23 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
8e004522f2 docs: define performance priorities for analysis workflows (#2245)
Set agent tokens and context first, analysis time second, and CPU and memory
third while preserving correctness, Evidence and existing contracts.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 00:42:06 +08:00
GamerDubzandClaude Opus 5.5 2da5e86289 fix(release): allow only version changes to plugin manifests after the notes target
The publication history check admitted the plugin manifests whole, so a
release could change plugin behavior, such as the bundled skill path or the
server environment, and still pass as release-only. Compare each manifest
across the release with its version and pinned server launch blanked, and
treat any other difference as a product change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:25:24 +02:00
d80b55e11b fix(adb): preserve existing bugreport archives (#2221)
* fix(adb): preserve existing bugreport archives

* fix(adb): retain bugreports after publication failure

Record acquired archive facts before publishing, preserve the staging archive when the exclusive copy fails, and keep retained evidence out of automatic cleanup. Reuse the same observation through publication and digest failures.

Add a real-filesystem ENOSPC regression covering error projection, existing evidence, subsequent collection and provider shutdown. Verified 96 focused tests, static checks and docs on macOS with simulated ADB; no real-device verification.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 00:25:17 +08:00
Luis ArayaandGitHub 85c5477930 docs(readiness): align required stage identifier and checks with evaluator (#2200) 2026-10-13 00:15:54 +08:00
Saim ShafiqueandGitHub 7724edbdd3 fix(process): report single empty settlement observation as unverifiable (#2232)
observeSettlement classified a group as alive_at_deadline when the settlement
budget expired after one empty observation, though no live member was ever
seen. Track observed liveness and return unverifiable when the deadline ends
observation with only empty rounds. The two-consecutive-empty rule for
quiesced is unchanged, and the cleanup_outcome projection for non-quiesced
settlements stays cleaned.

Fixes #2170.
2026-10-13 00:15:12 +08:00
GamerDubz 6ebae721f6 Merge remote-tracking branch 'origin/main' into feat/claude-code-plugin
# Conflicts:
#	docs/installation.md
2026-10-12 18:12:04 +02:00
0baad58e60 feat(ghidra): admit RISC-V ELF analysis through a pinned generic lane (#2151)
* feat(ghidra): admit RISC-V ELF analysis through a pinned generic lane

Refs #2063.

Recognize `EM_RISCV` (243) as a provider-neutral family that carries its own ELF
facts, and bound the first Ghidra lane to little-endian `ET_EXEC` images that
declare the double-float ABI, pinned to `RISCV:LE:32:default` or
`RISCV:LE:64:default` with the `gcc` compiler specification.

Ghidra 12.1.4 is not reliable without the pin: its `riscv.opinion` pins
`variant="default"` for the 64-bit constraint and leaves the 32-bit constraint
with the comment that the loader "must choose between 'default' and 'AndeStar'
variants". Measured on the installed release tree, a source-owned generic RV32
ELF with no Andes declaration imports as `RISCV:LE:32:AndeStar_v5:gcc`, while
RV64 resolves to `RISCV:LE:64:default:gcc`. The lane therefore states the
language per width and refuses any caller-selected processor outside it instead
of inheriting the header guess.

Refusals name the declaration rather than falling back to a guessed profile:
big-endian images (Ghidra 12.1.4 ships no big-endian RISC-V language),
non-`ET_EXEC` images, `EF_RISCV_RVE`, `EF_RISCV_TSO`, and every float ABI other
than double, which the pinned generic compiler specifications model. The raw
e_flags bits stay in Evidence as declarations, not as instruction semantics.

Family recognition now has one owner for the ELF-metadata families:
`ELF_METADATA_ARCHITECTURES`, `isElfMetadataArchitecture`, and
`HeaderArchitecture` replace ten `Exclude<BinaryArchitecture, "mips">` sites that
would each have needed a second hand-edited member. `ghidraPinnedLanguage` is
that owner for the import commitment: the session readback previously re-derived
only the MIPS pin, so a profile committing any second pinned language failed with
a language-mismatch error before the bridge started.

Hopper and IDA refuse RISC-V by name, matching the MIPS wording. Caller-supplied
raw-binary entry keeps its existing four-family list; this lane is ELF-header
admission only.

Verification on macOS 15.7.7 arm64, Node 24.18.0, Ghidra 12.1.4 PUBLIC and JDK
21.0.12.1, with the host decompiler built from the release sources for
`os/mac_arm_64`: `npm run check` passes; `npm run test:fast` passes 396 test
files; `npm run verify:test-discovery` discovers all 917 test files exactly once.
Source-owned RV32 and RV64 double-float `ET_EXEC` fixtures complete
`rea analyze --provider ghidra` and `rea decompile rea_riscv_entry`, committing
`RISCV:LE:32:default` and `RISCV:LE:64:default` with `language_selection:
provider-pinned-v1`; the soft-float RV64 shape is refused with the named
float-ABI reason, and the full refusal matrix including the RV32 and
width-mismatch shapes is covered by unit tests. No `verify:ghidra:riscv` lane
script is included in this change; the runs above were performed manually against
the installed engine. Fork pull requests receive no CI check runs, so the
real-provider scope above is the evidence available for review.

* fix(ghidra): bound the RISC-V lane by declared flags and document it

Address review feedback on #2151.

`e_flags` bits outside `EF_RISCV_RVC | EF_RISCV_FLOAT_ABI` are now refused with
the offending value named, so a reserved or newer producer declaration cannot
enter the verified generic lane by falling through the RVE, TSO and float-ABI
checks.

Admission reads `e_type` only, so the refusal text no longer calls `ET_EXEC`
static: a fixed-address declaration does not establish static linkage, and
`PT_INTERP` and dynamic dependencies are not inspected. The session records that
boundary as a limitation rather than inferring a load model.

Add `docs/ghidra-riscv.md` in the shape of the MIPS lane guide (admission
boundary, refusal table, why the language is pinned per class, Evidence fields,
and what the fixtures do not establish), and correct
`docs/firmware-analysis.md`, which still told users that MIPS and RISC-V
decompilation had not been added.

Verification after the change: `npm run check` and `npm run docs:check` pass; the
RISC-V module tests pass (17 lane cases plus 9 parser cases); on Ghidra 12.1.4
with JDK 21.0.12.1 the RV32 and RV64 double-float fixtures still analyze and
decompile with `RISCV:LE:32:default` / `RISCV:LE:64:default` and
`language_selection: provider-pinned-v1`, the soft-float fixture is still refused
by name, and the new linkage limitation appears in the emitted Evidence.

* fix(ghidra): align RISC-V pinning with admission

Reuse the complete lane admission boundary when selecting a language, retain unsigned unsupported flag bits in diagnostics, and derive float ABI metadata from the source declaration.

* test(ghidra): verify RISC-V CLI and MCP workflows

Build source-owned RV32/RV64 fixtures with Clang and LLD, establish independent ELF and symbol facts with LLVM readelf, and verify real provider evidence, instruction bytes, calls, decompilation, schema projections, snapshot replay, refusal diagnostics, source immutability and cleanup.

Verified on macOS ARM64 with Ghidra 12.1.4, JDK 21 and LLVM/LLD 23.1.2. Dynamic linkage, big-endian, Andes and RVE/TSO workflows remain unverified; ISA attributes are not inspected.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 00:11:35 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1770f38cfc test(native): verify real PE and DOS entry views
Record real Ghidra/MCP and Codex CLI verification of shipped ShareX native PE and FreeDOS DOS MZ entry views. Preserve original Evidence provenance, separate run-specific references from facts, and reject stale/colliding producer identities or unsupported model claims.

Sixteen focused experiment regression cases, original public-reply/transcript replays, and CI run 38273269072 pass for head 0667f7cb7c85f134311c7470ff4ee99ef9cc45f4. No production API/default change or general native speedup claim; final resource counters remain unknown. Continues #2016.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 00:04:00 +08:00
YsopwnableandGitHub 428eee00fd ci: keep validation fresh and preserve failed-shard reports (#2210)
Run the existing README consistency and Python producer guards from their
CI owners. Execute full local validation fresh while preserving cached
preparation, and permit scoped reuse of earlier portable build entries.

Collect failed-shard diagnostics before enforcing success and publish
compact, portable timing history only after a complete successful merge.
Preserve installer truncation failure across supported Bash versions.

Keep cancellation tests focused on the called MCP tool, observe server
progress before timing target readiness, and retain delayed-startup,
cancellation, cleanup, and live follow-up request assertions.

Validation: all 32 CI jobs and the separate Go lane passed; 935 files,
9,630 cases, 9,544 passed, 86 skipped, zero failed. Controlled regressions
and fault/restoration probes are recorded in PR #2210.

Refs #2190.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-12 23:42:47 +08:00
morlutoandJax367 94153f1c75 fix(uninstall): label YAML configuration syntax failures correctly
Report malformed Hermes and DeepSeek Harness documents as YAML. Carry the
compiled setup/uninstall diagnostics coverage from #2153 onto the existing
validation from #2157, including unchanged files and no backup writes.
Add malformed YAML scenarios for both clients.

Validation: 174 focused tests, npm run check, test discovery, package
verification, and npm pack --dry-run.

Co-authored-by: Jax367 <239124526+Jax367@users.noreply.github.com>
2026-10-12 23:38:39 +08:00
YsopwnableandGitHub ac6ef95e12 docs: define contribution scope and simplify proposal forms
Define REA contribution scope and fail closed on unresolved integration boundaries.

Make proposal forms easier to complete with structured choices, explicit unknowns, and optional technical details; welcome exploratory drafts while retaining maintainer review before integration.
2026-10-12 23:36:56 +08:00
b1bf4a70d4 fix(setup): name invalid client config shape errors clearly (#2157)
* fix(setup): name invalid client config shape errors clearly

Setup was dumping raw Zod issues for non-object roots or server tables,
and uninstall always claimed the file was not valid JSON. Report the
structural problem instead, and reserve parse failures for real syntax errors.

Fixes #2152

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

* fix(setup): preserve client configuration record validation

Keep descriptive shape errors while retaining Zod record validation and its own-property copy. Reject tagged YAML objects before setup or uninstall writes and prevent inherited JSONC properties from fabricating client registrations.

Add production-boundary regressions for tagged YAML roots and server tables, plus inherited Cursor and OpenCode settings. All 91 focused tests, npm run check, verify:test-discovery, verify:package, and npm pack --dry-run passed.

---------

Co-authored-by: Beqa Ksovreli <beqa@Beqas-Macbook-Pro-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 23:24:24 +08:00
7711b00005 fix(apple): preflight XML plist nesting before keyed archive decoding (#2161)
* fix(apple): preflight XML plist nesting before keyed archive decoding

inspect_keyed_archive documented a 128-level nesting limit, but the XML
plist path reached recursive decoding before any depth check: plist and
the prototype-key traversal in parseXmlPropertyList recurse once per
nesting level, so a ~75 KiB, 3000-level XML plist exhausted the call
stack and reported Maximum call stack size exceeded instead of the
bounded-resource limit that shallower over-limit documents (129, 200)
already produced. The CLI classified both as truncated, so no crash or
classification bypass was involved; the diagnostic was the defect.

Reject excessive nesting in parseXmlPropertyList before any recursive
pass, using the projection's own limit (MAX_PLIST_DEPTH) so the error is
identical to the one projectPlistValue raises: callers keep the
limit/truncated classification with no error-handler translation. Depth
counts values the way the projection does - container members nest one
level deeper, dictionary keys name members rather than nesting, and a
self-closing element is the value at its own level - so the guard
rejects exactly the documents the projection rejected, at the same
boundary, and every in-limit document decodes unchanged. The guard sits
at the shared decoder boundary, so Interface Builder, app Info.plist
resolution and native inspect_plist gain the same deterministic limit;
the binary plist path keeps its existing expansion preflight.

Binary plist behavior, ordinary XML decode, __proto__ omission counting,
and archive digest/Evidence provenance are unchanged. OpenStep
autodetection is untouched by this guard, as the existing Interface
Builder preflight also scopes its depth check to XML.

Regression coverage: decoder unit tests at and just past the limit for
dictionary and array nesting with self-closing and opaque markup,
module tests through decodeKeyedArchiveBytes including a valid
NSKeyedArchiver XML envelope at the limit, a production-boundary test
through inspectBundleKeyedArchive, and the compiled public CLI
reporting code truncated / reason limit for a 3000-level plist.

* fix(apple): check XML nesting against parsed element structure

Reuse the iterative XML decoder document for depth preflight and prototype-key lookup. Bound recursive plist wrappers alongside dictionaries and arrays without interpreting markup inside attributes or DOCTYPE declarations as values.

Validated with 104 focused tests including compiled CLI, MCP prototype-key, and Interface Builder coverage; npm run check and verify:test-discovery passed. No real-provider verification was run.

---------

Co-authored-by: Zburgers <Zburgers@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 23:12:10 +08:00
c9aecde580 fix(frida): reject an unreadable script file before starting a session (#2211)
* fix(frida): reject an unreadable script file before starting a session

The shared instrument_with_frida workflow started the Frida session, which
attaches or spawns the target, before the provider read a file script
source. A missing script file then failed loadScript with a bare
invalid_request, and session cleanup resumed the paused spawn, so the
target ran without instrumentation. Check that the selected file is a
readable regular file first and report the path, or access_denied for
EACCES/EPERM, before any device effect.

Fixes #2209

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

* fix(frida): let cancellation win over the script file check

An already-aborted request with an unreadable file source returned the
input or access-denied error instead of AnalysisCancelledError. Check the
signal before the script file preflight and again after it, so file
sources keep the existing cancellation semantics.

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

* refactor(frida): keep the pre-start checks out of instrument

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:10:39 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
ae412654d5 docs: preserve the native guide title anchor (#2224)
Keep the former native-ui-and-dispatch-investigation fragment after the
page title was changed in #2207. The old and current fragments resolve to
the same heading position in the generated page.

Validation: VitePress production build, generated-document checks, format
and link checks, and real Chrome navigation to both fragments.
Refs #2156.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-12 23:09:11 +08:00
86de439ba4 fix(frida): name the argument each Frida input error rejects (#2217)
Frida session lookups, script lookups, unreadable script files and the
instrument-with-frida PID and script checks returned invalid_request with
no issues, while the remediation told callers to correct the listed
arguments. Attach the session_id, script_id, path, target or script issue
with the reason so CLI and MCP callers can see what to fix.

Fixes #2213

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:00:59 +08:00
109769319a feat(clients): register Qoder CLI during setup (#1572)
* feat(clients): register Qoder CLI during setup

- SupportedClients: qoder definition (~/.qoder/settings.json, format
  json, QODER_CONFIG_DIR override honored)
- Qoder CLI documents mcpServers as the user-scope server table with
  command/args/env and stdio as the default transport; no personal
  skills directory is documented, so setup skips the skill copy
- tests: qoderClientConfiguration.test.ts (path resolution and
  QODER_CONFIG_DIR override, upsert preserving unrelated settings,
  fresh/empty configs, uninstall)

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/qoderClientConfiguration.test.ts 6/6.

* fix(clients): install Qoder CLI's personal REA skill

Review follow-up (#1572): the Qoder CLI Skills documentation lists
~/.qoder/skills/{skill-name}/SKILL.md with automatic discovery, so the
definition now carries skillPath following Qoder's configuration
location (QODER_CONFIG_DIR honored) and setup installs the bundled
skill copy there. The installation docs no longer claim Qoder has no
personal skills directory, and the bundled REA skill's supported
client list includes qoder.

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/qoderClientConfiguration.test.ts 7/7.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 22:55:55 +08:00
bfa805decf feat(clients): register Trae during setup (#1573)
* feat(clients): register Trae during setup

- SupportedClients: trae definition (VS Code-style user directory,
  User/mcp.json, format json)
- Trae documents mcpServers as the top-level server table with
  command/args/env and stdio as the default transport; it has no
  personal skills directory, so setup skips the skill copy
- tests: traeClientConfiguration.test.ts (per-platform user directory
  resolution, upsert preserving unrelated settings, fresh/empty
  configs, uninstall)

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/traeClientConfiguration.test.ts 6/6.

* fix(clients): install Trae's personal REA skill

Review follow-up (#1573): the Trae Skills documentation lists global
skills under ~/.trae/skills on macOS/Linux and %userprofile%/.trae/skills
on Windows, so the definition now carries that skillPath and setup
installs the bundled skill copy there. The installation docs no longer
claim Trae has no personal skills directory, and the bundled REA
skill's supported client list includes trae.

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/traeClientConfiguration.test.ts 7/7.

* docs(clients): qualify Trae configuration path evidence

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 22:53:30 +08:00
51d6c87cdb feat(clients): register ZCode during setup (#1564)
* feat(clients): register ZCode during setup

- SupportedClients: zcode definition (~/.zcode/cli/config.json, format
  zcode, personal skills at ~/.zcode/skills)
- ClientConfigurationDocument: zcode dialect reads/writes the nested
  mcp.servers table; withClientServers now tolerates a missing outer
  table for fresh documents
- docs: add ZCode to the supported-clients table with configuration notes
- tests: zcodeClientConfiguration.test.ts (path resolution, nested
  upsert, fresh/empty configs, uninstall)

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/zcodeClientConfiguration.test.ts 6/6.

* fix(clients): preserve ZCode fallback servers and honor enable flag

Review follow-up (#1564), two integration gaps:

- ZCode reads the user-scope ~/.agents/mcp.json fallback only while the
  native .zcode config defines no servers at all, with no merging. Setup
  now copies the fallback's mcpServers entries into the native table when
  writing the first native server, so registrations that only live in
  the fallback keep loading; the fallback file is left untouched. A
  malformed fallback fails setup closed (configure and preflight)
  instead of being silently suppressed.
- ZCode disables a server with "enable": false on its entry, which the
  generic status parsing (disabled/enabled) did not recognize: doctor
  reported such a registration as aligned although ZCode never starts
  it. The ZCode dialect now treats enable: false as disabled (state
  stale) and setup rewrites the entry on rerun.

Docs describe both behaviors; the bundled REA skill's supported client
list includes zcode.

Verified: tsc --noEmit clean; vitest run
tests/boundary/filesystem/zcodeClientConfiguration.test.ts 10/10;
npm run check:fast green; npm run verify:test-discovery 733 files.

* fix(clients): reject non-regular ZCode fallback files before reading

Resolve the fallback through the client-config transaction path and the
regular-file admission before parsing it, so a FIFO or other non-regular
file at ~/.agents/mcp.json fails setup closed instead of blocking the
read indefinitely. Directory and FIFO fallback cases are covered in
tests, and the preflight remediation names unreadable fallbacks too.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 22:51:40 +08:00
e4d3e1399d feat(flutter): identify Dart snapshot builds and inspect AOT payloads by pure APK parsing (#1582)
* feat(flutter): identify Dart snapshot builds by pure APK parsing

Add a flutter-provider family with one operation, identify_flutter_build,
that reads an APK's lib/<abi>/libapp.so and libflutter.so through the
hardened ZIP reader and reports build facts as observations:

- The Dart VM snapshot hash, read 20 bytes after every snapshot magic
  (f5f5dcdc). The vm and isolate sections are cross-checked: agreeing
  sections collapse to one snapshot_hash, disagreeing sections keep every
  candidate with snapshot_hash null and partial coverage.
- Per-ABI library digests and byte sizes, plus the engine library's GNU
  build-id note and any labeled 'Dart VM version' or Android clang/LLVM
  toolchain strings, verbatim; release builds without a version string
  report null rather than a guess.
- The APK is digested before parsing so Evidence binds the observation to
  the exact input, and a target with no libapp.so reports not-detected
  with complete coverage.

The scanner is streaming with explicit pending-magic and rescan
bookkeeping, so chunk boundaries cannot double-count or drop sections, and
magic-free streams never accumulate. No engine, no external lookups: the
hash-to-SDK mapping stays with external datasets by design.

Verified against Flutter Gallery 2.9.2 (112 MB, three ABIs): every ABI
reports the same agreeing hash 65817c30a78bb44c3dc3771876b6010a from two
sections, per-ABI build-ids, and the clang toolchain line; a plain
aapt2-built APK reports not-detected with complete coverage. Scanner
goldens come from the same libraries. READMEs carry the new 'Flutter apps'
row in every supported language.

* feat(flutter): inspect Dart AOT snapshots through the ELF symbol table

Add inspect_dart_aot to the flutter family: for one ABI's libapp.so it
locates the four _kDart*Snapshot* symbols through the ELF dynamic symbol
table (ELF32 and ELF64 little-endian, bounds-checked reads; a payload
without exported symbols is refused), reports each section's offset and
size, validates data-section headers (magic, kind, length; instructions
sections honestly report nulls for the fields their layout does not
carry), and projects a bounded string pool from the isolate data section:
distinct package: source URIs (the app's dependency inventory), dart: SDK
URIs, and identifier-shaped tokens, each with counts, bounds, and partial
coverage when truncated.

The string pool is labeled as a byte-pattern projection, not deserialized
cluster semantics: typed class and function recovery remains an
SDK-specific parser, which the snapshot hash now unlocks. The ABI is
caller-selected with a deterministic default (arm64-v8a, x86_64, then
first available).

Verified against Flutter Gallery 2.9.2: all four symbols on both ELF64
(arm64-v8a) and ELF32 (armeabi-v7a) images, header validation, hash
agreement with identify_flutter_build, and a 756 package: / 201 dart:
URI pool from the isolate data section; goldens in DartElf.test.ts come
from the same libraries.

* fix(flutter): own snapshots, bound image reads, and preserve cancellation

Mirror the ownership bar the maintainers applied across the merged JEB,
ADB, and Apktool PRs:

- Copy the admitted APK into a provider-owned, read-only snapshot (EXCL
  create, chmod 0400) before digesting and parsing; every later read uses
  the snapshot, so a concurrently modified original cannot mix into one
  observation. The result reports the snapshot's byte size and digest.
- Retain failed snapshot-root cleanup in a provider set and retry it on
  close(), throwing an aggregate error when a root is still owned; root
  removal is injectable so the retention is testable.
- Allocate the AOT image buffer at exactly the declared library size
  before reading (bounded before allocation, not concat-after-stream),
  keep the 256 MiB ceiling, and check cancellation between digest and
  stream chunks through a captured abort reason.
- Accept APK paths containing spaces (covered by a fixture test) and
  escape the ABI pattern's dash for v-mode regex compatibility.

Also add tests for cleanup retention with close retry and for
cancellation before any snapshot root is created.

* docs(flutter): cover the Italian README with the Flutter row

The Italian translation landed between the merged feature PRs; its table
now carries every merged row plus this one, keeping its documentation-link
set identical to README.md.

* fix(flutter): make the ABI pattern portable for v-mode validators

Use a hex-escaped hyphen in the ABI character class, matching the form
the ADB portability fix uses, so clients compiling advertised schemas in
unicodeSets mode accept the inspect_dart_aot input schema.

* refactor(flutter): reuse shared harnesses and deduplicate the vertical

The duplicate-detection gate (jscpd, 2.5% threshold) tipped to 2.51% with
the flutter files added. Reuse the existing machinery instead of repeating
it:

- FlutterBuildAnalysisService delegates to executeProviderOperation, the
  shared admission/Evidence composition, instead of restating its body.
- The CLI registers both commands from one table-driven spec.
- Boundary tests use the shared serveCli and assertAdvertisedMcpContracts
  harnesses rather than hand-rolled equivalents.
- The provider test consolidates the four payload-shape cases into one
  table-driven case; the service test uses one admission table and
  distinct phrasings.

Duplicate lines fall from 2.51% to 2.48%, back under the gate.

* fix(flutter): address review findings from real-app verification

CassieFox23 verified the PR against three real F-Droid Flutter apps and
a control app, and found five gaps. All are fixed and re-verified against
cone (info.tangential.cone), one of the tested apps:

- Build-id: the scanner now reads the descriptor length from the note
  header instead of assuming 20 bytes, so 8-byte ids from older
  toolchains parse; the schema accepts 8-128 hex characters. Verified
  against cone's readelf-reported ids on all three ABIs.
- Compiler line: the toolchain capture bound rises from 200 to 500
  characters, keeping cone's 262-character clang line verbatim.
- Zero-byte libapp.so: the extraction target is created before streaming,
  so an empty entry yields a clean unsupported_target refusal for
  inspect_dart_aot and elf_header_valid: false with partial coverage for
  identify_flutter_build, instead of a raw ENOENT with code UNKNOWN.
- Truncated library: identify_flutter_build marks fewer than two snapshot
  sections as partial coverage with a limitation naming the count, and a
  missing ELF magic gets its own limitation.
- Docs: the default ABI preference now matches the code exactly
  (arm64-v8a, x86_64, x86, armeabi-v7a, then the rest alphabetically).

Also: every test that read the author's /tmp/opencode goldens now builds
a self-contained synthetic ELF64 image with dynsym, four snapshot
symbols, two snapshot sections, and a string pool, so the suite runs
anywhere; the verify lane now requires a present hex build-id per ABI
instead of accepting null.

* fix(flutter): address the Codex review findings

Eight findings, all applied:

- P1: SnapshotHashScanner keeps candidates in a Set (no quadratic
  Array.includes) with a 64-distinct-hash cap, so a hostile payload of
  magic+hash records cannot stall the scan or bloat the response.
- P1: projectStringPool stops admitting new distinct entries once each
  category reaches twice its projection bound and reports
  collectionCapped; allocation and sorting now track the response, not
  the payload.
- P2: ZIP reader rejections during entry iteration or entry streaming
  map into the typed error algebra (cancellation vs input refusal)
  instead of escaping as unhandled exceptions in both operations.
- P2: magic_valid now marks coverage only for the *Data symbols; a
  complete four-symbol image with valid data magics reports complete
  coverage (the instruction symbols intentionally lack the magic).
- P2: the MCP server closes the Flutter provider during shutdown, so a
  retained snapshot root is retried and reported, not leaked.
- P2: LabeledStringScanner commits matches only when a printable run is
  delimiter-terminated; the carry grows to 600 bytes so a 500-character
  toolchain line survives any chunk boundary untruncated.
- P2: both Flutter tool effects declare writesFilesystem: true; the
  owned APK snapshot and extracted libapp.so are temporary writes.
- P2: the verify lane asserts every ABI agrees on exactly one snapshot
  hash instead of discarding the computed set.

* fix(flutter): address the second Codex review round

Nine findings on the first-round fixes:

- P1: identify_flutter_build enforces a cumulative extraction budget
  (768 MiB) across every library entry, not just per library, so an APK
  with many ABIs cannot stream an unbounded total.
- P1: a single NUL-free printable run stops materializing at 1024 bytes
  and marks the pool capped, so an adversarial one-run section cannot
  allocate unbounded strings.
- P1: ELF cString bounds the NUL-terminator scan to the name maximum
  plus one, so malformed symbol tables cannot scan the whole image per
  entry.
- P2: mapReaderFailure distinguishes ArtifactReaderFailure texts —
  budget/limit rejections map to resource constraints, integrity
  failures (CRC/overlap/signature) to their own input refusal — instead
  of collapsing all reader failures into one message.
- P2: the labeled-string carry is cleared when a delimiter commits a
  run, so consecutive toolchain lines are never concatenated.
- P2: string-pool counts are renamed *_count_at_least and documented as
  lower bounds, because capped collection discards values beyond twice
  the projection bound.
- P2: inspect_dart_aot marks hash disagreements, non-pair source counts,
  and candidate-cap saturation as partial coverage.
- P2: only valid lowercase-hex hash records count as snapshot sources;
  complete magic records with non-hex fields do not inflate agreement.
- P2: the per-entry size refusal in inspect_dart_aot moves after ABI
  selection, so an oversized unselected ABI no longer refuses a small
  request.

* fix(flutter): address the third Codex review round

Five findings:

- P2: the owned snapshot revalidates the source after the copy (size,
  mtime, inode), so an in-place replacement or swap mid-copy refuses
  the observation instead of publishing mixed bytes.
- P2: a failed ZipArtifactReader close retains the snapshot root for
  close() retry instead of silently swallowing the descriptor leak,
  in both operations.
- P2: snapshot headers are parsed within their symbol bounds and a
  symbol smaller than the 16-byte header reports magic_valid false,
  so trailing-image bytes can no longer fake a valid data section.
- P2: the verify lane counts provider.close() cleanup failures as
  lane failures instead of certifying a run with retained roots.
- P2: LabeledStringScanner reports toolchain_capped when further
  distinct matching lines are observed past the 4-line projection;
  identify marks such results partial with a naming limitation.

* fix(flutter): address rounds three and four of the Codex review

Round three (one missed finding) plus round four (five findings):

- P2: the GNU build-id note descriptor size is decoded from all four
  bytes of the little-endian field, rejecting nonzero high bytes
  instead of masking them to 16 bits.
- P1: the Flutter MCP handlers route through createContractToolHandler
  and the session's analysisAdmission, so close_binary and snapshot
  operations drain in-flight Flutter calls instead of racing them.
- P2: the owned-snapshot copy is a streamed, abort-checked write (no
  copyFile), so cancellation stops the write promptly while the
  source-identity revalidation (size, mtime, inode) still guards
  against mid-copy replacement.
- P2: identify marks any snapshot_hash_sources value other than exactly
  two as partial, matching the inspection path's pair requirement.
- P2: the verify lane refuses to run without --apk (exit 2) instead of
  printing a false-green pass, and the engine check requires every
  libapp-carrying ABI to have a present libflutter with a valid hex
  build-id rather than filtering missing engines away vacuously.

* fix(flutter): address the fifth Codex review round

Six findings plus the upstream duplication baseline:

- P2: the flutter-provider kind reports available in the capability
  inventory, so binary_session no longer lists always-registered
  Flutter tools as provider_missing.
- P2: printable runs that exceed the 1024-byte cap are discarded at
  their delimiter instead of emitting their truncated prefix as a
  fabricated observation.
- P2: ELF symbols must fit inside the file-backed portion of their
  PT_LOAD segment (p_filesz), so addresses in gaps or BSS are refused
  instead of accepting unrelated bytes.
- P2: the build-id carry spans the complete 80-byte maximum note
  (16-byte header + 64-byte descriptor) so chunk-boundary splits
  cannot drop the first byte.
- P2: the labeled-string carry is sized for the toolchain pattern's
  true 605-byte maximum, not the previous 600.
- P2: the AOT completeness predicate requires exactly two sources and
  one candidate unconditionally, so zero-source images with valid data
  magics report partial instead of complete.

Also: the shared ELF fixture builder and snapshot-section helper moved
to a dedicated FlutterTestFixtures module, eliminating cross-file test
duplication; jscpd now passes at 2.50% while main sits at 2.52%.

* fix(flutter): address the sixth Codex review round

Five findings:

- P2: snapshot-hash scanning is bounded to the exported data symbols'
  byte ranges, so decoy or stale magic/hash sequences elsewhere in the
  image cannot inflate source counts or contribute candidates.
- P2: labeled-string runs longer than the carry bound are rejected
  instead of truncated, preventing a regex prefix from being published
  as a verbatim source string.
- P2: the AOT image reread checks cancellation between file reads and
  before the synchronous parsing stage, so a cancelled caller cannot
  receive a success from a completed 256 MiB read.
- P2: the verify lane requires exactly two snapshot sources per ABI,
  matching the provider's pair requirement, instead of accepting any
  count >= 2.
- P2: ELF section and program header entry sizes are validated against
  the class-defined minimum widths before walking either table, so
  undersized entries cannot cause field reads to cross into
  neighboring bytes.

* fix(flutter): address the seventh Codex review round

Six findings:

- P2: data-symbol ranges are deduplicated by offset and size, so aliased
  symbols mapping to the same bytes are scanned once and the result
  marks partial coverage with a limitation naming the shared range.
- P2: openEntrySafely forwards return/throw to the underlying Readable's
  iterator, so an early for-await exit closes the ZIP entry stream and
  releases its producer instead of leaking a backpressured pipe.
- P2: inspect_dart_aot now reports snapshot_hash_candidates and
  snapshot_hash_sources alongside snapshot_hash, so a null hash from
  conflicting evidence is distinguishable from missing hash data.
- P2: overlong printable runs only set toolchain_capped when they begin
  with a recognized label prefix (Dart VM version: or Android (); other
  long strings such as license text are skipped without marking partial.
- P2: readElfSymbol falls back to PT_DYNAMIC/DT_SYMTAB when section
  headers are absent (e_shnum 0, ET_DYN), so stripped Flutter libraries
  with exported snapshot symbols are no longer refused; the dynamic
  table is bounded by DT_STRSZ, DT_SYMENT, and the image end.
- P2: the verify lane exercises inspect_dart_aot for every ABI carrying
  libapp.so, not just the preferred default.

* fix(flutter): address the eighth Codex review round

Five findings:

- P2: each data-symbol range gets a fresh SnapshotHashScanner so the
  carry from one noncontiguous slice cannot fabricate a record spanning
  into the next; results are aggregated after scanning.
- P2: dynsym name lookups are bounded to the linked string table —
  st_name offsets outside the table and terminators beyond it are
  rejected instead of reading unrelated payload bytes as symbol names,
  in both the section-header and PT_DYNAMIC paths.
- P2: identify_flutter_build routes its reader.entries and reader.open
  calls through the same collectEntriesSafely and openEntrySafely
  wrappers as inspect_dart_aot, so malformed ZIPs surface as input
  refusals instead of AnalysisProtocolError.
- P2: mapReaderFailure recognizes ArtifactReaderFailure texts carrying
  a cancelled reason, mapping them to AnalysisCancelledError instead
  of AnalysisInputError.
- P2: the cumulative extraction cap is enforced inside the chunk loop,
  not after each library completes, so refusal happens before the
  remaining bytes are streamed and hashed.

* fix(flutter): harden streaming, sizes, and cancellation after self-review

Four issues found in a systematic self-audit across all bug classes
from eight Codex rounds:

- createReadStream in both the digest function and the owned-snapshot
  copy now receives the AbortSignal directly, so cancellation aborts
  the filesystem read at the source instead of waiting for the next
  chunk to arrive on a slow filesystem.
- The owned snapshot enforces a 2 GiB APK-level size cap before any
  copy I/O, so a path pointing at an enormous file is refused before
  disk usage grows.
- identify_flutter_build preflights the sum of declared entry sizes
  against the 768 MiB cumulative budget, refusing before any ZIP
  extraction when the declared total already exceeds it.
- The LabeledStringScanner triple result() call is collapsed to a
  single invocation (the previous code called result() three times in
  the emission, relying on end() being idempotent).

No behavior change for valid inputs; all 36 tests, check:fast, and
the cone verify lane pass unchanged.

* fix(flutter): require structurally parseable ELF headers for complete coverage

CassieFox23's third corrupt-header case: an ELF with valid magic but
zeroed section/program header offsets passes the 4-byte magic check
and the byte scanner still finds hash patterns in the data sections,
so identify_flutter_build reports complete coverage for a structurally
broken file.

The elf_header_valid check now also verifies beyond the magic:
e_type must be a known value (ET_REL/ET_EXEC/ET_DYN), at least one
of e_shoff/e_phoff must be non-zero, and at least one of
e_shnum/e_phnum must be non-zero. A file with valid magic but zeroed
structure reports partial coverage with the existing ELF-magic
limitation.

* fix(flutter): validate ELF and snapshot coverage

Share bounded ELF header parsing between identification and inspection. Accumulate headers across ZIP chunks and validate table entry widths and extents before claiming complete identification coverage.

Bind AOT hashes to the exported VM and isolate headers, validate stored snapshot lengths including the magic bytes, and preserve partial evidence for missing, malformed, aliased, or overlapping data headers.

Preserve native stream cancellations and APK resource constraints across both operations. Bound snapshot copies while streaming and retain useful input-failure diagnostics and lower-bound count wording.

Validation: 72 focused tests; check:fast; check including formatting; docs:check; test discovery; jscpd; real cone APK and non-Flutter control on Linux. Verified CLI/MCP parity for valid and corrupted ELF headers and AOT inspection across three ABIs. Native Windows and macOS were not run.

* fix(flutter): unify artifact ownership and bound ELF evidence

* fix(flutter): preserve ELF provenance and partial coverage

Read GNU build IDs only from bounded ELF note ranges and retain conflicting candidates. Preserve source versus owned storage failures and honor cancellation through parsing and final cleanup.

Separate snapshot structure from string-pool coverage. Verify every Gallery ABI with digest-bound engine labels and URI counts, and document the supported snapshot-kind layout.

---------

Co-authored-by: REA Contributor <contributor@rea.tools>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 22:35:55 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
7ae3c50a9d docs: correct drift and reorganize English guides (#2207)
Put setup, developer commands, MCP replies, and investigation recipes before
client details and implementation references. Preserve existing guide anchors,
examples, provenance, partial-result recovery, and provider restrictions.

Correct verified documentation drift, synchronize the Ghidra capability
wording, and regenerate metadata for skill version 40. Keep uncertain platform
and lifecycle findings unchanged. Refs #2156.

Validation: 56 focused tests, check:fast, docs:check, VitePress production build,
format and link/example checks. Real isolated CLI/MCP probes cover Ghidra,
NativeAOT, HAR, cancellation, Chrome, large ELF delivery, native type limits,
and the new JS/managed/process/setup recipes. Heavy work uses two CPUs;
a resource-heavy DOM probe was stopped and replaced by a smaller real CDP
payload-limit control. Windows, macOS GUI, ADB, and the full CI matrix were
not executed.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-12 22:14:25 +08:00
akushonkamenandGitHub 10400fdb57 fix(ida): thin the selected FAT slice into the private headless input (#2201)
Universal Mach-O admission reported the selected slice architecture while the IDA provider's private input copy handed the whole FAT container to upstream idalib, which then picked its own slice (x86_64 for /bin/cat) while Evidence labeled the session arm64. FAT parsing now exports the selected slice's byte range as universalSlice, and the private input copy writes exactly those bytes: the whole source digest still anchors the copy against admission-time changes, and the thinned result anchors the post-operation identity check.

Thin targets keep the whole-file copy, and mach-o-universal remains the container format across Evidence, snapshots, and provider routing.

Validation: typecheck, oxlint (no new warnings), oxfmt, and targeted vitest runs — ida adapters 12 files/116 tests, domain and services projects 60 files/736 tests, ghidra/hopper behavior 2 files/50 tests, filesystem and universal-extraction boundary 2 files/49 tests, native signature MCP 1 file/2 tests, plus a new thinning suite (5 tests, red before the fix: 3 failed). The thinned product was verified byte-identical to the `clang -arch arm64` input of a `lipo -create` universal fixture. Controlled fixtures only; no live IDA analysis was performed for this change.

Closes #2139
2026-10-12 22:07:49 +08:00
4a4ae9b2b9 feat: add Frida instrumentation support (#1359)
* feat: add Frida instrumentation support

* fix: resolve Frida CI checks

* test: cover Frida contract validation branches

* docs: link Frida instrumentation guide

* fix(frida): handle cancellation cleanup safely

* fix(frida): address PR review findings

* fix(frida): preserve resume and message observations

* fix(ci): use canonical registry for rolldown

* fix(frida): declare foreign code effects

* fix(frida): retain failed startup resources for cleanup retries

Keep failed startup session and spawn handles owned until detach, resume,
and remote lease cleanup complete. Serialize retries and release each
remote reference once. Await CLI provider cleanup while preserving the
primary failure and any completed observations.

Add regressions for retained startup resources, concurrent cleanup,
shared remote leases, and CLI cleanup outcomes.

Validation: 41 focused tests pass on the PR head and its latest-main
integration; serial static and docs checks pass. Real Linux Frida 17.23.2
workflows and the native spawn/controlled-failure cleanup probe pass.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

---------

Co-authored-by: N0zoM1z0 <1705004398@qq.com>
2026-10-12 22:01:58 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
0b7145361e feat(wasm): add opt-in first-call summary delivery (#2193)
Add explicit first-call WASM summary delivery while preserving omitted/complete MCP requests, CLI output, original Evidence identity and provider inputs. Reuse exact retained export views and schema-based capability discovery.

Nine paired public SDK comparisons and ten actual Codex CLI answers preserve selected facts and source references. Record token reductions alongside mixed latency and unverified final resource-counter conditions. Current-main integration passes seven relevant WASM MCP workflows, a fresh real-WABT SQLite workflow and required static gates. Refs #2016.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-12 22:01:47 +08:00
7c9c1ebdb7 fix(mcp): keep shared optional providers open across discarded probes (#2205)
startMcpTransport loads optional observation providers once per process
and passes them to every server instance the SDK constructs. Each
instance's closeProviders also closed the shared browser scenario
provider, so when the SDK discarded a server/discover probe instance for a
legacy initialize, every later capture_browser_scenario call failed with
"Browser scenario provider is closing." Server instances no longer close
the caller-supplied provider; the transport returns
closeOptionalProviders and process shutdown closes it once.

Fixes #2195

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:56:45 -07:00
d35035780a fix(setup): remove emptied skill directories on uninstall (#2182)
* fix(setup): remove emptied skill directories on uninstall

Uninstall removes only the managed skill files so user files survive,
but it left every emptied reverse-engineer-anything directory and its
references/ subdirectory behind. After the ownership manifest is removed,
remove the managed directories deepest first with a non-recursive rmdir,
which keeps any directory that still holds user files.

Fixes #2159

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

* fix(setup): report emptied skill directories that cannot be removed

Only a directory that still holds user files, is already absent, or is no
longer a directory is kept silently. Any other rmdir failure, such as a
parent that denies deletion, now fails the skill item with the directory
path instead of reporting the skill as removed.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:54:38 -07:00
51367cae6f fix(setup): name structural problems in client configurations (#2154)
A client configuration that parses but whose root or MCP server table is
not an object (for example {"mcpServers": null} or []) made setup print a
raw Zod issue dump and made uninstall claim the file was not valid JSON.
parseClientConfiguration now raises a ClientConfigurationShapeError naming
the root or the servers key, which setup reports as the malformed detail
and uninstall reports instead of a syntax error.

Fixes #2152

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:53:55 -07:00
90e37f4152 fix(managed): report malformed PE layouts as invalid input (#2194)
* fix(managed): report malformed PE layouts as invalid input

readManagedPeLayout raises ManagedReaderFailure for DOS, COFF and section
header failures such as section data past EOF. Those escaped the managed
inspectors, so ManagedStaticProvider wrapped them as ProviderAdapterError
and the CLI and MCP reported execution_failure with advice to run rea
doctor. Map format failures to an invalid_format input issue carrying the
reader's detail, as inspect-pe-resources and inspect-go-binary do.

Fixes #2191

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

* fix(managed): route unmappable PE layouts through one input error

inspect-managed-native-boundaries caught a layout-level ManagedReaderFailure
itself and returned an empty malformed inspection with exit 0, while the
other managed operations now report the same bytes as invalid input. Let
the layout failure reach the provider's invalid_format mapping; metadata
failures after a readable layout still return malformed coverage.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:53:26 -07:00
df2c08b546 fix(jeb): confine an invalid REA_JEB_MCP_URL to the JEB tools (#2145)
JebProvider throws when REA_JEB_MCP_URL fails endpoint validation, and the
composition built it eagerly for the CLI and every MCP connection. A
malformed value, or http://localhost:8425/mcp, therefore crashed every CLI
command with a stack trace and failed every MCP connection with an
internal server error. The composition now parses the endpoint first and,
when it is unusable, returns a JEB port that reports not_configured
availability and a ConfigurationError naming REA_JEB_MCP_URL.

Fixes #2143

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:49:15 -07:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
58fb372071 feat(analysis): add opt-in retained inventory and WASM views (#2122)
Add opt-in retained inventory and WASM Evidence views with exact nested output contracts and a small static WASM descriptor. Preserve old complete CLI/MCP workflows, producer choices, original authenticated Evidence and cleanup ownership.

Validated with real public applications, genuine old Evidence migration, 123 focused assertions, 11 synchronized description checks and 42 installed-package WABT cases. Detailed generated experiment JSON stays outside the final tree; concise findings and reproduction inputs remain. Main-existing completion/package CI failures are separately attributed and left unchanged.

Refs #2016

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-12 21:17:52 +08:00
3159732597 fix(cli): report an invalid REA_LOG_LEVEL as configuration (#2140)
createCli parsed REA_LOG_LEVEL with a throwing schema, so an invalid or
uppercase value crashed every command, including doctor and --help, with
a ZodError stack trace. Parse it into a ConfigurationError result and have
the executable print the same configuration message as the MCP adapter
and exit 1 before building the CLI.

Fixes #2135

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:07:37 -07:00
4f46eac453 ci: narrow native-host lane selection and reuse build caches (#2131)
* ci: narrow native-host lane selection and reuse build caches

Unowned src/ and bridge/ files no longer fall back to the full baseline.
They run the Linux suite plus the Linux x64 installed-package/Inspector
baseline. New apple and package scopes own the Apple artifact lane, the
package matrix and the Windows curated lane, including the test files those
lanes invoke. Manual-only verifier entrypoints run static checks and the
Linux suite. CHANGELOG, SECURITY, GitHub templates and the native Windows
README are documentation-only.

Pull requests omit the macOS x64 package host unless labelled ci:full.
The Windows and Apple lanes restore the shared Turbo build cache, the
Windows lane drops its duplicate typecheck, and the Linux suite uses six
shards. Main pushes keep the full baseline.

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

* ci: match package script entrypoints against scope globs

Script-only package.json edits resolved their entrypoint with literal
equality, so glob-owned verifiers such as scripts/verify/apple/** and
scripts/verify/inspector/** fell back to the full baseline instead of
selecting their owning lane.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:07:22 -07:00
GamerDubz 677997bd59 Merge remote-tracking branch 'origin/main' into feat/claude-code-plugin 2026-10-12 15:06:48 +02:00
5190843f8f fix(setup): name each skill directory in the uninstall summary (#2118)
Uninstall checks every skill destination but labeled each result only as
"skill" (or "Claude Code skill"), so a retained or failed destination
could not be located. Label each result with the directory it refers to.

Fixes #2111

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:55:15 -07:00
60259a91d9 fix(managed): validate CLI type selections with the member contract (#2125)
`inspect-managed-members --type` accepted repeated and empty names that
the MCP contract rejects. The provider's contract parse then threw, and
the CLI reported execution_failure with advice to run `rea doctor`.
Reuse the contract's type_names schema so the CLI rejects them as a
validation error on --type.

Fixes #2121

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:26:32 +08:00
672bdea21f fix(target): recognize binary plists by magic under any extension (#2133)
Target resolution admitted a property list only when its name ended in
.plist, so keyed archives with other extensions (for example .archive)
were rejected as an unsupported binary format before any plist tool ran.
Classify bplist00 bytes as a plist, as the artifact inventory already does.

Fixes #2129

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:09:53 +08:00
morluto fe4b94a661 test(mcp): align completion checks with provider readiness
Exercise cold and warm completion through ordinary provider operations. Bind fixture observations to the selected analysis profile so the warm-up call satisfies session validation, and make package completion candidates explicit with useful failure diagnostics.
2026-10-12 18:44:02 +08:00
xin zhuangandGitHub abc8c6dadf fix(wasm): preserve leading BOM in custom section names (#2102) 2026-10-12 18:39:01 +08:00
621cee192d test(mcp): ready providers before live prompt completion checks (#2137)
07b36c148 made document and procedure completion read only from an
already-started provider client. Two checks still completed right after
opening a target, so they observed an empty cold result:

- promptCompletion.test.ts used a fixture client without executeIfReady
  and never ran an ordinary operation, so the composite provider had not
  created a client yet.
- The packaged MCP smoke expected live completions on non-Linux hosts
  immediately after open_binary, before current_document started Hopper.

Both now assert the cold result is empty, ready the provider through an
ordinary operation, and then assert the unchanged live candidates.

Fixes #2123

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:36:58 +08:00
GamerDubzandClaude Opus 5.5 b22d5245b1 fix(release): require the full package specifier in plugin launch validation
The checkpoint stripped an optional rea-agents@ prefix before comparing,
so a manifest launching a bare release number passed even though npx
would fetch a package named after that number. Compare the whole
specifier and report the launched value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:48:37 +02:00
GamerDubzandClaude Opus 5.5 5806c94008 fix(release): reject release candidates with stale plugin manifests
The publication checkpoint validated package, lockfile, server and
generated metadata versions but not the plugin manifests, so a candidate
could be tagged with a manifest version or pinned server launch left at
the previous release. Validate both fields in both manifests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:31:47 +02:00
morluto c22111eca2 fix(process): return private runtime ownership at allocation
Use POSIX mkdtemp owner-only allocation directly instead of adding a fallible permission rewrite and rollback before returning the cleanup owner. Respect restrictive umasks, assert privacy and real scratch access, and align provider documentation.
2026-10-12 16:30:53 +08:00
morluto 07b36c1489 fix(mcp): keep prompt completion within ready provider sessions
Avoid lazy provider startup for document and procedure completion. Propagate MCP cancellation through completers, cancel lookups during session transitions, and share provider result projection with ordinary operations. Cover cold, starting, warm, queued, cancelled, and closed sessions through MCP composition and provider boundaries.
2026-10-12 16:27:51 +08:00
morluto 0d5033ad12 fix(checks): order artifact-backed lint after compilation
Prevent import resolution from racing with dist cleanup in build-backed gates while retaining the source-only fast check.
2026-10-12 16:17:36 +08:00
morluto d1dcebce2c fix(evidence): preserve literal keys and response body ownership
Retain exact empty and computed literal keys in JavaScript observations. Bind CDP response bodies to request occurrences, account for late responses, and validate the target after final progress callbacks.
2026-10-12 16:17:29 +08:00
GamerDubzandClaude Opus 5.5 c224ce482a docs(skill): make connection recovery aware of plugin installs
The bundled skill said installing it never registers the MCP server and
sent every missing-tools case to rea doctor --client and rea setup. For a
plugin-only install those report missing and setup would add a second rea
server. Tell the agent how to recognise a plugin install and to use the
client's own plugin and MCP listings instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:07:49 +02:00
GamerDubzandClaude Opus 5.5 102661ca6f fix(plugin): pin the plugin server launch to the release version
Both manifests started `rea-agents@latest`, so a server start could run a
different package version from the installed plugin and bundled skill, and
would execute whatever npm's latest tag pointed at. Pin the launch to the
manifest version and have Release Please rewrite it with each release.

Release Please reserializes JSON extra-files with JSON.stringify, which the
formatter would reject in every release PR. Store both manifests in that
form, exempt them from the formatter, and assert the form in conformance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:50:02 +02:00
morluto 79156e5bed test(cli): preserve Windows runtime paths and failure diagnostics
Supply the system root and temporary directory in isolated Windows CLI environments, while preserving explicit caller overrides. Include captured command output in web acceptance exit-code failures so startup and codec errors remain diagnosable.
2026-10-12 15:46:42 +08:00
YsopwnableandGitHub 974f11d010 docs: clarify responsible use and preserve third-party notices (#2082)
Ship the responsible-use statement, third-party attribution records and
preserved license texts in npm packages. Clarify historical-version and
channel coverage without changing existing licenses or installation behavior.

Assisted-by: Eiki (gensokyo-skills:eiki-rule-review)
Assisted-by: Aya (gensokyo-skills:aya-source-investigation)
2026-10-12 15:26:26 +08:00
xin zhuangandGitHub 75a4a14fa0 fix(artifacts): compare package signatures without ASCII folding (#2069) 2026-10-12 15:18:03 +08:00
morluto b3dec005bc test(hopper): align startup fixtures with cleanup and lease contracts
Inspect the typed primary startup failure retained by aggregate cleanup errors. Materialize isolated targets before real lease acquisition so launcher scenarios reach the behavior under test. Repairs the Linux shard and Windows curated test failures without weakening cleanup retention coverage.
2026-10-12 15:02:43 +08:00
Saim ShafiqueandGitHub d4e8ef7499 fix(ghidra): retain launcher command outcome in launch failure diagnostics (#2011)
* fix(ghidra): retain launcher command outcome in launch failure diagnostics

Ghidra headless launch failures surfaced only the GhidraLaunchError name
and a generic message, dropping the launcher command's exit code/signal
and JVM stderr. Retain the failed command outcome on GhidraLaunchError in
the shared captured-output envelope and project it as launch_outcome in
the session failure_cause, mirroring the compiler_outcome diagnostics.

Fixes #1992

* test(ghidra): skip POSIX JVM fixture tests on Windows

The bin/java fixture is an extensionless POSIX shell script handed to
execFile, which Windows cannot execute. Follow the repo pattern
(test.skipIf) so the suite is a named Windows skip.
2026-10-12 14:59:18 +08:00
xin zhuangandGitHub 9b780c47bc fix(browser): isolate script URLs with empty query or fragment (#2074) 2026-10-12 14:56:51 +08:00
morluto 98d8470d36 fix(io): consolidate managed snapshots and retain cleanup ownership
Reuse managed snapshot admission and hashing in sequential path comparisons, retaining caller paths and completed inspection Evidence on later failure. Preserve exact failed-close owners alongside typed read and cancellation failures, and retry them through caller-owned resource scopes. Require canonical Hopper lease identities, remove signed offset coercion, and repair stale setup cleanup expectations that failed CI.
2026-10-12 14:46:37 +08:00
morluto e9970855ba fix(evidence): preserve trace uncertainty and partial provider observations
Distinguish an unresolved goal from an exhausted call graph and preserve provider failures through cancellation. Retain completed IDA replies and post-operation identity observations as partial Evidence without claiming a verified final binding. Remove source-wide bundler cache-name heuristics and stale expectations.
2026-10-12 14:46:10 +08:00
morluto fa87dd2aac docs: shorten responsible-use notices across languages 2026-10-12 14:27:37 +08:00
morluto d864ad5471 fix(docs): synchronize disclaimer links across translations 2026-10-12 14:18:36 +08:00
morluto 4a5aec8ad6 docs: add responsible-use disclaimer 2026-10-12 14:14:36 +08:00
morluto d8c76b6296 fix(io): retain cleanup ownership and honor cancellation
Keep failed JSON read handles in the existing server resource scope and fail admission until cleanup succeeds. Thread cancellation through every process CLI input read and replace timer-driven cancellation checks with a shared first-read gate.

Classify authenticated standalone Evidence as wrong-kind input while preserving tampered-record integrity failures and actionable messages across ledger, CLI, and MCP consumers. Record unrelated CS_RESTRICT process neighbors as unverified without weakening related-process ownership checks. Update stale ADB fixtures for the actual shell protocol.

Validated focused CLI, MCP, filesystem, ownership, and error projection tests; static, docs, and duplication gates pass. The host-level CS_RESTRICT load reproduction was not run. Refs #2050, #2035, #2064.
2026-10-12 14:12:44 +08:00
morluto e4532b5376 fix(javascript): preserve TypeScript loader and dependency semantics
Share loader selection across static findings and module relationships: CTS static imports and re-exports use CommonJS lookup while dynamic imports retain ESM lookup. Retain runtime import-equals dependencies, bindings, and exported names; do not resolve require file-URL strings as local artifacts. Restore import-map descriptions in CLI help.

Validated resolver and semantic tests, application reconstruction boundaries, and CLI help. Installed TypeScript NodeNext emission and Node require behavior were checked independently. Refs #2056, #2057, #2058, #2053.
2026-10-12 14:12:19 +08:00
morluto 966f7cc84b fix(browser): preserve body API provenance through script export
Label new captured response bytes as Playwright body API results and retain the possibility of a credentialed follow-up load. Preserve legacy representations and source limitations through export without treating body-origin uncertainty as missing event coverage.

Validated source and filesystem boundary tests plus real Chrome CLI and MCP network workflows covering binary, compressed, empty, unfinished, and same-URL responses and profile cleanup.
2026-10-12 14:12:19 +08:00
morluto 6bae774328 fix(evidence): authenticate identities and retain extraction provenance
Authenticate residual unknown IDs against their immutable identity fields. Permit valid empty ZIP extraction manifests and join firmware child provenance to parent-owned chunk and multi-file reports without inventing missing source ranges.

Validated with focused identity, archive, cleanup, and firmware normalization tests. Firmware joins were checked against the pinned Unblob producer; no real Unblob extraction was run.
2026-10-12 14:12:19 +08:00
4573e8cf35 fix(ghidra): recover stale clients on same-target reopen (#2014)
* fix(ghidra): recover stale clients on same-target reopen

* test(ghidra): allow supervised shutdown in health regression

* fix(ghidra): read process exit without diagnostic snapshots

Health and startup exit checks called the supervisor snapshot, which
concatenates retained launcher output only to read the exit code.

* test(adb): align fixtures with literal output and directory commands

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 14:06:52 +08:00
69e097a8ca fix(scripts, install): honor test:fast "without build" promise and orient Node.js engine mismatches (#2026)
* fix(scripts): defer dist/ imports in verifier helpers so test:fast runs without build

Two helpers under scripts/lib/ reached into ../../dist/ at module load even
though the top-level import was only needed by one of their exports. That
turned mocked, dist-free tests into failing tests under `npm run test:fast`,
which docs/testing.md § test scripts explicitly promises runs "without build":

- tests/conformance/providers/hopper/realHopperSemantic.test.ts only calls
  requireCurrentDocument and resolveFixtureProcedure (both mock-only), but
  scripts/lib/real-hopper-semantic.mjs eagerly imported
  requireFunctionDossierOracle from dist/application/RealHopperAssertions.js.
  Moved the dist-dependent dossier oracle into a lazy loader called from
  verifyRealHopperFixture, which is where it is actually used (against the
  real Hopper bridge, after build:cached).

- tests/evaluation/package/packagePlatformDiagnostics.test.ts only uses
  assertLinuxPackageProviderFailure, which depends on `json`, but
  scripts/lib/verify-package-core.mjs eagerly re-exported helpers from
  mcp-verifier-results.mjs, which in turn pulls four dist/ modules. Moved
  that import into verifyCompleteToolCatalog (the only consumer in this
  file). Downstream scripts (verify-recorded-crash.mjs, verify-package-mcp.mjs,
  verify-windows-package.mjs) are unaffected because they already run after
  build:cached.

Reproduction before fix:
  rm -rf dist && npm run test:fast → 2 failed (both import-time errors)

Reproduction after fix:
  rm -rf dist && npm run test:fast → 388 passed, 4924 tests, 0 failed
  npm run build:cached && npm run test:fast → 388 passed, 4924 tests, 0 failed

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

* fix(install): orient Node.js version mismatch failures with actionable remediation

install.sh previously told operators "use Node.js 22.x (>=22.19), 24.x (>=24.11),
or 26+" without saying how to install one. The curl|bash onboarding path then
left the operator to guess between nvm, fnm, n, Volta, system packages, or
nodejs.org — a lookup that bounces across search engines for a first-time user.

Replace the one-line failure with a block that:
- Names the detected Node.js version (so the operator confirms it is theirs).
- Shows the three most common install paths (nvm, fnm, direct tarball).
- Pins the Node.js release line (24) that satisfies the engine, so copy-paste
  commands produce a supported runtime instead of whichever "latest" is on top.
- Maps uname -m / uname -s to the Node.js tarball label pair for this host
  (e.g. darwin/arm64, linux/x64), so the operator can grab the right archive
  from https://nodejs.org/dist/latest-v24.x/ without a second lookup.

Verified with four Node.js versions (24.18 compatible, 25.9/20.11 outside the
supported ranges, 22.18 below the minor floor) that remediation triggers only
on real mismatches and names the host labels correctly. shellcheck install.sh
and npm run check:fast remain clean.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-12 13:54:57 +08:00
morluto 3bdc8730bd fix(adb): preserve directory identities and share byte capture
Use Android 8-compatible batched find/stat with NUL-framed names and raw symlink targets. Preserve malformed or invalid UTF-8 records as partial evidence with exact source bytes. List hidden direct children without synthetic navigation entries.

Share process byte capture with UTF-8 callers and screenshots, removing duplicate timeout and cancellation supervision. Verified 35 focused process, ADB, CLI and MCP tests; compatibility checked against AOSP Oreo sources. Live Android device/emulator verification was not run.
2026-10-12 13:06:51 +08:00
morluto 3bcc68c037 fix(evidence): enforce managed identity and cancel bundle reads
Validate managed reconstruction through the shared artifact and MVID scope boundary, including known/unknown identity contradictions. Forward MCP comparison cancellation into both bundle reads and consolidate pre-cancel coverage.

Verified managed graph/reconstruction and filesystem/MCP cancellation workflows. A controlled missing-signal mutation reads 130 chunks instead of the expected one; cancellation leaves the session unchanged and retry succeeds.
2026-10-12 13:06:30 +08:00
83a4445f44 test(sourcemap): consolidate source map regressions (#2034)
fix(sourcemap): retain named originals beside unknown source names

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-12 12:44:58 +08:00
783fcb06a4 test(javascript): consolidate module lookup regressions (#2031)
fix(javascript): resolve relative ESM URLs as exact files

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-12 12:44:43 +08:00
morluto e55ef72135 fix(runtime)!: preserve unknown debugger attachment state
Keep omitted discovery attachment state as null through Node, browser, Electron, and reconciliation evidence. Report the uncertainty while retaining Node observation unless discovery explicitly marks the target attached. Reject worker TargetInfo records missing the required boolean with partial coverage.

Validate omitted and explicit states through provider, CLI, and MCP boundaries and real Node 24 Inspector workflows. Consolidate Electron regression coverage into the existing list-and-inspect workflow.

BREAKING CHANGE: attached fields in JavaScript runtime, browser, and Electron target discovery and inspection results may be null when discovery does not report attachment state.
2026-10-12 12:37:00 +08:00
morluto 48c352b6bf fix(cli): reuse firmware and web module input constraints
Derive extraction limits and web module fields from the shared contracts. Reject empty or NUL-containing local paths before CLI path resolution, preserve firmware relative-path handling, and apply the advertised extraction defaults.

Validate advertised JSON Schemas with Ajv and check the registered parser defaults; prune redundant schema-shape and description assertions.
2026-10-12 12:37:00 +08:00
morluto 6fb3a345fe fix(analysis): preserve producer identity and partial evidence
Reconcile Mach-O UUID observations without hiding malformed or conflicting sources. Require source-map identity digests to be supported by consistent observations. Keep truncated Android manifests as partial raw evidence, validate WABT section framing, and reject missing JEB inventories or error-marked success replies.

Consolidate native UUID parsing and simplify capacity and malformed-input fixtures. Focused source and public workflow checks pass, including native arm64e inspection and real WABT framing probes; a successful real JEB GUI project opening remains unverified.
2026-10-12 12:37:00 +08:00
morluto 1c64fe3eff fix(session): invalidate snapshots before metadata write dispatch
Stop cached metadata replay before a provider write starts, including while its reply is pending or fails after applying the mutation. Keep snapshot import and export disabled until the target is reopened.

Validate failed replies, conflicting provider identity, and overlapping reads through the shared session boundary.
2026-10-12 12:37:00 +08:00
GamerDubzandClaude Opus 5.5 2f134aaa7e docs(plugin): run the plugin readiness check through npx
A plugin-only install has no rea executable on PATH, so the documented
rea doctor command failed with "command not found". Show the package
runner form instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:09:56 +02:00
Alessandro CostaandGitHub 538f8cc722 docs(cli): add a managed assembly inspection workflow (#2037) 2026-10-12 12:09:06 +08:00
0d78c99c11 docs: celebrate 110,000 GitHub stars 🎉 (#2042)
Update the star milestone in the English README and all 18 translated
READMEs, keeping each locale's number formatting.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:06:25 -07:00
GamerDubzandClaude Opus 5.5 e8e265a0bc docs(plugin): tell plugin users how to check readiness without rea setup
rea doctor's client and skill checks only read state that rea setup
manages, so a plugin-only install reports missing and recommends the
second registration the plugin section warns against. Say so, and point
plugin users at the client's own plugin listing and provider-scoped
doctor checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:00:14 +02:00
morluto 21eff3bae1 fix(setup): preserve Cordis ownership across uncertain patch structure
Separate stable registration ownership from setup editability. Keep group-owned rows removable, retain expression-backed structures without edits, and reject malformed literal patches before setup writes. Preserve foreign rows and backups during uninstall.

Correct stale alias fixtures to use valid Harness patch rows and remove redundant alias coverage. Validation: 78 configuration boundary tests, 6 public setup/uninstall CLI tests, full static checks, and docs checks passed. Harness behavior was verified from pinned source; no real Harness runtime verification is claimed.
2026-10-12 11:59:23 +08:00
morlutoandhhang b7f563e15f test: strengthen public input and module lookup controls
Incorporate the independent query-bearing ESM and cjs-only directory cases from #2031 and #2032 into existing Node-backed CLI/MCP tests. Their production patches are already superseded on main.

Make invalid Swift-symbol validation prove it precedes artifact access, and bound the test child so failures retain output before the outer test deadline. Clarify that network API content types project only the type/subtype essence.

Validation: module lookup CLI/MCP and native demangle CLI tests pass. A bounded Flameox reproduction passed in under one second; the CI timeout was not reproduced and no provider hang fix is claimed.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-12 11:59:23 +08:00
morluto 4a4d36f881 fix(evidence): retain unreadable PE and Darwin process facts
Distinguish absent CLI directories from unreadable directory counts and truncated slots while preserving known PE headers and readable CLI data.

Retain environment-entry coverage in the Darwin parser and classify restricted empty, unavailable and ambiguous environments at the native boundary. Keep cleanup fail-closed; code-signing flags do not establish ownership.

Validation: 116 managed source tests, 13 ownership tests, native Swift parser probe and two live Darwin run-token tests; build, static checks and docs checks pass. Restricted-flag cases use parser fixtures; no real restricted capture is claimed. Related to #2035.
2026-10-12 11:59:23 +08:00
d0249282c6 docs(install): explain Claude Code upfront tool loading on third-party endpoints (#2029)
Claude Code turns MCP tool search off when ANTHROPIC_BASE_URL points to a
non-first-party host, so every REA tool definition is sent with every
request. On main at 04089d5be the forwarded names, descriptions and input
schemas measure about 496 KB (~120k tokens) with the full profile and about
290 KB (~68k tokens) with the compact profile. When Claude Code assumes a
smaller window than the model has, as with `deepseek-v4-flash` without the
`[1m]` suffix, autocompact thrashes because compaction cannot remove tool
definitions.

Document the window, tool-search and compact-profile remedies, and that
DeepSeek's Anthropic API does not list `tool_reference` blocks.

Refs #2019

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:56:19 -07:00
GamerDubzandClaude Opus 5.5 d3359ebef0 fix(plugin): give the Codex plugin server REA's 30-second startup timeout
The Codex manifest left startup_timeout_sec at the client default, unlike
the registration rea setup writes, so a cold npx start could be cut off
before tools appeared. Carry the owned timeout in the Codex server entry
and assert it against MCP_STARTUP_POLICY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:50:28 +02:00
GamerDubzandClaude Opus 5.5 2aea370e1c fix(plugin): declare the capabilities Codex plugin validation requires
Codex's plugin validator rejects a manifest whose interface omits
capabilities. Declare them in .codex-plugin/plugin.json and assert the
required interface fields in the release configuration test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:37:44 +02:00
GamerDubzandClaude Opus 5.5 a34e48b35b test(release): cover plugin manifests in the publication history check
The release-only allowlist gained both plugin manifests without a test
naming them. The publication history test now commits both manifests in
the release commit, so it fails with "Product changed after notes target"
if either path is dropped from the allowlist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:37:10 +02:00
a1b5d51a8d feat(go): inspect embedded binary build metadata (#1363)
* feat(go): inspect embedded binary build metadata

* fix(go): preserve byte metadata and validate container mappings

* fix(go): support moduleless legacy build metadata

Recognize optional module string headers in declared zero-filled memory while rejecting ambiguous and unmapped pointers. Preserve the original module-pointer field as the zero-byte source anchor.

Classify initial symlink-loop and overlong-path failures as invalid input, with CLI and MCP regressions.

* fix(go): reject undersized PE optional headers

* fix(native): stabilize ELF memory checks and clean up exec wrappers

Use existing parent/group/run-token ownership for caller-selected Python launchers, which can exec through a shell or prlimit. Add cancellation regressions for the valid wrapper and wrong-parent/token cases.

Replace the timing-dependent 30,000-section memory workload with a valid 32 MiB padded ELF under a 64 MiB address-space limit. Check the same image successfully through CLI and MCP before requiring a typed memory failure, preserving the production timeout and all error assertions.

Validation: npm run check and check:fast passed; 61 focused tests passed including Go CLI/MCP and session snapshots. Real decoder probes with pinned packages passed on Python 3.12.3 and 3.12.14. The full local ELF verifier is blocked by this container's procps/process-ownership boundary; the exact pushed head requires GitHub's real-binary-layout lane.

* test: repair Electron fixtures and stabilize catalog startup checks

Declare Electron imports in option, preload override, and path-resolution fixtures after upstream began requiring proven API provenance. Retain all option and preload assertions and adjust exact source ranges for the added import line.

Use the existing bounded acceptance timeout for the cold catalog lifecycle test instead of a separate 15-second ceiling. This retains the coverage allowance and all catalog, error, and cleanup assertions without retries.

Validation: npm run check, final check:fast, formatting and diff checks passed. Source path regression and Go boundary/MCP/service checks passed. All 20 Go CLI cases and both supported-host runtime cases passed on rerun; two macOS-only cases skipped on Linux. A prior contended local run timed out once in a Go CLI diagnostic; that case passed on rerun. Electron process-boundary verification is blocked locally by procps namespace inspection and requires fresh CI.

* test(mcp): isolate nested schema references between tool contracts

Clear Ajv's non-meta schema registry after each root, including compilation failures. Disabling root registration alone leaves nested IDs available to later tools and can let a dangling reference pass the catalog gate.

Add regressions for leakage after successful and failed compilation while retaining shared meta-schema validators. The regression failed before the fix; all 11 schema cases and full npm run check pass. Reviewed independently.

* test(ci): align Go effects and source-map verification contracts

Audit the Go inspector wording as static metadata inspection. Assert retained source declaration indexes in BOM reconstruction and the compiler-derived nested-map oracle. Keep a 45,000-map admission control below the 32 MiB output budget and the 50,000-map overflow case above it with the expanded representation.

Validation: reproduced all four failing assertions, then passed 120 focused tests and the final admission suite. npm run check and real Chrome 154 / esbuild 0.25.10 source-map verification passed, including all four nested-map scenarios and verified process lineage.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 11:34:02 +08:00
morluto bd74c7af2b test(sourcemaps): assert semantic evidence and actual output admission
Replace stale closed-property expectations with assertions for the evidence under test. Verify compiler coordinates and declaration indices separately in the real-browser workflow.

Consolidate duplicate 50000-map admission cases and derive admission from exact serialized bytes rather than IP-address-specific output shapes. Retain capacity, omission, and aggregate-byte coverage. A bounded Flameox CPU profile completed in about 1.4 seconds and showed no producer bottleneck; targeted admission and real Chrome CLI/MCP verification pass.
2026-10-12 11:26:56 +08:00
morluto 9f67e44e5d fix(javascript): preserve loader-specific package ancestry
Require a package directory for bare ESM imports. Continue CommonJS root and subpath lookup through ancestor packages after ordinary misses, while keeping exports authority, unavailable metadata, and failed nonempty main entrypoints terminal.

Carry failed-main ownership in the internal resolution result and retain the nearest local miss after exhausting ancestors. Consolidate package selection at its owning boundary and qualify loader differences with real Node 24, CLI, and MCP regressions.
2026-10-12 11:26:48 +08:00
GamerDubz d206a19c7d Merge remote-tracking branch 'origin/main' into feat/claude-code-plugin 2026-10-12 05:18:13 +02:00
morluto ee50ed121e fix(sourcemaps)!: preserve nullable source declarations
Accept absent map-level sourcesContent and preserve null source-name slots alongside known originals and aligned content. Represent unnamed local declarations explicitly, retain leaf-local and flattened indices, and preserve declared indexed-section order across consumers.

Account for added indices before browser output allocation. Accept legacy retained rows without indices. Consolidate public CLI/MCP source-map fixtures and update the stale named-source-only assertion.

BREAKING CHANGE: Browser source-map original and mapping rows expose null for unavailable source names instead of inventing an unknown-source string. New index fields are optional when reading older retained evidence.

Refs #2023, #2024
2026-10-12 11:10:49 +08:00
morluto c0904b4c1a fix(javascript): apply loader-owned module lookup rules
Keep relative ESM imports exact, restrict Node legacy probes to .js/.json/.node, and treat empty package main as absent. Stop Node package-main lookup before nested package reinterpretation, while retaining generic artifact inference.

Replace stale recursive and .cjs probe expectations. Verify CLI and MCP graph results against real Node 24 runtime controls, including exact suffixes, index fallback, and nested package main refusal.

Refs #2020, #2021, #2022
2026-10-12 11:10:32 +08:00
d9d2a17381 feat(setup): register DeepSeek Harness through setup, doctor and uninstall (#1929)
* feat(setup): register DeepSeek Harness through setup, doctor and uninstall

DeepSeek Harness registers MCP clients through a Cordis YAML patch
sequence in the home patch rather than a server table, so setup could
not configure it and users had to append the documented entry by hand.

Add a dsh client: the home patch $DSH_HOME/cordis.patch.yml
(~/.dsh/cordis.patch.yml when unset) is parsed as a patch list, the REA
entry owns the stable rea-mcp id, and setup appends or replaces exactly
that entry while preserving unrelated patches and comments in place.
Doctor reports the registration through the same entry, and uninstall
removes it, dropping insert operations it emptied. Skill destinations
follow ~/.agents/skills ($DSH_AGENTS_HOME/skills when configured), the
home the rest of the client machinery already uses.

Fixes #1839

* fix(setup): carry environment and identity validation into DSH registrations

Address review: the DSH entry discarded discovered provider overrides;
config.env is now emitted and retained like the other dialects. An entry
carrying the rea-mcp id under a wrong or missing package name, or
duplicated entries, reads back as absent so setup rewrites exactly one
canonical entry instead of reporting unchanged. Doctor now requires
serverName rea and stdio transport before calling a DSH registration
aligned, and the Harness skill identifier in the docs matches the
installed reverse-engineer-anything skill.

* fix(setup): apply Cordis patch order and collapse DSH registrations

Address review: a later direct { id, config } operation replaces the
row's config under the native patch DSL, so the registration view now
applies patches in order instead of only reading insert operations;
doctor reports the effective command rather than the inserted one.
Setup rewrites exactly one canonical entry: duplicate id-matching rows
collapse to the first, direct patches to the entry are dropped from the
document and from the edited YAML, and uninstall removes them with the
entry. The client-location test fixture now clears DSH_HOME and
DSH_AGENTS_HOME so a developer environment cannot point the suite at
real Harness homes.

* fix(setup): repair direct-only registrations and preserve Cordis entry state

Serialize now edits the first insertion carrying rea-mcp in place, so a
grouped entry keeps its scope, neighbors, and comments instead of moving
to a fresh root-level insertion, and later carrying insertions and direct
patches collapse away. Entry-level disabled state projects into the
registration the doctor and setup compare, so a disabled row reads as
stale and the repair re-enables it. A direct-only registration no longer
suppresses the canonical insert append, and an update retains the
registration environment. Duplicate entries collapse across operations,
not only within one.

* fix(setup): preserve Harness row state through identity and refresh repairs

A wrong-package row still reads back as a server record carrying its
environment and caller fields, projected with a package marker the
alignment path treats as stale, instead of vanishing and dropping
config.env from the repair. A direct Cordis patch without a preceding
insertion configures nothing, so a direct-only registration reads as
absent even when its config matches and the repair appends the insert.
Caller-owned config fields beyond the REA-owned keys, such as the cwd
the native verifier sets, survive a refresh instead of being silently
dropped.

* fix(setup): preserve Cordis tags and collapse aligned Harness rows

A refresh rebuilt Harness config from parsed values, so a caller !!js
expression was written back as a literal. Owned keys are now overlaid on
the winning config node and other nodes stay in place. Two already-aligned
rea-mcp insertions read as stale, so setup collapses them to one row, and
the bundled skill lists dsh with the other clients.

* fix(setup): drop the unused DSH configuration type

Knip reports DshConfigurationFormat as an unused export. The format union
does not depend on that alias.

* fix(setup): keep Harness env tags and comment-only patches

A refresh replaced the whole env map, so a caller !!js value was written
back as a literal. Environment entries now stay in place unless the value
changed. A comment-only cordis.patch.yml parses as an empty patch list and
keeps those comments when the registration is appended. Docs describe
plain rea uninstall, which scans supported clients and has no --client flag.

* fix(setup): own Harness rows by entry id and expression state

Uninstall removes a rea-mcp row even when the launcher is stale or the
patch never established a server record. A non-boolean disabled value,
including a Cordis expression, reads as stale. Expression text on a known
environment setting is not validated as a literal path, so a refresh can
keep the tag.

* fix(setup): collapse aliased Harness insertions

An anchored insert followed by an alias parses as two rea-mcp rows, but
the YAML edit kept the alias, so readback stayed duplicated and setup
restored the file. Aliases that resolve to the owned entry are now
dropped, including an alias of the entry inside one insert. Aliases of
other patches stay.

* fix(setup): resolve aliased Harness lists and config maps

An insert value that aliases another entry list still loaded a second
rea-mcp row after the operation-level alias collapse. That alias is now
dropped once the anchored list is rewritten. A config: *cfg map is
resolved and cloned before the refresh, so a caller !!js field on the
aliased map is not rewritten as a plain literal.

* fix(setup): follow Harness aliases for ids, lists, and env tags

An insert alias whose anchor is not another insert is copied so
unrelated rows stay while the owned row is rewritten. An aliased
entry id is resolved before ownership checks, and an environment alias
of an unchanged !!js scalar is left in place.

* fix(setup): keep aliased Harness env maps through uninstall

An env alias is cloned before a refresh so tagged values are not rebuilt
as literals, and expression-key scans follow aliases before path checks.
Insert aliases that carry REA are recorded before uninstall empties their
anchor, so the later alias is dropped instead of dangling.

* fix(setup): follow root Harness aliases and mark dsh as main-only

Winning-config walks resolve a root alias before patch order, so a replayed direct config's !!js env is not validated as a literal path. Root aliases that own rea-mcp are recorded before uninstall empties their anchor. An insert whose only owned member is an external alias is cloned so Cordis tags survive. Docs state dsh is not in npm 6.4.0.

* fix(setup): keep rows behind inert Harness aliases

A root alias whose anchor is not a loaded operation is copied and only the owned row is edited, so unrelated rows stay active. Entry id aliases become a scalar before their anchor patch is dropped. Root-alias ownership follows insert-list aliases, so a replay is removed instead of dangling after uninstall.

* refactor(setup): centralize Cordis source ownership

* fix(javascript): share Electron mutation proof with wrapped calls

* test(mcp): align graph and catalog checks with current contracts

* fix(setup): separate Cordis ownership from editability

* docs: clarify ownership and interpretation boundaries

* fix(setup): preserve Cordis applicability and caller metadata

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 11:06:26 +08:00
pablodiazjorgeandGitHub 04089d5bec test(clients): verify native Gemini CLI integration on Windows (#1936)
* test(clients): verify native Gemini CLI integration on Windows

The Gemini CLI client lane asserted POSIX. On Windows it also needs the npm install's JavaScript entry point, because only a gemini.cmd shim reaches PATH, and two setup-plan checks compared paths with a hard-coded slash. Remove the assertion, run a selected .js, .mjs or .cjs entry point under the verifier's Node, build those prefixes with path.sep, name the entry point in the Windows preflight error, and record 0.63.0 as verified on native Windows in docs/testing.md.

Fixes #1935

* test(clients): resolve the selected Gemini CLI entry point once

The preflight runs from the caller's directory and the client run from the disposable workspace, so a relative REA_VERIFY_GEMINI_COMMAND passed the preflight and then failed with Cannot find module. Resolve a selected JavaScript entry point to an absolute path before both launches.
2026-10-12 10:50:11 +08:00
880a603fe8 feat(mcp): derive an API surface view from retained web network captures (#1891)
* feat(mcp): derive an API surface view from retained web network captures

* fix(mcp): fail closed on sensitive parents and budget api-surface path expansion

* fix(mcp): keep recorded api-surface paths and budget only templated segments

* fix(mcp): read api-surface paths only from canonical URLs and charge new values once

* fix(mcp): reject authority-less api-surface URLs and key groups by rule positions

* fix(mcp): label api-surface confidence from all groups and cap transient path memory

* fix(mcp): budget api-surface dedup sets and narrow detail outputs

Charge retained deduplication sets before allocation, advertise only
each analysis operation's summary from its detail output, and document
that any templated group makes every API-surface page inferred.

* fix(mcp): bound api-surface path text and record limitations

Charge retained literal path copies before allocation, publish only
valid media-type essences, and deduplicate examined record limitations
onto the view.

* fix(mcp): keep api-surface refusals and path splits inside the reply budget

Sensitive-parent refusals now use the token sensitive_values_unretained so declared literals are not echoed. A later path split is charged against bytes already retained, and the published example includes the HAR record limitation with its matching digest.

* fix(mcp): budget api-surface method text before group keys

Charge method and origin text against the reply budget before the group key is serialized. Document that samples merge only when method, origin, and exact segment rules match.

* fix(mcp): bound api-surface copies and control-character authorities

Reject authorities containing a tab or newline so WHATWG cannot move userinfo into the recorded path. Charge four path copies, two method and origin copies, and each distinct content type against bytes already retained.

* fix(mcp): preflight combined api-surface group charges

Total path, method, origin, and segment charges before retaining a new group. Track content types in a set so distinct MIME essences are not compared quadratically.

* fix(mcp): charge api-surface content-type sets on first use

Create the per-group content-type set when the first essence is retained, and charge it before allocation. Groups with no content type no longer keep an empty set.

* refactor(analysis): centralize API-surface ownership and admission

Own grouping, deduplication, and combined retained/temporary resource admission in one accumulator. Charge fixed group storage before allocation and materialize only the requested page.

Share recorded URL parsing between capture redaction and projection, preserving source paths and excluding ambiguous credential fields. Cover resource admission invariants and the compiled HAR-to-view workflow.

* fix(analysis): admit capture schema clone before parsing

* fix(javascript): share Electron mutation proof with wrapped calls

* test(mcp): align graph and catalog checks with current contracts

* fix(analysis): admit and validate portable capture views

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 10:44:17 +08:00
morluto 64b71b180a docs: clarify ownership and interpretation boundaries 2026-10-12 10:43:34 +08:00
2ff850de68 fix(session): attribute selected-provider capabilities to the resolved identity (#2013)
Profile resolution gives Ghidra and Hopper a concrete version, but the
selected candidate's capabilities and the session route kept the
provider's static identity with a null version. binary_session therefore
grouped capability sets under a different identity than the binding and
the selected candidate.

The registry now rebinds the selected provider's capabilities to the
resolved identity once, on the binding, and the route uses them. Evidence
provenance already compensated downstream by substituting the profile
identity; that repair is removed because the route now carries the
resolved identity.

Fixes #2009

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:36:28 +08:00
morluto 42b0ed126d fix(javascript): retain guards without duplicating observed IPC calls
Include enclosing control-flow predicates when deciding whether symbolic
wrapper facts can be reported without guards. Preserve the guarded paths
and unresolved coverage instead of inventing unconditional exposures or
channel pairing candidates.

Keep fully observed renderer transmissions singular when tracing an exposed
callback. Distinct wrapper registrations still retain invocation identities.
This fixes the Electron package baseline's doubled transmission count.

Validation: check; 43 exact source and public CLI/MCP tests; existing artifact,
extraction, Electron analysis and routed-analysis verifier against the built
CLI. Installed-package platform coverage remains with CI.
2026-10-12 10:08:39 +08:00
morluto b8fab74297 fix(javascript): preserve execution and invocation provenance
Keep direct empty bridge objects observed, reject unexecuted initializers,
invalidate modified bootstrap tables and Electron identities, and retain
selection predicates through symbolic values and wrapper paths. Track lexical
binding identity and completed activation boundaries without leaking analysis
order into earlier reads.

Separate wrapper invocation provenance from the underlying IPC source range
so repeated registrations remain ambiguous through transfer and graph joins.
Use the same offered transfer budget for module preflight and final writing.

Index static literal properties once instead of rescanning every member. Keep
the original 10,001-member capacity fixture; Flameox confirmed the repeated
lookup cost and the optimized fixture completes in under one second locally.
Prune the stale compact-schema tool inventory assertion and update the public
call-graph registration expectation to its declared result envelope.

Validation: build, check, docs:check, 83 focused tests, 10 Electron identity
acceptance cases, 2 full-scale finding-capacity cases, and 7 compact-schema
transport tests. No real Electron runtime execution is claimed.
2026-10-12 10:03:17 +08:00
pabloandGitHub edf46248f2 fix(targets): project invalid raw-binary loads as caller input (#2004)
* fix(targets): project invalid raw-binary loads as caller input

* fix(targets): name real load options and cover DOS COM kind pairing
2026-10-12 09:34:45 +08:00
morluto 5dfb9d050c fix(analysis)!: scope call-graph caveats to their observed relationships
Return deduplicated execution caveats once and preserve provider-owned relationship diagnostics and completeness on graph nodes. Keep complete relationship reads exhaustive despite contextual caveats, including portable call-path reconstruction.

Preserve omitted diagnostic fields in historical Evidence and bind reported diagnostics into semantic identity. Revise graph workflow and Ghidra adapter profiles so old cached representations cannot replay under the new contracts.

Remove stale duplicate-lookup test claims and redundant fixture validation. Prune million-entry duplicate cases while retaining argument-stack and deep-chain regressions; bounded Flameox captures identified validation and canonical hashing costs.

Validation: static and architecture checks, generated docs, 364 focused tests before pruning, 21 call-path tests after pruning, public MCP schema validation and targeted profile tests. Real engine behavior was not locally exercised.

BREAKING CHANGE: get_call_graph normalized_result now contains { graph, limitations }; traversal-depth groups are under graph.
2026-10-12 09:29:07 +08:00
morluto b38429cb6a fix(native): preserve universal Mach-O container identity
Keep FAT container format distinct from selected slice architecture through target admission, Evidence, snapshots, and CLI/MCP lifecycle results. Preserve native, Hopper, Ghidra, extraction, and dylib routing for the Mach-O family.

Reject selected FAT ranges overlapping their table or too short for a Mach-O header. Replace empty-slice fixtures with in-bounds executable headers and cover thin, universal, single-slice, FAT64, and swapped containers.

Validation: static and architecture checks, generated docs, targeted producer and public MCP tests. Provider routing uses controlled fixtures; no live Hopper, IDA, JADX, or Ghidra analysis was performed for this change.
2026-10-12 09:29:07 +08:00
87b9793c53 fix(javascript): trace wrapped Electron IPC paths in large bundles (#1874)
Co-authored-by: David <17119984+fengdwx@users.noreply.github.com>
2026-10-12 09:28:59 +08:00
d93fbda390 perf(cli): bundle runtime entries to reduce Windows startup I/O (#1996)
Bundle ordinary CLI and output entries to reduce Windows module filesystem resolution while preserving source URLs, lazy loading and worker resources. Keep release identity in its shared owning module.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 09:25:27 +08:00
Vinícius AndradeandGitHub 6611bcf6e8 test(android): verify duplicate class inventory behavior on empty discovery queries (#1984)
Assert that search_android_classes preserves duplicate class-name rows and reports duplicate_names when querying with an empty string on an APK reporting duplicated class inventories.
2026-10-12 09:06:00 +08:00
morluto e19879328a refactor: prune obsolete provider and verification code
Remove unused imports, private inferred types, superseded limit constants and a duplicate MCP assertion. Preserve intentional field omissions while deleting unused local bindings.

Validation: typecheck, lint, formatting, unused-code and architecture checks pass. Runtime behavior is unchanged.
2026-10-12 08:59:14 +08:00
morluto b5ededd0f9 fix(providers): retain native parsing and compiler failure diagnostics
Parse otool field occurrences once and reject valueless required fields while retaining empty dylib options. Preserve compiler exit outcomes and stderr through capture errors and nested Ghidra causes, with explicit UTF-8 truncation after redaction.

Strengthen provider failure assertions and document the manual Ghidra 12.1.4 macOS x64 decompiler build using upstream instructions.

Validation: focused producer tests, nine fake-compiler preparation and cancellation tests, temporary-directory hygiene, static and documentation checks. No real Swift or Ghidra compilation was run locally.
2026-10-12 08:59:14 +08:00
morluto aada0ca812 fix(evidence): validate closure projections and browser body coverage
Share ledger status, coverage, reports, graphs, Evidence links and closure commitments between the producer and readiness validation. Preserve limitation-driven unknown results and reject stale or inconsistent projections.

Distinguish malformed CDP response envelopes from unavailable JSON payload shapes. Validate required encoding flags and preserve partial request facts.

Validation: 116 focused tests, static and architecture checks, documentation checks. Browser regression coverage uses controlled CDP responses.
2026-10-12 08:59:14 +08:00
f889a69fb9 fix(build): resolve rolldown from the npm registry in package-lock.json (#1986)
The rolldown 1.2.12 entry resolved from mirrors.cloud.tencent.com while
every other entry uses registry.npmjs.org, so npm ci failed in
environments that can reach the npm registry but not that mirror.
Version and integrity are unchanged.


Claude-Session: https://claude.ai/code/session_01Q3rXQaLacJ7gNBRkp7DovF

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-12 08:58:44 +08:00
adde8a8092 fix(native): reject required otool fields truncated before their value (#1995)
* fix(native): reject required otool fields truncated before their value

#1967 let requireUniqueField count a label at end of line so an empty
dylib-use options row is present. That applied to every required field,
so truncated output such as a bare vmaddr, vmsize, uuid or entryoff line
passed the presence check while parseField dropped it, and the value
silently became null.

Let parseField decide which labeled line carries a value: the single
matching line must parse as that field. The empty options row remains
the only valueless field parseField admits.

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

* fix(native): admit the empty segname of Mach-O object files

MH_OBJECT files put their sections in one segment whose name is empty,
and otool -l prints it as a bare segname label. Requiring a value for
every scalar field rejected valid object-file output as unreadable.

Let parseField admit an empty segname like the empty dylib-use options
row, and keep projecting an empty or absent segment name as "unknown",
as main did.

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

* fix(native): admit all-NUL Mach-O section names

An all-NUL section_64.sectname prints as a bare sectname label, like an
empty segname. Admit it in parseField and keep projecting the empty name
as "unknown", as main did.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:55:50 +08:00
pabloandGitHub 091cb81fc3 fix(process): report the Swift compiler outcome with bounded diagnostics (#1999)
* fix(process): report the Swift compiler outcome with bounded diagnostics

* fix(process): keep a signaled compiler's null exit code
2026-10-12 08:55:36 +08:00
Dmitry KhanukovandGitHub ed6af7eb8c fix(call-path): preserve relationship coverage from analysis (#2001)
Forward observed relationship coverage into function Evidence and use its typed completeness when searching call paths. Preserve contextual limitations, legacy fallback, provenance, and unknown outcomes.

Avoid argument-spread exhaustion on large valid limitation lists. Cover the public MCP producer/consumer chain, metadata tampering, terminal and unreachable coverage, and large-list capacity.

Related to #1964; separate get_call_graph and trace_call_path work is not included. AI-assisted with Hermes (Nous Research).
2026-10-12 08:55:19 +08:00
Vinícius AndradeandGitHub 976c2c083e fix(contracts): reject OS-invalid local paths in binary and artifact tools (#1925)
* fix(contracts): reject OS-invalid local paths in binary and artifact tools

Enforce localPathStringSchema across inspect_pe_resources,
inspect_binary_layout, inspect_recorded_crash, inspect_wasm_artifact,
and inspect_evm_interface input schemas to reject embedded NUL bytes at
the contract boundary instead of failing later during filesystem
operations.

Add corresponding acceptance test cases to the advertised MCP contracts
matrix in localToolContracts.test.ts.

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* style(contracts): wrap pe resources path description under print width

Wrap the inspectPeResourcesInputSchema path property description to keep lines within the 80-column oxfmt limit.

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

---------

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-12 08:53:03 +08:00
Aurelio BernalandGitHub eaf88aca2a test(ghidra): require pending multi-HI16 coverage in the real MIPS relocatable lane (#1974)
* test(ghidra): require pending multi-HI16 coverage in the real MIPS relocatable lane

Post-merge review on #1950: the relocation oracle exposes coverage.multiple_hi16, but the real-Ghidra lane never required it, and Clang's C output never produces a LO16 that resolves several pending HI16s. Ghidra's loader could therefore mishandle that stream while the lane still passed, although most GCC-built kernel modules checked contain it.

The relocatable fixture adds an assembly block with two lui %hi(rea_mips_rel_carry); LLVM's relocation sort then leaves one HI16 pending until a later LO16 that resolves it together with another HI16, including the carry. The lane now requires multiple_hi16: false on the previous fixture, true for all three variants (LE, BE, soft-float module) on real Ghidra 12.1.4.

* test(ghidra): make the MIPS multi-HI16 fixture independent of relocation sorting

Review on #1974: the block used two %lo of rea_mips_rel_carry, a symbol with other %lo uses, so the relocation stream depended on the assembler's HI16/LO16 sort. Clang 21 happened to leave a HI16 pending; Clang 17 (per review) and GNU as 2.45 (checked here: HI@4 LO@8 HI@0 LO@10) pair each HI16 with its own LO16, so multiple_hi16 stays false and the lane fails.

Use a dedicated symbol with two %hi and exactly one %lo. Any assembler that orders each HI16 before a matching LO16 must then emit both HI16s ahead of that single LO16: Clang 21 gives HI@0 HI@4 LO@8, GNU as 2.45 gives HI@4 HI@0 LO@8. The real-Ghidra lane passes for LE, BE and the soft-float module variant with multiple_hi16 true.
2026-10-12 08:42:08 +08:00
Vinícius AndradeandGitHub 1febc22907 fix(wasm): inspect modules whose custom section names contain NUL (#1981)
* fix(wasm): frame custom section names at their displayed NUL

wasm-objdump prints a custom section name with printf's %.*s, so an
embedded NUL ends the displayed name, as it does for module names.
The section row was compared with the complete name decoded from WAT,
so a valid module whose custom section name contains U+0000 failed
inspection with "WABT custom-section header does not match decoded
WAT". Compare the row with the displayed prefix instead.

* test(wasm): cover NUL-terminated custom section names

Frame the real objdump golden with a decoded custom section name that
continues after a NUL, and keep rejecting a displayed prefix that
differs from the decoded name.

* test(wasm): verify a NUL-containing custom section name with real WABT

Add a custom section named "visible\0hidden" to the real-tool custom
name fixture. WABT 1.0.42 decodes it as an escaped WAT name and
displays "visible" in wasm-objdump, so the verifier expects that
description through the compiled CLI and MCP.

* refactor(wasm): share WABT's NUL-terminated name display

Module-name preamble lines and custom-section rows each truncated the
decoded name at its first NUL. Keep that display rule in one helper so
both framings follow the same producer behavior. Output is unchanged.
2026-10-12 08:41:55 +08:00
Vinícius AndradeandGitHub 4b653fe6ad fix(native): accept universal binaries with LIB64 x86_64 slices (#1982)
* fix(native): apply printed lipo capability bits to CPU subtypes

lipo -detailed_info prints a slice's capability byte separately from its
symbolic subtype: CPU_SUBTYPE_LIB64 for x86_64 and ppc64, and other bits
as hex. Only the arm64e pointer-auth form was applied, so a LIB64 x86_64
slice normalized to subtype 3 while its FAT entry carries 0x80000003,
and slice validation rejected the universal binary as an integrity
mismatch. Fold the printed capability byte into the subtype code.

* fix(native): leave lipo CPU subtypes unknown without capability bits

The lipo shipped with macOS 26 prints an empty capabilities line for
x86_64 slices whose FAT capability byte is 0x80 as well as 0x00, so a
symbolic subtype alone does not identify the slice's CPU subtype. The
parser assumed no capability bits and still reported subtype 3 for
LIB64 slices, which made universal applications such as Sublime Text
fail slice validation. Report the subtype code as unknown when lipo
prints no capability byte, as arm64e already does; slice validation
then relies on the remaining physical facts.

* test(native): cover printed and blank lipo capability bits

Fold CPU_SUBTYPE_LIB64 and hex capability bytes into x86_64 subtype
codes, and keep the code unknown for the blank capabilities line that
macOS 26 lipo prints.

* test(native): match a LIB64 x86_64 slice with blank lipo capabilities

Read a universal image whose x86_64 FAT entry carries 0x80000003 while
lipo reports CPU_SUBTYPE_X86_ALL with a blank capabilities line, as
macOS 26 lipo does, and expect both slices instead of an integrity
failure.

* fix(native): compare named lipo subtype bits without capability bytes

A blank capabilities line leaves the subtype code unknown, and slice
matching then skipped the subtype entirely. A FAT entry whose named
subtype differs, such as CPU_SUBTYPE_ARM64_V8 recorded by lipo as
CPU_SUBTYPE_ARM64_ALL, was accepted. Keep comparing the complete
subtype when lipo printed its capability byte, and otherwise compare
the subtype bits its record names.

* test(native): share LIB64 slice reader setup

Read the LIB64 universal fixture through one helper that takes the
x86_64 subtype lipo reports, so further cases reuse the same image and
lipo record. The existing expectation is unchanged.

* test(native): reject a blank-capability lipo subtype naming other bits

With the capabilities line blank, a lipo record naming
CPU_SUBTYPE_X86_64_H for the x86_64 LIB64 slice still disagrees with
the FAT entry's subtype bits and fails slice validation.
2026-10-12 08:41:40 +08:00
Jeremy LevartovskyandGitHub 0d11f71e0d fix(managed): admit ReadyToRun images built for non-Windows hosts (#1985)
ReadyToRun images compiled for Linux, macOS or the BSDs store the COFF machine XORed with IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE (dotnet/runtime src/coreclr/inc/pedecoder.h). Every framework assembly in a Linux .NET install has machine 0xFD1D, so target admission and the managed PE reader rejected them as an unsupported architecture.

Decode the PE machine once in src/domain/peMachine.ts, replacing the two duplicated switches, and accept an OS override only on images with a CLI directory.
2026-10-12 08:41:23 +08:00
Vinícius AndradeandGitHub 43f9c39dfb fix(setup): configure an empty Hermes config.yaml (#1983)
* fix(setup): treat an empty Hermes config.yaml as a new document

YAML parses an empty file to null, so Hermes configuration was checked
as an object before the shared empty-file rule and rejected. Setup then
failed its readback gate for a zero-byte config.yaml, although a missing
file is configured and the Hermes serializer already writes a fresh
document for empty original text. Apply the empty-file rule before the
Hermes YAML parse, as #1117 did for JSON clients.

* test(setup): configure an empty Hermes config.yaml

Configure zero-byte and newline-only config.yaml files through the
real filesystem workflow, then read the registration back as aligned.
2026-10-12 08:41:07 +08:00
skoodsterGitHubskaredClaude Opus 5.5Ysopwnablefactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
a149d183ef feat(managed): select declaring types in inspect_managed_members
Select exact declaring types before managed signatures and CIL bodies are decoded, preserving caller choices in CLI/MCP Evidence. Keep unmatched names and selected coverage explicit; comparisons and graphs retain unknowns outside their observed scope. Reuse existing metadata ownership/accounting and advertise unique selections through the actual SDK Schema.

Validated with 229 focused tests across 21 files, 11 managed conformance controls, fullstatic/docs checks and real Linux CLI and fresh MCP inspection of two unchanged DLLs. All 118 selected CIL hashes matched original PE-mapped bytes; result/caller-argument parity and unmatched controls passed. Full artifact/native-boundary metadata is complete; whole-assembly members retain the unchanged 64 MiB resource constraint. No target code executes.

Refs #1878

Co-authored-by: skared <190618464+skoodster@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 08:31:37 +08:00
Ha1baraA1GitHubYsopwnablefactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
f28f338283 fix(targets): remove path advice for format refusals
Readable executable format refusals report their source format reason and an unsupported-target category instead of misleading missing-path advice. Preserve actual path/permission errors, WASM static recovery, and native target source inspection.

Verified on Linux x64 through actual CLI/fresh stdio MCP: three NE/LE/LX controls, exact canonical projection parity, unchanged bytes, responsiveness and a missing-path control. 103 focused tests across nine files, full static gate and generated documentation passed.

Fixes #1979
Refs #1852

Co-authored-by: Ha1baraA11 <243105435+Ha1baraA11@users.noreply.github.com>
Co-authored-by: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 08:08:16 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
e9fe6437a7 feat(ghidra): admit verified MIPS16 shared ELF images
Admit the verified ELF32 o32 MIPS32r2 MIPS16 shared-image profile after complete source load and relocation inspection. Preserve caller-selected bytes, pin the endian-matched language, and report per-address decoder context. Refuse unverified relocation shapes with explicit reasons.

Verified on Linux x64 with Ghidra 12.1.4: eight source-owned public CLI/MCP controls, original-artifact instruction/pseudocode/export replay, snapshot restore and owned cleanup. 152 integrated focused tests, static checks and documentation checks passed.

Fixes #1851

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 07:50:31 +08:00
6395e8d300 perf(mcp)!: compact repeated session discovery facts (#1988)
Group repeated session tool availability and provider capability facts, preserve complete named contracts and ordered limitations, and remove duplicate catalog descriptors. Update consumers and migration guidance.

Validated by all 31 CI jobs, focused source checks, and actual Claude Code/Linux Hopper/native Windows Ghidra small-target workflows on the current integration tree.

Refs #723.

BREAKING CHANGE: binary_session no longer includes server_identity.catalog.tools; tool_availability and provider capabilities use grouped facts documented in docs/mcp-contracts.md.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 07:40:19 +08:00
SII-YDDGitHubYsopwnablefactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
1f17ac8228 feat(target): explain static WASM inspection after native rejection
Explain recognized WASM v1 native-admission rejection with an exact call to the existing static inspection tool and its caller-supplied WABT prerequisites. Preserve selected paths, explicit raw-binary choices, typed missing-tool outcomes and connection usability.

Refs #1852

Co-authored-by: SII-YDD <38822679+SII-YDD@users.noreply.github.com>
Co-authored-by: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 07:00:30 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
89f5e489ad test(native): replay complete dylib-use provider output
Replay the complete real Apple dylib-use output through the native provider boundary and retain rejection of an empty repeated options field. Align skill-install Result assertions and CDP initiator prerequisites with current contracts. The producing parser fix is already retained from main.

Refs #1970

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 06:36:45 +08:00
morluto 7285e15839 fix(workflows): retain staged output ownership and cancellation semantics
Give atomic Evidence and snapshot writes an explicit resource owner, preserve primary failures and completed publication receipts, and retry retained staging cleanup at the owning lifecycle. Verify inode identity before publishing or deleting; unknown path disappearance remains a cleanup failure. Keep snapshot admission reusable when a session closes and reopens.

Consolidate JavaScript source-scope classification so cancellation cannot fabricate failures for unselected files. Classify configuration failures as invalid input. Remove stale assertions and reuse shared test cleanup ownership.

Validation: 266 focused tests before final ownership tightening, 108 affected tests afterward, static and documentation checks, actual invalid-configuration CLI behavior, and isolated test temporary-directory hygiene.

Refs #1968
2026-10-12 06:29:27 +08:00
morluto 8bd5ba2d23 fix(native): clarify Mach-O offset origins and reject partial ELF tables
Expose the selected Mach-O slice and its container offset, derive that identity once, and map entrypoint and nonempty segment Evidence to container coordinates. Reject relocation sections whose byte length is not divisible by the entry size instead of silently accepting a floored count.

Validation: native parser and selected-architecture boundaries, actual public CLI and stdio MCP on a compiled universal Mach-O, and a pyelftools 0.33 malformed-table probe. Full Linux pwntools bridge verification was unavailable on this host.

Refs #1973
2026-10-12 06:29:19 +08:00
morluto 0677468877 fix(android): preserve decoded values and duplicate class inventories
Parse Apktool YAML and XML at the producer boundary, preserving scalar spelling, XML text and partial string coverage. Retain repeated JADX inventory rows with explicit duplicate-name counts and lookup ambiguity. Make the Apktool verifier report cleanup failures.

Validation: parser, provider and JADX fixture boundary tests; typecheck, lint and documentation checks. Real Apktool and JADX engines were not exercised.

Refs #1978
2026-10-12 06:29:13 +08:00
GamerDubzandClaude Opus 5.5 15a5e08338 fix(release): treat the plugin manifests as release-version metadata
Release PRs now bump .claude-plugin/plugin.json and .codex-plugin/plugin.json,
but the publication history check rejected them as post-notes product
changes and the release configuration test still expected the previous
extra-files list. Admit both manifests in the release-only allowlist, expect
them in the Release Please configuration, and assert that each manifest
version matches package.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:07:33 +02:00
GamerDubzandClaude Opus 5.5 4c945f570d feat(plugin): add a Codex plugin manifest and self-hosted marketplaces
Add .codex-plugin/plugin.json so Codex loads REA with its own display
metadata, and make the repository a single-plugin marketplace for both
clients through .agents/plugins/marketplace.json and
.claude-plugin/marketplace.json. Users can now add morluto/rea as a
marketplace and install the plugin without waiting for a directory listing.

Release Please bumps the Codex manifest version alongside the Claude one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:01:17 +02:00
morluto 55b61485e9 fix(adb): preserve device paths and prevent destination collisions
Interpret device paths once as POSIX paths and apply only the selected host's filename constraints. Preserve original PM paths and diagnostics, reject duplicate destinations before pulls, and use exact-name filesystem reservations to detect case and Unicode aliases without guessing volume rules.

Consolidate failed-package cleanup ownership and strengthen no-output assertions. Validated 44 provider fixture tests, 47 filename/parser tests, six compiled CLI tests, static/docs checks, and package checks. No Android hardware was used.
2026-10-12 05:57:35 +08:00
morluto 264615059c refactor(mcp): trust validated inputs and tighten contract fixtures
Remove repeated MCP input parsing after the SDK has validated named contracts. Reject unmodeled fixture operations and locate prompt contracts by name so unrelated catalog ordering cannot select a contract.

Validated focused browser, Electron, runtime-observation and MCP workflow tests, advertised schemas, packaged MCP and static checks. Application-boundary validation remains in place.
2026-10-12 05:57:35 +08:00
morluto 74d7da6ced fix(artifacts): preserve ZIP identities and partial managed metadata
Reject malformed UTF-8 ZIP filenames at the decoding boundary while retaining CP437, Unicode Path overrides and leading U+FEFF. Preserve valid CustomAttribute parent and value facts when constructor identity is invalid, with an explicit partial-coverage issue.

Validated ZIP integrity and filesystem-provider workflows, managed producer cases, temporary-file hygiene, package and static checks. Removed the superseded producer-only invalid-constructor regression.
2026-10-12 05:57:35 +08:00
morluto dcfc5d403a fix(setup): retain regular-file cleanup ownership through workflows
Thread the existing resource scope through skill and client configuration reads. Keep failed-close handles owned, wait for admitted reads, and surface unresolved cleanup alongside doctor, uninstall and update results.

Validated focused setup/doctor/update and filesystem boundary checks, temporary-file hygiene, packaged setup/update/uninstall workflows, and static checks. No external provider installation was performed.
2026-10-12 05:57:35 +08:00
morluto 9c8511409c fix(analysis): preserve relationship coverage across evidence and replay
Separate observed relationship coverage from general provider caveats. Authenticate and replay coverage without changing legacy Evidence IDs; expose unknown and capped relationship sets through CLI/MCP graphs and traces.

Validated focused domain, snapshot, IDA/Ghidra boundary, MCP, schema, package and static checks. Provider fixtures establish contract behavior; licensed engine behavior was not reverified.
2026-10-12 05:57:35 +08:00
Aliciawqueandmorluto f1924cf9c1 fix(plist): decode formatted XML dictionary keys 2026-10-12 05:57:34 +08:00
GamerDubzandClaude Opus 5.5 6649e8e7ba feat(plugin): make the repository installable as a Claude Code plugin
Add .claude-plugin/plugin.json at the repository root so Claude Code plugin
directories and marketplaces can list REA. The manifest bundles the authored
reverse-engineer-anything skill from .agents/skills/ and registers the
existing stdio server through npx; it adds no second distribution artifact.

Release Please now bumps the manifest version with each release, which is
when installed plugins refresh the bundled skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:53:19 +02:00
b777e5d4d8 fix(wasm): preserve debug module names in WABT output (#1967)
* fix(wasm): preserve debug module names in WABT output

* fix(native): accept otool dylib-use options

Preserve modern dylib options while requiring one encoding metadata row and retaining duplicate and multiline-output checks. Share field parsing between preservation and validation.

Verify delayed dylibs through real macOS CLI/MCP workflows and refresh the high-address golden with the provider's -h -l capture. Validated 85 focused native tests, npm run check, and verify:macos-bundle on macOS arm64.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:44:15 +08:00
1d4c4eb030 fix(adb): reject unsafe host file names when pulling one device file (#1962)
pull_adb_file derived its host file name from the last path segment of a
caller-supplied device path and joined it to the output directory without
checking that the name is usable on the host. A device path ending in a
separator, a dot segment, a control character, a Windows reserved device
name, a trailing dot or space, or a ':' alternate-data-stream marker could
resolve to an unexpected host path or fail opaquely after the transfer.

The single-file path now derives the name with POSIX semantics and refuses
any non-portable segment as invalid_request before creating a directory or
running adb pull. This matches the package path's pending host-name policy
and never renames a device-reported file.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:33:40 +08:00
9eee709f0c fix(wasm): accept unpadded WABT section rows (#1963)
* fix(wasm): accept unpadded WABT section rows

WABT 1.0.42 right-aligns section names in a nine-column field. DataCount
fills that field, so its objdump row has no leading whitespace. Accept
that legal row. Header recognition, the required field shape, and section
range checks still reject malformed output.

Fixes #1956

* fix(ci): refresh MIPS expectations and Windows test paths

Assert admitted soft-float ABI metadata and its committed storage policy through the public target resolver, retain FP64 rejection, and point the curated Windows lane at the moved filesystem reconstruction tests.

Validation: reproduced all four prior failures; 97 focused tests and check:ci pass. Native Windows and real Ghidra execution were not performed locally.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:29:04 +08:00
morluto beb80a07ba test(cli): consolidate schema coverage and assert input refusals
Remove 30 redundant truthy-JSON schema checks. Validate advertised groups and property parity across every primary command, retain required-field checks, and assert structured malformed-input errors. Exercise JEB positional validation rather than an unknown flag.

Validation: 13 public CLI adapter tests, combined checks, and temporary-directory hygiene. Host configuration refusal coverage from main is preserved.
2026-10-12 05:24:33 +08:00
morluto 108b5f9425 fix(native): reject ambiguous otool load-command framing
Validate Mach command counts, byte totals, scalar uniqueness, section counts, and build-tool counts before claiming exhaustive evidence. Preserve raw output in typed parsing failures and distinguish dylib commands from the dynamic loader. Correct inconsistent handcrafted fixtures and label their provenance.

Validation: 38 native tests, combined checks, and temporary-directory hygiene. Multiline dylib-name ambiguity was reproduced with the host Apple otool; regression workflows use captured output and injected command runners.
2026-10-12 05:24:29 +08:00
morluto a8d4984404 fix(browser): preserve uncertainty in malformed network observations
Require observed request method and initiator metadata instead of inventing defaults. Preserve valid redirect history while excluding later events for malformed reused IDs. Keep unavailable or invalid encoded lengths unknown and retain valid zero values.

Validation: 57 CDP boundary tests plus temporary-directory hygiene and combined checks. Protocol events come from the fake CDP server; no live browser verification is claimed.
2026-10-12 05:24:24 +08:00
morluto c002b89e2d fix(snapshot): bind replayed subjects to the selected target path
Rebind display paths only for matching artifact identities in primitive and composed replay. Preserve semantic Evidence IDs, payloads, and historical snapshot provenance. Classify snapshot filesystem workflows as boundary tests.

Validation: cache, direct workflow, MCP replay, and Evidence tests; combined checks and temporary-directory hygiene. Provider interactions are simulated for replay verification.
2026-10-12 05:24:20 +08:00
morluto 2260887f20 fix(artifacts): close mounted directory resources before DMG detach
Wait for directory operations, close owned streams and traversal handles, and retain the mounted reader when cleanup fails so close can retry before detach. Consolidate stable-artifact filesystem regressions into the boundary suite.

Validation: 35 artifact tests and temporary-directory hygiene, including real hdiutil diagnostics. Directory-close and traversal races use injected faults.
2026-10-12 05:24:15 +08:00
morluto c2407689cb fix(setup): preserve skill files and report rollback failures
Track attempted writes, preserve original content and modes from one regular-file observation, and continue rollback after individual failures. Return tagged failure details and retained backup paths through setup.

Validation: setup filesystem and recovery tests, combined compile/typecheck/lint/format/metadata/architecture checks. Filesystem fault injection covers backup, ambiguous publication, and rollback failures.
2026-10-12 05:24:10 +08:00
39418ea173 fix(adb): share host configuration refusals with apktool (#1954)
The ADB provider, which the apktool provider mirrors, had the same two
defects: a relative REA_ADB_PATH escaped as UNKNOWN because selection
ran before the try that maps configuration failures, and a missing adb
binary projected generic target advice instead of its reason.

Move the configuration-failure-to-refusal mapping into one
providerConfigurationRefusal helper used by both providers, resolve the
ADB selection inside the mapped try, and share the CLI refusal assertion.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:09:13 +08:00
7d92d93a55 fix(managed): share first-match member list ownership (#1955)
Inventory attribute lookups and the members inspector each interpreted
TypeDef member-list ownership. The members lookup binary-searched ordered
ranges but fell back to a per-member scan of every type when malformed
lists were unordered, which was quadratic (8 s for 100,000 types).

Both now use firstMatchListOwners, one near-linear first-match index over
one-based list positions. Remove the ordered/fallback split and the
production declaringType scan, which remains only as the test oracle. The
inventory reserves only its retained index.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:06:41 +08:00
morluto eb97da3388 fix(artifacts): retain ASAR resources through cleanup failures
Give snapshot capture, header admission, packed members, and container streams one file-handle owner. Serialize adoption with reader shutdown, retain failed closes for retry, and remove snapshots only after descriptor cleanup succeeds.

Cover unread members, pending opens, zero-byte members, and paired format/cleanup failures. Keep intentionally unrecoverable test fixtures hygienic and strengthen managed identity and process cleanup-authority expectations.

Validation: full npm run check; 99 targeted tests and temporary-directory hygiene; exact discovery of 877 test files. No real vendor-provider verification was performed.
2026-10-12 05:05:21 +08:00
290d301ec7 feat(ghidra): admit relocatable MIPS32r2 ELF objects (#1950)
* feat(ghidra): admit soft-float MIPS32r2 ELF with integer-register float storage

Soft-float o32 executables (Linux kernel builds, bare-metal firmware) were
refused because their ABI flags record declares no CPR1 and FP ABI 3.
Ghidra's MIPS32 compiler specifications model only hard-float o32, so typed
float/double signatures (DWARF) would be bound to $f12/$f14/$f0 and the
decompiler loses the real dataflow: `return sel ? a : b` decompiled to
`return in_f0;`, and callers passed phantom FPU inputs.

Admit ELF32 ET_EXEC o32 MIPS32r2 targets whose ABI record declares soft-float
with no CPR1, and commit `mips_soft_float_storage: o32-integer-registers-v1`
in their analysis profile only (hard-float profile digests are unchanged).
ReaGhidraMipsSoftFloat.java runs after auto-analysis and, for each function
with a float/double formal type, either keeps the types with the storage
Ghidra's o32 model assigns to a same-size integer (pair alignment, stack
spill), or, for auto-parameters such as hidden struct-return pointers, swaps
float types for same-size integers under dynamic storage. No function is left
on hard-float storage. The report is written last; the provider client
requires it exactly when the profile commits the policy and refuses the
session before any operation otherwise.

Verification: real Ghidra 12.1.4 lane (verify:ghidra:mips) now builds
hard- and soft-float fixtures in both byte orders and checks exact
parameter/return storage through inspect_native_api, caller constants,
snapshot profile and Evidence outcome; test:fast, verify:package and an
installed-tarball soft-float run pass.

* fix(ghidra): model soft-float MIPS as a prototype for every typed callable

Review feedback on #1949: rewriting storage per Function left external
functions and indirect calls through typed function pointers on Ghidra's
hard-float o32 prototype, which still read $f12/$f14/$f0 while the session
reported success. A negative control confirms it: without the change,
`(*rea_mips_soft_callback)(8.5f, 9.5f, sel)` decompiles to
`(*rea_mips_soft_callback)(in_f12, in_f14, sel)`.

ReaGhidraMipsSoftFloat.java now derives a `__rea_soft_float` prototype from
the program's own default o32 model (encoded, float pentries and
float-consuming rules removed, so byte-order-specific aggregate rules and the
hidden struct-return pointer are kept), installs it as a specification
extension, and assigns it with dynamic storage to every non-thunk function,
external function and function-pointer definition whose formal types use
float or double. Thunks follow their targets. Float types are kept
everywhere, so the integer-retype fallback is removed. The script fails
without writing its report if the derived prototype or any reassigned
function still uses an FPU register.

The policy becomes `o32-soft-float-prototype-v1` with a
{functions, definitions} report. The soft-float fixture adds an indirect
call through a typed pointer; the real-Ghidra lane requires its constants
with no FPU inputs and the struct return's `float scale` in $a1.

* test(ghidra): pin ODDSPREG acceptance for soft-float MIPS ABI records

Review on #1949 suggested requiring flags1 == 0 for soft-float, since MIPS_AFL_FLAGS1_ODDSPREG names CPR1 registers. GCC 5.2 kernel modules, LLVM/Rust firmware and Clang -msoft-float objects all record flags1 = 1 with FP ABI soft-float: it is the MIPS32 o32 toolchain default and selects nothing without CPR1. Keep accepting it, say why at the check, and test that soft-float with ODDSPREG is admitted while any other general flag is refused.

* feat(ghidra): admit relocatable MIPS32r2 ELF objects

Linux MIPS kernel modules (.ko) and other ELF32 o32 MIPS32r2 ET_REL objects
were refused by the Ghidra MIPS lane. Ghidra's ELF loader already places each
allocated section and applies REL relocations; this admits ET_REL alongside
ET_EXEC (ET_DYN and PRX stay refused) under its own support lane,
`elf32-rel-o32-arch32r2-standard-v1`, so executable targets keep theirs.
Relocatable sessions carry limitations: addresses are analysis coordinates,
undefined symbols live in Ghidra's EXTERNAL block, and HI16 relocations report
PARTIAL because Ghidra applies them with their paired LO16.

Verification: new `verify:ghidra:mips-relocatable` real-Ghidra lane builds
-c -G0 objects in both byte orders plus a soft-float -mlong-calls module
variant. GNU readelf supplies sections, symbols and REL entries; every
relocation in an allocated section must encode S + A in the loaded word
(S from Ghidra placement, A from original bytes), with HI16/LO16 checked as
the effective address each LO16 forms, and coverage of a shared HI16, a LO16
carry, external code/data and R_MIPS_32 is asserted. The executable lane,
unit suites pass, and on 30 real .ko files 54,972 relocations matched (93 reference empty sections and are not checkable).

* fix(ghidra): verify every MIPS HI16 and archive-typed soft-float externals

Review feedback on #1950:

- The relocation oracle kept one pending HI16, so a second HI16 before the
  LO16 that resolves both overwrote the first, and unrelated relocations
  cleared it; the earlier site's high half and carry were never compared
  with S + A. Every HI16 now waits for the next LO16 of its symbol, which
  checks all of them; later LO16s reuse those HI16s, and a HI16 left
  unresolved at a section end fails. On 30 real soft-float kernel modules,
  24 contain several HI16s per LO16; all 54,972 placed relocations match.
- External callees with float prototypes: the relocatable fixture now calls
  powf, whose prototype Ghidra's C library archive applies by name. Every
  variant must decompile `powf(1.5,2.5)` without FPU inputs, and the
  soft-float module variant must report that __rea_soft_float covered it
  (with the external step removed it decompiles as `powf(param_1,param_2)`).
  `-fno-builtin` keeps the call from being folded.

Also updates the module variant's expected soft-float policy to
`o32-soft-float-prototype-v1`.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:02:55 +08:00
Aurelio BernalandGitHub 5e219de441 feat(ghidra): admit soft-float MIPS32r2 ELF with integer-register float storage (#1949)
* feat(ghidra): admit soft-float MIPS32r2 ELF with integer-register float storage

Soft-float o32 executables (Linux kernel builds, bare-metal firmware) were
refused because their ABI flags record declares no CPR1 and FP ABI 3.
Ghidra's MIPS32 compiler specifications model only hard-float o32, so typed
float/double signatures (DWARF) would be bound to $f12/$f14/$f0 and the
decompiler loses the real dataflow: `return sel ? a : b` decompiled to
`return in_f0;`, and callers passed phantom FPU inputs.

Admit ELF32 ET_EXEC o32 MIPS32r2 targets whose ABI record declares soft-float
with no CPR1, and commit `mips_soft_float_storage: o32-integer-registers-v1`
in their analysis profile only (hard-float profile digests are unchanged).
ReaGhidraMipsSoftFloat.java runs after auto-analysis and, for each function
with a float/double formal type, either keeps the types with the storage
Ghidra's o32 model assigns to a same-size integer (pair alignment, stack
spill), or, for auto-parameters such as hidden struct-return pointers, swaps
float types for same-size integers under dynamic storage. No function is left
on hard-float storage. The report is written last; the provider client
requires it exactly when the profile commits the policy and refuses the
session before any operation otherwise.

Verification: real Ghidra 12.1.4 lane (verify:ghidra:mips) now builds
hard- and soft-float fixtures in both byte orders and checks exact
parameter/return storage through inspect_native_api, caller constants,
snapshot profile and Evidence outcome; test:fast, verify:package and an
installed-tarball soft-float run pass.

* fix(ghidra): model soft-float MIPS as a prototype for every typed callable

Review feedback on #1949: rewriting storage per Function left external
functions and indirect calls through typed function pointers on Ghidra's
hard-float o32 prototype, which still read $f12/$f14/$f0 while the session
reported success. A negative control confirms it: without the change,
`(*rea_mips_soft_callback)(8.5f, 9.5f, sel)` decompiles to
`(*rea_mips_soft_callback)(in_f12, in_f14, sel)`.

ReaGhidraMipsSoftFloat.java now derives a `__rea_soft_float` prototype from
the program's own default o32 model (encoded, float pentries and
float-consuming rules removed, so byte-order-specific aggregate rules and the
hidden struct-return pointer are kept), installs it as a specification
extension, and assigns it with dynamic storage to every non-thunk function,
external function and function-pointer definition whose formal types use
float or double. Thunks follow their targets. Float types are kept
everywhere, so the integer-retype fallback is removed. The script fails
without writing its report if the derived prototype or any reassigned
function still uses an FPU register.

The policy becomes `o32-soft-float-prototype-v1` with a
{functions, definitions} report. The soft-float fixture adds an indirect
call through a typed pointer; the real-Ghidra lane requires its constants
with no FPU inputs and the struct return's `float scale` in $a1.

* test(ghidra): pin ODDSPREG acceptance for soft-float MIPS ABI records

Review on #1949 suggested requiring flags1 == 0 for soft-float, since MIPS_AFL_FLAGS1_ODDSPREG names CPR1 registers. GCC 5.2 kernel modules, LLVM/Rust firmware and Clang -msoft-float objects all record flags1 = 1 with FP ABI soft-float: it is the MIPS32 o32 toolchain default and selects nothing without CPR1. Keep accepting it, say why at the check, and test that soft-float with ODDSPREG is admitted while any other general flag is refused.
2026-10-12 04:54:38 +08:00
morluto 5ba1642a7d test: strengthen archive and resource cleanup assertions
Replace duplicate path-helper checks with real ZIP inventory coverage and replace global memory sampling with bounded-chunk assertions while retaining 64 MiB digest coverage. Await artifact client cleanup, preserve Hopper lease ownership during teardown, and expose typed application failures.
2026-10-12 04:53:27 +08:00
morluto 804c287e44 test: classify filesystem application workflows as boundaries 2026-10-12 04:53:10 +08:00
morluto 918713af37 fix(artifacts): bind directory reads to captured file state
Capture exact filesystem identity and nanosecond timestamps, validate descriptor/path state before and after streaming, and enforce the captured byte count. Bound growing files to their captured size plus one byte using precise positional reads; keep unrepresentable display sizes explicitly unknown.

Reuse the captured-state comparison around Apple assetutil and retain existing reader cleanup ownership. Validation: focused reader and affected consumer suites, full static/architecture checks, and a compiled directory-read probe on Bun 1.3.14 passed. Includes same-size mutation, growth, truncation, cancellation, FIFO replacement, cleanup retry, ZIP identity forgery, ASAR and Mach-O coverage.
2026-10-12 04:45:17 +08:00
morluto a82e37c806 fix(browser): retain module resolver cleanup ownership
Serialize resolution with shutdown, retain the owned browser after close failures, and retry cleanup before another launch. Wire cleanup through the shared service and CLI/MCP lifecycles, preserving primary failures and completed native reports alongside cleanup diagnostics.

Separate the browser resolution workflow from lifecycle ownership. Validation: seven resolver source tests, eleven application tests, MCP shutdown retry, public CLI parity, and full static/architecture checks passed. Real Chromium behavior remains unverified.
2026-10-12 04:44:25 +08:00
morluto c2b56e3e1c test: classify filesystem and process workflows by boundary
Move artifact inventory, application projection, runtime reconciliation, and TODO scanner suites into their real boundary lanes. Extract Hopper process/socket cleanup cases while keeping injected session cleanup cases in composition.

Validation: all 56 focused tests passed; discovery finds all 876 test files exactly once. Test assertions and provider verification scope are unchanged.
2026-10-12 04:44:13 +08:00
morluto 12fa8fd9f9 fix(ida): recover cleanup after worker sessions are reaped
Accept the supervisor absent-session response only for the owned session, then independently verify session and private-input absence before releasing the workspace. Keep failed inventory verification retryable without resending a completed close.

Validation: 59 focused source tests passed, including live private-input peers, unavailable inventory, and mismatched session IDs. Real IDA engine behavior remains unverified. Fixes #1710.
2026-10-12 04:40:41 +08:00
morluto dc303a8ee8 test(javascript): classify artifact workflows as boundary tests
Move the six JavaScript application suites that read production artifacts or launch compiled analysis workers out of composition. Focused test planning now prepares their runtime prerequisites instead of treating them as source-only tests.

Validation: all six relocated suites and adjacent artifact/reference suites pass (70 tests across eight files); discovery finds every test exactly once.
2026-10-12 04:33:51 +08:00
morluto 3d83135987 fix(reference): verify symlink identity around target reads
Bracket symlink target observation with the identity captured during enumeration. Report replacements and disappearing links as changed, and keep cancellation and I/O mapping at one owner boundary.

Remove duplicate target-error wrappers and unreachable external-target handling. Validation: reference reader filesystem suite passes, including deterministic replacement during observation.
2026-10-12 04:33:47 +08:00
morluto 09b6828516 fix(process): cancel run-token reads during sampling
Propagate the sample AbortSignal through readProcessRunId to host environment inspection so cancellation can settle a blocked ownership-token read.

Validation: ownership and capture lifecycle suites pass, including an abort-waiting host regression. Native provider runtime behavior was not exercised.
2026-10-12 04:33:42 +08:00
morluto f64790bfc3 refactor(javascript): reuse validated source-map decoding
Pass typed map objects into the codec and reuse validated TraceMap instances for regular and indexed lookups. Remove whole-map serialization, duplicate leaf decoding, an array-copy wrapper, and the unused offset-validation mode in artifact declaration collection.

Validation: source-map and browser consumer suites (126 tests), declaration tests, and artifact reconstruction boundaries pass. Section overlap checks and raw source declarations remain intact.
2026-10-12 04:33:38 +08:00
morluto c42232f566 fix(adb): share actionable provider configuration refusals
Catch binary selection failures at the adapter boundary and share the domain error projection with apktool. Missing and relative adb paths now report capability_unavailable with host configuration guidance in both message and remediation.

Validation: ADB/apktool provider suites and public CLI refusal suites pass. No connected-device behavior changed or verified. Refs #1952.
2026-10-12 04:33:34 +08:00
morluto 52e76c5d39 fix(managed): share first-match member ownership indexes
Resolve fields, methods, and attribute constructors through one budgeted index per layout. Preserve malformed-list first-match ownership without per-member TypeDef scans; remove the superseded lookup implementations.

Validate pointer identity and malformed ranges through production parsers, including a 40,000-type/method case and allocation admission. Managed source tests and CLI/MCP pointer acceptance pass. Refs #1953.
2026-10-12 04:33:25 +08:00
Minghang ChamlingGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
1449e8e051 fix(mcp): provide native evidence paging continuation
Supply schema-validated inspect_analysis_view arguments after completed native function Evidence is recorded and exceeds MCP delivery capacity. Preserve the full canonical result and its provenance. Verify exact source paging, export and following-call usability through real Ghidra stdio MCP.

Fixes #1849

Co-authored-by: Minghang Chamling <133689405+cold-atom@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 04:26:05 +08:00
7cdb7981c3 fix(apktool): name host requirements in configuration refusals (#1946)
A missing apktool launcher projected the generic "unavailable for the
current target" message and remediation, leaving the actual reason only
in details.reason. A relative REA_APKTOOL_COMMAND escaped as UNKNOWN
because command selection ran before the try that maps configuration
failures.

Resolve the selection inside that try for both execution and
availability, and pass the refusal reason as userMessage, as JADX and
the adapters fixed in #1224 do.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:13:23 +08:00
0ae38e1e31 fix(ida): preserve non-x86 call edges and workflow uncertainty (#1906)
* test(ida): expect callee edges from the call graph on non-x86 targets

* fix(ida): read callee edges from the call graph on non-x86 targets

* test(ida): pin address order of call graph additions

* docs(ida): describe the call graph source for non-x86 callees

* test(ida): pin the x86 baseline, the call graph reader and its failure modes

* fix(ida): check the call graph root, scope truncation to the root and degrade without callgraph

* test(ida): expect managed PE off the x86 path, possible-truncation wording and a complete ARM64 traversal

* fix(ida): keep managed PE and x86 reads off the call graph path and qualify truncation

* test(ida): answer only expected leaf roots and assert the requested call graph roots

* test(ida): expect a call graph the producer cannot give consistently to fail the callee read

* fix(ida): fail the callee read when the call graph cannot be obtained consistently

* fix(ida): preserve call edge authority and workflow uncertainty

* fix(ida): preserve explicit root call graph caps

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 04:10:22 +08:00
c2cf7dcb86 fix(managed): index attribute constructor owners once per layout (#1943)
Each CustomAttribute whose constructor is a local MethodDef scanned the
TypeDef table to find the declaring type, so inspect_managed_artifact did
O(attributes x types) row decoding. A 584 KB assembly with 16,000 types
and 16,000 such attributes blocked for about 38 s.

Build a first-match owner index once per metadata layout. Already-owned
list positions are skipped, so overlapping malformed method lists keep
their previous first-match owner in near-linear time. The index is
charged to the decode budget, and a build failure is cached like the
member pointer lookup.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:09:50 +08:00
14935e4d47 fix(javascript): preserve package and source-map boundary identity (#1939)
* fix(javascript): preserve bare package specifier suffixes

* fix(javascript): resolve ESM package self references

* fix(javascript): recover retained prefixed source maps

* test(javascript): cover suffixed ESM self references

* fix(javascript): retain legacy ESM package suffix behavior

* fix(javascript): reject legacy ESM directory and extension fallback

* refactor(javascript): consolidate package resolution and workflow coverage

* fix(javascript): report exact ESM package subpath misses

* fix(javascript): compare parsed names for ESM self references

* refactor(javascript): parse bare package requests once

* fix(javascript): derive ESM dependency lookup from package URLs

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-12 04:09:17 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
85c1d2587a feat(javascript): analyze selected application sources
Analyze exact selected sources in first-request order before unrelated
static scopes, preserving full artifact identity and byte verification.
Retain selection in CLI/MCP Evidence and summaries, with explicit unknowns
and partial coverage for unselected sources. Keep default full analysis.

Fixes #1848

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 04:07:14 +08:00
morluto 0d8b728d24 docs(agents): require related brittleness cleanup in bug fixes 2026-10-12 03:58:24 +08:00
9878bb7296 feat(ghidra): apply validated analyzer overrides before analysis
Apply caller-selected analyzer opt-outs before Ghidra analysis, reject unknown or ineffective names, and commit raw/header imports to the same analyzer profile. Use supported x86 raw compiler specs and cover the real analyzer/readback and raw workflows.

Adopts the original implementation from #1800 with validated corrections. Refs #1783, #1836.

Co-authored-by: akushonkamen <74654766+akushonkamen@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 03:49:31 +08:00
12ee785e5e feat(website): follow the system dark mode and fix small-screen text (#1894)
* fix(website): keep text inside small screens and tidy small type and spacing

From a layout audit of the built pages:

- Long terminal prompts and code-header buttons wrap instead of being cut
  off at 320px (WCAG 1.4.10).
- Headings and table cells break long words, so the 1.4.12 text-spacing
  test no longer widens pages past 320px.
- Header and footer links get a 24px minimum height (2.5.8).
- Labels that rendered under 12px now render at 12px or more, and inline
  code never drops under 0.75rem.
- The home page's hero and definition rows share one column gap and align
  on the first baseline, and stacked boxes share one corner size.
- Highlight chips get room around the text, and the active run in a step
  listing is one band instead of a box per line.

Colors are unchanged.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

* feat(website): follow the system dark mode

The site was light only, so a reader with a dark system still got a white
page. Add one prefers-color-scheme: dark block to the stylesheet that swaps
the six color variables to Catppuccin Mocha (MIT) and overrides the fixed
colors that would break on a dark page: selection, buttons, tinted
highlights, flow nodes and arrows, and a visible border on the agent
terminal.

Add a dark theme-color tag beside the light one in the page template and
the 17 pages, and a Dark column to the style guide's color table.

Light colors are unchanged. The SVG figures follow in the next commit; the
blog page's inline strokes, the dinosaur game and the social card are left
as they are.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

* feat(website): give the five SVG figures a dark scheme

Pages load the figures with <img>, so page CSS cannot reach inside them.
Each figure now carries its own @media (prefers-color-scheme: dark) block
of attribute-selector overrides that map every light color to a Catppuccin
Mocha color, with one shared map across all five. No markup, labels,
shapes or positions change, and nothing uses !important.

Four figures also paint a mantle canvas so "Open figure" works on its own;
analyst-thinking.svg stays transparent, since it sits on the page rather
than in a figure box.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

* chore(website): refresh translation source references

The theme-color tags move the page lines that the catalogs cite, so
python3 scripts/website.py translations extract rewrites the source
references in messages.pot and the 18 PO files. No message text changes.
The same run moves the "Main navigation" entry to its new position in each
catalog.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

* fix(website): keep inline step highlights inline

The full-width band for the active step now applies only to code-step
blocks. The inline parameter highlights in the dx-ball and th04
signatures stay inline again.

🤖 Generated with Claude Code (Opus 5.5) 🦊

* test(website): check every page offers light and dark theme colors

Each authored reading page, and its built copy in every language,
must carry one theme-color tag for light and one for dark. The
stylesheet must keep its dark color scheme block. On the commit before
dark mode, this test fails on every page.

🤖 Generated with Claude Code (Opus 5.5) 🦊

* fix(website): draw the dinosaur game in the page colors

In dark mode the game box turned dark, but the game still drew with
its light colors, so the dinosaur, cacti and text fell to about 2:1
contrast. The canvas now reads five colors from CSS variables on
.dino-stage. Light values are the old ones, so light mode draws the
same pixels. Dark values are Catppuccin Mocha: 8.3:1 for the
dinosaur, 7.9:1 for cacti and text, 7.6:1 for the crash color.

The moved lines in dino-demo.js shift the translation catalog
references, so the catalogs are refreshed too (reference comments
only).

🤖 Generated with Claude Code (Opus 5.5) 🦊

* fix(website): recolor the Touhou flywheel arrows in dark mode

The inline flywheel SVG on the Touhou article hard-codes its stroke
colors, so on the dark page its connectors fell to about 2:1. Two
dark-only rules map those strokes to the page accent and muted colors
(7.8:1 and 7.4:1). Light mode is unchanged.

🤖 Generated with Claude Code (Opus 5.5) 🦊

* fix(website): redraw the dinosaur game when the system theme changes

The canvas reads its colors only when it draws, so an idle, paused or
crashed game kept the old palette after a theme switch. Each demo now
redraws on a prefers-color-scheme change.

The added lines shift the translation catalog references, so the
catalogs are refreshed too (reference comments only).

🤖 Generated with Claude Code (Opus 5.5) 🦊

---------

Co-authored-by: Claude Sonnet 5.5 <sonnet@cassie-pc>
Co-authored-by: Claude Opus 5.5 <opus@cassie-pc>
2026-10-12 03:17:49 +08:00
e59db2f5de fix(session): verify retained client liveness on open fast path
Verify retained provider operation health before reusing the same target and profile. Retire failed Hopper clients through existing owned cleanup, preserve healthy reuse and cancellation, and start replacement children lazily on the next query.

Refs #1719. Actual headless Linux Hopper child-death/reopen recovery and 85 related tests passed against the current-main merge tree; Ghidra/IDA liveness and the historical snapshot report remain outside this verification.

Co-authored-by: sx4im <sx4im@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 03:16:13 +08:00
Lluís AriñoandGitHub d802304c54 fix(install): describe automatic setup and guard truncated downloads (#1902)
* fix(install): describe automatic setup and guard truncated downloads

The help text said the installer only installs the CLI and that
`rea setup` must be run separately, but the installer starts
`rea setup` interactively whenever a terminal is available. Describe
that behaviour and the --no-setup/--no-prompt opt-out.

Wrap the installer body in `main` and call it on the last line, so a
truncated `curl | bash` download fails to parse instead of executing a
partial script. Behaviour is otherwise unchanged.

Add an installer scenario that runs a copy truncated right after the
npm install line and requires that it prints nothing and never invokes
npm; it fails against the previous script.

Validation: bash -n, shellcheck 0.10.0, installScript boundary tests.

* fix(install): require the closing token before running main

Wrapping the installer in main() kept a truncated download from running
a partial body, but the last line was a bare `main "$@"`. If the piped
download stopped right after `main` (or `main `), bash saw a complete
command at EOF and ran main with no positional parameters. Under the
documented `curl ... | bash -s -- --dry-run` (or --version / --no-setup)
path that silently dropped the caller's flags, so a dry run could resolve
the latest release, run npm, and start setup.

The call now sits in a brace group:

  {
    main "$@"
    exit $?
  }

Bash does not run the group until it has read the closing brace, so
every truncation inside it is a syntax error and nothing executes; a
truncation before the group leaves only function definitions. A comment
in install.sh records why the bare call is not enough.

Validation: a new boundary test pipes install.sh to `bash -s --
--dry-run` truncated at every byte offset from the end of main() to EOF
and asserts that npm and the rea binary are never invoked; offsets
inside the group must exit non-zero with a bash parse error and no
stdout, offsets before it must print nothing, and the complete group
must finish the dry run. Reproduced the original bug by cutting the old
script after `main`: the dry run invoked npm install. `bash -n` and
shellcheck 0.11.0 are clean.

* fix(install): wrap the whole script in the truncation guard

The previous guard only wrapped the final `main "$@"` invocation, so the
preamble streamed on the `curl ... | bash` path before any incomplete
compound command was open. A download cut off right after the bytes
`set` reached EOF with a complete, argument-less `set`, which prints every
shell variable, so inherited tokens could reach stdout.

The brace group now opens on line 2, directly after the shebang, and
closes on the last line, enclosing `set -euo pipefail`, the constants, the
functions and the final `main "$@"` / `exit $?`. Bash must therefore read
the whole file before running anything; any earlier cut is a syntax error
at EOF. `set -euo pipefail` still applies inside the group (checked
manually for `false`, an unbound variable, and a failing function body),
and shellcheck 0.11.0 and `bash -n` are clean.

The truncation test now pipes every prefix of install.sh, from byte 0 to
EOF (about 6,100 cuts, run 16 at a time with a minimal environment that
includes a sentinel variable), into `bash -s -- --dry-run`. Before the
group opens it must exit quietly; inside the group it must fail with a
syntax error; in every case stdout stays empty and neither npm nor rea is
invoked. Only the complete script performs the dry run.
2026-10-12 02:47:31 +08:00
0f4992fb9e perf(ghidra): compile bundle activator with bridge scripts (#1931)
Include the no-op OSGi lifecycle class in the packaged source bundle so
Ghidra does not generate and compile an extra activator afterward.
Check the selected activator in the real packaged Windows verifier.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 02:46:32 +08:00
72489a9445 fix(javascript): report available parent result budget in coverage limits (#1915)
* fix(javascript): size the future static reserve by remaining sources

The parent reserved 80% of its result budget for future static facts
whenever any source remained, so one tiny later file could discard an
earlier file's completed semantic graph. Reserve at most one bounded
static checkpoint's retained size per remaining source, within the same
shared pool, so many remaining sources keep the previous protection.
Parent-budget rejections now report the effective admission budget and
explain that the CLI/MCP process heap, not max_heap_mb, bounds them.

Refs #1911

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

* fix(javascript): make parent budget remediation and its test runtime-aware

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

* fix(javascript): suggest a concrete parent heap size in remediation

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

* fix(javascript): let parent budget rejections state their retained facts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:43:31 +08:00
morlutoandGitHub b6651cb99f fix(javascript): retain completed facts through cancellation (#1934) 2026-10-12 02:39:52 +08:00
morluto 0b2aa69531 Merge remote-tracking branch 'origin/main' into codex/pr-1899-remove-duplicate-schema-test 2026-10-12 02:38:57 +08:00
morluto b5b73019f9 test(web): remove duplicate path schema cases 2026-10-12 02:38:16 +08:00
Lluís AriñoandGitHub d0edeec100 test(native): skip the inaccessible-directory diagnostic as root (#1900)
The artifact directory diagnostic makes a bundle member unreadable with
mode 000 and expects inventory_artifact to report its path and EACCES.
Root ignores the mode, so the read succeeds and the test fails when the
suite runs as uid 0 (for example in a container). Skip it there with the
same process.getuid?.() === 0 condition the DMG EACCES diagnostic uses.
2026-10-12 02:35:31 +08:00
morlutoandGitHub 3b490a743f test(apple): consolidate keyed archive integer verification (#1933) 2026-10-12 02:34:24 +08:00
morluto 3298219d8c docs(readme): remove CI badges from translations 2026-10-12 02:33:29 +08:00
morluto b07e54a10e docs(readme): remove CI status badge 2026-10-12 02:32:41 +08:00
fb43dd9716 fix(managed): honor metadata ExtraData before table decoding (#1893)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 02:28:22 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
0485cd3bce fix(javascript): preserve graphs within parent capacity (#1921)
Reserve the next sequential static checkpoint using its protocol size and
existing retained-expansion accounting instead of 80% of the parent budget.
Preserve total/live heap protections and explain parent admission failures
with effective capacity, retention estimates and usable retained facts.

Fixes #1911

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 02:17:38 +08:00
c32d84622f test(clients): verify native Claude Code integration on Windows (#1926)
The Claude Code client lane asserted POSIX, although nothing in it depends
on it. Remove the assertion, tell Windows users to select claude.exe when
the default command cannot be spawned (npm installs put only a claude.cmd
shim on PATH), and record 2.1.296 as verified on native Windows in
docs/testing.md.

Fixes #1923

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:14:55 +08:00
morlutoandGitHub 601814ed0e fix(hopper): preserve observed process failures across requests (#1927)
* fix(hopper): preserve observed process failures across requests

* test(hopper): consolidate retained cleanup and recover failed helper fixtures

* docs(hopper): clarify first-use demo prompt and startup recovery
2026-10-12 02:13:53 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822morluto
dfe31172ca fix(test): audit static Wasm and Harmony tool wording (#1917)
Account for explicit nonexecution wording in the existing tool-effects audit.
WABT validates and decompiles the module; Harmony projects inventory Evidence.
Retain declared effects and stale-exception checks.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:49:33 +08:00
morluto 6407718f30 test(managed): use the current attribute fixture contract 2026-10-12 01:42:02 +08:00
morluto f8513a42dc fix(javascript): keep TypeScript substitution out of runtime paths 2026-10-12 01:41:23 +08:00
a95795c6ed fix(managed): resolve member ownership through pointer tables (#1886)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-12 01:40:48 +08:00
morlutoandGitHub 369cd3dfdc fix(javascript): normalize package URLs before array selection (#1916) 2026-10-12 01:36:42 +08:00
morluto 2976fac30d test(architecture): consolidate import boundary verification 2026-10-12 01:31:33 +08:00
morluto f598bab488 fix(apple): preserve ARM64E X1 architecture identity 2026-10-12 01:31:33 +08:00
morluto bb508134ef fix(setup): verify independently selected skill destinations 2026-10-12 01:31:32 +08:00
morluto 1dfb933e47 fix(javascript): share TypeScript emitted import resolution 2026-10-12 01:31:32 +08:00
morluto 486b56724c fix(browser): compare only observed HTTP response facts 2026-10-12 01:31:23 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822morluto
9d8e42bdd6 fix(javascript): follow actual receivers in nested intrinsic calls (#1912)
Consume arguments per generic function invocation rather than property-chain
depth. Preserve the callable receiver through call/apply, Reflect.apply and
bound invocation. Cover nested mutations and fresh targets through source
analysis and inert production CLI/MCP fixtures.

Fixes #1909.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:22:26 +08:00
d28b41ea4e feat(javascript): resolve exact package import aliases (#1862)
* feat(javascript): resolve exact package import aliases

Resolve exact #alias specifiers against the importer's enclosing
package.json imports map using the shared condition, array, and null
target selection. Nested packages keep their own maps; missing keys,
explicit nulls, and out-of-scope aliases report a precise reason.
Wildcard, recursive, and external-package import targets remain
unresolved.

* fix(javascript): keep nested import aliases in CLI graphs

Bound nested #alias requires produce application-graph require edges
from the enclosing package. Node reports MODULE_NOT_FOUND when a nested
package.json has no imports field, without inheriting the ancestor map.

* fix(javascript): resolve literal package import keys before path admission

* fix(javascript): preserve import keys and invalid-target fallback

* fix(javascript): preserve unmatched package target arrays

* fix(javascript): retain package target refusal evidence

* test(browser): match real verifier startup budget

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:17:58 +08:00
e8491e023a fix(artifacts): mark Windows launchable files as host executables (#1824)
Host-file occurrences derived `executable` from POSIX execute bits, which
Windows never reports, so every Windows host file (including PE .exe
images classified as executable) reported `executable: false`.

Derive the fact once through hostExecutable: POSIX hosts keep the execute
bits; Windows marks regular files with the extensions it launches directly
(.exe, .com, .bat, .cmd), without reading the ambient PATHEXT.

Fixes #1819

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:15:35 +08:00
cyanseekandGitHub 097f0d54af perf(javascript): skip nested callables during fingerprint traversal (#1871) 2026-10-12 01:10:31 +08:00
Itoje DollarandGitHub e4ac7bbfc2 fix: order remaining outputs independently of host locale (#1867)
* fix: order remaining outputs independently of host locale

String.prototype.localeCompare without a pinned collation follows the
host's ICU default locale, so the same input could be ordered differently
on different machines. 614fb768 moved canonical domain projections to
compareUnicodeCodePoints; provider, application, catalog, and setup
orderings still used locale collation.

Order every remaining source site by Unicode code point, including Mach-O
symbols, firmware extraction paths, ASAR entries (previously pinned to
"en" collation), evidence ID sets, browser and Electron target, script,
and resource inventories, reconstruction obligations, capability and
provider listings, and setup environment details.

The current tool and prompt catalog names order identically under both
comparators, so the catalog identity digest is unchanged.

* fix(artifacts): order ASAR entries by their portable path

@electron/asar lists host-spelled paths, so code-point order over the raw
spelling still differed by host: "a\b" sorts after "a0" on Windows while
"a/b" sorts before it on POSIX. Pair each listing with its portable
archive path once, order by that path, and keep the host spelling for
@electron/asar calls and adapter keys.
2026-10-12 01:10:01 +08:00
Lluís AriñoandGitHub 0b7fc40eee fix(contracts): mark foreign execution and device mutation destructive (#1904)
MCP annotations derived destructiveHint only from mayDiscardData, so tools
that run the analysis target or a caller-selected executable
(capture_process_scenario, observe_native_calls) and tools that change an
attached Android device (install/uninstall/push/start/stop) advertised
destructiveHint: false.

Add explicit executesForeignCode and mutatesDevice effect flags, audit them
across all 170 tool effect records, and derive
destructiveHint = mayDiscardData || executesForeignCode || mutatesDevice.
readOnlyHint semantics are unchanged.

Hopper/Ghidra metadata writers (set_address_name, set_addresses_names,
set_bookmark, set_comment, set_inline_comment, annotate_native_function)
can replace or clear existing analyst names and comments, so they now
declare mayDiscardData as well.

A contract test pins the destructive set and requires every tool whose
description mentions running, launching, executing, starting, installing or
uninstalling to declare one of the new flags or carry a reasoned exception,
so wording and effects cannot silently drift. The binary_session effects
output schema carries the new flags.
2026-10-12 01:09:01 +08:00
akushonkamenandGitHub 7a0140b4aa fix(android): bound inline search Evidence with retained inventory provenance (#1890)
Focused and zero-match search_android_classes responses embedded every
examined class name: JadxSession records each raw list_classes page into
observed calls, those calls ride into Evidence raw_result on the
registration path, and the complete inline Evidence is serialized into
every tool response (issue #1850: 382,435 bytes for one match).

When the server session retains Evidence, record the complete raw-call
record first, then deliver a summary Evidence that keeps the pagination
audit (totals, offsets, page sizes, elided item counts), the unchanged
normalized matches and truthful coverage metadata, and links the
retained complete inventory by Evidence ID. Sessions without retention
keep the complete inline delivery, so complete CLI JSON output is
unchanged and no provenance is discarded.

🤖 Generated with AI assistance (disclosure voluntary per repo policy)
2026-10-12 01:07:22 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822morluto
9b3e59804d fix(javascript): prove namespace interop before attributing Electron APIs (#1857)
* fix(javascript): prove Electron module namespace conversion

* refactor(javascript): share AST child field metadata

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(javascript): retain destructured intrinsic writes

Share one pattern-target walker across lexical definitions, member mutation
tracking and Electron identity writes. Keep overwritten globals unresolved
without treating property keys or shadowed locals as global replacements.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(javascript): preserve intrinsic projections and fresh target identity

Resolve known mutators through static binding projections and prove stable
identifier chains to fresh literals before widening unknown mutation targets.
Keep reassigned, uninitialized and member-projected targets conservative.
Cover both review regressions through source tests and inert CLI/MCP fixtures.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(javascript): retain fresh function and class target identity

Recognize stable local function, undecorated class and RegExp targets before
widening intrinsic mutations. Keep decorated classes opaque because their
binding value can be replaced, and preserve class-initialization writes.
Cover fresh targets and replacement decorators in inert CLI/MCP fixtures.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(javascript): preserve intrinsic alias capture timing

Validate each alias read after initialization and retain the original function capture across copies. Share declaration initializer facts with RPC and Electron analysis, including class declarations used for fresh-target identity. Accept single-statement control blocks while keeping lexical roles and added operations checked. Cover the capture regressions through inert source and public CLI/MCP workflows.

* fix(javascript): respect hoisted fresh function declarations

Function declarations provide fresh targets before their textual position. Retain initialization-order checks for variable and class values, including aliases. Cover the reviewed regression with source controls and the public CLI/MCP fixture.

* fix(javascript): preserve proven interop result projections

* fix(javascript): retain stable declarations and bound intrinsic writes

Keep repeated variable declarations with one unconditional initializer while
rejecting assignments, conditional initialization and multiple values.
Normalize known intrinsic calls, aliases and bound argument prefixes at the
semantic owner, retaining opaque targets without tainting unused bindings or
fresh local objects. Cover both regressions through source and CLI/MCP tests.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(javascript): retain bound intrinsic mutation effects

* fix(javascript): preserve bound receivers for intrinsic invocation

* fix(javascript): prove generic binding and invocation effects

* fix(javascript): retain Reflect set receiver effects

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:06:33 +08:00
Dery FerdikaandGitHub 1769417001 fix(firmware): name missing extractor dependencies in partial extraction (#1879)
Unblob reports a missing external extractor as an
ExtractorDependencyNotFoundReport nested inside a chunk or multi-file
report, and normalizeUnblobReport forwarded that value verbatim into
diagnostics as { input_path, report }. A caller that reached partial
coverage therefore had to decode the raw producer report to learn which
extractor was absent and at which stage, even though the report already
names the dependencies.

Extraction diagnostics now normalize those entries: the named
dependencies are collected from the report and any nested value, the
producing handler is recorded as the stage, and the original report stays
retained for provenance. Entries without a dependency report keep their
previous shape.

Validation: FirmwareReports unit cases cover the chunk-nested and
multi-file-nested dependency reports and the retained raw report;
firmware suite passes; typecheck, oxlint and oxfmt on changed files. Real
Unblob extraction is unverified here because it needs the external
extractor stack.
2026-10-12 01:05:01 +08:00
cyanseekandGitHub 898d5096f9 perf(javascript): index source-map ignore lists per leaf (#1905) 2026-10-12 01:01:33 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
38539a2f4a fix(process): retain owned command cleanup diagnostics (#1907)
Preserve per-member verification failures and sweep uncertainty across
supervisor stop and owned-command failures. Report the observed token
mismatch with its PID coordinates instead of implying PID reuse.

Keep unresolved ownership unsignaled and retain the supervisor for a later
verified stop. Exercise cancellation, timeout and cleanup revalidation with
source-owned process fixtures and cooperative settlement.

Closes #1856

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 01:00:08 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
23d50ea3be fix(managed): bind row budget charges to metadata identities (#1897)
Charge fixed row bookkeeping once per table and row within an admitted
metadata layout. Repeated owner lookups no longer permanently consume
capacity for temporary cursors; distinct rows and failed admissions retain
the existing representation limit and typed failure behavior.

Verify repeated local attribute-constructor lookups through public CLI
metadata/member inspection and preserve the remaining capacity guards.

Closes #1878

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 00:33:51 +08:00
DeryFerd 499ac18011 fix(web): reject NUL bytes in web tool local path inputs
Four web tools accepted caller-selected local paths as plain strings, so
a NUL byte passed contract validation and reached filesystem calls. The
services only checked isAbsolute, and isAbsolute admits a NUL, so the
failure surfaced later as a raw ERR_INVALID_ARG_VALUE instead of a named
input constraint. Every other local-input tool already refuses NUL through
the shared local-path schema, and the existing OS-invalid-local-input
harness covers those but not these web tools.

inspect_web_network_capture.capture_path, export_web_scripts.capture_path
and output_directory, trace_web_source_location.manifest_path and
source_map.path, and trace_web_module_imports.manifest_path and
import_map.path now use localPathStringSchema. Each field already required
a nonempty string, so the advertised pattern and minLength stay the same
and only the NUL refusal is added.

Validation: a new schema test rejects NUL in all seven fields while
accepting ordinary absolute paths; the four tools are added to the
OS-invalid-local-input harness and reject NUL through the real advertised
MCP contracts; typecheck, oxlint, oxfmt, and toolSchemaValidity with a
regenerated catalog. Two webScriptExport boundary cases fail identically
with and without this change on this Windows host (symlink EPERM and
private-runtime-root authority), so they are environmental.
2026-10-11 23:32:52 +07:00
7c207c7951 feat(native): admit raw binaries with caller-supplied architecture and entry (#1868)
* feat(native): admit raw binaries with caller-supplied architecture and entry

* fix(native): honor raw-binary preparation and entry identity

* fix(ghidra): stream raw snapshot attestation with cancellation

* fix(native): preserve and validate raw lifecycle entries

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 00:06:29 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
d3e83b0899 fix(test): wait for Hopper reconnect listener readiness (#1885)
The socket path exists after bind but before listen. Wait up to two seconds
for the initial connection on ConnectionRefusedError, closing each failed
socket. Propagate other errors and keep reconnect attempts immediate.
Preserve authentication, lost replies, expiry, cleanup and thread assertions.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-12 00:02:01 +08:00
29d2624b34 fix(ghidra): report progress while startup is pending (#1877)
Forward Ghidra startup waiting observations through the existing progress reporter. Count emitted MCP status notifications and omit the unknown total to keep progress strictly increasing.

Verified with focused tests, type/lint/docs checks, packaging and actual installed Windows timeout/recovery workflows. Native first-query speed and large-target memory remain open under #1065.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-11 23:55:39 +08:00
Dery FerdikaandGitHub fa9a7e5ec0 fix(evidence): reject NUL bytes in subject and snapshot paths (#1873)
Evidence subject local_path and analysis-snapshot subject path accepted
any string, so a caller-supplied inline Evidence record could carry a NUL
byte into the observation and bundle output even though every other
selected-path boundary already refuses it. Both fields now use the shared
NUL-free local-path schema: the snapshot path keeps its nonempty rule,
and the Evidence local_path stays free to be empty because network
capture Evidence uses an empty path as a deliberate sentinel.

localPath.ts now exports the NUL-free base schema separately so the
nonempty rule is composed rather than duplicated; the advertised pattern
and minLength for existing localPathStringSchema consumers are unchanged.

Validation: new unit cases reject a NUL local_path and snapshot path while
admitting an empty Evidence path and an ordinary path; domain suite
126 files / 1704 tests pass, evidence bundle and retained-evidence MCP
boundaries pass, localToolContracts NUL harness passes, toolSchemaValidity
passes with a regenerated catalog, typecheck, oxlint, oxfmt.
2026-10-11 23:45:37 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
efbc25fbea fix(android): preserve executable static initializer evidence (#1876)
Return the exact static initializer disassembly before Java projection can
lose field assignments. Retain byte limits and representation markers, and
verify fully folded and mixed initialization through the real CLI/MCP lane.

Closes #1846

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-11 23:40:42 +08:00
pablodiazjorgeandGitHub ea40f717b2 fix(windows): bring the real Windows Ghidra verifiers up to date (#1863) 2026-10-11 23:38:34 +08:00
fb94d9dde0 fix(errors): stop recommending retries for non-retryable failures (#1869)
* fix(errors): stop recommending retries for non-retryable failures

Unreadable output and provider adapter failures project retryable=false,
but their standard message and remediation said "Retry once", so a
firmware run whose expected report was never published told callers to
repeat a non-retryable operation. Point those failures at `rea doctor`
and the reported details instead, leaving retryability decided once by
the projected error code.

Fixes #1855

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

* fix(errors): align Hopper remote failure guidance with retryability

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:17:35 +08:00
96547d373f docs: sync French, Indonesian and Italian READMEs with English (#1861)
Add the missing Bun update documentation (bun add --global install
variant, Bun 1.2.17 version requirement, bunx one-off command), the
missing WASM artifact table row, and the IDA provider mention (fr).
Also fix small translation issues: it Quick start heading, approvale
typo, fr moteur précis/filières wording.

Co-authored-by: peter-research <peter-research@users.noreply.github.com>
2026-10-11 23:10:30 +08:00
YsopwnableGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>Kaoru0822
bcd350c365 fix(artifacts): preserve physical ZIP member identities (#1865)
Bind each ZIP occurrence independently of its filename so engine metadata
inspection can read repeated resources without replacing retained sources.
Keep logical-path collision checks in artifact inventory.

Closes #1860

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-11 22:58:01 +08:00
Yann RobertoandGitHub 7e2f79ab45 fix(setup): retain symlinked data roots during uninstall (#1821) 2026-10-11 22:54:03 +08:00
Mykola DementievandGitHub d3e9ee5505 fix(ida): report a procedure IDA cannot resolve as invalid input (#1785)
* test(ida): expect a missing procedure to be reported as invalid input

* test(ida): name the invalid_request code and its category for a missing procedure

* fix(ida): report a procedure IDA cannot resolve as invalid input

* test(ida): pin the missing-procedure issue shape and the lookups that stay unreadable

* fix(ida): treat a lookup answer for another query as a producer fault

* test(ida): expect an argument-independent issue path and pin legacy error replies and escaping

* fix(ida): report a missing procedure without naming an argument path

* test(ida): expect provider-derived selectors to stay producer faults in composed workflows

* fix(ida): keep provider-derived procedure selectors out of the caller's input errors

* test(ida): cover session forwarding, parameter hygiene and structured derived-node errors

* docs(ida): describe when procedure resolution blames the caller
2026-10-11 22:52:16 +08:00
jhuang-twandGitHub 554cb45f7f fix(managed): bound transient CIL by decoded instruction count (#1822)
fix(managed): charge transient CIL by decoded instructions
2026-10-11 22:51:41 +08:00
ca11162d02 perf(browser): parse captured module URLs once per lookup (#1829)
Co-authored-by: Shayan Ali <217162076+shayanxcyber@users.noreply.github.com>
2026-10-11 22:51:16 +08:00
Yann RobertoandGitHub 37a9eb9d65 fix(javascript): preserve namespace re-export relationships (#1833)
* fix(javascript): preserve namespace re-export relationships

* fix(javascript): exclude erased namespace type exports
2026-10-11 22:51:00 +08:00
morlutoandGitHub cf7477eb46 fix(hopper): wait for late bridges during cleanup (#1866) 2026-10-11 22:50:27 +08:00
1f531fc257 fix(managed): decode generic, array, fnptr, and modifier signatures (#1835)
* fix(managed): decode generic, array, fnptr, and modifier signatures

Member signatures using GENERICINST, ARRAY, FNPTR, CMOD_REQD/CMOD_OPT,
or TYPEDBYREF were reported as unsupported, leaving their types unknown
and turning otherwise complete inspections partial. Decode them with an
explicit stack so nested signatures stay recursion-free, keep encoded
TypeDefOrRefOrSpec values, and report malformed shapes as malformed.

Fixes #1832

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

* fix(managed): bound array rank before rendering dimensions

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

* fix(managed): keep modifier order and reject non-method fnptr headers

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

* fix(managed): reject corrupt fnptr flags and misplaced typed references

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

* fix(managed): decode signature elements by grammar position

Track whether each element is a field type, return type, parameter,
nested Type, or pointee. VOID is valid only as a return type or
pointee, TYPEDBYREF only as a return type or parameter, and BYREF only
at the top of a return type, parameter, or ref field; other placements
are malformed. Function pointer items are parsed as a return type then
parameters, and generic arguments as Types.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:43:48 -07:00
069308bbe6 fix(hopper): preserve startup failures and recover denied launches
Preserve the primary Hopper startup failure and launcher diagnostics while retaining resources when cleanup cannot be confirmed. Reauthenticate matching external application sessions before document cleanup, keep the Python bridge available for bounded reconnects, and expose the provider-owned Hopper startup deadline.

Real headless Linux Hopper semantic analysis and 65 focused configuration, handshake, cleanup, and bridge tests passed. macOS authorization/crash causes and saved-HOP behavior require their own real-provider verification.

Refs #1808

Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-11 22:35:01 +08:00
ee5275cd9f fix(artifacts): count a changed partial-inventory root once (#1854)
When partial inventories omitted node details, the root got an unknown
availability row and the differing manifest root digests appended a
second, changed content row, so one logical path was counted as both
changed and unknown. Fold the manifest content change into an existing
unknown or changed root row instead, keeping its dimensions, occurrence
and artifact identities, and citations; contradiction and presence rows
are left as they are.

Fixes #1844

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:33:57 +08:00
5876779013 docs: celebrate 90,000 GitHub stars 🎉 (#1858)
Update the star milestone in the English README and all 18 translated
READMEs, keeping each locale's number formatting.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:28:33 -07:00
ed1519ddee fix(managed): report ExportedType rows in managed member inspection (#1828)
* fix(managed): report ExportedType rows in managed member inspection

Facade assemblies such as System.Runtime.dll provide their types only
through ExportedType rows (type forwarders), which member inspection
never read, so they reported complete coverage with no types. Report
each row as declared with its flags, forwarder bit, TypeDefId hint, and
implementation token, and record invalid or null implementation coded
indexes as partial coverage. Comparisons now state that exported types
are not compared when either side has them.

Fixes #1812

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

* fix(managed): read earlier member Evidence without exported types

Member Evidence recorded before ExportedType rows were read omits the
field. Parse it as null (not examined) instead of rejecting it, and
state in comparisons that such an observation's exported types are
unknown.

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

* fix(managed): leave exported types unknown when metadata is unavailable

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:02:16 -07:00
YsopwnableandGitHub 0b4ce83618 fix(windows): retain failed native inspection ownership (#1826) 2026-10-11 21:33:25 +08:00
6ac5c36503 fix(javascript): follow bundler namespace interop for Electron APIs (#1827)
* fix(javascript): follow bundler namespace interop for Electron APIs

Rolldown/tsdown emit `let electron = require("electron"); electron =
__toESM(electron);` and esbuild/tsc emit `__toESM(require("electron"))`
or `__importStar(require("electron"))` for namespace imports. The
reassignment or the helper call hid the Electron origin, so bundled
main processes reported zero BrowserWindows and preloads.

Locally declared, never reassigned interop helpers recognized by name
now pass the module through, and a binding's rewrap of its own value is
not treated as a reassignment. Other writes stay unresolved.

Fixes #1802

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

* fix(javascript): require emitted interop helper definitions

Only trust an interop helper name when it has a single program-scope
function definition, and state that helper bodies are not evaluated.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:28:19 -07:00
91f843bf38 fix(artifacts): mark Windows host executables by extension (#1823)
Windows has no POSIX execute bit; Node reports file modes from the
read-only attribute alone, so every host file, including PE images,
reported `executable: false` while the same occurrence reported
`artifact_kind: executable`. Derive the host executable marking once:
POSIX permission bits stay authoritative, and Windows marks regular
files with the CreateProcess/cmd launchable extensions.

Fixes #1819

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:26:30 -07:00
YsopwnableandGitHub 76e4153d34 perf(mcp): reduce Windows startup module resolution (#1814)
perf(mcp): bundle internal modules to reduce Windows startup I/O
2026-10-11 21:01:01 +08:00
morluto 43dafb5abe perf(test): share stdio analysis for comparison fixtures
Produce two comparison scenarios through the existing real MCP session while retaining fresh CLI comparisons, all authored source variants and native execution oracles. Keep the capture-lifetime case as a complete CLI analysis-to-comparison journey.

Validation: 92 tests pass with identical source coverage maps and hit sets across 1,156 files. The covered two-file cohort improved locally from 40.6s to 35.4s including the Git baseline optimization. Typecheck, lint and formatting pass.
2026-10-11 20:51:55 +08:00
morluto ef5f9aa2d7 perf(test): reuse the CI scope Git baseline
Copy a pristine repository into each case-owned workspace instead of repeating init, add, commit and rev-parse for 47 identical baselines. Keep every scenario mutation and real planner invocation, with seed cleanup owned by the file.
2026-10-11 20:51:40 +08:00
morluto f178bf1c00 perf(test): reuse CLI boundary worker module graphs
Share CLI boundary module evaluation in bounded forks while preserving fresh command instances and child processes. Keep module-mocked cleanup tests and source/compiled product catalog verification isolated, and update focused artifact selection.

Flameox full cohort with V8 coverage: 102.8s to 96.6s; 290 passed and two existing skips. Statement, function and branch coverage maps and hit sets match across all 1,153 source files. After syncing main, 75 focused tests, typecheck, lint, docs checks and formatting pass; all 834 test files are discovered exactly once.
2026-10-11 20:29:12 +08:00
Martin KleinandGitHub 30787297d4 fix(managed): charge retained member facts instead of decoding bounds (#1768)
* fix(managed): charge retained member facts instead of decoding bounds

inspect_managed_members returned no types or methods for ordinary
assemblies: Newtonsoft.Json 13.0.3 (netstandard2.0, 705 KB) exceeded
the 64 MiB representation budget. Each method body reserved 256 bytes
per IL byte and each signature 256 bytes per blob byte before decoding,
and those worst-case bounds were never returned, so the budget scaled
with total IL size rather than with the retained output. The final
projection admission also charged every decoded string at six bytes per
character and every scalar at 32 bytes, about eight times its JSON size.

Bound IL, exception-clause and signature decoding with a transient
admission that does not consume capacity, then reserve the decoded body
or signature that is actually retained. Projection admission now
charges the exact UTF-8 JSON bytes of decoded strings, keys and scalars;
escaped and multibyte text still counts at its encoded size.

Newtonsoft.Json now yields 496 types, 4185 methods and 18846 call
edges. Assemblies whose retained facts exceed the budget, such as the
2.3 MB System.Private.ServiceModel 4.7.0, remain unavailable.

* fix(managed): bound concurrent decoding and count JSON bytes in place

Review follow-up. A method's IL bound and its exception-section bounds
were each checked against the same remaining capacity although their
bookkeeping is live at the same time, so 40 MiB of IL plus 40 MiB of
exception clauses passed a 64 MiB budget. Transient admissions now
accumulate until the method body or signature is built, and reserve
respects bounds that are still live.

Projection admission also serialized each decoded string to measure it,
which could allocate about six times a near-limit string before the
check. It now counts the exact escaped UTF-8 bytes without building the
JSON text.

* fix(managed): admit derived call and field edges as they are built

Review follow-up. Released IL bounds no longer stand in for the call and field-access records that edges() derives from anchors. Each record is now admitted before the graph grows further, so a crafted assembly cannot materialize the whole derived graph before the representation guard fails.

* fix(managed): admit derived caller names before building them

Review follow-up. The caller name of call and field-access records was interpolated per anchor before any admission saw it. It is now built once per method, after its exact JSON size, counted from the type and member names, has been admitted.
2026-10-11 20:13:57 +08:00
f2150eb9a7 feat(harmony): project HarmonyOS package inventory evidence (#1447)
* feat(harmony): project HarmonyOS package inventory evidence

First HarmonyOS increment from #1431: HAP, HSP, and App Pack roots are
classified through the hardened ZIP reader and projected into an
execution-free application graph with packaging-model hints, exact
component paths and hashes, App Pack children by path, runtime-family
hints, and path-based N-API bridge hypotheses. HarmonyOS .har stays
unclassified by suffix because .har also names HTTP Archive JSON.

Adds the project_harmony_application_graph tool (CLI and MCP), synthetic
composition coverage, a pinned real-package verify lane, and docs.

* refactor(domain): share application graph projection scaffolding

The HarmonyOS projection mirrors the Android and Apple projections by
design, which pushed jscpd duplication over its 2.5 percent budget.
Extract the identical pieces (inventory input schema, projected
component schema and occurrence flattening, bridge candidate schema
factory, coverage assembly, comparator, canonical digest) into
domain/mobileApplicationGraphProjection.ts and route all three
projections plus the composition fixtures through it. Duplication drops
from 2.52 to 2.49 percent with no behavior change: projection ids,
classifications, and limitations are byte-identical.

* fix(harmony): address review, stable app-pack roots, and rotated fixture

- rebased onto main; the shared projection scaffolding now keeps main's
  nativeRuntimeConvention loop and the harmony runtime families adopt the
  same react-native/flutter/unity path conventions
- the .app App Pack suffix is now a root-input rule only (verified ZIP
  magic in classifyRootFormat and BinaryTargetResolver); nested .app ZIP
  members inside other packages keep the generic zip classification, with
  a regression test, so existing inventories do not change
- the signing limitation now states that an empty signing list does not
  mean unsigned because packaging-tool signatures live in the ZIP signing
  block, and the app-pack root rule is disclosed as suffix-based
- repinned the real-package fixture to the current VHome 2.6.14-beta asset
  after the upstream deleted v5.0.111-beta and replaced the v2.6.14-beta
  asset within hours; the pin records both observed digests

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 20:09:21 +08:00
林桉andGitHub b2da21abe1 test(evidence): cover workflow question whitespace normalization (#1739) 2026-10-11 19:58:55 +08:00
You WishandGitHub 0a6b46d5b0 test(ci): stabilize JADX recovery and ignore generated reports (#1668) 2026-10-11 19:56:19 +08:00
a8a9c4d817 fix(mcp): express inspect_analysis_view selector constraints in the contract schema (#1705)
The item view advertised its four selector alternatives independently of the
selected collection, while collection-specific constraints lived only in a
superRefine that does not survive JSON Schema conversion. Clients validating
against tools/list could not discover that modules accept only path/node_id
and sections/symbols accept only index/name.

Replace the refined object with a discriminated union on collection so the
converted draft-2020-12 schema rejects the same combinations the MCP server
rejects. Runtime selection semantics are unchanged.

Fixes #1695

Co-authored-by: Saim Shafique <sx4imm@gmail.com>
2026-10-11 19:55:16 +08:00
ArthasandGitHub cdd9fe7167 feat(native): expose typed incoming references at exact addresses (#1715) 2026-10-11 19:52:09 +08:00
CmsChaseandGitHub 031d7781f0 fix(build): preserve LF text checkouts on Windows (#1807)
Pin text checkouts to LF so Git for Windows with core.autocrlf=true
does not make the repository formatting gate fail on a clean clone.
Keep command fixtures in CRLF and retain automatic binary detection.
2026-10-11 19:51:36 +08:00
YsopwnableandGitHub 2e22330037 fix(build): preserve resource controls in strict Turbo tasks (#1809) 2026-10-11 19:50:50 +08:00
ffaf2c12f1 feat(wasm): inspect local artifacts with WABT (#1533)
* feat(wasm): inspect local artifacts with WABT

* fix(wasm): preserve custom names and failed cleanup ownership

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 19:49:00 +08:00
Jay-Alexander ElliotGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>morluto
e4407dbed5 feat(ghidra): admit bounded PSP ELF through installed Allegrex (#1599)
* feat(ghidra): admit bounded PSP ELF through installed Allegrex

Build on merged generic MIPS without weakening its policy. Bind caller-installed processor files into the analysis profile, check them again before launch, force and verify PSP language selection, retain limitations, and extend the public CLI/MCP verifier with source-owned PSP and target-switching checks. Local actual focused Vitest: 102 passed, one existing permission skip. Hosted static and real-provider results are pending.

* fix(ghidra): validate PSPSDK ABI records in PSP admission

* test(ghidra): bind PSP ABI verification to snapshot profiles

* test(ghidra): declare independent PSP ABI reader contract

* test(ghidra): retain the PSP fixture discriminated type

* test(ghidra): contextually type PSP metadata as the MIPS target variant

* test(ghidra): keep carved firmware checks scoped to generic MIPS fixtures

* style(ghidra): format PSP language-override guard

* style(test): format PSP language override regressions

* fix(ghidra): bound PSP extension enumeration before allocation

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 19:47:33 +08:00
morluto 0153b4d95b perf(test): batch compatible client profile lifecycles
Keep individual dry-run and scoped doctor checks for all eight profile cases. Share approved setup, idempotency, and uninstall for compatible roots; isolate Devin XDG and default OpenCode environments.

Reduce fresh CLI launches from 41 to 26. A bounded Flameox comparison measured 16.3s before and 12.0s after. All 40 focused client tests, static checks, and test discovery pass.
2026-10-11 19:43:00 +08:00
Jason CatandGitHub ed084b3dbb fix(process): observe already-due trailing callbacks before settling the capture journal (#1700)
A stalled event loop can leave the settle wake-up and a trailing observation
callback due at the same resume. The earlier wake-up runs first, declares
silence, and the capture becomes immutable before the trailing callback
appends its observation.

Drain the loop turn after the quiet sleep so timer and poll callbacks that
were already due run before silence is judged, and cover the stalled-loop
case with a deterministic regression test.

Fixes #1697
2026-10-11 19:24:51 +08:00
66c21572de fix(bun): report the real runtime and enforce worker memory budgets on Windows (#1730)
* fix(bun): report the real runtime and enforce worker memory budgets on Windows

Under Bun, REA recorded Node/V8 identity it did not observe and its JavaScript helper processes ran without a memory bound, because Bun ignores the V8 heap flags REA passes. Source-map trace Evidence claimed runtime node-v8 with Bun's synthetic V8 heap limit (the decoder then rejected that limit, so trace_web_source_location always failed), analysis resource failures reported heap limits Bun does not enforce, the HAR decoder claimed a fixed 192 MiB heap, and doctor reported Bun's Node.js compatibility version as the Node version.

Identify the runtime once from process.versions. Source-map reports record id bun with the Bun and Node.js compatibility versions and a null v8_heap_limit_bytes; the decoder checks the heap budget only for V8 reports and rejects a report from a runtime other than the launching one. The analysis worker reports a null heap limit under Bun, HAR limitations and failures omit heap sizes under Bun, and the doctor node check reports Bun and passes when its Node.js compatibility version is supported.

On Windows under Bun, each helper's owned Job Object now carries a commit limit of its heap budget plus a measured 512 MiB Bun runtime reserve (704 MiB for the codec and HAR decoder, 1536 MiB for the default analyzer). The native addon accepts an optional page-aligned job memory limit on process_spawn, verifies it by readback, associates a private completion port before the child is created, and reports a sticky memoryLimitReached and peak job commit on each poll; inspect reports jobMemoryLimit. A requested limit with an artifact lacking the control refuses the launch, and non-Windows launchers refuse a limit.

Reaching the limit is classified by the job notification rather than Bun's exit status: the analyzer fails the current source with a memory resource failure reporting worker_process_memory_limit_bytes, the codec returns a memory resource failure, and the HAR decoder returns its memory failure with process_memory_limit_bytes. Bun source-map Evidence records process_memory_limit_bytes, verified against the launcher. Node.js launches and outputs are unchanged.

* fix(windows): drain memory notifications after observing exit

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 19:20:00 +08:00
d412def722 fix(setup): register a stable launcher when run through bunx (#1731)
Setup run through bunx or bun x registered the bin inside bun's temporary
package cache. Detect bun's package-runner launch from npm_command=exec
with a bun user agent and register the pinned bunx launcher, wrapped in
cmd.exe on Windows like the npx launcher. Doctor, update maintenance and
uninstall recognize it as REA-owned, and setup's printed commands name
the runner that launched it.

Read the runner variables case-insensitively on Windows, where the CLI
environment snapshot upper-cases variable names and setup run through
npx also fell back to the cache path.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 19:17:15 +08:00
YsopwnableandGitHub 3a17882cc4 fix(javascript): retain unbound CommonJS semantic imports
Collect unbound literal CommonJS dependencies at the semantic module-link owner, retain null binding names and source provenance, and avoid duplicate links for calls already represented by bindings or re-exports. Preserve distinct nested calls, shadowing exclusions, dynamic binding uncertainty, and existing static asset references.

Fixes #1793.
2026-10-11 19:16:32 +08:00
40d9c7b836 test(native): skip the EACCES directory diagnostic as root (#1394)
Root bypasses the mode-0 file permission this test relies on, so
inventory succeeds and the expected EACCES failure never occurs. Match
the root guard used by the other permission-dependent boundary tests.


Claude-Session: https://claude.ai/code/session_01XPY2GmufMNGQKoEgRUx1pd

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-11 19:12:37 +08:00
morluto ba7df3f0f7 perf(ci): share bytecode across fresh Apple artifact subprocesses 2026-10-11 19:11:16 +08:00
Jason CatandGitHub 951c2aae3b fix(native): raise default PE resource entry budget to 16384 (#1703)
inspect-pe-resources rejected stock Windows resource images because their
tables exceed the 4096-entry default. Windows 11 25H2 SystemResources
measures imageres.dll.mun at 6476 examined entries, shell32.dll.mun at 5903
and wmploc.DLL.mun at 5440, so the default budget now admits each of them
with headroom while the maximum stays 65536. The independent 8 MiB metadata
budget still bounds allocation, and exceeding any budget still fails without
partial results.

The stable filesystem boundary test now emits a 6476-entry table through a
larger fixture section and asserts that the default budgets report complete
coverage, covering stock-image scale without a real Windows file.
2026-10-11 19:01:33 +08:00
Yann RobertoandGitHub 3ad5782dfd fix(javascript): honor package exports before sibling files
Resolve package exports before sibling-file fallback, preserve explicit export blocking, and retain precise CommonJS package self-reference behavior and caller provenance.

Fixes #1761.

Co-authored-by: Yann Roberto <165009858+shoyann@users.noreply.github.com>
2026-10-11 18:57:58 +08:00
morluto 847a1a3f25 test: share native keyed archive integer fixture generation 2026-10-11 18:54:55 +08:00
morluto a00c34dcb1 ci: reuse portable Turbo outputs across verification runs 2026-10-11 18:43:22 +08:00
12c416005f feat(javascript): recover code-split bundles from multiple inputs (#1686)
* feat(javascript): recover code-split bundles from multiple inputs

recover_javascript_sources accepted a single path, and
validateModuleSet bound every module's provenance to that one snapshot.
A code-split app (runtime + chunks, or an Electron main/preload/renderer
set) had to be recovered one file per call, and modules recovered alone
could not resolve imports across chunks: cross-chunk require(id)
references stayed as numeric ids with property reads.

Add an optional additional_paths input. The listed files are
concatenated, in order after path, into the single engine snapshot, so
one Wakaru run sees every module and cross-chunk imports resolve.
validateModuleSet's single-snapshot binding is unchanged. Module
provenance is attributed back to the input file its first extraction
range starts in, with byte ranges rebased to that file's own offsets;
a module whose ranges cross an input boundary keeps the concatenated
snapshot identity and offsets, recorded as an explicit limitation.
Single-path behavior is byte-for-byte unchanged (one segment,
offset 0).

Fixes #1605

* fix(javascript): preserve source identity in multi-input recovery

---------

Co-authored-by: JingHao-Leon <w15909130000@gmail.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 18:39:49 +08:00
YsopwnableandGitHub ce845ba428 fix(javascript): preserve and resolve complete source-map URLs (#1788)
Keep line-comment URL declarations intact and percent-decode local URL paths
once, preserving raw Evidence identity and artifact-root confinement.

Fixes #1781. Fixes #1782.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-11 18:19:39 +08:00
Nik1313132andGitHub a077e81bec chore(scripts): merge duplicate node:fs import in scan-todos (#1790)
Update scan-todos.mjs
2026-10-11 18:18:10 +08:00
morluto 8fa11751ea ci: reuse portable build cache in native package jobs
Transfer the build job Turbo cache to the four native package runners within the same workflow. Keep the normal build, prepack, pack, fresh installation, updater, and Inspector verification commands. Native artifacts and dependencies remain host-local.

Validation: actionlint 1.7.7, check:ci and docs:check passed. A fresh independent checkout restored 1124 byte-identical files from 1.64 MB of compile/skills cache in 163 ms; a source change invalidated compile and skills entries. Full package and real Node Inspector verifiers passed on macOS arm64. Cross-platform CI timing remains to be measured.
2026-10-11 18:16:04 +08:00
morluto 044c0a92d5 test: reuse application analysis across comparison directions
Reuse immutable source graphs for both export comparison orientations and obtain rest-argument seeds from the existing public CLI Evidence. Retain all direction-specific assertions while removing nine repeated application analyses.

Validation: 75 focused tests; covered locations and hit status unchanged across all 1137 source files in controlled before/after coverage runs. Comparison file execution fell from 19.2s to 15.3s locally. Static and generated-document checks passed.
2026-10-11 18:15:57 +08:00
morluto e90acaea67 fix(update): resolve Bun global directories before changing cwd 2026-10-11 18:11:52 +08:00
Martin KleinandGitHub 41544fb952 perf(javascript): skip the repeated-prefix scan on first capture visits (#1789)
Review follow-up. Only a capture already on the alias chain can repeat a prefix, so a first visit no longer slices and serializes every suffix of its path.
2026-10-11 18:10:13 +08:00
Aravind RaoandGitHub 262efe6c54 test(native): skip the DMG EACCES diagnostic as root (#1584) 2026-10-11 18:05:24 +08:00
morluto 6bcfeb0e55 test(cli): cache subprocess compilation within each worker 2026-10-11 18:01:32 +08:00
e4027e4afc feat(update): update Bun global installations (#1735)
* feat(update): update Bun global installations

`rea update` only recognized npm global installations and looked up
releases with `npm view`, so a `bun add --global rea-agents` install on a
machine without npm failed with `spawn npm ENOENT`.

Detect the running package inside Bun's global directory
(BUN_INSTALL_GLOBAL_DIR, $BUN_INSTALL/install/global,
$XDG_CACHE_HOME/.bun/install/global, ~/.bun/install/global), resolve the
latest release with `bun info`, install with
`bun add --global --ignore-scripts`, then verify the installed entry point
and plan integration maintenance exactly as for npm.

Bun commands run inside the owning global directory with
BUN_INSTALL_GLOBAL_DIR pinned, so the caller's current project is never
selected. A global directory without package.json is not treated as an
owned installation: Bun then installs into the nearest ancestor project.
npm installations keep their existing commands and messages.

* fix(update): gate Bun self-updates on supported CLI versions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 17:56:05 +08:00
Martin KleinandGitHub e365f3715f fix(javascript): bound self-alias mutation paths that never prune
Detect unprunable repeated self-alias prefixes at the JavaScript mutation owner, record conservative escape effects, and reuse settled repeated-prefix checks. Preserve finite-path precision and deeper-write uncertainty.

Refs #1665.

Co-authored-by: Martin Klein <36695017+flitzrrr@users.noreply.github.com>
2026-10-11 17:54:25 +08:00
YsopwnableandGitHub 817aef4068 fix(apple): validate build-version tool records within cmdsize (#1784) 2026-10-11 17:50:56 +08:00
morluto 85ccef34de test: reuse validators for identical MCP schemas 2026-10-11 17:43:34 +08:00
morluto 42951bbbbe test: reuse identical JavaScript analysis fixtures 2026-10-11 17:43:28 +08:00
YsopwnableandGitHub aaeadf2d4c fix(cli): await browser scenario cleanup on interrupts
Translate browser scenario CLI interrupts into cancellation and await owned provider cleanup. Preserve authenticated observations and cancelled steps with conventional exit codes 130/143.

Refs #1771.
2026-10-11 17:33:57 +08:00
Mykola DementievandGitHub 0bdc7a40ac fix(ida): wait out a transient busy worker health in headless sessions (#1778)
* test(ida): expect a transient busy worker health to be waited out

* fix(ida): wait out a transient busy worker health before reading identity

* docs(ida): record the verified macOS arm64 headless workflow and the busy-health wait

* test(ida): pin the busy wait deadline, cause and reported tool, and assert clean release

* fix(ida): repeat only a tool-name-shaped busy tool and keep the wait in its own module

* test(ida): drive the busy wait with a virtual clock and pin its bound and cancellation

* fix(ida): stop the busy wait at its deadline without another read and honour a late abort

* docs(ida): state when the busy wait times out, cancels or reports an unknown tool
2026-10-11 17:25:27 +08:00
N0zoM1z0 6e38529333 test(docs): type README drift scenarios as fixed pairs 2026-10-11 17:24:46 +08:00
N0zoM1z0 c9136eef71 fix(docs): validate translated README website destinations
Use the website language inventory when comparing README links. Accept the matching localized page while retaining checks for wrong locales, changed destinations, fragments and query parameters.
2026-10-11 17:24:46 +08:00
N0zoM1z0andKaoru0822 d981a58d7f fix(website): incorporate multilingual contributor refinements
Adapt CJK font stacks, original copy-button label restoration, accessibility wording and Traditional Chinese onboarding improvements from PR #1737. Protect the complete APK filename and correct setup terminology.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-11 17:24:46 +08:00
N0zoM1z0 a956275d8d fix(website): clarify translated terminology and RTL flows 2026-10-11 17:24:46 +08:00
N0zoM1z0 a2d3338e90 feat(website): add multilingual editions and translation workflow 2026-10-11 17:24:46 +08:00
morluto ff57b75e22 test: bound CLI validation subprocess concurrency
Run the 22 malformed process scenarios in pairs instead of starting 22 compiled CLI runtimes simultaneously. Retain every CLI/MCP error assertion and the no-launch check, and document nested subprocess limits.
2026-10-11 17:23:50 +08:00
morluto a4dd4e8e87 perf(cli): defer command-specific analysis implementations
Load application and Electron workflow implementations only when their commands execute. Keep command schemas and catalog registration eager, and extend the startup import boundary to cover the deferred services.
2026-10-11 17:23:41 +08:00
ae95e68ba5 fix(apktool): run Windows batch launchers through cmd.exe (#1777)
On Windows, Node refuses to spawn .bat and .cmd files without a shell
(CVE-2024-27980), so REA_APKTOOL_COMMAND pointing at Chocolatey's
apktool.bat or a java -jar wrapper failed its version probe with EINVAL,
reported as "did not report a usable version".

Move Ghidra's conservative cmd.exe invocation (no AutoRun, no delayed
expansion, every token quoted, interpreter metacharacters rejected) into
src/process/WindowsBatchCommand.ts. Ghidra keeps its error type, wording
and length limit. apktool now uses it for both the version probe and the
decode run when the launcher is a batch file on Windows. A launcher that
cmd.exe would reinterpret reports launcher_unsupported (not_configured)
with remediation instead of version_unresolved.

Fixes #1774

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:16:48 +08:00
Immanuel TikhonovandGitHub 914ba833de fix(javascript): report JSON status for imported package manifests
Use the existing JSON-text status checker for explicit package.json imports while retaining separate package-metadata validity. Preserve malformed/unavailable status and partial facts after cancellation.

Co-authored-by: Immanuel Tikhonov <122638311+immanuwell@users.noreply.github.com>
2026-10-11 17:07:57 +08:00
YsopwnableandGitHub 4daea941bf fix(cli): retry retained browser scenario cleanup
Retry retained browser scenario cleanup before the one-shot CLI exits. Preserve primary failures, partial observations and completed Evidence when shutdown fails.

Closes #1771.
2026-10-11 16:51:23 +08:00
morluto 40b37aeee1 ci: balance test shards and reuse package download caches 2026-10-11 16:40:17 +08:00
e8528cedcd fix(hopper): let failed one-shot commands exit while retaining a lease (#1763)
* fix(hopper): let failed one-shot commands exit while retaining a lease

A Hopper target lease retained after unconfirmed cleanup kept its
listening socket referenced, so a one-shot CLI emitted its final
cleanup_incomplete result but never exited, and its live lease rejected
every retry for the same target as already open.

Unreference the lease listener. A live MCP process still holds the
lease for as long as it runs; a completed CLI exits with status 1, and
the stale socket it leaves is recovered by the next acquirer.

Fixes #1751

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

* test(hopper): remove only this test's stale lease sockets

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:38:34 -07:00
cf692072b5 fix(ida): declare GUI navigation for attached legacy analysis (#1764)
Attached IDA mode always uses the legacy 1.4 producer, whose
disassemble_function jumps to the function and whose decompile_function
opens or reuses a pseudocode view whenever an IDA window is active.
Session discovery still advertised may_show_ui: false for every
operation.

Declare the UI effect for the attached operations that reach those
tools: pseudocode, assembly, raw function instructions and the function
dossier. Headless operations and the other attached operations keep
false. A conformance check now compares each declaration with the
upstream tools the operation actually calls.

Fixes #1712

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:36:56 -07:00
YsopwnableandGitHub 6d5d71fab5 fix(javascript): index repeated graph observations before final sorting
Retain stable live nodes and observation arrays while avoiding quadratic
collection rebuilds. Preserve duplicate handling and canonical output.

Fixes #1748

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-11 16:16:21 +08:00
YsopwnableandGitHub 713db4dc26 fix(process): align capture Evidence provider admission
Use the canonical current and supported legacy process-provider rule for authenticated capture Evidence at both CLI and MCP admission boundaries. Preserve successful Evidence and distinguish provider input errors from digest authentication failures.

Closes #1757.
2026-10-11 16:14:42 +08:00
58e91e14bd fix(setup): preserve unrelated Hermes YAML line formatting
Preserve unrelated long YAML scalars when editing an existing Hermes configuration.

Co-authored-by: Zayd <iam@zayd.wtf>
2026-10-11 16:07:44 +08:00
YsopwnableandGitHub 857a1b7b04 fix(process): aggregate incomplete trace-spec diagnostics
Summarize incomplete trace-specification pair declarations with exact counts and the first undeclared pair. Preserve complete pair coverage, successful Evidence and independent validation errors; remove temporary pair-scan copies and sorting.

Closes #1753.
2026-10-11 16:04:46 +08:00
YsopwnableandGitHub e5699d23b1 fix(mcp): cancel evidence imports before session mutation
Propagate request cancellation through immutable JSON bundle reading and
check it before committing session state. Preserve failed-close ownership.

Fixes #1746

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-11 15:59:21 +08:00
953a0ce5bd docs(i18n): celebrate 80,000 GitHub stars in translated READMEs (#1758)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:59:20 -07:00
6ff4d4a050 docs: celebrate 80,000 GitHub stars 🎉 (#1752)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:48:48 -07:00
clydeandGitHub 062723e44e fix(browser): retain effective source-map response urls
Preserve source-map response identity and legacy omission, admit identities before body reading, and retain completed results and cleanup causes within the existing output budget. Price fixed envelopes once.

Closes #1729.
Closes #1738.

Co-authored-by: allisaid <curruptdwastaken@gmail.com>
2026-10-11 15:35:43 +08:00
81dc64e3cd fix(managed): distinguish .NET Standard targets from runtimes
Preserve .NET Standard as an API compatibility target while leaving the runtime family unknown unless independent markers identify it. Keep real DLL metadata and existing runtime-marker precedence intact.

Closes #1736.

Co-authored-by: AC <ac@fabtek.cn>
2026-10-11 15:32:22 +08:00
YsopwnableandGitHub 21650455a9 fix(evidence): normalize unknown revisions and preserve mutation intent (#1733)
Normalize generated revision fields before hashing and retain complete typed
creation/update requests in a shared mutation Evidence producer. Keep imported
revision authentication and canonical persisted identity/digests unchanged.

Fixes #1723
Fixes #1724

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-11 14:56:46 +08:00
YsopwnableandGitHub 844fa52772 fix(evaluation): authenticate delivered Evidence citations
Authenticate completed successful REA Evidence records for citation accounting and shared fixture grading. Preserve genuine structured and complete text-only delivery, including authenticated inventory substeps.

Closes #1728.
2026-10-11 14:48:46 +08:00
YsopwnableandGitHub e36c82becd fix(artifacts): preserve emitted ZIP entry identity
Reject duplicate ZIP filenames before an emitted entry source lookup can be overwritten. Preserve valid repeated enumeration, case-distinct paths and typed inventory path failures.

Closes #1725. Related: #1480.
2026-10-11 14:25:03 +08:00
YsopwnableandGitHub 092de8f4b3 perf(snapshot): restore large legacy caches within live memory bounds
Stream legacy snapshot cache rows independently, authenticate complete Evidence once, and restore exact immutable payloads without assembling duplicate full graphs. Preserve the existing file format, provenance, resource admission, cancellation and cache export isolation.

Verified through real Linux SDK MCP import of the original 83 MB Ghidra snapshot and full 41 MB bundle hash, actual Windows CLI/MCP PE recovery and the installed Windows large-file decoder boundary, focused tests and static checks.

Fixes #1680. Refs #1065.
2026-10-11 13:59:40 +08:00
morluto 3f4ea32189 ci: reduce macOS runner contention 2026-10-11 13:57:32 +08:00
YsopwnableandGitHub 966dc3acde perf(evidence): reuse authenticated file snapshots during session import (#1721)
Seal file-backed MCP imports at the session boundary while preserving mutable
ordinary file reads. Validate internally constructed ledger candidates directly
with the existing immutable bundle constructor.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-11 13:53:01 +08:00
morluto 91f6840c35 ci: parallelize native verification and share test artifacts 2026-10-11 13:43:20 +08:00
YsopwnableandGitHub d9b4ae182d fix(native): retain failed command cleanup owners
Retain failed native command owners through both xcrun resolution and command capture. Retry all owned cleanup failures before new commands and after consumers join their launched work at close; preserve partial output and every resource diagnostic.

Refs #740.
2026-10-11 13:39:42 +08:00
8be7ad496c docs(website): highlight the setup guide on the homepage (#1714)
Show the hero's Setup guide link as a secondary button and add a link
to the step-by-step setup guide under the Set up REA panel.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:32:12 -07:00
morluto 01762b10c6 perf(tests): consolidate TypeScript dialect producer checks 2026-10-11 13:29:50 +08:00
YsopwnableandGitHub 29ff887f65 perf(domain): avoid duplicate readiness bundle validation
Avoid reparsing the already validated Evidence bundle in readiness reports; retain semantic ID, graph and manifest authentication. Reuse canonical digest ownership and remove an unused verifier temporary directory.

Refs #1065.
2026-10-11 13:29:11 +08:00
morluto 693ed332b5 perf(tests): remove timed output and synchronize browser fixtures 2026-10-11 13:20:16 +08:00
morluto d3088c3795 perf(javascript): reuse collected callable identities for returns 2026-10-11 13:20:16 +08:00
morluto a82d14e4db perf(cli): defer command-specific modules until dispatch 2026-10-11 13:20:15 +08:00
morluto 5f4c7ff6df perf(tests): reuse stateless workflows and immutable fixtures 2026-10-11 13:20:15 +08:00
YsopwnableandGitHub 13e03e9436 perf(evidence): fuse streamed JSON token handling (#1694)
Fuse upstream JSON parsing, scalar packing, heap admission, and value assembly into synchronous token stages while retaining asynchronous file reads and existing capacity, cancellation, decoding, identity, and typed-error semantics. Declare stream-chain 4.2.6 directly.

Validation: 30 focused tests; check:fast, Knip and formatting; actual Flameox paired full-content oracles; real Linux evidence replay and Windows source-built package/Ghidra snapshot recovery; final-head CI 31/31 success.

Refs #1065, #1680.
2026-10-11 12:36:35 +08:00
4353089c67 style(website): kbd, install-node width and nested collapsible indentation (#1699)
Style <kbd> keys, cap the Get started Node.js help section at the lede
width, and indent open .evidence-details content with a subtle left
border so nested collapsibles step in at any depth.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:32:32 -07:00
morluto 57c5acac2e docs(setup): confirm published 6.4.0 client support
Verify all 17 automatic client IDs through isolated npm package dry runs and confirm the OpenCode configuration directory override. Replace obsolete npm 6.3.0 warnings with the verified release checkpoint.

Fixes #1650.
2026-10-11 12:19:15 +08:00
0ecf3f2438 perf(mcp): keep Playwright and type-only imports off MCP startup (#1691)
* fix(build): emit no runtime imports for type-only bindings

Under verbatimModuleSyntax, an import whose specifiers all carry inline
`type` modifiers compiles to `import {} from "..."`, a side-effect import
that still resolves and evaluates the module. 78 compiled modules carried
one, including `import {} from "playwright-core"` in the browser scenario
session, which loaded Playwright's module graph at MCP startup.

Use top-level `import type` for these bindings and enable oxlint's
typescript/no-import-type-side-effects rule so the pattern cannot return.

Refs #1685

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

* perf(mcp): load Playwright when a scenario launches, not at startup

The browser scenario and Electron active providers are constructed before
the stdio server serves, and both bound Playwright's launchers at module
load. Resolve `chromium` and `_electron` from playwright-core when a
capture opens instead, as the web module resolver already does.

MCP startup now resolves 1,312 modules (4,902 resolutions) instead of
1,598 (6,076) before reading the first request, and no longer loads
playwright-core. Provider construction, the tool catalog, and capability
inventory are unchanged.

A boundary test spawns the stdio server, initializes, lists tools, and
asserts playwright-core and isomorphic-git were not resolved; the CLI
probe now exercises the real launch path to prove detection.

Closes #1685

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:04:05 -07:00
github-actions[bot]GitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>morluto
a56f7bb668 chore(main): release rea-agents 6.4.0 (#1399)
* chore(main): release rea-agents 6.4.0

* docs(release): write contributor-aware 6.4.0 changelog

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 11:57:29 +08:00
morluto ee46323b49 fix(windows): preserve drive-root private runtime paths
Join the admitted parent and generated child name without duplicating a drive-root separator. Reuse that name for handle-relative creation and path provenance.

Add native snapshot, readback, and cleanup coverage through the Ghidra dot-prefixed temp fallback, plus a session-root contract regression.

Fixes #1690
2026-10-11 11:51:39 +08:00
morluto a99857df23 test(release): keep credits attached to their contribution 2026-10-11 11:51:17 +08:00
morluto 6b043dc3de fix(release): avoid duplicated contributor thanks 2026-10-11 11:49:49 +08:00
morluto 4003151ec1 fix(ci): allow browser runtime action transport latency 2026-10-11 11:45:46 +08:00
92dac97102 docs(website): add beginner help to the native analysis guide (#1689)
Add a short glossary, an analysis-tool chooser linked to native setup,
a note that REA does not include DX-Ball, and help for copying a file's
full path.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:43:06 -07:00
YsopwnableandGitHub 89ea2cad19 perf(domain): reuse canonical JSON key encodings
Reuse exact JSON key encodings within the existing 8,192-code-unit digest-buffer quantum and avoid ordinary member value frames. Preserve canonical bytes and cooperative yielding; resume objects when a pending property is omitted.

Flameox paired experiments establish a local digest-workload improvement, with complete-result/EvidenceID oracles unchanged. Full CLI acceleration and billed token reductions are not claimed.

Refs #1065.
2026-10-11 11:40:47 +08:00
ea06264a56 docs(website): list the setup guide first on the Guides page (#1688)
Move the "Set up REA" entry ahead of the native, JavaScript and browser
guides so new readers see setup before the worked examples.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:30 -07:00
morluto 59b4a3c319 test(windows): verify native addon in a renamed Node host 2026-10-11 11:32:07 +08:00
xeropresenceandGitHub ea1c164e5e fix(windows): load native controls in Node-API hosts not named node.exe (#1531)
fix(windows): resolve Node-API imports from the host executable

Delay-load the Node-API import library and bind "node.exe" to the running
process, matching node-gyp's win_delay_load_hook. The addon now loads under
any Node-API host (Node.js, Bun) regardless of its executable file name.
2026-10-11 11:30:41 +08:00
dc676b4d85 docs(website): add beginner help to the Get started page (#1684)
Add collapsible help sections for installing Node.js, choosing a coding
agent, opening a terminal, restarting a client, copying a folder's full
path and finding an Electron app's .asar file.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:27:12 +08:00
ad3e41d46e fix(browser): resolve XLink and SVG prefixes in XHTML dom_urls (#1683)
* fix(browser): resolve XLink and SVG prefixes in XHTML dom_urls

DOMSnapshot has no namespace URI. HTML documents still use the parser's
local names. In XHTML and other XML documents, resolve in-scope xmlns
declarations so an element is SVG only in the SVG namespace, and report
any XLink href as href when plain href is absent.

Refs #1586

* fix(browser): bound XML namespace resolution and reject cyclic ancestry

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 11:26:26 +08:00
YsopwnableandGitHub 17f704050f feat(evidence): discover retained records without full payloads (#1682)
* feat(evidence): discover retained records without full payloads

* fix(evidence): guide oversized results to retained discovery

* style(tests): format retained discovery recovery assertion
2026-10-11 11:18:26 +08:00
c8820e03db fix(apple): decode binary app Info.plist files without plutil (#1476)
* fix(apple): decode binary app Info.plist files without plutil

* test(apple): use Windows-legal names in the binary plist bundle test

* fix(apple): ignore __proto__ entries in binary app Info.plist files

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 11:14:15 +08:00
morluto ab80ec640a fix(ci): report package verifier stage progress 2026-10-11 11:11:38 +08:00
morluto 9ce6ef895e test: reuse stdio MCP server in acceptance file 2026-10-11 11:11:38 +08:00
morluto b3e977e6d0 fix(browser): bound screenshot comparison memory 2026-10-11 11:11:38 +08:00
morlutoandYou Wish a99b2fd3b5 test: retain focused CommonJS export regressions
Keep uncovered assignment and binding regressions in the shared CLI and MCP runtime oracle, with a POSIX escaped-path fixture.

Co-authored-by: You Wish <29498139+Y0UW1SH@users.noreply.github.com>
2026-10-11 11:07:15 +08:00
Rudy Celekliandmorluto 4e4b15b825 fix(browser): validate PNG chunks and preserve unsupported encodings
Validate chunk checksums, type bytes, ordering and end boundaries before pixel comparison. Distinguish malformed PNGs and corrupt DEFLATE streams from recognized unsupported encodings through CLI and MCP error projection.

Adapted from #1375 with additional decoder and public boundary regressions. Eighteen decoder tests and six compiled CLI/MCP cases passed.

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 11:07:15 +08:00
morluto 21dbcf7a14 perf(cli): defer binary provider composition 2026-10-11 11:07:15 +08:00
Kaoru0822andmorluto 76296c5a38 fix(native): bind NativeAOT metadata to declared PE section extents
Section headers distinguish the declared virtual extent from the on-disk raw extent, which may include alignment padding. Resource RVA ownership and overlap use VirtualSize, falling back to SizeOfRawData when VirtualSize is zero, and payload reads still require file-backed bytes.

The NativeAOT snapshot reader applies the same range rule to RVA reads, metadata scans, executable targets, and derived overlay bounds. Page-rounded loader mappings may expose tail bytes, but those bytes lie outside the declared section range used by these parsers.
2026-10-11 11:07:15 +08:00
morlutoandYou Wish e38550462e test(native): stabilize resource-limited binary layout controls
Keep the configured launcher alive while its limited Python child runs so ownership checks retain the admitted executable identity. Trigger memory exhaustion with one oversized ELF zero-fill tail rather than racing section traversal against a timeout.

Adapted from #1357. JavaScript syntax, strict C syntax compilation, formatting, and static checks passed. The Linux pwntools verification lane was not run on this macOS host.

Co-authored-by: You Wish <29498139+Y0UW1SH@users.noreply.github.com>
2026-10-11 11:06:10 +08:00
morlutoandYou Wish 80896a1d64 fix(process): settle launcher exit without dropping descendant lineage
Refresh a mismatched launcher snapshot only when a new live process table proves its PID absent. Preserve uncertainty for detached descendants and continue checking ownership of surviving group members before signaling. Index retained lineage membership instead of rescanning it for each refreshed process.

Adapted from #1357. Validation: 71 source tests passed, one Linux-only identity case skipped; 32 real process-boundary tests passed on macOS.

Co-authored-by: You Wish <29498139+Y0UW1SH@users.noreply.github.com>
2026-10-11 11:06:10 +08:00
morluto e5bc4e85ec fix(ci): close speculative browser fixture connections 2026-10-11 11:05:08 +08:00
38221cb12e fix(native): bound PE resource RVAs by section virtual size (#1678)
The PE resource reader treated each section as covering
`max(VirtualSize, SizeOfRawData)` bytes, the mapping #1596 removed from
managed RVA reads. A resource payload in the raw alignment padding past
VirtualSize was reported with a hash of bytes the image loader never maps.
A later section whose virtual range starts inside that padding was refused
as an overlapping mapping.

Containment and overlap now use the loader's virtual extent: VirtualSize,
or SizeOfRawData when VirtualSize is zero. Ranges must still be file-backed.

Refs #1596

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:01:45 -07:00
3749ccec47 feat(ghidra): recover signed AArch64 relative jump tables (#1592)
* feat(ghidra): recover signed AArch64 relative jump tables

Generalise the AArch64 relative-table matcher to signed byte/halfword entries, UXTW indexes, byte or LSL #2 offsets, B.CS/B.HS bounds and a CBZ route for zero entries. Cross-check it against Ghidra's own load table when one exists, and add a fixture modelled on the RenderScript blend kernel.

Refs #487

* fix(ghidra): preserve typed labels for normalized switch selectors

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-11 10:45:35 +08:00
a67dbf6a48 fix: reject directory paths for the Wakaru limiter and mitmdump
`REA_JAVASCRIPT_PRLIMIT_COMMAND` and `REA_MITMDUMP_COMMAND` were admitted
after an access check alone, which a searchable directory passes. Admission
then succeeded, and the failure surfaced later as a launch-time adapter
error instead of the capability refusal that names the configured path.

Require a regular file, as #751 did for the firmware engine and limiter.
Linux-only tests mirror that change; both checks only run on Linux hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:43:48 +08:00
morlutoandClaude Opus 5.5 bd3a9bbbaf fix(native): classify PE resource input failures
Report missing and non-file selections as input issues, malformed PE images as format errors, and unstable reads as artifact changes. Keep existing limit, permission, cancellation, and other I/O classifications.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:43:48 +08:00
morlutoandClaude Opus 5.5 74050978be fix(javascript): keep dependency manifests out of application roots
Select the shallowest inventoried nondependency manifest and retain dependency package evidence without assigning Electron entry roles. Keep the artifact root when no application manifest is observed.

Adapted from PR #1392, with compiled CLI and stdio MCP regressions for root selection and dependency-only inputs.

Co-authored-by: Marnix Pluim <marnixpluim@users.noreply.github.com>

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:43:48 +08:00
2398 changed files with 589758 additions and 23674 deletions
+21
View File
@@ -0,0 +1,21 @@
{
"name": "rea",
"interface": {
"displayName": "REA"
},
"plugins": [
{
"name": "rea",
"source": {
"source": "npm",
"package": "rea-agents",
"version": "6.4.0"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
+18 -11
View File
@@ -25,6 +25,7 @@ node scripts/release-notes.mjs inventory \
--manifest /tmp/rea-VERSION.contributions.json
node scripts/release-notes.mjs record \
--manifest /tmp/rea-VERSION.contributions.json \
--summary \
--output /tmp/rea-VERSION.record.md
```
@@ -56,25 +57,31 @@ version/date/link heading. Use single-line Markdown list items under:
PR/commit references required by the existing checkpoint validator.
Group related work by analyst workflow or user impact. Explain the resulting
behavior; internal cleanup, tests, CI, and routine documentation generally
belong only in the contribution record. Read ambiguous PRs/diffs before making
behavior in short, concrete entries. Highlights summarize the release; avoid
repeating their full descriptions in Changes. Internal cleanup, tests, CI, and
routine documentation generally belong only in the provenance file. Read ambiguous PRs/diffs before making
claims. Distinguish simulated-provider checks from real-provider verification.
Each bullet covering a PR carries its link and `Thanks @...` for its verified
human author, known co-authors, and confirmed closing-issue reporters. Grouping
must preserve **all** those credits in the same bullet. Include the issue
links. Direct-commit bullets carry the commit link and known human credit.
Credit each represented contribution once in the public notes with its link
and `Thanks @...` for its verified human author, known co-authors, and confirmed
closing-issue reporters. Grouping preserves all those credits in one line.
When Highlights, detailed entries or migrations reference the same work,
put its thanks in one of those entries, not all of them. Include issue links.
Direct-commit bullets carry the commit link and known human credit.
Exclude GitHub bots and known agent accounts. REA's observed Anthropic and
Cursor co-author trailers resolve to `@claude` and `@cursoragent` with GitHub
type `User`; the credit policy excludes them while retaining their metadata.
Do not copy OpenClaw's maintainer-specific exclusions.
At the REA maintainer's request, omit `@morluto` self-thanks while preserving
authorship in the inventory. Other human contributors remain eligible.
Use explicit GitHub links for cross-repository references to avoid ambiguous
`#NNN` numbers. Unknown handles stay unknown; they are not fabricated credit.
Append the generated `### Complete contribution record` verbatim as the last
section of this release. It accounts for every discovered in-range PR and
direct contribution, including internal work omitted from the prose. Do not
hand-edit generated credits to make validation pass. Refresh the inventory
Append the generated `record --summary` provenance link verbatim at the end
of this release. The linked inventory accounts for every discovered in-range
PR and direct contribution, including internal work omitted from the prose.
Do not paste the full `record` output into public notes: it duplicates thanks
and obscures the user-facing changes. Full records remain available for review.
Do not hand-edit generated credits to make validation pass. Refresh the inventory
when product history changes; a prose-only correction can reuse the snapshot.
## Finalize with Release Please
+112
View File
@@ -0,0 +1,112 @@
---
name: rea-ci-tests-audit
description: Audit REA CI and test suites for correctness, fixture drift, false passes, and measured performance; use for suite or workflow investigations and efficiency changes, rather than ordinary feature testing or a single test failure.
---
# REA CI and tests audit
Establish which checks still catch real regressions and which costs can be
removed without losing that protection. The outcome may be a verified fix,
an evidence-backed recommendation, or an explicit decision to stop.
Follow the checkout's `AGENTS.md`, [testing guide](../../../docs/testing.md),
and [contribution rules](../../../CONTRIBUTING.md). Use their current commands
and pinned toolchain, rather than commands remembered from an earlier audit.
Preserve the requested scope: investigation alone does not authorize edits;
this skill does not grant permission to publish issues, push, or merge. Existing
user authorization still applies. Keep audit notes and raw results outside the
tracked tree, normally in a task-specific `/tmp` directory.
## Establish the executed baseline
Fetch when the user asks for latest main. Pin the baseline SHA and inspected
workflow revision; use an isolated worktree when changing or comparing code.
Do not modify another task's checkout. Record dirty changes and the exact
command, toolchain, host, selected tests, instrumentation, and prerequisites.
An old run is evidence for its executed revision, even if main advances.
Trace workflow triggers, changed-path selection, job dependencies, reusable
actions, package scripts, test discovery, artifact producers, and report gates
through to the commands that actually execute. Include scripts, generated data,
runtime registration, and subprocess boundaries missing from import graphs.
Check positive and negative selection paths: required files must be discovered
once per intended test configuration; deliberate platform/configuration repeats
are separate coverage. A missing explicit test or missing required report must
not turn green. Optional lanes may skip; preserve their stated prerequisites.
Use existing timing and failure artifacts to select suspicious families. Run
relevant checks to establish a baseline; reserve a full local suite for broad
CI diagnosis or uncertainty that focused checks cannot resolve. Do not rebuild
or rerun an unchanged complete suite after every documentation edit.
## Prove the failure or protection
For each candidate, state the expected observable behavior and its owner,
then construct the smallest reproduction against production behavior. Follow
the producer and affected consumers before deciding where to fix it.
- For drift, compare fixtures with current admission requirements, protocol
replies, named contracts, tool versions, and actual public entrypoints. A
plausible-looking mock can fail before the behavior its test claims to cover.
Search the whole affected fixture family, including other version strings.
Preserve intentionally malformed fixtures and test their intended rejection.
- For a suspicious green test, perturb the relevant behavior in an isolated
copy or inject a controlled failure. Verify that the existing assertion or
gate detects it. A test may merely repeat its mock, import abandoned code,
or hide a subprocess/checker failure. Restore mutations after the experiment.
- For shared setup or pruning, map distinct assertions to their retained
scenarios. Preserve malformed inputs, cancellation, cleanup failure,
permissions, capacity, identity, and meaningful concurrency interactions.
A faster run that skips a case or weakens its oracle is not an improvement.
- For provider coverage, separate fake-producer, boundary, and actual-engine
evidence. Inspect named skips and prerequisites; fixtures do not establish
real Ghidra, LLDB, browser, or other provider behavior.
Classify findings as reproduced defects, supported costs, hypotheses, or
unavailable evidence. Record the baseline command, observed output/exit status,
expected result, affected scope, and competing explanation. A failure on main
may be pre-existing; that does not by itself make it irrelevant to a CI audit.
Attribute PR failures using the executed revision and a clean baseline, not
just their resemblance to a previous failure. Retry only to test a specific
transient explanation; retain the original failure and stop when repetition
adds no evidence. A passing retry does not establish a root cause or a fix.
## Assess efficiency without losing the gate
Read [measurement and CI checks](references/measurement-and-ci.md) when
investigating slowness, caching, runner waits, shared artifacts, or log noise.
Compare equivalent completed work before claiming a speedup. Distinguish
setup/build time, test execution, coverage instrumentation, dependency waits,
and runner scheduling; state which costs remain unmeasured.
Prefer removing proven duplicate preparation, reusing immutable setup, and
reducing repeated subprocess work before shrinking correctness coverage.
Preserve independent sessions where lifecycle or concurrency is the oracle.
Keep live tests fresh; a cached previous pass cannot validate current host,
installed provider contents, permissions, or external state.
## Fix, verify, and hand off within scope
If implementation is authorized, make the smallest structural change at the
owning boundary and add a regression that fails on the baseline for the right
reason. Retest affected consumers and meaningful negative paths; verify cache
invalidation or discovery when those mechanisms change. Read the installed
Turbo documentation before changing its configuration or commands.
Run the repository's applicable checks and inspect complete current-head CI
results before an authorized merge. Preserve required coverage and discovery
gates. Do not suppress a failure, add blanket retries, raise timeouts, or delete
distinct assertions solely to make CI green. Changes to these controls need
their own demonstrated reason and retained failure detection.
Report each confirmed issue with exact revision, reproduction, consequence,
proposed correction, and validation limits. Report performance with workload
and measurements, not just a percentage. Use English issue/PR records when
requested; follow the user's assignment, commit identity, review, and merge
preferences without assuming them for other users. Open ready-for-review PRs
only when that lifecycle is authorized. Resolve necessary review findings and
check final main against the tested tree after merging.
Stop when the authorized work is verified, remaining benefits are unproven,
or further changes would trade correctness for speculative speed. List genuine
uncertainties without turning them into additional optimization rounds.
@@ -0,0 +1,4 @@
interface:
display_name: "REA CI and tests audit"
short_description: "Audit CI/test correctness and measured performance"
default_prompt: "Use $rea-ci-tests-audit to investigate CI and test correctness, drift, and performance before proposing changes."
@@ -0,0 +1,65 @@
# Measurement and CI checks
## Equivalent completed work
Record the source/workflow SHA, command, test identities and counts, skips,
assertions or output contract, exit status, coverage scope and thresholds,
toolchain/provider versions, host resources, and prepared artifacts. Inspect
both the child result and its report: a profiling wrapper may exit successfully
after the measured command failed. Focused tests can pass while global coverage
thresholds fail; describe both rather than calling the command green.
Measure wall time, user/system CPU, and peak process-family memory for relevant
completed tasks. Parent-only RSS misses workers and their children. On Linux,
summed RSS can double-count shared pages; report RSS/PSS semantics, sampling
interval, missing samples, and observed cleanup limits. Use equivalent host
tools elsewhere. State unknown costs, including agent tokens; fewer log bytes
alone prove neither fewer tokens nor faster tests.
Separate cold execution, warm build/file caches, and warm runtime caches.
Use repeated or alternating runs when variance affects the decision, with
enough observations to distinguish the proposed benefit from noise. Keep
workload, instrumentation, and host conditions comparable; record interference.
A run with fewer tests, early rejection, lost coverage, or a different runner
does not prove suite acceleration. CI-wide claims need end-to-end measurements,
not an extrapolation from one improved family.
## Workflow, caches, and shared artifacts
Trace each reused result from producer to consumers and identify its lifetime.
Check key/input coverage against the actual file inventory and result-affecting
configuration, environment, generated data, tool versions, and host state.
Exercise an affected input change to prove invalidation; exercise an irrelevant
change to support reuse where useful. Cache deterministic preparation and
static analysis only when their inputs fully describe the result.
Verify downloaded artifacts match the expected revision, platform, runtime,
and schema. Check restore misses, failed/partial producers, stale reports,
removed output files, and consumers after producer failures. Shared writers
need output ownership or locks; tests need isolated state, not an unnecessary
global lock. Worker counts do not bound children spawned within each test.
Preserve intentionally concurrent scenarios while bounding unrelated batches.
Check matrix expansion and all job pages before saying CI passed. Inspect
required gates after failed, cancelled, and skipped dependencies. `always()`
alone does not preserve failures; inspect the aggregation logic. Test/report
uploads should retain available diagnostics after failure, while absent or
malformed required results must fail closed. Do not demand an optional report
from a lane intentionally excluded by the selector.
## Runner waits and console noise
Use per-job and per-step timestamps where available. Workflow creation to job
start includes dependencies and other delays; it is not necessarily runner
queue time. Identify when the job became eligible before attributing waits to
runner capacity. If eligibility/queue timestamps are unavailable, report the
combined delay and unknown split. Compare equivalent lanes and runner types
across enough runs; a single busy runner is not a workflow regression.
For console reductions, retain checker exit codes, actionable errors, useful
summary counts, and complete machine-readable artifacts. Verify authored code
remains in the actual checker inventory when excluding generated outputs.
Introduce an authored error as a control; it must still fail. An ignore pattern
can hide real source as well as noise. Show that cached reports are restored,
stale successful reports cannot mask current failures, and report parse errors
remain failures. Measure readability or byte reduction separately from runtime.
@@ -2,7 +2,7 @@
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version: "37"
version: "41"
---
# REA
@@ -17,6 +17,9 @@ readiness or provider commands.
Installing this skill supplies instructions; it does not register the REA MCP
server, install analysis engines, or add tools to an already-running agent.
The exception is a Claude Code or Codex plugin install: the plugin bundles
this skill and registers its own `rea` server, outside the state that
`rea setup` and `rea doctor` manage.
If REA tools are available and their registration is not known to be stale,
proceed directly to the target. Do not run diagnostics or setup before every
@@ -31,6 +34,49 @@ Diagnose the observed blocker, continue useful supported work, and resume the
original task after repair. Client approval, shell PATH, registration and engine
availability have different remedies; avoid repeating setup for all of them.
1. Diagnose without changing REA or client configuration. `npx` may download
the package and write to npm's cache. For Codex, run
`npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
`gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
`commandcode`, `vscode`, `grok_build`, `omp`, or `grok_bot`. `grok_bot` has no local
file registration: doctor reports the Grok Bot chat step, and setup does not
write `mcp.json`. If the client is unknown, use `doctor --json` and
inspect its registration results before choosing a setup scope.
2. Distinguish the reason. Missing, malformed, or stale registration needs a
scoped configuration repair. An aligned registration with no tools in the
active session needs a restart/reconnection; doctor cannot prove that the
current agent has connected. A missing provider affects only tasks requiring
that provider: static JavaScript inspection needs neither Hopper nor Ghidra,
and Android inspection has separate bring-your-own JADX/Java prerequisites.
3. For a configuration repair, prepare the read-only plan:
`npx -y rea-agents@latest setup --client codex --dry-run --json`.
Use the current client's ID, show its exact proposed paths, backups, and
changes, and obtain approval before setup writes configuration or installs
Hopper. Setup normally installs the matching bundled skill too; include that
replacement in the reviewed plan. After approval, apply the same scope with
`npx -y rea-agents@latest setup --client codex --yes`. Add `--install-hopper`
only if that separate installation was needed and explicitly approved.
4. Restart/reconnect the affected agent. Verify that REA tools actually appear
in the session, then resume the original investigation. If they remain
absent, inspect the client's MCP launch error rather than repeating setup.
REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
JADX, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
software to repair
MCP registration. For an unsupported client, use manual stdio registration
with a version-pinned `rea-agents` package or continue through the CLI.
A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
```bash
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
```
No MCP registration or native engine is required. Read the returned Evidence,
graph, limitations, and unknowns with the same care as an MCP result; the CLI
returns the Evidence record directly. This fallback does not establish that
MCP is configured. Native CLI tasks still require their selected engine.
Prefer an existing `rea` CLI. When none is installed and REA package acquisition
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
Configuration repair starts with a read-only setup plan; show paths, backups and
@@ -61,17 +107,84 @@ explicit path or endpoint and do not need it.
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
- User-owned browser page already open: `list_browser_targets`.
- Retained HAR/native mitmproxy capture: `inspect_web_network_capture`. Select
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
connect Everywhere's separate MCP server for live traffic.
- Retained HAR/SAZ/native mitmproxy/aioquic qlog capture: `inspect_web_network_capture`. Select
original record ordinals when useful; inspect producer fields and byte/number
sidecars without fetching recorded URLs or claiming live attribution. See
[historical captures](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md) for the exact upstream
profile and credential exclusions.
profile and credential exclusions. Qlog support starts with aioquic 1.5.0
JSON 0.3; stream coverage and endpoint loss declarations do not recover bodies
or establish network loss. Prefer detail summary, then kind qlog trace/stream
pages; select one trace, stream ID and direction for contribution/range pages.
Facts retain all-recorded-events-before-selection scope. Other qlog layouts
and PCAP correlation are unsupported.
- User-owned Electron runtime already open: `list_electron_targets`.
- Packet-field discovery: `inspect_packet_fields` searches installed protocols
and field names/types before choosing fields for `inspect_packet_capture`.
Query/page the relevant records; registrations do not establish observed traffic.
- Recorded packet captures: `inspect_packet_capture` with selected TShark fields,
display filters and decode-as rules. Requires Linux, util-linux prlimit and `REA_TSHARK_COMMAND`; see
[capture guide](https://github.com/morluto/rea/blob/main/docs/packet-captures.md).
Packet limits count input packets before filtering. Output defaults to 50 matching
frames; use `match_offset` / `match_limit` for later pages, or explicit null
limit for complete matching output. Preserve the source digest across pages.
This does not perform live capture.
For address/DNS/SNI/protocol summaries use `inspect_pcap_capture` with MCP
detail summary, then retained kind pcap pages. Requires Wireshark 4.x tshark
and capinfos, with Linux prlimit or Windows Job Objects; see
[packet summaries](https://github.com/morluto/rea/blob/main/docs/pcap-analysis.md).
Select an exact DNS question for address pages; page exhaustion does not prove
network completeness. This reads recordings and does not acquire traffic.
- Decoder discovery: `inspect_signal_decoders` searches installed decoder IDs
or selected decoder help, including channels and options. Query and page the
relevant details before choosing settings; this does not scan hardware.
- Recorded logic/signal traces: `decode_signal_recording` with explicit decoder,
channel and protocol settings. On Linux with util-linux prlimit, supply sigrok-cli 0.7.2 through
`REA_SIGROK_COMMAND`; see [recording guide](https://github.com/morluto/rea/blob/main/docs/signal-recordings.md).
Output defaults to 50 literal lines; select annotation classes and page with
`line_offset` / `line_limit`, or explicit null for all remaining output.
Window counts are display lines, not annotations or samples. Preserve source digests across pages.
This reads existing files; it does not acquire hardware signals.
- Explicit local WebAssembly artifact: `inspect_wasm_artifact`. Supply WABT 1.0.42
through `REA_WABT_BIN_DIRECTORY`; retain exact byte/tool identity and decoded WAT.
Select JavaScript glue and candidate paths explicitly and preserve URL/path
ambiguity. No implicit fetch or target execution; WAT is not original source.
See the [WASM guide](https://github.com/morluto/rea/blob/main/docs/wasm-artifacts.md).
- Explicit local EVM bytecode carrier: `inspect_evm_interface` with caller-selected
`raw` or `hex` encoding. Preserve carrier/decoded digests and treat selectors,
argument strings and mutability as inferred candidates; this performs no chain
lookup or target execution. See the
lookup or target execution. For the complete inferred interface without hex or
duplicate stdout, discover support and select `detail: "summary"`; complete
Evidence stays retained under its original identity. Use the returned
`parent_evidence_id` for exact function or `bytecode_hex`/stdout/stderr pages.
Candidate coverage does not establish interface completeness. See the
[EVM bytecode guide](https://github.com/morluto/rea/blob/main/docs/evm-bytecode.md).
- Embedded Go compiler, module or build-setting metadata: `inspect_go_binary`
with the explicit local file. This built-in target-free inspection requires no
Go installation or disassembler. Preserve raw module bytes and source ranges;
absent metadata does not prove a binary is not Go, and unparsed text records
remain explicit. See the
[Go binary guide](https://github.com/morluto/rea/blob/main/docs/go-binaries.md).
- Embedded cargo-auditable Rust dependency metadata: `inspect_rust_binary`
with the explicit local ELF, PE, or thin Mach-O file. This built-in
target-free inspection requires no Rust installation or disassembler. Preserve
package order, dependency indices, and the section file range. A missing
`.dep-v0` section is null metadata, not proof the binary has no dependencies.
A rustc commit hash is an inference from a read-only `/rustc/<40-hex>/` path.
See the
[Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
- Explicit local SQLite database snapshot: `inspect_sqlite_database`. Inspect
with Node.js 24.x >=24.15 or 26+ providing native SQLite limits. Read schema
first (compact inventory by default), or select an exact ordinary table
and `columns` when rows are needed. Defaults are selected-table schema and
20 rows; request `schema_scope: "database"` for the complete stored schema. Preserve both database and existing sibling WAL identity; row samples
report truncation and have unspecified order. Use a quiescent snapshot rather
than assuming a live database/WAL pair was copied atomically. See the
[SQLite guide](https://github.com/morluto/rea/blob/main/docs/sqlite-databases.md).
- Explicit Linux ELF file for offline layout, symbols, relocations or static
mitigation evidence: `inspect_binary_layout`. This target-free operation uses
caller-supplied pwntools without opening a disassembler database. Preserve its
@@ -110,8 +223,14 @@ ask when matches are ambiguous. Never choose an example app on the user's behalf
In a target-free session, use `open_binary` to bind any archive/package or
native target whose analysis tool operates on the active target. Do not call an
unadvertised tool; inspect `binary_session` with `{}` and its
`result.tool_availability` for availability reasons and remediation. The tool
list stays complete; availability depends on the operation, target, and host.
`result.tool_availability` for availability reasons and remediation. Expand each
group's `names` into entries, inheriting the inventory-level client requirements
unless the group overrides them. In its closed effect and annotation vocabularies,
listed flags are true and omitted known flags are false. The grouped name union
is the complete tool inventory; use `tools/list` for canonical tool schemas.
Provider candidate facts describe possible providers, while top-level session
capabilities describe the current route and `analysis_provider_binding` names the
selected provider. Do not treat those scopes as interchangeable.
## Work summary-first
@@ -135,6 +254,16 @@ session to a selected path. The analysis remains complete in the ledger; avoid
repeating it merely to request the same oversized response. Complete CLI JSON
output also streams without a single MCP frame.
When the retained parent ID is unknown, use `get_evidence_bundle` with
`detail: "summary"` and exact filters for operation, target SHA-256,
analysis-profile digest or canonical procedure address. Select its returned
Evidence ID for the next view instead of requesting the complete bundle.
`retention: "complete-record"` means the whole record is stored; native
value-flow truncation and unknown metadata remain separate. For an exported
portable bundle, use `rea inspect-evidence-bundle` with JSON containing `path`,
`detail: "summary"` and the same `filters`. Reading that file still requires
validating its full contents within the local memory budget.
A client can also truncate a successful tool result without a REA transport
error. Treat that preview as incomplete; use the retained parent ID for smaller
views or export when the full record is needed. Do not parse the preview as
@@ -11,14 +11,14 @@ main and an older release expose the same arguments.
## Choose the remedy from the observed failure
| Observation | Meaning and next step |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Resolve only the prerequisite needed for the task; a complete tool catalog does not imply every operation is available. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
| Observation | Meaning and next step |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No REA tools in the active session | If REA was installed as a client plugin, follow the plugin section below. Otherwise inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
If the client cannot launch its shell/sandbox helper or read the installed skill,
inspect its launch diagnostic. For example, Codex can refuse helper aliases when
@@ -34,6 +34,33 @@ answers part of the question, and name the unavailable evidence separately.
Do not substitute another target. Label observations from ordinary shell tools
as such; only actual REA output supplies REA Evidence.
## REA installed as a client plugin
A Claude Code or Codex plugin owns its `rea` server and this skill. Doctor's
`--client` and `--skill` checks and `rea setup` read and write only
setup-owned state, so for a plugin-only install they report `missing`, and
applying setup would add a second `rea` server. If this skill was loaded from
a plugin, or the client lists a `rea` plugin, do not prepare a setup plan for
missing tools. Instead:
- Check that the plugin is installed and enabled with the client's own
listing: `claude plugin list` and `/mcp` in Claude Code, `codex plugin list`
in Codex.
- Restart or reconnect the client, then read its launch error for the `rea`
server if tools are still absent. The first start downloads the pinned
package through `npx` and can exceed the client's startup timeout; a second
start uses the cache.
- Check analysis engines through the connected server's
`binary_session.result.tool_availability`. `rea doctor --provider <id> --json`
from a shell reports that shell's environment, which can differ from the
environment the client gave the plugin server. If you still run it through
the package runner, use the version pinned in the plugin manifest's server
entry (`rea-agents@<version>`), not `@latest`, so it matches the server the
plugin starts.
Use the setup repair below only when the user wants a setup-owned registration
instead of the plugin, and remove or disable the plugin first.
## Repair registration when needed
For Codex:
@@ -45,8 +72,8 @@ rea setup --client codex --dry-run --json
Use the current supported client's ID: `claude_code`, `claude_desktop`, `codex`,
`cursor`, `gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`,
`copilot_cli`, `commandcode`, `qwen_code`, `vscode`, `grok_build`, `omp`, `pi`,
`hermes`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
`copilot_cli`, `commandcode`, `qwen_code`, `vscode`, `trae`, `grok_build`, `omp`, `pi`,
`hermes`, `qoder`, `droid`, `zcode`, `dsh`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
scope. `grok_bot` uses a chat registration step rather than local `mcp.json`.
For unsupported clients, use version-pinned manual stdio registration or CLI.
@@ -11,6 +11,19 @@ returned inline. When complete `inspect_binary_layout` or
`inspect_analysis_view` for a summary, one section/symbol/module, or a stable
page instead of repeating the producer.
Completed `inspect_artifact` inventory also supports a summary and
`view: {kind: "inventory", collection: "occurrences", logical_paths: ["package.json", "main.js"]}`.
Omit `logical_paths` to page all available occurrences; `[]` selects none.
Other inventory collections are `nodes`, `edges` and
`integrity_contradictions`. Keep `source_complete` separate from page exhaustion:
a missing match in partial source Evidence does not establish absence. Use the
returned parent Evidence ID for subsequent views and export before closing the
session when the source must survive it.
When `tools/list` advertises `inspect_artifact.detail`, request `"summary"` to
retain its complete source and receive that view immediately. Without that
advertised choice, keep the existing complete call. CLI inspection returns
portable complete Evidence; save it before projecting a selected inline view.
Use `record_unknown` to track unresolved questions in the user's investigation
and name the authority or environment still required. Supply supporting and
contradicting evidence IDs. It updates the session registry directly.
@@ -46,3 +59,13 @@ observed exit signal stays in `exit.signal`, `exit.code` is null for every
deadline exit, and the code of a finalizer that exited on its own is in
`exit.finalization.exit_code` (null on a signal exit or when no exit was
observed).
Use `inspect_analysis_view` with `view: {kind: "api-surface"}` on retained
`inspect_web_network_capture` HAR Evidence to group observed request lines by
method, origin and path template. Format matches are inferences, not handler
identity or schemas. Group pages cite parent record ordinals; consult record
coverage and exclusions before claiming completeness. No requests are replayed.
When the connected schema advertises `include_query_parameter_names`, select
it in the view for distinct observed names with original record ordinals. Query
values are omitted; the names establish neither required parameters nor a
complete server schema. Keep the original parent Evidence for exact URLs.
@@ -1,7 +1,30 @@
# JavaScript and Electron application artifacts
For captured or bundled JavaScript that needs readable source recovery, use
`recover_javascript_sources` with an absolute `path` and absent
`output_directory`. Supply related runtime/shared/lazy files in
`additional_paths` when advertised, then analyze the returned `analysis_input`
directory together. Each input retains its own snapshot, digest and byte-range
provenance; inputs and recovered code are never executed. Recovery requires a
caller-supplied Wakaru on Linux x64; see [the recovery guide](https://github.com/morluto/rea/blob/main/docs/javascript-recovery.md).
Use `analyze_javascript_application` directly on the operator-supplied ASAR or
extracted tree. When size is unknown or large and the connected schema advertises
extracted tree. When the question concerns known source paths and the connected
schema advertises `source_paths`, select exact inventoried paths such as
`["package.json", "main.js", "preload.js"]`. Include supporting manifests or
source maps explicitly; dependencies are not implicitly analyzed. REA retains
the complete artifact inventory and identity but reads source text and analyzes
only selected files. The result and summary record `source_selection`; files
outside scope and their relationships remain unknown, so partial application
coverage does not mean the selected source failed. Use repeated `--source-path`
on either static application CLI route. Omit the selection when the question
requires complete application analysis. `detail: "summary"` reduces delivery
and does not by itself reduce analysis work.
Exact `#alias` specifiers resolve through the importer's
enclosing package `imports` map to package-local files; nested `node_modules`
packages use their own map. Wildcard, recursive, and external-package import
targets stay unresolved with that limitation. When size is unknown or large and the connected schema advertises
`detail`, request `detail: "summary"`. This returns an `inspect_analysis_view`
Evidence record with a distinct ID and a `normalized_result.parent_evidence_id`
for the complete retained application analysis. Source-map contents are part of
@@ -12,7 +35,10 @@ the complete application Evidence inline when it fits.
When the connected schema advertises `max_heap_mb` and `analysis_timeout_ms`,
these select the isolated source analyzer's old-space heap and per-source
deadline. Defaults are 1024 MiB and 300000 ms. They do not change the MCP
process heap. Resource-limited analyses preserve completed static facts and
process heap. A Bun-hosted REA does not enforce the heap choice; on Windows it
instead limits the analyzer's committed memory to that choice plus a 512 MiB
runtime reserve.
Resource-limited analyses preserve completed static facts and
report partial semantic coverage with specific unknown scopes; this does not
mean the source is malformed. Inspect those limits before retrying a source.
When expanded relationships exceed the parent's available memory, inspect the
@@ -1,5 +1,43 @@
# Native, managed, and packaged artifacts
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or thin Mach-O when the
question concerns the embedded compiler version, module/dependency versions,
local replacements or build settings. No `open_binary`, Go installation or
target execution is required. The CLI equivalent is `inspect-go-binary PATH`.
Preserve artifact SHA-256, original file ranges, framed module bytes and every
unparsed module-text line. A missing record is an unknown, not proof of a
non-Go binary; observed metadata does not authenticate its claims. Function,
source and type recovery remain a separate native-analysis question.
## Rust dependency metadata
Use `inspect_rust_binary` for an explicit local ELF, PE, or thin Mach-O when
the question concerns the cargo-auditable crate list embedded in `.dep-v0`
(Mach-O: `__DATA,.dep-v0`). No `open_binary`, Rust installation, or target
execution is required. The CLI equivalent is `inspect-rust-binary PATH`.
Preserve artifact SHA-256, package order, dependency indices, and the section
file range. A missing section is null metadata, not proof the binary has no
dependencies. An omitted package kind stays null. A rustc commit hash is
inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
schema and optional rows from one exact ordinary table. It requires Node.js
24.x >=24.15 or 26+ with the native `DatabaseSync.limits` API. It also consumes the
existing sibling `-wal`; retain both original digests when citing its result.
Default output is a compact inventory; a selected table expands only its own
schema and samples 20 rows. Select `columns` to avoid irrelevant or large cells,
and request `schema_scope: "database"` only for complete schema details.
Choose `row_limit` when a finite sample is useful and keep truncation and
unspecified row order explicit. Virtual tables, shadow tables and views are
schema facts, not row-read targets. REA inspects private copies and leaves
the source files untouched; obtain a coherent quiescent database/WAL snapshot
before inspection. See [SQLite snapshots](https://github.com/morluto/rea/blob/main/docs/sqlite-databases.md).
## Recorded Linux crashes
Use `inspect_recorded_crash` with an explicit Linux x86-64 ELF core path to
@@ -25,6 +63,20 @@ retained, call `inspect_analysis_view` for a summary, mitigations or linkage
facet, one section or symbol, or a stable page. Do not re-run the decoder to
read a single object.
## Offline WASM
Use `inspect_wasm_artifact` with caller-supplied WABT for selected local bytes.
For counts, validation and WAT identity, select `detail: "summary"` on the first
MCP call. The complete original Evidence stays retained; use
`normalized_result.parent_evidence_id` for exact exports or text pages. Omitted
or complete detail keeps the existing complete result.
For a completed result, use `inspect_analysis_view` with its reported retained
reference for a summary or `view: {kind: "wasm", facet: "exports", offset: 0, limit: 32}`.
Facets also include sections, imports, candidates, glue, WAT, headers, details
and tool profile. Text offsets count UTF-16 code units; follow `next_offset`.
The complete source remains retained after an oversized MCP reply. No target
execution or network acquisition is implied, and WAT is decoded representation.
## Native targets
After `open_binary`, use focused search, procedure, or function tools directly.
@@ -43,6 +95,14 @@ names, the native API record, and value-flow summaries. Pseudocode offsets and l
code units; use the returned `next_offset` for continuation. Provider limitations
and unavailable facts remain explicit; the complete parent Evidence is retained.
`inspect_macho` on a large framework returns megabytes of imports and exports.
Select `detail: "summary"` for slice identity, UUID and per-collection counts,
then page rows with `view: {kind: "macho", facet: "exports", offset: 0, limit: 64}`
and `normalized_result.parent_evidence_id`. Facets are entrypoints,
architectures, build_metadata, load_commands, dependencies, imports, exports,
segments and provenance. `source_exhaustive: false` means unreturned symbols
may exist.
To see which functions or Objective-C methods a Mach-O actually calls in one
run, use `observe_native_calls` with explicit breakpoints and a bounded
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
@@ -50,10 +110,19 @@ runs and may change files or use the network. Hardened-runtime targets need the
`get-task-allow` entitlement.
Use `binary_session` with no arguments to check the open target, selected
provider, and alignment. Its default `result.tool_availability` includes the
complete tool inventory with availability reasons and remediation. When choosing
a tool, use its entry in that result; `tools/list` retains the complete catalog
when the target or provider state changes.
provider, and alignment. Its default `result.tool_availability` carries a
complete inventory grouped by shared facts. Expand each group's `names` and
inherit the inventory-level client requirements unless a group overrides them;
availability, reasons, remediation, modes, and flags remain inline. The
`effects` and `annotations` arrays name true flags from fixed vocabularies, so an
omitted known flag is false. `tools/list` remains the canonical source for full
tool schemas when target or provider state changes.
Provider capability groups carry provider identity and ordered limitations.
Prepend each set's `shared_limitations` to a group's `limitations` to recover the
operation's full ordered list. Candidate capabilities describe possible
providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## DOS MZ with Ghidra
@@ -62,6 +131,8 @@ loaded bytes, file mappings, relocations and the entry against the immutable
snapshot. Keep `mismatch` and `unsupported` explicit. Verified import covers the
reported static image, not runtime DOS or PC-98 hardware behavior.
For Ghidra decompilation, `procedure_pseudo_code` also accepts `procedure: "file-offset:0x..."`; CLI `rea decompile <path> --file-offset=0x... --provider ghidra` resolves the source byte offset internally. An unresolved 0x-prefixed numeric procedure selector also falls back to a unique file mapping. Existing symbols and analysis addresses win; multiple mappings fail as ambiguous. Preserve the returned file-offset and address locations and raw selection observations. ELF virtual addresses are distinct from file offsets; never assume an arbitrary offset plus image base is an analysis address.
Use `read_bytes` for initialized analysis memory and `address_to_file_offset` to
anchor one observed address to original file bytes. Loader fixups can change a
word; a source offset does not imply byte equality. Complete function body ranges
@@ -86,6 +157,18 @@ assembly. Keep managed/native boundaries and unavailable reconstruction facts
explicit. A bring-your-own reconstruction oracle is separate from the canonical
parser and must not become an implicit setup dependency.
For identity/classification questions, `inspect_managed_artifact` can explicitly
select `detail: "summary"` on the first MCP call. It retains complete metadata
and returns a linked overview with full classification/evidence, nullable
module/MVID, frameworks, metadata coverage/issues/limitations and returned-row
counts. Default complete requests remain unchanged. Read original rows with
`inspect_analysis_view`, source `normalized_result.parent_evidence_id`, and
`view: {kind: "managed", collection: "attributes", offset: 0, limit: 16}`.
Collections are `references`, `resources` and `attributes`. Paging does not
repeat parsing. Returned-row exhaustion
does not prove complete metadata or absence under partial/unavailable coverage;
preserve build-local tokens and their artifact/MVID scope.
### .NET NativeAOT
NativeAOT binaries contain native machine code, so ordinary CIL decompilers
@@ -101,15 +184,17 @@ them. For packaged apps, inventory/extract with REA's artifact tools and retain
the exact contained image identity. PDB, ELF debug, or dSYM sidecars are
additional evidence only when the provider accepts and matches them.
For deeper type recovery, the optional third-party
[Ghidra NativeAOT analyzer](https://github.com/Washi1337/ghidra-nativeaot)
rehydrates ReadyToRun metadata and can recover method tables, type relationships,
frozen objects, and strings. This is a Ghidra extension with its own install and
interactive workflow; it is not currently part of REA's managed parser or
headless Ghidra bridge. Treat its annotations as analysis evidence, retain the
binary identity, and do not present recovered types as original source. Header
discovery may require a symbol or analyst identification of the ReadyToRun
header, depending on the binary and analyzer version.
REA has a built-in read-only snapshot parser for x64 PE targets with .NET 8 RTR
9.1 metadata. An optional headless Ghidra adapter adds database annotations and
broader metadata recovery on Linux; it requires an explicitly configured
NativeAOT JAR. These are separate routes. The built-in parser also runs on
Windows Ghidra P0; read frozen literals from its report, because
`search_strings` does not include them. See the
[NativeAOT guide](https://github.com/morluto/rea/blob/main/docs/ghidra-nativeaot.md)
for setup, supported layouts, and host restrictions. Preserve binary identity
and distinguish derived metadata from Ghidra database observations. Recovered
types and native pseudocode are not original C# source. Windows Ghidra P0 has
no database mutation authority.
## Packages and extraction
@@ -133,8 +218,9 @@ every executable in the bundle. It also lists missing or weak loads and earlier
`architecture`. System paths stay undetermined because the dyld shared cache
provides them.
Use `extract_artifact` when materialized files are needed. It takes no arguments
and materializes all regular files into a fresh temporary directory chosen by
Use `extract_artifact` when materialized files are needed. It extracts the active
target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md).
It materializes all regular files into a fresh temporary directory chosen by
REA. Symlinks and encrypted entries are inventory facts, not extractable files.
The result reports the materialized directory; callers do not choose the
destination. No separate permission grant is needed.
@@ -150,6 +236,16 @@ For headerless DOS COM, explicitly open with `format: "dos-com"` (CLI
analysis: the entry is `0x10100`, file offset zero, with imposed real-mode segment
context. PSP/stack/device state remains unmodeled. See [DOS guide](https://github.com/morluto/rea/blob/main/docs/ghidra-dos.md).
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, or `6502`), and `entry_point` (`0x` hex). File offset 0 is mapped at
that address. Hopper and IDA refuse this interpretation; Ghidra uses
`BinaryLoader` with a little-endian generic language. Big-endian, Thumb-only,
and Cortex-M variants are unsupported. `6502` selects NMOS 6502; the entry and
complete file mapping must fit within `0x0000..0xffff`. 65C02, 65816, cartridge
containers, bank switching, and mapper semantics are unsupported. Windows
Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/raw-binary.md).
### Function annotations in Ghidra
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier.
@@ -165,3 +261,9 @@ resolution is explicit; missing references and size mismatch remain visible.
Other payloads are opaque, and this operation does not decode images or simulate
Windows resource loading. Consult the budgets and supported tree profile in
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/binary-diagnostics.md).
For exact core-module import/export differences, use `compare_wasm_interfaces`
with two complete inspection Evidence records (or current-session retained
references) and explicit scope. Preserve unknown types/duplicate names; static
interface equality is not runtime compatibility. CLI accepts complete inline
Evidence in `rea compare-wasm-interfaces input.json --json`.
@@ -26,3 +26,18 @@ passive browser/Electron Evidence exist. Prefer captured-source digest identity;
report path/digest disagreement as a mismatch and preserve ambiguity. Explicit
path mappings are inference inputs and never broaden filesystem or CDP authority.
A bundle observed at runtime does not prove every contained module executed.
## Fiddler Everywhere traffic debugging
For large saved captures, request `detail: "summary"`, then page retained Evidence
with `inspect_analysis_view` and `view: {kind: "capture", detail: "preview", offset: 0, limit: 20}`.
Select complete record pages only for needed headers, metadata or payloads; views
read the historical parent without repeating capture decoding.
Inspect native `.saz` snapshots with `inspect_web_network_capture` and
`format: "saz"`; encrypted archives are unsupported. For live
traffic analysis, connect Everywhere's separate MCP server and use its
advertised session tools with an explicit `sessionsSource` (`LiveTraffic` or
`AgentCalls`). Preserve existing sessions and filters when inspecting a
recorded run. See the [capture and MCP guide](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md)
for the workflow and Telerik's public setup and traffic-debugging skills.
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "rea",
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"owner": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"plugins": [
{
"name": "rea",
"description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
"source": {
"source": "npm",
"package": "rea-agents",
"version": "6.4.0"
},
"category": "development"
}
]
}
+28
View File
@@ -0,0 +1,28 @@
{
"name": "rea",
"version": "6.4.0",
"description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
"author": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"homepage": "https://rea.tools/",
"repository": "https://github.com/morluto/rea",
"license": "MIT",
"keywords": [
"reverse-engineering",
"binary-analysis",
"decompiler",
"mcp-server"
],
"skills": "./skills",
"mcpServers": {
"rea": {
"command": "node",
"args": [
"${CLAUDE_PLUGIN_ROOT}/scripts/plugin-mcp-launcher.mjs",
"6.4.0"
]
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"name": "rea",
"version": "6.4.0",
"description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
"author": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"homepage": "https://rea.tools/",
"repository": "https://github.com/morluto/rea",
"license": "MIT",
"keywords": [
"reverse-engineering",
"binary-analysis",
"decompiler",
"mcp-server"
],
"skills": "./skills",
"mcpServers": "./.mcp.json",
"interface": {
"displayName": "REA",
"shortDescription": "Reverse engineer anything with agents",
"longDescription": "Use REA to explain shipped behavior from native binaries, managed assemblies, Electron and JavaScript bundles, packages, firmware, and browser targets, with evidence that separates observations from inferences.",
"developerName": "morluto",
"category": "Developer Tools",
"capabilities": [
"Interactive",
"Read",
"Write"
],
"websiteURL": "https://rea.tools/",
"defaultPrompt": [
"Use REA to explain how this binary implements a feature.",
"Use REA to compare two versions of this application.",
"Use REA to recover the sources from this JavaScript bundle."
]
}
}
+8
View File
@@ -0,0 +1,8 @@
# Keep text checkouts consistent with the repository formatter on every platform.
* text=auto eol=lf
# Windows command fixtures retain their native line endings.
*.cmd text eol=crlf
# Preserve whitespace in original upstream licensing texts.
licenses/npm-runtime-notices.txt whitespace=-blank-at-eol
+3
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Contribution scope and integration requirements
url: https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope
about: Check supported capabilities, excluded workflows, and the fail-closed integration rule before proposing a feature.
- name: REA Documentation
url: https://github.com/morluto/rea#readme
about: Read the README and AGENTS.md for usage guides and configuration details.
+55 -13
View File
@@ -1,5 +1,5 @@
name: Design discussion
description: Resolve an REA architecture, provider, or contract question before implementation
description: Discuss an REA architecture, provider, contract, or scope decision
title: "[Design] "
labels:
- enhancement
@@ -9,7 +9,12 @@ body:
attributes:
value: |
Use this for a substantial provider, public/persisted contract, or
authority-boundary decision. Keep one decision per discussion.
authority-boundary decision. One question and a short explanation are
enough to start. Choose "Not sure yet" where needed; maintainers can help
with technical details, alternatives, and verification.
Read the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
Unresolved boundaries block integration, not opening a discussion.
- type: checkboxes
id: before-submitting
@@ -18,14 +23,16 @@ body:
options:
- label: I searched the open and closed issues and pull requests for a related discussion.
required: true
- label: I removed credentials, tokens, private paths, and sensitive target data.
- label: I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here.
required: true
- label: This discussion does not include credentials, private samples, or sensitive target data.
required: true
- type: textarea
id: decision
attributes:
label: Decision needed
description: State the exact question maintainers need to resolve.
description: What would you like maintainers to help decide? A short question is enough.
placeholder: Should REA ... or ...?
validations:
required: true
@@ -34,23 +41,57 @@ body:
id: context-evidence
attributes:
label: Context and evidence
description: Explain why the decision is needed now. Separate observed behavior, derived evidence, and inference.
description: Explain the problem or motivation. If you have evidence, distinguish observed behavior from ideas or guesses.
validations:
required: true
- type: dropdown
id: target-interaction
attributes:
label: Expected interaction with targets
description: >-
Select all known effects, including upstream tool behavior. Add "Not sure
yet" if anything is unclear. Use "No target interaction" only on its own.
These selections help discussion and do not establish scope approval.
multiple: true
options:
- Read existing files or captured data
- Launch or attach to a process
- Inject instrumentation or run scripts/code
- Change process memory or registers
- Write or change files/configuration
- Contact network services
- Recover protected code or work around protections
- No target interaction (documentation or usability only)
- Not sure yet
validations:
required: true
- type: textarea
id: boundary-invariants
attributes:
label: Boundary and invariants
description: Identify the owning layer, provider/bridge boundary, artifact or evidence invariants, and any process or authorization constraints.
validations:
required: true
label: Boundary details (optional)
description: >-
If known, clarify target selection, selected effects, provider boundaries,
non-goals, or evidence invariants. Leave blank if you need help identifying
them. Do not include private samples or proof-of-authorization documents.
- type: textarea
id: options
attributes:
label: Options and tradeoffs
description: Compare a small number of viable approaches, including platform, compatibility, safety, evidence, and maintenance costs.
description: Optional approaches or tradeoffs. One idea is enough; maintainers can help compare alternatives.
- type: dropdown
id: verification-readiness
attributes:
label: Verification available so far
description: Pick the closest match. An idea without samples or test results is welcome.
options:
- Idea only; I need help defining checks
- I can suggest a sample or manual check
- I have a prototype or observed results
- Not sure yet
validations:
required: true
@@ -58,9 +99,10 @@ body:
id: success-criteria
attributes:
label: Success criteria and open questions
description: State what outcome would settle the decision and list only unresolved questions that could change the choice.
validations:
required: true
description: >-
Optional expected output, sample, possible check, or unresolved question.
A complete test plan is not required to start; maintainers and implementers
will resolve material verification gaps before integration.
- type: textarea
id: references
+64 -11
View File
@@ -8,9 +8,13 @@ body:
- type: markdown
attributes:
value: |
Keep one primary capability or workflow outcome per request. For a
substantial provider, public contract, or architecture change, explain
the decision maintainers need to make before implementation.
Describe one problem and the outcome you would like. Short answers are
welcome. Choose "Not sure yet" wherever needed; you do not need code, a
finished design, or a complete test plan to propose an idea.
Read the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
Maintainers can help resolve scope and technical questions. Unresolved
boundaries block integration, not opening a request for discussion.
- type: checkboxes
id: before-submitting
@@ -19,7 +23,9 @@ body:
options:
- label: I searched the open and closed issues and pull requests for a related proposal.
required: true
- label: I removed credentials, tokens, private paths, and sensitive target data.
- label: I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here.
required: true
- label: This request does not include credentials, private samples, or sensitive target data.
required: true
- type: dropdown
@@ -36,6 +42,7 @@ body:
- MCP or agent integration
- Documentation
- Other
- Not sure yet
validations:
required: true
@@ -43,7 +50,7 @@ body:
id: current-limitation
attributes:
label: Current limitation
description: What user or agent workflow is blocked, ambiguous, or unnecessarily difficult today?
description: What are you trying to understand or do, and what is difficult today? A short example is enough.
validations:
required: true
@@ -51,23 +58,69 @@ body:
id: use-cases
attributes:
label: Use cases
description: Give concrete applications, targets, callers, or investigation workflows. Explain why current tools are insufficient.
validations:
required: true
description: Optional additional examples of applications, targets, or callers. Leave blank if covered above.
- type: textarea
id: desired-outcome
attributes:
label: Desired outcome and success criteria
description: Describe observable behavior, inputs, outputs, evidence, and compatibility criteria that would make this useful.
description: What would a useful result look like? Describe the output or behavior you want; implementation details are optional.
placeholder: For a selected APK, show whether it is packed and include the detection evidence.
validations:
required: true
- type: dropdown
id: target-interaction
attributes:
label: Expected interaction with targets
description: >-
Select all known effects, including upstream tool behavior. Add "Not sure
yet" if anything is unclear. Use "No target interaction" only on its own.
These selections help discussion and do not establish scope approval.
multiple: true
options:
- Read existing files or captured data
- Launch or attach to a process
- Inject instrumentation or run scripts/code
- Change process memory or registers
- Write or change files/configuration
- Contact network services
- Recover protected code or work around protections
- No target interaction (documentation or usability only)
- Not sure yet
validations:
required: true
- type: textarea
id: boundaries
attributes:
label: Authority boundaries and non-goals
description: Call out process execution, target mutation, provider licensing, evidence provenance, supported platforms, and work that should remain out of scope.
label: Target and scope details (optional)
description: >-
If known, name the target/platform, clarify selected effects, or list a
non-goal or scope question. You can leave this blank; maintainers can help.
Do not include private samples or proof-of-authorization documents.
- type: dropdown
id: verification-readiness
attributes:
label: Verification available so far
description: Pick the closest match. An idea without samples or test results is welcome.
options:
- Idea only; I need help defining checks
- I can suggest a sample or manual check
- I have a prototype or observed results
- Not sure yet
validations:
required: true
- type: textarea
id: verification
attributes:
label: Verification details (optional)
description: >-
Share an expected output, a possible check, or existing results if you have
them. Self-owned or appropriately licensed samples can help, but are not
required to open a request. Do not attach private or sensitive data.
- type: textarea
id: alternatives
+42
View File
@@ -0,0 +1,42 @@
name: Cache npm package downloads
description: Restore npm's download cache and save it only from trusted default-branch events.
outputs:
can-save:
description: Whether this run may update the shared npm download cache.
value: ${{ steps.context.outputs.can-save }}
path:
description: The npm download-cache directory.
value: ${{ steps.context.outputs.path }}
runs:
using: composite
steps:
- id: context
shell: bash
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
LOCKFILES_HASH: ${{ hashFiles('npm-shrinkwrap.json', '.npmrc') }}
run: |
cache_path="$(npm config get cache)"
runtime_arch="$(node -p 'process.arch')"
can_save=false
if [[ -n "${DEFAULT_BRANCH}" && "${GITHUB_REF}" == "refs/heads/${DEFAULT_BRANCH}" && ( "${GITHUB_EVENT_NAME}" == "push" || "${GITHUB_EVENT_NAME}" == "schedule" || "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ) ]]; then
can_save=true
fi
cache_key="npm-download-${RUNNER_OS}-${runtime_arch}-${LOCKFILES_HASH}"
{
printf 'can-save=%s\n' "${can_save}"
printf 'path=%s\n' "${cache_path}"
printf 'key=%s\n' "${cache_key}"
} >> "${GITHUB_OUTPUT}"
- name: Restore and save npm package downloads
if: steps.context.outputs.can-save == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
- name: Restore npm package downloads without saving
if: steps.context.outputs.can-save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
@@ -0,0 +1,53 @@
name: Portable build cache
description: Restore portable Turbo build outputs from the current run or cross-run cache.
inputs:
save:
description: Save newly built outputs after the job succeeds.
default: "false"
build_cache_artifact:
description: Reuse portable Turbo outputs uploaded by the current workflow run.
default: ""
runs:
using: composite
steps:
- name: Download portable build outputs from this workflow run
if: inputs.build_cache_artifact != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.build_cache_artifact }}
path: .turbo/cache/
- name: Resolve portable build task hashes
id: plan
if: inputs.build_cache_artifact == ''
shell: bash
env:
BUILD_CACHE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.npmrc', '.nvmrc', 'npm-shrinkwrap.json') }}
run: |
npm run --silent test:prepare -- --dry=json |
node --input-type=module -e '
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
const { tasks } = JSON.parse(readFileSync(0, "utf8"));
const hashes = tasks.map(({ taskId, hash }) => {
if (typeof hash !== "string")
throw new Error(`Build task ${taskId} must have an eager Turbo hash`);
return `${taskId}:${hash}`;
}).sort();
const hash = createHash("sha256").update(hashes.join("\n")).digest("hex");
console.log(`key=${process.env.BUILD_CACHE_PREFIX}-${hash}`);
console.log(`prefix=${process.env.BUILD_CACHE_PREFIX}-`);
' >> "$GITHUB_OUTPUT"
- name: Restore portable build outputs
if: inputs.build_cache_artifact == '' && inputs.save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: ${{ steps.plan.outputs.key }}
restore-keys: ${{ steps.plan.outputs.prefix }}
- name: Restore and save portable build outputs
if: inputs.build_cache_artifact == '' && inputs.save == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: ${{ steps.plan.outputs.key }}
restore-keys: ${{ steps.plan.outputs.prefix }}
+29 -3
View File
@@ -19,17 +19,39 @@ describe the use case and observable outcome. -->
<!-- Explain the chosen approach, why it fits REA's architecture, and what is
intentionally not included. -->
Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the final review checklist
unchecked. Maintainers can help resolve open questions.
## Contract and boundary impact
<!-- Complete the applicable lines; use "none" or "not applicable" explicitly. -->
<!-- Short answers are welcome. Complete applicable lines; mark unresolved facts as "unknown".
Use "none" or "not applicable" only when established. -->
- Semantic owner and earliest changed stage:
- Contribution scope fit and linked maintainer decision:
- CLI and MCP/tool-catalog contract:
- Provider, bridge, target-format, or platform compatibility:
- Evidence, artifact, provenance, or reconstruction contract:
- Process execution, authorization, cleanup, or containment impact:
- Target selection, authority, upstream behavior, and cleanup details (if applicable):
- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs):
Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.
- [ ] 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
<!-- State whether evidence is executed, source-derived, or proposed. For fixes,
@@ -68,9 +90,13 @@ limits, security/privacy/containment implications, and meaningful proof gaps. --
- Package or release metadata impact:
- Security, privacy, process, or containment review:
## Review checklist
## Before merge
<!-- Draft PRs may leave pending items unchecked. Resolve material scope and
authority questions before integration. -->
- [ ] 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.
+18
View File
@@ -0,0 +1,18 @@
name: Full PR qualification
on:
pull_request:
branches: [main, "release/*"]
types: [labeled]
permissions:
contents: read
actions: read
jobs:
qualification:
name: Qualify labeled PR revision
if: github.event.label.name == 'ci:full'
uses: ./.github/workflows/ci.yml
with:
source_sha: ${{ github.sha }}
+84
View File
@@ -0,0 +1,84 @@
name: CI timings
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
concurrency:
group: ci-timings-${{ github.repository }}
cancel-in-progress: false
permissions:
contents: read
actions: read
pull-requests: read
jobs:
report:
name: Collect recent completed workflow attempts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# Consume only API data and execute batch code from the trusted default branch.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
continue-on-error: true
uses: ./.github/actions/npm-cache
- name: Diagnose missing and startup-failed PR CI
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CI_REPOSITORY_ID: ${{ github.repository_id }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: node scripts/ci/pr-ci-health.mjs
- name: Record queue, execution, and matched step timing
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
run: node scripts/ci/job-timings-batch.mjs
- name: Upload CI timing artifacts
if: always() && hashFiles('.cache/ci-job-timings/*') != ''
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ci-job-timings-batch
path: .cache/ci-job-timings/
include-hidden-files: true
if-no-files-found: error
overwrite: true
retention-days: 14
- name: Install dependencies for static failure attribution
id: failure-deps
if: ${{ !cancelled() && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
continue-on-error: true
run: npm ci
- name: Attribute supported static failures against main
if: ${{ !cancelled() && steps.failure-deps.outcome == 'success' && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: node scripts/ci/failure-report.mjs
- name: Upload CI health and failure diagnostics
if: ${{ !cancelled() }}
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ci-health-diagnostics
path: |
.cache/ci-pr-health/
.cache/ci-failures/
include-hidden-files: true
if-no-files-found: warn
retention-days: 14
+520 -171
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -12,22 +12,23 @@ jobs:
name: Build Windows x64 NativeAOT fixture
# .NET 8.0.22's bundled linker discovery supports VS 2019/2022.
runs-on: windows-2022
timeout-minutes: 15
# Two bounded restore attempts and one compile can each take five minutes.
timeout-minutes: 20
env:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_PROCESSOR_COUNT: "1"
COMPlus_GCHeapHardLimit: "30000000"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- uses: actions/setup-dotnet@v4
- uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
dotnet-version: "8.0.416"
- name: Build benign fixture and independent byte oracle
run: node scripts/generate-nativeaot-fixtures.mjs
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: nativeaot-win-x64-${{ github.event.pull_request.head.sha || github.sha }}
path: |
+5 -3
View File
@@ -19,10 +19,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:build
+29
View File
@@ -0,0 +1,29 @@
name: PR title
on:
pull_request:
branches: [main, "release/*"]
types: [opened, synchronize, reopened, edited]
permissions: {}
concurrency:
group: pr-title-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
pr-title:
name: Conventional PR title
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Validate title for Release Please
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >-
node -e 'const title = process.env.PR_TITLE ?? "";
const conventional = /^(?:feat|fix|docs|refactor|perf|test|build|ci|chore|revert)(?:\([a-z0-9._/-]+\))?!?: .+/u;
if (!conventional.test(title)) {
console.error("PR title must use Conventional Commits, for example: feat: add historical source import");
process.exit(1);
}'
+14 -7
View File
@@ -8,14 +8,16 @@ on:
type: boolean
default: false
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-binary-layout-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -26,12 +28,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire exact upstream libraries in an isolated runner environment
shell: bash
run: |
+15 -8
View File
@@ -2,12 +2,14 @@ name: Real browser verification
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
concurrency:
group: real-browser-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
@@ -19,12 +21,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
cache: npm
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: npm run verify:browser
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
+76
View File
@@ -0,0 +1,76 @@
name: Real upstream Cutter verification
on:
workflow_dispatch:
repository_dispatch:
types: [real-cutter-upstream]
permissions:
contents: read
jobs:
windows-native-artifact:
name: Build Windows bridge security boundary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install the cross compiler used by the Windows native lane
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- run: npm run build:windows-native
- uses: actions/upload-artifact@v4
with:
name: cutter-windows-native
path: native/windows/build/
if-no-files-found: error
cutter-upstream:
name: Cutter upstream smoke (${{ matrix.platform }})
needs: windows-native-artifact
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- platform: linux
runner: [self-hosted, cutter-upstream, linux]
- platform: macOS
runner: [self-hosted, cutter-upstream, macOS]
- platform: Windows
runner: [self-hosted, cutter-upstream, Windows]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- if: matrix.platform == 'Windows'
uses: actions/download-artifact@v4
with:
name: cutter-windows-native
path: native/windows/build/
- if: matrix.platform == 'Windows'
run: npm run verify:windows-native:artifact
- if: matrix.platform == 'Windows'
run: npm run build:cached
- if: matrix.platform == 'Windows'
run: npm run verify:windows-native
- name: Verify host and real Cutter bridge
env:
EXPECTED_PLATFORM: ${{ matrix.platform }}
run: npm run verify:cutter
- uses: actions/upload-artifact@v4
if: always()
with:
name: cutter-upstream-${{ matrix.platform }}
path: cutter-smoke.json
if-no-files-found: ignore
retention-days: 30
+16 -7
View File
@@ -3,14 +3,16 @@ name: Real offline EVM interface
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-evm-interface-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -20,12 +22,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire unchanged fixture compiler in isolated runner storage
shell: bash
run: |
@@ -38,3 +45,5 @@ jobs:
echo "REA_VERIFY_SOLC_MODULE=${evm_fixture_root}/node_modules/solc/index.js" >> "${GITHUB_ENV}"
- name: Verify source-owned compiled bytecode through public CLI and MCP
run: npm run verify:evm:interface
- name: Verify content-hash-free and CBOR-free owned compiler artifacts
run: npm run verify:solidity:metadata
+28 -8
View File
@@ -16,8 +16,8 @@ jobs:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Prepare cross compiler
@@ -31,11 +31,25 @@ jobs:
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- run: npm run build:windows-native
- run: npm run verify:windows-native:artifact
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: real-ghidra-windows-native
path: native/windows/build/
if-no-files-found: error
# The boundary observations need a separate Windows executable, which the
# native lane skips when it is absent. Cross-build it here, where MinGW is
# already installed, so the proof run observes them too.
- name: Build Windows process boundary fixture
run: |
mkdir -p .cache/windows-process-boundary
x86_64-w64-mingw32-g++ -municode -std=c++17 -O2 -static -Wall -Wextra \
-o .cache/windows-process-boundary/process-boundary.exe \
tests/fixtures/windows/processBoundary.cc -ladvapi32
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: real-ghidra-windows-process-boundary
path: .cache/windows-process-boundary/
if-no-files-found: error
verify:
name: Real Ghidra 12.1.4 Windows x64 PE proof
@@ -45,17 +59,23 @@ jobs:
runs-on: [self-hosted, Windows, x64, ghidra-12-1-2]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: real-ghidra-windows-native
path: native/windows/build/
- run: npm run verify:windows-native:artifact
- run: npm run verify:windows-native
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: real-ghidra-windows-process-boundary
path: .cache/windows-process-boundary/
- run: >-
npm run verify:windows-native -- .
"${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe"
- name: Verify all admitted Windows Ghidra operations
env:
REA_ANALYSIS_PROVIDER: ghidra
@@ -88,7 +108,7 @@ jobs:
env:
REA_ANALYSIS_PROVIDER: ghidra
run: npm run verify:ghidra:windows:package -- --x86 --dll | Tee-Object -FilePath windows-ghidra-x86-dll-package-proof.log
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: windows-ghidra-proof
+60
View File
@@ -0,0 +1,60 @@
name: Real Go binary metadata
on:
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/real-go-binary.yml"
- "src/go/**"
- "src/domain/go/**"
- "src/domain/dosMz.ts"
- "src/application/go/**"
- "src/contracts/go/**"
- "src/composition/go.ts"
- "src/cli/goCommands.ts"
- "src/server/registerGoTools.ts"
- "src/server/createServer.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/artifacts/readStableArtifact*.ts"
- "src/artifacts/ArtifactReader.ts"
- "src/process/PrivateRuntimeRoot.ts"
- "scripts/verify-go-binary.mjs"
- "scripts/lib/mcp-verifier-results.mjs"
- "scripts/rea.mjs"
- "package.json"
- "npm-shrinkwrap.json"
- ".nvmrc"
permissions:
contents: read
concurrency:
group: real-go-binary-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
jobs:
real-go-binary:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version: "1.26.1"
cache: false
- run: npm ci
- name: Verify compiler-produced ELF, PE and Mach-O through public CLI and MCP
run: npm run verify:go:binary
+10 -6
View File
@@ -15,11 +15,13 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Pack CLI for setup verification
shell: bash
@@ -36,11 +38,13 @@ jobs:
runs-on: [self-hosted, Linux, hopper]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
+5 -3
View File
@@ -15,11 +15,13 @@ jobs:
runs-on: [self-hosted, macOS, hopper]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
+15 -8
View File
@@ -2,15 +2,17 @@ name: Real JavaScript recovery
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: javascript-recovery-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
NODE_OPTIONS: "--max-old-space-size=768"
@@ -21,12 +23,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
cache: npm
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Fetch pinned Wakaru release into the job workspace
shell: bash
run: |
+60
View File
@@ -0,0 +1,60 @@
name: Real packet capture
on:
workflow_dispatch:
pull_request:
paths:
- "tests/boundary/captures/**"
- "tests/boundary/cli/captureCleanup.test.ts"
- "tests/boundary/mcp/packetCaptureMcp.test.ts"
- "src/captures/**"
- "src/pcap/**"
- "src/domain/pcap/**"
- "src/domain/analysisView/pcapCaptureView.ts"
- "src/application/pcap/**"
- "src/contracts/pcap/**"
- "src/server/registerPcapTools.ts"
- "tests/boundary/pcap/**"
- "src/domain/captures/**"
- "src/application/captures/**"
- "src/contracts/captures/**"
- "src/composition/captures.ts"
- "src/cli/captureCommands.ts"
- "src/server/registerCaptureTools.ts"
- "src/server/createServer.ts"
- "src/server/sessionAvailabilityPolicy.ts"
- "src/application/CapabilityInventory.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/process/**"
- "src/artifacts/readStableArtifact.ts"
- "scripts/verify-packet-capture.mjs"
- ".github/workflows/real-packet-capture.yml"
- "package*.json"
- ".nvmrc"
permissions:
contents: read
concurrency:
group: real-packet-capture-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
tshark:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark util-linux python3
- run: npm ci
- run: npm run build:cached
- run: npm run test:focused -- tests/boundary/captures/fieldDiscovery.test.ts src/process/LinuxResourceLimits.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/captures/captureDescriptorCleanup.test.ts tests/boundary/captures/resourceLimits.test.ts tests/boundary/captures/packetOutput.test.ts tests/boundary/mcp/packetCaptureMcp.test.ts tests/boundary/cli/captureCleanup.test.ts
- run: node scripts/verify-packet-capture.mjs
env:
REA_TSHARK_COMMAND: /usr/bin/tshark
REA_CAPINFOS_COMMAND: /usr/bin/capinfos
+14 -7
View File
@@ -3,14 +3,16 @@ name: Real recorded crash evidence
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-recorded-crash-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -23,12 +25,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install GDB on the disposable CI runner
shell: bash
run: |
@@ -0,0 +1,52 @@
name: Real signal recording
on:
workflow_dispatch:
pull_request:
paths:
- "tests/boundary/signals/**"
- "tests/boundary/cli/signalCleanup.test.ts"
- "tests/boundary/mcp/signalRecordingMcp.test.ts"
- "src/signals/**"
- "src/domain/signals/**"
- "src/application/signals/**"
- "src/contracts/signals/**"
- "src/composition/signals.ts"
- "src/cli/signalCommands.ts"
- "src/server/registerSignalTools.ts"
- "src/server/createServer.ts"
- "src/server/sessionAvailabilityPolicy.ts"
- "src/application/CapabilityInventory.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/process/**"
- "src/artifacts/readStableArtifact.ts"
- "scripts/verify-signal-recording.mjs"
- ".github/workflows/real-signal-recording.yml"
- "package*.json"
- ".nvmrc"
permissions:
contents: read
concurrency:
group: real-signal-recording-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
sigrok:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo apt-get install -y sigrok-cli util-linux python3
- run: npm ci
- run: npm run build:cached
- run: npm run test:focused -- tests/boundary/signals/decoderDiscovery.test.ts src/process/LinuxResourceLimits.test.ts src/domain/signals/annotationTextWindow.test.ts tests/boundary/signals/recordingProvider.test.ts tests/boundary/signals/recordingDescriptorCleanup.test.ts tests/boundary/signals/resourceLimits.test.ts tests/boundary/mcp/signalRecordingMcp.test.ts tests/boundary/cli/signalCleanup.test.ts
- run: node scripts/verify-signal-recording.mjs
env:
REA_SIGROK_COMMAND: /usr/bin/sigrok-cli
+3 -3
View File
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Download the pinned Termux app and verify its release checksum
shell: bash
run: |
@@ -38,7 +38,7 @@ jobs:
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Build and capture a page inside Termux
uses: reactivecircus/android-emulator-runner@4c44018e59b437e86cdfc41da381398f93ed8808 # v2
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
with:
api-level: 30
arch: x86_64
@@ -50,7 +50,7 @@ jobs:
script: bash scripts/verify/termux/emulator.sh
- name: Upload Termux and Android diagnostics
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: termux-browser-diagnostics
path: ${{ runner.temp }}/rea-termux/diagnostics
@@ -3,14 +3,16 @@ name: Real historical web network captures
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-web-network-captures-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
HUSKY: "0"
NODE_OPTIONS: --max-old-space-size=768
@@ -20,14 +22,19 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- name: Use pinned package manager
run: npm install --global npm@11.16.0
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire fixed upstream native profile in runner temporary storage
shell: bash
run: |
+16 -8
View File
@@ -1,12 +1,15 @@
name: Real website runtime attribution
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: real-web-runtime-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
jobs:
@@ -14,13 +17,18 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
cache: npm
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run verify:browser:runtime
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: env -u DBUS_SESSION_BUS_ADDRESS npm run verify:browser:runtime
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+15 -7
View File
@@ -1,12 +1,15 @@
name: Real website source-map verification
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: real-web-source-map-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
jobs:
@@ -14,12 +17,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
cache: npm
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install the pinned compiler into an isolated fixture directory
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
- run: npm run verify:browser:source-maps
+37 -22
View File
@@ -48,7 +48,7 @@ jobs:
echo "frozen=true" >> "$GITHUB_OUTPUT"
fi
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
if: steps.editorial.outputs.frozen != 'true'
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
@@ -70,13 +70,13 @@ jobs:
sha: ${{ steps.release.outputs.sha }}
steps:
- name: Check out reviewed release merge
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -104,7 +104,7 @@ jobs:
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
--source "$SOURCE_SHA" --pr "$RELEASE_PR"
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
@@ -191,14 +191,14 @@ jobs:
fi
- name: Check out release controller and Git history
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Node.js for checkpoint validation
uses: actions/setup-node@v6
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -226,7 +226,7 @@ jobs:
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
--source "$SOURCE_SHA"
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
# A dedicated credential starts PR CI automatically; the built-in
@@ -255,7 +255,7 @@ jobs:
- name: Check out release pull request
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
fetch-depth: 0
@@ -263,7 +263,7 @@ jobs:
- name: Set up Node.js for generated documentation
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -290,24 +290,36 @@ jobs:
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
run: npm run docs:check && git diff --exit-code
publish:
name: Publish to npm
qualification:
name: Qualify exact release revision
needs: [merged-release, release-please]
if: >-
always() && !cancelled() &&
((needs.merged-release.result == 'success' && needs.merged-release.outputs.release_created == 'true') ||
(needs.release-please.result == 'success' && inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'))
permissions:
contents: read
actions: read
uses: ./.github/workflows/ci.yml
with:
source_sha: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
publish:
name: Publish to npm
needs: [merged-release, release-please, qualification]
if: >-
always() && !cancelled() && needs.qualification.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
persist-credentials: false
- uses: actions/setup-node@v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -315,17 +327,20 @@ jobs:
- run: npm ci
- name: Prepare Windows native build toolchain
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- name: Download the exact qualified package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verified-release-package
path: .cache/verified-package/
- name: Build and verify bundled Windows native controls
run: npm run build:windows-native && npm run verify:windows-native:artifact
- name: Verify packaged release before publishing
run: npm run verify:package
- name: Verify qualified package provenance and digest
id: package
run: node scripts/ci/verified-package.mjs validate .cache/verified-package
- name: Publish
shell: bash
env:
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
run: |
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
published_version="$(npm view "rea-agents@${version}" version --json 2>/dev/null || true)"
@@ -333,7 +348,7 @@ jobs:
echo "rea-agents@${version} is already published; verifying the existing package."
else
tag="$(node scripts/release-npm-tag.mjs "${version}")"
npm publish --access public --tag "${tag}"
npm publish "${VERIFIED_PACKAGE_TARBALL}" --access public --tag "${tag}"
fi
- name: Verify published CLI from npm
@@ -377,7 +392,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
persist-credentials: false
+39
View File
@@ -0,0 +1,39 @@
name: SQLite runtime compatibility
on:
workflow_call:
permissions:
contents: read
env:
HUSKY: "0"
jobs:
sqlite:
name: SQLite runtime compatibility (Node ${{ matrix.node }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
# Exercise unavailable-runtime diagnostics, the first supported release,
# and new patches on both supported lines without hand-editing versions.
# The repository-pinned Node 24 version also runs the complete test suite.
node: ["22.x", "24.15.0", "24.x", "26.x"]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node }}
check-latest: true
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-test-runtime
path: .
- name: Verify SQLite discovery, inspection and cleanup
run: npm run test:sqlite:run
+2 -6
View File
@@ -7,10 +7,6 @@ on:
permissions:
contents: read
concurrency:
group: website-check-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check:
name: Website checks
@@ -18,9 +14,9 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Python for website assets
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
+11 -11
View File
@@ -47,9 +47,9 @@ jobs:
exit 1
fi
- name: Check out the repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Python for website assets
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
@@ -68,17 +68,17 @@ jobs:
import os
from pathlib import Path
Path("website/public/deployment.json").write_text(
Path("website/_site/deployment.json").write_text(
json.dumps({"commit": os.environ["GITHUB_SHA"]}) + "\n",
encoding="utf-8",
)
PY
- name: Configure Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
- name: Upload the website
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: website/public
path: website/_site
deploy:
needs: prepare
@@ -95,15 +95,15 @@ jobs:
steps:
- name: Check out publishing configuration
if: inputs.target == 'both'
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Node.js for Wrangler
if: inputs.target == 'both'
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Download the verified website
if: inputs.target == 'both'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: github-pages
path: ${{ runner.temp }}/website-archive
@@ -114,7 +114,7 @@ jobs:
tar -xf "$RUNNER_TEMP/website-archive/artifact.tar" -C "$RUNNER_TEMP/rea-website-public"
- name: Publish the website to Cloudflare
if: inputs.target == 'both'
uses: cloudflare/wrangler-action@v4
uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4.1.3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -123,7 +123,7 @@ jobs:
command: deploy --cwd website --env production --assets "${{ runner.temp }}/rea-website-public"
- name: Publish to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
- name: Verify the selected published revisions
env:
PAGES_URL: ${{ steps.deployment.outputs.page_url }}
+10
View File
@@ -33,8 +33,12 @@ codedb.snapshot
htmlcov/
.coverage
coverage/
.stryker-tmp/
reports/mutation/
.vitest/
.vitest-reports/
test-results.xml
test-timings.json
.turbo/
.cache/
docs/.vitepress/cache/
@@ -46,6 +50,7 @@ Thumbs.db
# Local reference sources and analysis artifacts
/_reference/
/experiments/analysis-efficiency/results/
# Generated website download
/website/public/examples/notes-example.zip
@@ -67,3 +72,8 @@ Thumbs.db
/docs/public/product-catalog.json
/docs/verification/managed-conformance-manifest.json
/docs/verification/managed-conformance-ledger.json
# Complete static website output, including generated language editions.
website/_site/
# Temporary translation output prepared before replacing the complete site.
website/.i18n-*/
+1 -2
View File
@@ -1,6 +1,5 @@
{
"_comment": "Test files are intentionally NOT excluded: the gate used to report zero clones because it skipped **/*.test.ts, hiding all test-tree duplication. Current actual is ~2.47% lines across src+ and tests/, down from 3.4% when tests were included. The 2.6% limit leaves modest headroom for ordinary changes; lower it as duplication is removed.",
"threshold": 2.6,
"threshold": 3.5,
"reporters": ["consoleFull"],
"ignore": [
"node_modules/**",
+10
View File
@@ -0,0 +1,10 @@
{
"mcpServers": {
"rea": {
"command": "node",
"args": ["./scripts/plugin-mcp-launcher.mjs", "6.4.0"],
"cwd": ".",
"startup_timeout_sec": 30
}
}
}
+3 -1
View File
@@ -9,7 +9,9 @@
"docs/api/**",
"node_modules/**",
"CHANGELOG.md",
"package-lock.json",
"npm-shrinkwrap.json",
".claude-plugin/plugin.json",
".codex-plugin/plugin.json",
"_reference/**",
"third_party/**"
]
+2 -1
View File
@@ -22,7 +22,8 @@
],
"max-params": ["warn", 4],
"no-var": "warn",
"no-var-requires": "error"
"no-var-requires": "error",
"typescript/no-import-type-side-effects": "error"
},
"overrides": [
{
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "6.3.0"
".": "6.4.0"
}
+34 -1
View File
@@ -4,6 +4,8 @@
REA is a local-only reverse-engineering tool with shared CLI/MCP workflows. Distinguish observations, derivations, inferences, and unknowns; preserve Evidence provenance and session semantics through both adapters. Provider support and setup live in [README.md](README.md#choosing-a-deep-analysis-provider) and its linked guides.
Follow the [contribution scope and fail-closed integration rule](CONTRIBUTING.md#contribution-scope). Do not integrate capabilities while material scope, authority, side-effect, or required verification questions remain unresolved. For new providers or capabilities involving target execution, injection, mutation, or protection recovery, seek the required maintainer scope decision before substantial implementation for integration. Welcome early proposals, small exploratory prototypes, and draft PRs that help resolve questions; they do not authorize integration.
Ghidra uses ephemeral databases without modifying executable bytes or controlling a GUI; Windows Ghidra P0 has no mutation authority. IDA analysis is read-only; never save or close an attached GUI database.
Configuration changes must be additive and idempotent, with backups. Installers must not install or upgrade unrelated software, including Homebrew, Node.js, npm, Java, or Ghidra. Ghidra and IDA are bring-your-own. `rea setup` must show its planned changes and require approval before writing files or installing Hopper.
@@ -14,14 +16,45 @@ Preserve caller-selected inputs and local evidence, including output, URLs, path
Dependencies flow inward: domain semantics and contracts, provider adapters, shared application workflows, then CLI/MCP adapters. Keep engine protocols and provider-specific code out of domain/application layers. See [docs/architecture.mermaid](docs/architecture.mermaid) when changing composition.
Give each interpretation and resource a clear owner. Preserve identity, omission, uncertainty, and source evidence across boundaries. Retain ownership of resources whose cleanup failed. Reuse `src/process/` supervision and identity primitives; a PID and executable path alone do not establish ownership after exit or reuse.
Give each interpretation and resource a clear owner. Consumers must use that owner's parsed facts rather than reinterpret raw source. Preserve identity, omission, uncertainty, and source evidence across boundaries. Retain ownership of resources whose cleanup failed. Reuse `src/process/` supervision and identity primitives; a PID and executable path alone do not establish ownership after exit or reuse.
Keep proof of ownership separate from validity, editability, and readiness. Malformed owned data may still be safely removable. Unresolved identity or applicability must remain unknown rather than be treated as absent, foreign, or aligned.
Assess brittleness in touched code during implementation, debugging, review, and maintenance. Fix the producing representation or ownership rule before adding downstream repairs. Normalize once at the owning boundary using format-aware parsers. Remove superseded representations, aliases, wrappers, and redundant tests after verifying affected consumers. Introduce abstractions only when they remove observed complexity.
When fixing a bug, inspect the producing boundary and affected consumers for duplicated interpretation or unclear ownership. Complete any directly related refactor in the same change and remove superseded code. Passing regression tests alone does not establish that the brittleness is resolved. If related cleanup remains, explain what remains and why.
When addressing a review thread, inspect the producing boundary and affected consumers for brittleness. Complete directly related refactoring in the same change. Before resolving the thread, explain the root cause, structural fix, validation, and any remaining cleanup.
Parse unknown boundary values and model expected failures with the tagged error algebra and `Result`. Preserve partial facts and meaningful failure reasons. Keep identity and lookup values separate from display formatting; preserve source values and explicit unknowns when normalization loses information. Absolute paths, file URLs, and HTTP paths have different semantics.
Validate advertised JSON Schemas after SDK conversion against their declared dialect and actual clients. Bind handlers to named contracts with exact types; catalog ordering must never select an operation or schema.
## Performance
Design analysis workflows and agent interfaces for efficiency from the first
implementation. Correctness, useful answers, Evidence provenance, caller choices,
compatibility and safe resource bounds remain required.
Optimize in this order:
1. Agent tokens and context. Return the smallest sufficient answer with
necessary provenance, coverage and limitations. Use task selection,
summaries, paging and retained Evidence where supported; honor requested
detail and keep complete Evidence accessible. Count the whole task,
including schemas, extra calls and retries.
2. Analysis time. Reuse validated results and shared work. Cache keys must
cover result-affecting inputs; define invalidation and lifetime. Validate
freshness before reporting cached live state as current.
3. CPU and memory. Avoid unnecessary work and copies; bound concurrency,
retained state and serialization growth.
For changes affecting these costs, measure representative completed tasks and
compare equivalent workflows before and after where available. Report token
usage, elapsed time, CPU time and peak process-family memory. Separate cold
and warm runs, label estimates, and state unmeasured costs. Fewer bytes alone
do not establish fewer tokens or faster analysis.
## Development
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`.
+64
View File
@@ -1,5 +1,69 @@
# Changelog
## [6.4.0](https://github.com/morluto/rea/compare/rea-agents-6.3.0...rea-agents-6.4.0) (2026-10-11)
### Highlights
- Android: added device inspection, split-APK acquisition, logs, screenshots, file transfers and explicit app lifecycle actions through ADB, plus manifest and localized-string decoding with Apktool. ([#1552](https://github.com/morluto/rea/pull/1552), [#1556](https://github.com/morluto/rea/pull/1556)) Thanks @3QL.
- JEB: browse project units and decompile selected items through a caller-started local JEB MCP server. ([#1536](https://github.com/morluto/rea/pull/1536)) Thanks @3QL.
- Large analyses: discover retained Evidence without loading every payload, and inspect JavaScript graphs, binary layouts and function dossiers through summaries and selected views. ([#1682](https://github.com/morluto/rea/pull/1682), [#1568](https://github.com/morluto/rea/pull/1568), [#1570](https://github.com/morluto/rea/pull/1570), [#1345](https://github.com/morluto/rea/pull/1345), [#1507](https://github.com/morluto/rea/pull/1507), [#1247](https://github.com/morluto/rea/issues/1247)) Thanks @jhuang-tw, @kaoru0822-kitauji, @N0zoM1z0.
- JavaScript: isolate source analysis in an owned process with configurable heap and timeout limits; retain completed facts and explicit unknowns when analysis runs out of resources. ([#1651](https://github.com/morluto/rea/pull/1651), [#1563](https://github.com/morluto/rea/issues/1563), [#1565](https://github.com/morluto/rea/issues/1565)) Thanks @N0zoM1z0, @peter-research.
- Ghidra: configure import language/compiler specifications and seed functions, code and labels before auto-analysis. ([#1667](https://github.com/morluto/rea/pull/1667)) Thanks @sqmagellan.
- RPC discovery: recognize supported tRPC calls, HTTP-link URLs and named GraphQL operations in JavaScript applications and web bundles. ([#1670](https://github.com/morluto/rea/pull/1670)) Thanks @pompiliodonatoapp.
### Changes
- Added Ghidra support for Windows PE DLLs and bounded MIPS ELF analysis, including carved MIPS inputs with incomplete section tables. Windows P0 remains read-only. ([#1303](https://github.com/morluto/rea/pull/1303), [#1362](https://github.com/morluto/rea/pull/1362), [#1629](https://github.com/morluto/rea/pull/1629), [#1290](https://github.com/morluto/rea/issues/1290), [#718](https://github.com/morluto/rea/issues/718), [#1619](https://github.com/morluto/rea/issues/1619)) Thanks @bqw371, @jaelliot, @kvalls, @N0zoM1z0.
- Enabled `set_address_name` and `set_addresses_names` for Ghidra sessions with mutation authority, including namespace-aware data labels and per-address batch results. ([#1635](https://github.com/morluto/rea/pull/1635), [#1634](https://github.com/morluto/rea/issues/1634)) Thanks @giri256.
- Recovered additional signed AArch64 relative jump-table patterns. ([#1592](https://github.com/morluto/rea/pull/1592)) Thanks @RishTheFish21.
- Added PE resource inspection and read-only NativeAOT metadata parsing for supported x64 PE32+/.NET 8 RTR 9.1 inputs. The NativeAOT parser’s Windows-host Ghidra workflow remains unverified on a real Windows host. ([#1482](https://github.com/morluto/rea/pull/1482), [76d8f68](https://github.com/morluto/rea/commit/76d8f68cb918dfb43869f2b649a0b6c0c1cf266d), [#1254](https://github.com/morluto/rea/issues/1254)) Thanks @N0zoM1z0, @thangldw.
- Added Pi, Hermes and Qwen Code setup, including Hermes active-profile support. ([#1376](https://github.com/morluto/rea/pull/1376), [#1403](https://github.com/morluto/rea/pull/1403), [#1415](https://github.com/morluto/rea/pull/1415), [#1409](https://github.com/morluto/rea/issues/1409)) Thanks @rasatpetabit, @yiliang114, @zaydiscold.
- Added `REA_MCP_INPUT_SCHEMA_PROFILE=compact` for size-capped MCP clients such as Kimi/Moonshot, while retaining canonical runtime validation. ([#1514](https://github.com/morluto/rea/pull/1514), [#1580](https://github.com/morluto/rea/pull/1580), [#1484](https://github.com/morluto/rea/issues/1484), [#1581](https://github.com/morluto/rea/issues/1581)) Thanks @haythemsellami, @N0zoM1z0.
- Added live process-capture progress and optional `finalization_ms` to capture shutdown output before timeout escalation; cancellation remains immediate. ([#1483](https://github.com/morluto/rea/pull/1483), [#1583](https://github.com/morluto/rea/pull/1583), [#1252](https://github.com/morluto/rea/issues/1252), [#1253](https://github.com/morluto/rea/issues/1253), [modelcontextprotocol/typescript-sdk#2997](https://github.com/modelcontextprotocol/typescript-sdk/issues/2997)) Thanks @N0zoM1z0, @nikolay-dementiev, @sathariels.
- Improved Codex onboarding and recovery from observed client, PATH and provider blockers. ([#1613](https://github.com/morluto/rea/pull/1613)) Thanks @N0zoM1z0.
- Reduced redirected JSON and terminal output size, and deferred heavy imports until their commands need them. ([#1413](https://github.com/morluto/rea/pull/1413), [#1450](https://github.com/morluto/rea/pull/1450), [#1439](https://github.com/morluto/rea/pull/1439), [21dbcf7](https://github.com/morluto/rea/commit/21dbcf7a1458219989ad291e44ce7d017378b727)) Thanks @kaoru0822-kitauji.
### Fixes
- Electron analysis: follow renamed and default-imported APIs and optional calls, ignore overwritten identities and media preload attributes, and resolve packaged `app.asar` pages and unpacked companions. ([#1391](https://github.com/morluto/rea/pull/1391), [#1393](https://github.com/morluto/rea/pull/1393), [#1400](https://github.com/morluto/rea/pull/1400), [#1664](https://github.com/morluto/rea/pull/1664), [#1673](https://github.com/morluto/rea/pull/1673)) Thanks @kaoru0822-kitauji, @marnixpluim, @N0zoM1z0, @suninrain086.
- JavaScript data flow: preserve returned/yielded reference uncertainty, invalidate property-stored aliases correctly, and retain bundled-module runtime scope. ([#1437](https://github.com/morluto/rea/pull/1437), [#1445](https://github.com/morluto/rea/pull/1445), [#1471](https://github.com/morluto/rea/pull/1471), [#1577](https://github.com/morluto/rea/pull/1577), [#1428](https://github.com/morluto/rea/issues/1428), [#1442](https://github.com/morluto/rea/issues/1442), [#1449](https://github.com/morluto/rea/issues/1449), [#1576](https://github.com/morluto/rea/issues/1576)) Thanks @CassieFox23, @dakjdakd, @N0zoM1z0.
- JavaScript packages: resolve exact exports subpaths, preserve trailing-slash directory requests and exact CommonJS keys, and respect shadowed `require` bindings. ([#1424](https://github.com/morluto/rea/pull/1424), [#1615](https://github.com/morluto/rea/pull/1615), [5485483](https://github.com/morluto/rea/commit/5485483f7da2f6cb66441bb784aaf2a1a5f8731a), [8b2f851](https://github.com/morluto/rea/commit/8b2f8514c50e469c3add8ce9ba7ebebef888b14f), [f998fcb](https://github.com/morluto/rea/commit/f998fcb1cca9994c3589fb70d6e0127fc215f76f), [1e51c73](https://github.com/morluto/rea/commit/1e51c732bbf08191688d6526bf58ab2650b601e3), [#1365](https://github.com/morluto/rea/issues/1365)) Thanks @akshitkhokhani, @AWUQAQ, @immanuwell, @rudycelekli.
- Large JavaScript inputs: handle oversized inline source maps, deep returns and reassignment loops; avoid argument expansion limits and yield during large analysis passes. ([#1386](https://github.com/morluto/rea/pull/1386), [#1368](https://github.com/morluto/rea/pull/1368), [#1390](https://github.com/morluto/rea/pull/1390), [#1539](https://github.com/morluto/rea/pull/1539), [#1525](https://github.com/morluto/rea/pull/1525), [#1542](https://github.com/morluto/rea/pull/1542), [#1545](https://github.com/morluto/rea/pull/1545), [#1557](https://github.com/morluto/rea/pull/1557), [#1380](https://github.com/morluto/rea/issues/1380), [#1401](https://github.com/morluto/rea/issues/1401), [#1538](https://github.com/morluto/rea/issues/1538), [#1462](https://github.com/morluto/rea/issues/1462)) Thanks @cold-atom, @KadirJeager, @kaoru0822-kitauji, @marnixpluim, @N0zoM1z0, @rudycelekli.
- JavaScript receivers: expand escaped methods once, index returned-object targets, and avoid replaying earlier writes or duplicating retained graph allocations. ([#1517](https://github.com/morluto/rea/pull/1517), [#1537](https://github.com/morluto/rea/pull/1537), [#1444](https://github.com/morluto/rea/pull/1444), [#1601](https://github.com/morluto/rea/pull/1601), [#1495](https://github.com/morluto/rea/issues/1495)) Thanks @CassieFox23, @geekwolf, @kaoru0822-kitauji, @N0zoM1z0, @zeynelacikgoez.
- Browser captures: retain WebSocket URLs, empty connections and frame order; compare only common known payload coverage and preserve incomplete WebMCP replay coverage. ([db4e360](https://github.com/morluto/rea/commit/db4e3608465486466591b594cf77037ac150c988), [aa257ea](https://github.com/morluto/rea/commit/aa257ea4b16f2d2a35c12df63359d650b48b1154), [e52c0a7](https://github.com/morluto/rea/commit/e52c0a72c59c740315e89149881a27ce4d89a11c))
- Browser scenarios: seed storage in the correct partition, normalize loopback attach identities, retain declared-secret redaction and require verified lineage before Windows Electron signals. ([#1604](https://github.com/morluto/rea/pull/1604), [#1590](https://github.com/morluto/rea/pull/1590), [#1588](https://github.com/morluto/rea/pull/1588), [8b4c3de](https://github.com/morluto/rea/commit/8b4c3de3b3a3b580adec266f94f29ccedf1da228), [#1497](https://github.com/morluto/rea/issues/1497), [#1591](https://github.com/morluto/rea/issues/1591), [#1589](https://github.com/morluto/rea/issues/1589)) Thanks @Adolanium, @akushonkamen, @dakjdakd.
- Added Termux Android browser-scenario support. ([#1655](https://github.com/morluto/rea/pull/1655)) Thanks @tyloryang.
- Web inputs: accept BOM-prefixed HAR, preserve the first duplicate DOM attribute and resolve SVG/XLink URLs in HTML and XHTML. ([#1509](https://github.com/morluto/rea/pull/1509), [#1516](https://github.com/morluto/rea/pull/1516), [#1602](https://github.com/morluto/rea/pull/1602), [#1683](https://github.com/morluto/rea/pull/1683), [#1496](https://github.com/morluto/rea/issues/1496), [#1586](https://github.com/morluto/rea/issues/1586)) Thanks @CassieFox23, @kaoru0822-kitauji, @sathariels, @taljeri.
- Screenshot comparisons: validate PNG chunks, distinguish corruption from unsupported encodings and bound comparison memory. ([4e4b15b](https://github.com/morluto/rea/commit/4e4b15b8250b29e176d4191c9610a3931b693474), [b3e977e](https://github.com/morluto/rea/commit/b3e977e6d073ab1783cece8581279f87fc360411)) Thanks @rudycelekli.
- Archives: preserve case-distinct names, report collisions at their diverging segment, detect nested formats from bytes and reject Windows alternate-stream extraction paths. ([#1480](https://github.com/morluto/rea/pull/1480), [#1505](https://github.com/morluto/rea/pull/1505), [#1553](https://github.com/morluto/rea/pull/1553), [573598c](https://github.com/morluto/rea/commit/573598c6324698cc48ceafd24ed3864670e08d00), [#1470](https://github.com/morluto/rea/issues/1470), [#1491](https://github.com/morluto/rea/issues/1491)) Thanks @CassieFox23, @kaoru0822-kitauji.
- Cleanup: retain failed readers, ASAR handles, output trees and provider workers for retry without discarding completed output or partial observations. ([ffe0574](https://github.com/morluto/rea/commit/ffe0574e17b9d973860371083cb1a351be912131), [de0d341](https://github.com/morluto/rea/commit/de0d341524dc68250a45b41eb7e49fcecc1e32a9), [3f8c51e](https://github.com/morluto/rea/commit/3f8c51e9b506d88fc41aded7b1a22b9a09037c26), [d1235fa](https://github.com/morluto/rea/commit/d1235fadc901e1fb9ab3149d53063306c8545414), [026126e](https://github.com/morluto/rea/commit/026126eafe741e5a8b857433587fd615cdd44c19))
- Providers: canonicalize IDA workspace roots before identity checks, respect IDA cancellation, serialize JEB acquisition and correct Ghidra no-return/imported-symbol recovery. ([#1481](https://github.com/morluto/rea/pull/1481), [#1419](https://github.com/morluto/rea/pull/1419), [5f30ea6](https://github.com/morluto/rea/commit/5f30ea6ff2dd89aab7af6da6e8dfa5e508ed459b), [f88c990](https://github.com/morluto/rea/commit/f88c99021b88ce67e513025901f57478d8107d0c), [49e6f00](https://github.com/morluto/rea/commit/49e6f00ed08b225abf9ddb7c762553607fdab115)) Thanks @AS9929, @JonahSimon, @Y0UW1SH.
- Binary metadata: respect PE/.NET section extents, bound binary-plist expansion, scope keyed-archive traversal and decode app `Info.plist` files without `plutil`. ([#1678](https://github.com/morluto/rea/pull/1678), [#1515](https://github.com/morluto/rea/pull/1515), [#1378](https://github.com/morluto/rea/pull/1378), [#1476](https://github.com/morluto/rea/pull/1476), [975dff2](https://github.com/morluto/rea/commit/975dff2e25b14b634e14dd5e0db1537d0ca9815b), [76296c5](https://github.com/morluto/rea/commit/76296c5a3868ba3b54be6ed3896719283d6de5f8)) Thanks @jayhemnani9910, @kaoru0822-kitauji, @rudycelekli.
- Windows runtimes: preserve drive-root paths when creating private provider directories. ([ee46323](https://github.com/morluto/rea/commit/ee46323b493994902b04824a7dc8aae2a8117903))
- Compatibility: load Windows native controls under Node-API hosts with renamed executables, preserve dylib directory requirements, support Wakaru 1.x and reject directory-valued Hopper launchers. ([#1531](https://github.com/morluto/rea/pull/1531), [#1623](https://github.com/morluto/rea/pull/1623), [#1540](https://github.com/morluto/rea/pull/1540), [#1426](https://github.com/morluto/rea/pull/1426)) Thanks @evenafterallisaid, @Fazmin, @pionxzh, @xeropresence.
- Reference imports: normalize internal symlink lookup consistently, preserve exact paths and exclusion provenance, report root access failures and reject FIFO replacements without blocking. ([#1535](https://github.com/morluto/rea/pull/1535), [2ca3ad2](https://github.com/morluto/rea/commit/2ca3ad238608adfca80a7ff1aaadea6e59a58225), [6636045](https://github.com/morluto/rea/commit/6636045c9c5b472bba51afee9d434394861e93a4), [3967cfa](https://github.com/morluto/rea/commit/3967cfa43e786c65a57e89c265343983d19ef9d0), [f1c58cc](https://github.com/morluto/rea/commit/f1c58cc53fbd83b968e11b3e2eccebca51299c54)) Thanks @anujsingh-cse.
- Setup: honor active configuration and skill roots, preserve configured MCP environments, validate Hermes/OMP profiles before writing, recognize Omarchy and retain recovery backups. ([#1504](https://github.com/morluto/rea/pull/1504), [#1607](https://github.com/morluto/rea/pull/1607), [#1528](https://github.com/morluto/rea/pull/1528), [#1562](https://github.com/morluto/rea/pull/1562), [#1662](https://github.com/morluto/rea/pull/1662), [a011485](https://github.com/morluto/rea/commit/a01148553e2a37c72ae78405418ab648876a96a6), [#1606](https://github.com/morluto/rea/issues/1606), [#1521](https://github.com/morluto/rea/issues/1521), [#1561](https://github.com/morluto/rea/issues/1561)) Thanks @Adkid-Zephyr, @CassieFox23, @LuisTaffelli, @N0zoM1z0, @peter-research.
- Process capture: retain descendant lineage after launcher exit, preserve identity uncertainty before signaling and release terminals after observation failures. ([80896a1](https://github.com/morluto/rea/commit/80896a1d64b216ef893907249cd4252f86215d49), [c916850](https://github.com/morluto/rea/commit/c9168508527302ac57bc86385ccf0d824697b5dc)) Thanks @Y0UW1SH.
- CLI/MCP errors: accept JSON files for web comparisons, preserve native option help and union correction details, remove invalid aliases and validate doctor against its discovered output contract. ([#1397](https://github.com/morluto/rea/pull/1397), [#1395](https://github.com/morluto/rea/pull/1395), [#1425](https://github.com/morluto/rea/pull/1425), [#1461](https://github.com/morluto/rea/pull/1461), [8b4c96e](https://github.com/morluto/rea/commit/8b4c96e6c6b3ed6806e4cd72b556b5db6114304a), [#1421](https://github.com/morluto/rea/issues/1421), [#1454](https://github.com/morluto/rea/issues/1454)) Thanks @Fazmin, @kaoru0822-kitauji, @N0zoM1z0.
### ⚠ BREAKING CHANGES
- WebSocket results: use `network.websocket_connections` instead of `network.websocket_events`. Connections contain `request_id`, `url` and `events`; allow `payload_bytes: null` when size is unknown. No legacy alias remains. ([db4e360](https://github.com/morluto/rea/commit/db4e3608465486466591b594cf77037ac150c988))
- Direct resource APIs: pass a caller-owned `ArtifactResourceScope` to inventory/extraction, JavaScript analysis, reference imports and web exports; retain it through cleanup/shutdown. For `readJavaScriptArtifactFiles`, scope is argument three and abort signal is argument four. Keep each root inventory source’s single cleanup owner. Use `OwnedFileHandle` owners, injected ASAR `fd`/`read` handles instead of `createReadStream`, VCS `Result` values, and `WebScriptExportError`’s options object. CLI/MCP inputs for the scoped operations are unchanged. ([ffe0574](https://github.com/morluto/rea/commit/ffe0574e17b9d973860371083cb1a351be912131), [53e7fad](https://github.com/morluto/rea/commit/53e7fadbd078422ba3d55b5499a70d237c5f4908), [684199b](https://github.com/morluto/rea/commit/684199ba0be7822266d57ddb8a40e95f087fdc42), [be75ed5](https://github.com/morluto/rea/commit/be75ed5e4b4bfb146af497f8fa5d172a1ea19462), [be6272f](https://github.com/morluto/rea/commit/be6272f26115bd8e94f2ab0df4cf092004587a18), [e0e49e9](https://github.com/morluto/rea/commit/e0e49e9dd53d9d1f1d2330b23f7f880dc1c81774), [95a77b6](https://github.com/morluto/rea/commit/95a77b6e25420b3879bd0e32bdbb14d452b38776))
- Saved reference graphs: exclusions require their matching `pattern` and distinguish `project-ignored` from `default-ignored`. Re-import graphs missing that provenance. ([6585293](https://github.com/morluto/rea/commit/6585293c562853b95c3a70df02c12ad8cc17afc4))
- Terminal captures: keep initial and resized buffers within 1,000,000 cells using `columns × (rows + scrollback)`; oversized scenarios are rejected before launch. ([f9ea9a9](https://github.com/morluto/rea/commit/f9ea9a9268c854d31048da9f8a50cfa37703fe5a))
- Optional agent-verification reports: accept schema version 4, nullable `expectedFirstTool` and `targetClarificationPassed`. Production CLI/MCP contracts are unchanged. ([#1613](https://github.com/morluto/rea/pull/1613))
- Capture comparisons: recapture both sides with matching `literal-replacements-v1` semantics when custom normalization patterns are used. Legacy unversioned pattern captures remain readable but cannot be compared with new pattern captures; captures without patterns are unaffected. ([#1456](https://github.com/morluto/rea/pull/1456), [2b6f14f](https://github.com/morluto/rea/commit/2b6f14f528297cf12c776c960af0859f5cfbefbf)) Thanks @Madhavan006, @N0zoM1z0.
- Artifact graph edges: read child artifact IDs and paths from the occurrence selected by `occurrence_id`, not edge fields `child_artifact_id`/`logical_path`. Earlier inventory Evidence no longer validates against the new commitment. ([#1493](https://github.com/morluto/rea/pull/1493)) Thanks @kaoru0822-kitauji.
- Semantic graph schema: nodes and relations no longer include `identifier_strategy`, and relation IDs no longer hash it. Earlier Evidence no longer validates against this schema. ([#1474](https://github.com/morluto/rea/pull/1474)) Thanks @kaoru0822-kitauji.
- Semantic identities: select IDs from the parent graph; read exact event names from `event_name`, endpoints from `endpoint` and literal values from `properties.value`. New analyses use digest-based IDs for long/reserved event and endpoint names and `value-sha256` literal role keys, with shorter labels; string literal labels now read `string literal`. Previously stored graphs retain their original IDs and remain valid workflow inputs. ([#1427](https://github.com/morluto/rea/pull/1427), [#1423](https://github.com/morluto/rea/pull/1423), [#1407](https://github.com/morluto/rea/pull/1407)) Thanks @N0zoM1z0.
### Contributors
- Thanks also to @BaaRai, @commit-regrets, @coygeek, @Franky100-pig, @inii-sayaa, @jopemachine, @JosephDemarest, @keemsisi, @Totopo27 for contributions included in this release.
Full contribution record: [336 commits and 163 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.4.0/docs/releases/6.4.0.contributions.json).
## [6.3.0](https://github.com/morluto/rea/compare/rea-agents-6.2.0...rea-agents-6.3.0) (2026-10-09)
+130
View File
@@ -17,8 +17,108 @@ targets; optional adapter failures must preserve successful peers. Run
`npm run verify:test-discovery` after adding or moving tests. Resolved import
boundaries are checked by `npm run verify:module-boundaries`, included in lint.
For analysis workflows and agent interfaces, follow the
[performance priorities](AGENTS.md#performance). Explain the selected output
scope, reuse/cache decisions, and relevant before/after measurements when these
costs change.
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
## Contribution scope
REA focuses on software structure, runtime behavior, and evidence through
general-purpose reverse-engineering workflows. Evaluate a proposal by its actual
capabilities, side effects, defaults, and complete workflow. A tool name or a
reference to vulnerability research, malware analysis, or CTFs does not determine
whether it belongs in REA.
Supported contributions include format inspection, ordinary archive and standard
compression extraction, disassembly, decompilation, cross-references, general
static deobfuscation, vulnerability analysis, and evidence reporting. Static
protection detection is distinct from defeating that protection. Security fixes
and isolated regression tests remain in scope.
Runtime investigation needs explicit boundaries. Launching or attaching to a
process, injecting instrumentation, running scripts, and changing memory or
registers require review of target selection, execution and mutation authority,
provenance, resource limits, cancellation, and cleanup. General Frida or debugger
support is not inherently read-only, and existing support does not authorize
every workflow built on it. General-purpose interfaces are not classified as
dedicated bypass workflows solely because caller-provided code could be misused;
their execution and mutation authority must still be reviewed.
REA does not integrate or officially distribute:
- Purpose-built workflows to defeat commercial packing or code-virtualization
protections and automatically recover protected code.
- Anti-debugging or anti-instrumentation evasion, or DRM, license, anti-cheat, or
access-control bypass workflows.
- Exploit or attack-payload automation, credential theft, covert persistence,
unauthorized access, or bulk attack workflows.
These boundaries apply to first-party providers, adapters, plugins, bundled
scripts, defaults, examples, and documentation. Moving a capability into an
optional dependency or plugin does not exempt it. Review what the integration
exposes, including upstream defaults and raw-command or script execution paths.
Ordinary extraction and general static deobfuscation are not excluded merely
because the input is packed or obfuscated; assess added bypass or execution
behavior separately.
This is a product and maintenance boundary, not a judgment about a contributor's
intentions or a declaration that all excluded research is unlawful. Contributors
must have the rights and authorization needed for their work. A local-only design
or an "authorized use only" disclaimer does not establish containment or settle
legal and scope questions.
### Fail-closed integration
If scope fit, target or authorization boundaries, side effects, or the verification
needed to establish those boundaries remain materially unresolved, **do not merge,
enable, or include the capability in official packages or distributions**. Keep
the proposal pending, narrow it, or decline it. Record unknowns explicitly; do not
turn them into "none" or assumed safety. Passing CI, completed implementation,
upstream popularity, or silence from maintainers is not scope approval.
For a new provider or a capability involving target execution, injection,
mutation, or protection recovery, seek an explicit maintainer scope decision in
the linked issue or design discussion before substantial implementation intended
for integration. Small exploratory prototypes and draft PRs may help inform that
decision; they do not authorize integration. The decision must cover the final
proposed behavior; a scope expansion needs a new decision. Routine fixes and
documentation changes within existing boundaries do not need a separate advance
decision, but must not silently expand those boundaries.
### Starting a proposal
To open a feature request or design discussion, describe the problem and desired
outcome or decision. Short answers and the forms' "Not sure yet" options are
welcome. You do not need code, a complete design or test plan, legal analysis, or
private proof-of-authorization documents to start a discussion. Share what you
know and leave optional details blank when you need help.
Form selections guide triage; they are not proof of authorization or scope
approval. Unselected effects must not be treated as established absence.
Maintainers and implementers work together to resolve the following before
integration:
- The concrete analyst question, requested inputs and outputs, and why existing
REA operations are insufficient.
- Why the workflow fits this scope, its non-goals, and any protection recovery,
bypass, or attack automation it would expose.
- The selected targets and every execution, injection, mutation, network, or
persistence effect, including behavior inherited from upstream tools.
- How the boundaries and outcome will be verified with self-owned or appropriately
licensed samples; identify real CLI/MCP and provider coverage, cleanup and
resource checks, and remaining unknowns. Do not publish private binaries,
credentials, or sensitive target data; follow [SECURITY.md](SECURITY.md) for
private vulnerability reports.
Mixed proposals should separate acceptable analysis capabilities from excluded
workflows so each can be decided independently. An issue may discuss unresolved
questions; opening an issue or PR does not authorize integration. This is a
maintainer integration gate, not a per-call confirmation requirement or a sandbox
guarantee.
## Development setup
Use the development toolchain pinned in `.nvmrc` and
@@ -31,6 +131,9 @@ npm ci
npm run check:fast
```
`npm-shrinkwrap.json` is the sole dependency lock for development and the
published plugins. Do not add a root `package-lock.json`; npm would ignore it.
`npm ci` installs the exact dependencies and prepares the Husky hooks without
building the project. Run `npm run build:cached` when you need the standalone
CLI or MCP server; it compiles the runtime and bundles the authored skill.
@@ -42,6 +145,21 @@ caches deterministic builds and static checks across Git worktrees. After a pack
Repository invariants live in [AGENTS.md](AGENTS.md). Keep provider-specific
protocols in adapters and shared CLI/MCP workflows in the application layer.
### Windows source checkouts
The published npm package includes the Windows x64 native addon and its
manifest. A source checkout does not: `native/windows/build/` is ignored by
Git, and neither `npm ci` nor `npm run build:cached` (or `npm run build`)
produces `rea-windows-x64.node` or `manifest.json` in that directory.
Windows workflows and tests that require native controls need these artifacts;
without them, those controls are unavailable even after a successful runtime build.
Follow the [Windows native build guide](native/windows/README.md#build) before
running those workflows from source. The artifact build requires Linux,
MinGW-w64, and `tar`; the build script does not run on Windows. See that guide
for the build and artifact-verification commands and the separate real Windows
verification prerequisites. Keep the generated addon and manifest uncommitted.
## Documentation website
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
@@ -66,6 +184,10 @@ The separate GitHub Pages host uses only the manual
## Development feedback and PR verification
For routine merges, wait for the required checks to pass for the current PR
revision. Administrators retain an override for deliberate maintainer recovery.
If `main` is broken, prioritize a focused repair before updating affected PRs.
Select checks for the behavior changed. For a source edit:
```bash
@@ -87,6 +209,14 @@ CI diagnosis. Packaging/setup/distribution changes additionally require
`npm run verify:package` and `npm pack --dry-run`; provider changes require the
matching real-provider lane. State which host/provider workflows ran.
For CI/test correctness, drift, or performance investigations, use the
[`rea-ci-tests-audit` contributor skill](.agents/skills/rea-ci-tests-audit/SKILL.md).
It guides reproduction and comparable measurements before changing checks.
`check:pr` reuses build and static-check outputs while executing the test suite
fresh, including the fast Python producer checks. `docs:check` also runs the
onboarding example consistency suite for README-only changes.
Pre-commit formats and lints staged files; pre-push runs `check:fast`.
### Generated files and command ownership
+136
View File
@@ -0,0 +1,136 @@
# Disclaimer
## Responsible use
REA is built for lawful analysis: security research, malware and incident
analysis, vulnerability research and coordinated disclosure, CTFs and education,
interoperability, software preservation, and debugging or auditing software you
own or are authorized to examine.
The REA project does not support, and will not help with:
- circumventing DRM, license checks, anti-cheat systems or other technical
protection measures;
- developing game cheats or tampering with online services;
- bypassing authentication, bot protection or access controls on systems you are
not authorized to test;
- copying proprietary software or reproducing its protected code and assets;
- evading the usage policies or safety measures of AI model providers;
- reselling or sharing AI accounts or API access.
You are responsible for obtaining any required authorization and for complying
with applicable laws, license agreements and terms of service. Reverse
engineering may be restricted in your jurisdiction or by the license of the
software you analyze.
Requests for help with the uses above will be closed without response in issues
and discussions, and may result in removal from the REA community.
## User responsibility
Do not use REA for unauthorized access or exploitation, credential theft,
malicious deployment of malware, unlawful collection or disclosure of data,
unauthorized service disruption, or unlawful circumvention of access controls or
licensing restrictions. Legitimate security research and malware analysis must
remain within the rights and permissions applicable to the work.
You are responsible for your use of REA, including commands, configurations,
scripts, and actions you direct an AI agent to perform. Establish the legal
authority and scope required for the targets, accounts, devices, software, data,
and operations involved. Comply with applicable laws, contracts,
intellectual-property rights, privacy obligations, and tool licenses. Public
availability of software, possession of a file, or access to a debugging
endpoint does not by itself establish permission for every operation.
## Runtime, network, and device operations
Some REA workflows execute or observe programs, attach to runtime or browser
endpoints, capture network traffic, interact with applications, or operate on
connected devices. These actions can affect application state, files, accounts,
devices, and services. Authorization to inspect an artifact does not necessarily
authorize execution, interception, device changes, or interaction with another
party's system.
Review the operations you request, including agent-generated plans and scripts,
and use an appropriate environment for untrusted targets. You are responsible
for protecting credentials, personal data, captured traffic, and investigation
artifacts, and for obtaining any permission required to collect, retain,
process, or share them. Local operation does not establish authorization or
guarantee that executed targets and third-party tools have no external effects.
## Analysis results and generated output
Decompilation, reconstruction, heuristics, and AI-assisted analysis may be
incomplete or incorrect. Results do not guarantee accuracy, completeness,
security, compatibility, or legal compliance. Review the underlying evidence and
verify consequential conclusions before relying on them.
Obtaining source-like output or an analysis report does not grant rights to the
original program, its data, or the generated output. Check the target's rights
and the producing tool's applicable terms before publishing, sharing, or using
results in another product or service.
## Third-party tools and licenses
Third-party components and analysis tools retain their own licenses, copyright
notices, and usage conditions. Consult
[Third-party notices](THIRD_PARTY_NOTICES.md) for their roles, licensing
records, and preserved upstream texts.
You must legally obtain IDA Pro and Hex-Rays decompilers, Hopper, JEB, and any
other proprietary tools, together with the rights required for your use. REA
provides no commercial-tool license, key, or activation entitlement. Permitted
trials and demos remain subject to vendor terms. An open-source bridge or
accessible API does not grant additional user, machine, feature, automation,
hosting, or output-sharing rights. Cracked copies, stolen keys, unauthorized key
sharing, and bypassing license restrictions are not authorized by REA.
## Disclaimer of warranty
REA is provided **AS IS**, without express or implied warranties, including
merchantability, fitness for a particular purpose, and noninfringement, as set
out in the [MIT License](LICENSE). You assume the risks of using the software
and deciding whether it is suitable for your task. No project description,
example, analysis result, or third-party integration guarantees a particular
outcome or establishes that your intended use is lawful or safe.
## Limitation of liability
The warranty and liability provisions of the [MIT License](LICENSE) govern REA's
authors and copyright holders, subject to applicable law. That license disclaims
liability for claims, damages, and other liability arising from or in connection
with the software or its use, whether in contract, tort, or otherwise. This
document does not expand those provisions or override rights and obligations
that cannot be excluded under applicable law.
## Disclaimer of endorsement
References to third-party projects, contributors, products, services, and
trademarks identify their work and REA's integrations. Credits and thanks do not
imply affiliation, sponsorship, approval, or endorsement by the named parties,
including the National Security Agency and commercial tool vendors.
Redistribution, bundling, or hosting by a third party does not by itself imply
affiliation with or endorsement by the REA project.
## Relationship to software licenses
This document explains REA's intended use, user responsibilities, and community
support boundaries. It does not amend the MIT license, impose additional
copyright-license conditions, replace upstream licenses, or grant rights to
third-party software or targets. The applicable software licenses and agreements
retain their own terms.
## Versions, distribution channels, and platforms
The project's position on lawful use and non-endorsement of illegal or
unauthorized activity covers all REA versions, distribution forms, and
platforms. This includes historical releases and Git commits, npm packages,
bundled or separately distributed REA skills, source checkouts, release
archives, and redistributed copies, regardless of installation method, agent
client, operating system, or platform. Using an older version or another
distribution channel does not imply project endorsement or remove obligations
under applicable law or to respect third-party rights.
Each copy remains subject to the licenses accompanying that copy. This statement
does not retroactively amend existing license grants, add license conditions, or
expand historical warranty or liability exclusions.
+55 -13
View File
@@ -1,6 +1,7 @@
<div align="center">
**English** · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
· [Ελληνικά](docs/readme/README_el.md)
# REA: Reverse Engineer Anything
@@ -9,7 +10,6 @@
**See a feature you like. Understand how it works, down to the binary level.**
[![npm version](https://img.shields.io/npm/v/rea-agents?style=flat-square&color=cb3837)](https://www.npmjs.com/package/rea-agents)
[![CI](https://img.shields.io/github/actions/workflow/status/morluto/rea/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/morluto/rea/actions/workflows/ci.yml)
[![MCP tool catalog](https://img.shields.io/badge/MCP-tool_catalog-5c4ee5?style=flat-square)](docs/mcp-contracts.md#generated-catalog)
[![Node.js requirements](https://img.shields.io/badge/Node.js-requirements-339933?style=flat-square&logo=nodedotjs&logoColor=white)](#what-you-can-analyze)
[![skills.sh](https://skills.sh/b/morluto/rea?style=flat-square)](https://skills.sh/morluto/rea/reverse-engineer-anything)
@@ -24,6 +24,12 @@
<code>npx rea-agents setup</code>
> [!IMPORTANT]
> Get REA from [morluto/rea](https://github.com/morluto/rea),
> [rea.tools](https://rea.tools/), or the [rea-agents npm package](https://www.npmjs.com/package/rea-agents).
> 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.
<br />
<img src="docs/assets/rea-hopper-analysis.png" alt="REA launching its analysis bridge inside Hopper while inspecting a native binary" width="1200" />
@@ -111,12 +117,15 @@ snapshots and scripting.
REA changes quickly, and new releases include frequent bug fixes. Keep your
installation up to date.
For an npm-installed CLI:
For a CLI installed with `npm install --global` or `bun add --global`:
```bash
rea update
```
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.
@@ -127,7 +136,8 @@ 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.
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
@@ -151,17 +161,25 @@ Additional tools and host support depend on the target:
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](https://rea.tools/guides/native/) |
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
| Go binaries | Embedded compiler version, modules, replacements and build settings | Built-in static ELF/PE/thin Mach-O inspection; no Go installation required |
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
| 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](docs/evm-bytecode.md) |
| 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](docs/recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; [application analysis](https://rea.tools/guides/javascript/) |
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](https://rea.tools/guides/browser/) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR; mitmdump on Linux for native mitmproxy captures; [capture guide](docs/web-network-captures.md) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
| 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](docs/jeb-analysis.md) |
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
| Flutter apps | Dart snapshot hash, per-ABI library digests and engine build provenance | None (static APK parsing); [Flutter guide](docs/flutter-analysis.md) |
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
| Recorded packets | Selected fields, repeated values, filters and decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](docs/packet-captures.md) |
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests | Caller-supplied tshark and capinfos (Linux-verified); [pcap guide](docs/pcap-analysis.md) |
| Recorded logic signals | Decoder annotations, sample ranges and diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](docs/signal-recordings.md) |
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
@@ -169,15 +187,20 @@ 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](docs/reverse-engineering-tools.md)
for provider setup, CLI commands, and Evidence limitations. REA does not install
or update either provider.
<a id="choosing-a-deep-analysis-provider"></a>
Native formats and host support vary by provider. See
[Hopper and Ghidra setup](docs/installation.md#hopper), the
[IDA guide](docs/ida-provider.md), and
[IDA guide](docs/ida-provider.md) (including an optional native MCP bridge), and
[experimental Windows Ghidra support](docs/windows-ghidra-p0.md).
Ghidra also supports [16-bit DOS analysis](docs/ghidra-dos.md).
For large binaries, raise its startup deadline with
`REA_GHIDRA_STARTUP_TIMEOUT_MS`.
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](docs/cli.md#choose-a-provider).
Check [release availability](docs/installation.md#released-package-and-main)
for features added since the latest npm release.
@@ -240,7 +263,9 @@ Ghidra and IDA use your existing installations. See [provider setup](docs/instal
<summary><strong>Do I need to start Hopper first?</strong></summary>
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. See
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](docs/installation.md#launcher-paths-and-troubleshooting).
</details>
@@ -276,7 +301,7 @@ model provider has its own data policy.
Update first; a recent release may already fix it.
For an npm-installed CLI:
For a CLI installed with `npm install --global` or `bun add --global`:
```bash
rea update
@@ -310,7 +335,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
## Star history
🎉 **60,000 GitHub stars — thank you!**
🎉 **180,000 GitHub stars — thank you!**
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
@@ -324,9 +349,13 @@ Thanks to everyone using REA, reporting bugs, requesting features, testing build
## Disclaimer
REA provides tools for lawful reverse-engineering research, analysis, and reconstruction. You are responsible for obtaining any required authorization and complying with applicable laws. The project does not endorse illegal or unauthorized use.
See [DISCLAIMER.md](DISCLAIMER.md) for responsible-use guidance.
REA is an open-source software project. We have not issued or endorsed any cryptocurrency or token. Tokens using the REA name are not affiliated with the project.
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
@@ -341,3 +370,16 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks,
## License
[MIT](LICENSE)
REA-authored code is licensed under MIT. Third-party material retains its own
licenses and copyright notices; see
[Third-party notices](THIRD_PARTY_NOTICES.md) 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.
-267
View File
@@ -1,267 +0,0 @@
<div align="center" dir="rtl">
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی** · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
# REA: مهندسی معکوس هر چیزی
### یک MCP برای مهندسی معکوس فایل‌های باینری، برنامه‌ها و رفتار زمان اجرا.
**قابلیتی دیده‌اید که دوستش دارید؟ سازوکارش را تا سطح باینری درک کنید.**
[![npm version](https://img.shields.io/npm/v/rea-agents?style=flat-square&color=cb3837)](https://www.npmjs.com/package/rea-agents)
[![CI](https://img.shields.io/github/actions/workflow/status/morluto/rea/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/morluto/rea/actions/workflows/ci.yml)
[![MCP tool catalog](https://img.shields.io/badge/MCP-tool_catalog-5c4ee5?style=flat-square)](docs/mcp-contracts.md#generated-catalog)
[![Node.js requirements](https://img.shields.io/badge/Node.js-requirements-339933?style=flat-square&logo=nodedotjs&logoColor=white)](#current-status)
[![skills.sh](https://skills.sh/b/morluto/rea?style=flat-square)](https://skills.sh/morluto/rea/reverse-engineer-anything)
[![MIT license](https://img.shields.io/badge/license-MIT-f4c430?style=flat-square)](LICENSE)
[![Discord](https://img.shields.io/discord/1556595354999332884?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/GkcryMnJDM)
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55" /></a>
**[وب‌سایت](https://rea.tools/) · [راهنماها](https://rea.tools/guides/) · [نمونه‌های عملی](https://rea.tools/showcase/)**
[شروع سریع](#شروع-سریع) · [REA چگونه کار می‌کند؟](#rea-چگونه-کار-میکند) · [چه چیزهایی را میتوان تحلیل کرد؟](#چه-چیزهایی-را-میتوان-تحلیل-کرد) · [نمونه‌های عملی](#نمونههای-عملی) · [پرسش‌های متداول](#پرسشهای-متداول) · [مستندات](#مستندات)
<code>npx rea-agents setup</code>
<br />
<img src="docs/assets/rea-hopper-analysis.png" alt="اجرای پل تحلیل REA در Hopper هنگام بررسی یک فایل باینری بومی" width="1200" />
<br /><br />
<table aria-label="انجمن REA"><tr><td align="center" width="360"><a href="https://discord.gg/GkcryMnJDM"><img src="docs/assets/discord.svg" height="42" alt="Discord" /><br /><strong>به انجمن مهندسی معکوس بپیوندید</strong></a><br /><sub>دیسکورد · پرسش و پاسخ · نمایش دستاوردها</sub></td></tr></table>
<br />
</div>
---
<div dir="rtl">
قابلیتی در یک برنامه دیده‌اید که می‌خواهید در محصول خودتان داشته باشید؟ از ایجنت خود بخواهید با REA آن را بررسی کند. ایجنت می‌تواند بدون دسترسی به کد منبع برنامه، نحوهٔ کار قابلیت را بررسی و توضیح دهد، شواهد را نشان دهد و نسخه‌ای مشابه برای پروژهٔ شما بسازد.
REA ایجنت شما را به ابزارهایی برای بررسی باینری‌های بومی، برنامه‌های JavaScript و Electron، اسمبلی‌های .NET و وب‌سایت‌ها متصل می‌کند. همین ابزارها از ترمینال نیز قابل استفاده‌اند. تحلیل به‌صورت محلی انجام می‌شود و نتایج شامل شواهد و محدودیت‌های پشت هر نتیجه‌گیری هستند.
فرایند راه‌اندازی، REA را در ایجنت شما ثبت می‌کند و دستورالعمل‌های گردش‌کار متناظر را نصب می‌کند. تحلیل بومی می‌تواند از نصب موجود Hopper یا Ghidra استفاده کند؛ همچنین راه‌اندازی می‌تواند با تأیید شما Hopper را به‌صورت اختیاری نصب کند. تحلیل ایستای JavaScript به هیچ‌یک از این موتورهای تحلیل نیاز ندارد.
> برای دستورالعمل‌های راه‌اندازی، راهنماهای تصویری و مطالعات موردی واقعی، **[به وب‌سایت REA مراجعه کنید](https://rea.tools/)**.
## شروع سریع
### ایجنت خود را راه‌اندازی کنید
پس از نصب Node.js و npm، دستور زیر را اجرا کنید:
```bash
npx rea-agents setup
```
ایجنت‌های موردنظر را انتخاب کنید، تغییرات پیشنهادی را بررسی و تأیید کنید. فرایند راه‌اندازی، سرور MCP مربوط به REA و دستورالعمل‌های گردش‌کار متناظر را اضافه می‌کند و از پیکربندی موجود نسخهٔ پشتیبان می‌گیرد. پس از آن ایجنت خود را مجدداً راه‌اندازی کنید.
راه‌اندازی از Claude Code، Codex، Cursor، Gemini CLI، Grok Build و [ایجنت‌های دیگر](docs/installation.md#supported-agents) پشتیبانی می‌کند. برای پیکربندی ارائه‌دهنده و ثبت دستی MCP، [نصب و راه‌اندازی](docs/installation.md) را ببینید.
### از ایجنت خود بپرسید
```text
بررسی کن جست‌وجو در برنامهٔ Notes چگونه کار می‌کند، شواهدش را نشان بده و قابلیتی مشابه برای پروژهٔ من بساز.
```
به‌جای Notes، نام برنامهٔ هدف و قابلیتی را قرار دهید که می‌خواهید درک کنید.
### استفاده از ترمینال
برای بررسی پوشهٔ استخراج‌شدهٔ یک برنامهٔ JavaScript/Electron یا فایل ASAR:
```bash
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
```
نتیجه شامل ماژول‌ها، importها، مرزهای Electron و شواهد مربوط به آن‌هاست. مسیر را با مسیر برنامهٔ هدف جایگزین کنید؛ مثلاً در ویندوز `"D:/apps/example"`.
برای نصب دستور `rea` جهت استفادهٔ مداوم:
```bash
npm install --global rea-agents
rea --help
```
برای تحلیل بومی ابتدا یک ارائه‌دهنده پیکربندی کنید. برای دستورات بومی، انتخاب ارائه‌دهنده، snapshotها و اسکریپت‌نویسی، [راهنمای CLI و شواهد](docs/cli.md) را ببینید.
### به‌روزرسانی REA
REA به‌سرعت تغییر می‌کند و نسخه‌های جدید معمولاً شامل رفع اشکال‌های متعددی هستند. نصب خود را به‌روز نگه دارید.
برای CLI نصب‌شده از طریق npm:
```bash
rea update
```
برای به‌روزرسانی ثبت ایجنت‌ها و skill، دستور setup نمایش‌داده‌شده توسط به‌روزرسانی را اجرا کنید.
اگر از `npx` استفاده می‌کنید، راه‌اندازی ایجنت را با دستور زیر به‌روز کنید:
```bash
npx rea-agents@latest setup
```
تغییرات راه‌اندازی را بررسی کنید و ایجنت را دوباره اجرا کنید. برای اجرای یک‌بارهٔ دستورات CLI، از `npx rea-agents@latest` و سپس دستور موردنظر استفاده کنید.
## REA چگونه کار می‌کند؟
ایجنت شما از طریق MCP، REA را فراخوانی می‌کند تا هدف را بررسی و کد مرتبط را ردیابی کند. REA یافته‌ها را همراه با شواهد برمی‌گرداند. ایجنت از این نتایج برای طرح پرسش‌های تکمیلی، توضیح رفتار یا نوشتن و آزمایش یک پیاده‌سازی استفاده می‌کند. دستورات CLI نیز از همین گردش‌کارها بهره می‌برند.
![جریان بررسی REA: ایجنت دربارهٔ هدف محلی سؤال می‌پرسد، REA آن را با ابزارهای تحلیل بررسی و ردیابی می‌کند و ایجنت از کد، ارجاعات و ناشناخته‌های بازگردانده‌شده برای توضیح، پیاده‌سازی و آزمایش استفاده می‌کند.](website/public/assets/figures/rea-investigation-flow.svg)
[نمایش تصویر در اندازهٔ کامل](website/public/assets/figures/rea-investigation-flow.svg).
<a id="current-status"></a>
## چه چیزهایی را می‌توان تحلیل کرد؟
REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، به‌همراه npm نیاز دارد. ابزارهای اضافی و پشتیبانی سیستم میزبان به نوع هدف بستگی دارند:
| هدف | خروجی REA | پیش‌نیازها و راهنما |
| --------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| باینری‌های بومی | شبه‌کد، اسمبلی، رشته‌ها، نمادها، فراخوانی‌ها و ارجاعات | Hopper، Ghidra یا IDA؛ [تحلیل بومی](https://rea.tools/guides/native/) |
| ساختار ELF آفلاین | بخش‌ها، سگمنت‌ها، نمادها و relocationهای اصلی و گزینه‌های احتمالی کاهش آسیب‌پذیری ایستا | pwntools تأمین‌شده توسط فراخواننده روی Linux x64؛ [تشخیص باینری](docs/binary-diagnostics.md) |
| بایت‌کد EVM | selectorهای dispatch، offsetهای بایتی، آرگومان‌های استنباط‌شده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](docs/evm-bytecode.md) |
| کرش‌های ثبت‌شدهٔ لینوکس | یادداشت‌های خام، رجیسترها/سیگنال‌های تمام threadهای ثبت‌شده و گزینه‌های احتمالی نگاشت | pwntools تأمین‌شده توسط فراخواننده روی Linux x64؛ GDB/pwndbg اختیاری؛ [کرش‌های ثبت‌شده](docs/recorded-crashes.md) |
| JavaScript / Electron | ماژول‌ها، importها، source mapها، مسیرها، IPC و ارتباط افزونه‌های بومی | Node.js و npm؛ [تحلیل برنامه](https://rea.tools/guides/javascript/) |
| وب‌سایت‌ها | ساختار صفحه، اسکریپت‌ها، مشاهدات شبکه و اسکرین‌شات‌های درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](https://rea.tools/guides/browser/) |
| داده‌های ضبط‌شدهٔ شبکه | درخواست‌ها، پاسخ‌ها، payloadهای آشکارشده و مکان آن‌ها در منبع | HAR؛ ابزار mitmdump در لینوکس برای ضبط‌های بومی mitmproxy؛ [راهنمای ضبط شبکه](docs/web-network-captures.md) |
| اسمبلی‌های .NET | فراداده، دستورالعمل‌های CIL، وابستگی‌های بومی اعلام‌شده و مقایسهٔ buildها | بررسی ایستا؛ [راهنمای کد مدیریت‌شده](docs/managed-code-analysis.md) |
| فایل‌های APK اندروید | تعاریف Manifest، کلاس‌ها، متدهای دیکامپایل‌شده و ارجاعات | JADX بدون رابط گرافیکی و JDK کامل در Linux/macOS/Windows x64؛ [راهنمای اندروید](docs/android-analysis.md) |
| دستگاه‌های اندروید | دستگاه‌ها، بسته‌ها، فرایندها، لاگ‌ها، دامپ‌ها، اسکرین‌شات‌ها و انتقال APK/فایل | adb تأمین‌شده توسط فراخوان‌کننده (شبیه‌ساز یا دستگاه)؛ [راهنمای ADB](docs/adb-device-analysis.md) |
| پروژه‌های JEB | واحدهای پروژه از موتور، هش‌های مصنوع و شبه‌کدِ نوع/متد | یک کلاینت JEB که توسط فراخوان‌کننده اجرا شده و MCP ارائه می‌دهد و با REA_JEB_MCP_URL انتخاب می‌شود؛ [راهنمای JEB](docs/jeb-analysis.md) |
| منابع اندروید | مانیفست رمزگشایی‌شده، جداول رشته‌ها، زبان‌ها و اطلاعات نسخه | راه‌انداز Apktool تأمین‌شده توسط فراخوان‌کننده؛ [راهنمای منابع](docs/apktool-resource-analysis.md) |
| Firmware | ناحیه‌ها، نتایج استخراج و تحویل به ابزارهای تحلیل بومی | Binwalk / Unblob در لینوکس؛ [راهنمای Firmware](docs/firmware-analysis.md) |
| بسته‌ها و منابع | فهرست فایل‌ها، digestها، فایل‌های plist، ساختار bundle اپل و منابع استخراج‌شده | [راهنمای آرتیفکت و JavaScript](docs/javascript-artifact-reconstruction.md)، [برنامه‌های اپل](docs/apple-application-analysis.md) |
| رفتار فرایند | خروجی ترمینال، تعاملات، وضعیت خروج، مشاهدات سیستم فایل و مقایسهٔ اجراها | Linux/macOS با PTY بومی؛ [ضبط فرایند](docs/process-capture.md) |
بررسی ایستای JavaScript و .NET فایل‌های ارائه‌شده را بدون اجرای برنامه می‌خواند. ضبط زمان اجرا، هدف انتخاب‌شده را با مجوزهای کاربری شما اجرا می‌کند یا با آن تعامل دارد؛ آثار این عملیات در راهنمای مربوط به هر نوع ضبط توضیح داده شده‌اند.
<a id="choosing-a-deep-analysis-provider"></a>
فرمت‌های بومی و پشتیبانی سیستم میزبان بر اساس ارائه‌دهنده متفاوت‌اند. [راه‌اندازی Hopper و Ghidra](docs/installation.md#hopper)، [راهنمای IDA](docs/ida-provider.md) و [پشتیبانی آزمایشی Ghidra در ویندوز](docs/windows-ghidra-p0.md) را ببینید. Ghidra همچنین از [تحلیل DOS شانزده‌بیتی](docs/ghidra-dos.md) پشتیبانی می‌کند. برای انتخاب ارائه‌دهنده، به [راهنمای CLI](docs/cli.md#choose-a-provider) مراجعه کنید. برای قابلیت‌هایی که پس از آخرین انتشار npm اضافه شده‌اند، [وضعیت انتشار](docs/installation.md#released-package-and-main) را بررسی کنید.
## نمونه‌های عملی
[![تصویرهایی از نمونه‌های پنینگ صدای DX-Ball، پل کلیپ‌بورد Notion و حلقهٔ گلوله‌های TH04](docs/assets/rea-showcases.png)](https://rea.tools/showcase/)
### DX-Ball: بازسازی محاسبهٔ موقعیت استریوی صدا
یک فراخوانی صوتی را تا تابع کمکی تبدیل موقعیت به pan دنبال کنید، دستورالعمل‌ها را بررسی کنید و شبه‌کد ناقص را به C تبدیل کنید. بازسازی حاصل، هر ۳٬۲۰۵ مورد آزمایش x86 اصلی را با موفقیت پشت سر می‌گذارد و هر ۶۳ بایت تابع کامپایل‌شده را دقیقاً بازتولید می‌کند.
[مطالعهٔ موردی](https://rea.tools/showcase/dx-ball/) · [مخزن بازسازی](https://github.com/N0zoM1z0/dx-ball)
### Notion: ردیابی پل کلیپ‌بورد Electron
API کلیپ‌بورد در renderer را پیدا کنید، مسیر آن را از preload و IPC تا فرایند اصلی دنبال کنید و قالب غنی کلیپ‌بورد را بررسی کنید.
[مطالعهٔ موردی](https://rea.tools/showcase/notion/)
### TH04: بازیابی محاسبهٔ حلقهٔ گلوله در DOS
دستورالعمل‌های ۱۶بیتی بازی اصلی PC-98 را بررسی کنید، محاسبات زاویهٔ ثابت و هدف‌گیری‌شده را بازیابی کنید و کد C++ بازسازی‌شده را با خروجی کامپایلر تاریخی مقایسه کنید.
[مطالعهٔ موردی](https://rea.tools/showcase/th04/) · [مخزن بازسازی](https://github.com/N0zoM1z0/th04)
اگر از REA برای بررسی موضوع جالبی استفاده کرده‌اید، خوشحال می‌شویم آن را به اشتراک بگذارید. یک [issue](https://github.com/morluto/rea/issues) یا [pull request](https://github.com/morluto/rea/pulls) ثبت کنید و هدف، پرسش خود، نقش REA و یافته‌هایتان را توضیح دهید.
## پرسش‌های متداول
<details><summary><strong>کدام ایجنت‌ها می‌توانند از REA استفاده کنند؟</strong></summary>
هر ایجنتی که از سرورهای MCP محلی پشتیبانی کند. فرایند setup، [ایجنت‌های پشتیبانی‌شده](docs/installation.md#supported-agents) را پیکربندی می‌کند؛ سایر کلاینت‌ها می‌توانند از [ثبت دستی MCP](docs/installation.md#mcp-registry) استفاده کنند.
</details>
<details><summary><strong>آیا به Hopper، Ghidra یا IDA نیاز دارم؟</strong></summary>
تحلیل عمیق بومی به یکی از آن‌ها نیاز دارد. بررسی ایستای JavaScript و .NET بدون موتور تحلیل بومی کار می‌کند. setup می‌تواند با تأیید شما Hopper را نصب کند؛ Ghidra و IDA از نصب‌های موجود شما استفاده می‌کنند. [راه‌اندازی ارائه‌دهنده](docs/installation.md#hopper) را ببینید.
</details>
<details><summary><strong>آیا باید ابتدا Hopper را اجرا کنم؟</strong></summary>
REA زمانی که عملیاتی به Hopper نیاز داشته باشد آن را اجرا می‌کند. در macOS، ممکن است هنگام نخستین اجرا پنجره‌ای برای انتخاب حالت آزمایشی یا فعال‌سازی مجوز نمایش داده شود. [راه‌اندازی Hopper و رفع اشکال](docs/installation.md#launcher-paths-and-troubleshooting) را ببینید.
</details>
<details><summary><strong>نصب skill از skills.sh چه کاری انجام می‌دهد؟</strong></summary>
این skill دستورالعمل‌های بررسی را برای ایجنت فراهم می‌کند. برای ثبت سرور MCP مربوط به REA و نصب دستورالعمل‌های متناظر، `npx rea-agents setup` را اجرا کنید و سپس ایجنت را مجدداً راه‌اندازی کنید. [نصب مستقل skill](docs/installation.md#skill-only-installation) را ببینید.
</details>
<details><summary><strong>REA چه نوع کدی برمی‌گرداند؟</strong></summary>
تحلیل بومی، شبه‌کد و اسمبلی برمی‌گرداند. تحلیل JavaScript/Electron ماژول‌ها و روابط میان آن‌ها را بازیابی می‌کند. ایجنت شما از این یافته‌ها برای نوشتن و آزمایش یک پیاده‌سازی استفاده می‌کند؛ [نمونه‌های عملی](#نمونههای-عملی) مثال‌های گام‌به‌گام ارائه می‌دهند.
</details>
<details><summary><strong>آیا REA برنامهٔ من را آپلود می‌کند؟</strong></summary>
REA اهداف را به‌صورت محلی تحلیل می‌کند. ایجنت شما نتایج ابزارها را دریافت می‌کند و ارائه‌دهندهٔ مدل آن سیاست مستقل خود را دربارهٔ داده‌ها دارد.
</details>
<details><summary><strong>اگر با خطا مواجه شدم چه کنم؟</strong></summary>
ابتدا به‌روزرسانی کنید؛ ممکن است مشکل در نسخه‌ای جدید رفع شده باشد.
برای CLI نصب‌شده با npm:
```bash
rea update
```
برای راه‌اندازی ایجنت با `npx`:
```bash
npx rea-agents@latest setup
```
اگر از ایجنت استفاده می‌کنید، [به‌روزرسانی راه‌اندازی](#بهروزرسانی-rea) را تکمیل و ایجنت را مجدداً اجرا کنید. همان کار را دوباره امتحان کنید. اگر مشکل ادامه داشت، یک [issue ثبت کنید](https://github.com/morluto/rea/issues) و نسخهٔ REA، نوع هدف، مراحل بازتولید و خروجی خطا را ذکر کنید.
</details>
## مستندات
از [راهنماهای عملی وب‌سایت](https://rea.tools/guides/) شروع کنید. برای گزینه‌های دقیق، پیش‌نیازها و قراردادهای خروجی:
- [نصب و راه‌اندازی](docs/installation.md): ثبت ایجنت، پیکربندی ارائه‌دهنده، به‌روزرسانی و حذف نصب.
- [آمادگی و رفع اشکال](docs/installation.md#check-readiness-for-your-task): عیب‌یابی یک ایجنت یا موتور تحلیل.
- [CLI و شواهد](docs/cli.md): دستورات، انتخاب ارائه‌دهنده، snapshotها، ورود/خروج داده و وضعیت‌های خروج.
- [قراردادهای MCP](docs/mcp-contracts.md) و [پرامپت‌های ایجنت](docs/mcp-prompts.md): نتایج ابزارها، نشست‌ها و بررسی‌های هدایت‌شده.
- [فهرست ابزارها](docs/mcp-contracts.md#generated-catalog): فهرست تولیدشده هنگام build از ابزارها، ارائه‌دهندگان و دستورات CLI.
- [نقشهٔ راه](docs/roadmap.md): برنامه‌های آینده و پیگیری قابلیت‌ها.
آسیب‌پذیری‌ها را از طریق [SECURITY.md](SECURITY.md) گزارش کنید.
## تاریخچهٔ ستاره‌ها
🎉 **۵۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
از همهٔ کسانی که از REA استفاده می‌کنند، خطاها را گزارش می‌دهند، قابلیت‌های جدید پیشنهاد می‌کنند، buildها را آزمایش می‌کنند و در بهبود پروژه مشارکت دارند سپاسگزاریم.
<a href="https://www.star-history.com/?repos=morluto%2Frea&amp;type=date"><picture><source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date&amp;theme=dark&amp;legend=top-left" /><source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" /><img alt="تاریخچهٔ ستاره‌های GitHub پروژهٔ REA" src="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" /></picture></a>
## سلب مسئولیت
REA ابزارهایی برای پژوهش، تحلیل و بازسازی قانونی در حوزهٔ مهندسی معکوس فراهم می‌کند. مسئولیت دریافت مجوزهای لازم و رعایت قوانین مربوط بر عهدهٔ شماست. این پروژه استفادهٔ غیرقانونی یا بدون مجوز را تأیید نمی‌کند.
## مشارکت
از کمک شما به REA استقبال می‌کنیم! برای گزارش خطا یا پیشنهاد قابلیت، یک [issue باز کنید](https://github.com/morluto/rea/issues) یا برای بهبود کد و مستندات [pull request بفرستید](https://github.com/morluto/rea/pulls).
برای راه‌اندازی محیط توسعه و بررسی‌ها، [CONTRIBUTING.md](CONTRIBUTING.md)، برای مراحل اعتبارسنجی [آزمایش‌ها](docs/testing.md) و برای ساختار پروژه [نقشهٔ معماری](docs/architecture.mermaid) را ببینید.
## مجوز
[MIT](LICENSE)
</div>
+6 -3
View File
@@ -24,9 +24,12 @@ provider-dependent behavior to be reproduced safely.
## Security boundary
REA authenticates each local provider bridge session with a random capability
token and a current-user Unix socket. Tokens are transferred through private
session descriptors rather than process arguments or environment variables.
REA authenticates its Hopper and Ghidra bridges with per-session random
capability tokens. POSIX uses current-user Unix sockets; Windows Ghidra uses
authenticated loopback TCP with a private NTFS runtime whose DACL admits only
the current user and SYSTEM. See [Windows Ghidra P0](docs/windows-ghidra-p0.md#private-runtime-transport-and-lifecycle).
Tokens are transferred through private session descriptors rather than process
arguments or environment variables.
This is not a sandbox and does not protect against malicious processes already
running as the same operating-system user. Opening an untrusted binary
delegates parsing and analysis to the selected local provider with that user's
+134
View File
@@ -0,0 +1,134 @@
# Third-party notices
REA-authored code is licensed under [MIT](LICENSE). Third-party material retains
its own copyright, license, attribution, and NOTICE requirements. REA's MIT
license grants no rights to proprietary analysis tools. Project and product
names identify integrations; they do not imply affiliation or endorsement.
This inventory covers the locked npm dependency graph, code or assets derived
from upstream projects, and separately obtained engines used by REA. A listed
integration is not a statement that its executable is bundled. Upstream
components and caller-selected versions may have additional terms; retain the
notices supplied with the software you actually obtain or redistribute.
## Distributed dependencies, derived code, and assets
| Material | Use and licensing records |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| npm dependencies | [Complete inventory](licenses/npm-dependencies.txt) of every entry in `npm-shrinkwrap.json`, including transitive, optional platform, development, and website dependencies. [Runtime notices](licenses/npm-runtime-notices.txt) preserve the copyright, license, and NOTICE documents from integrity-verified release archives, plus the MIT notice for Rolldown's generated runtime helpers. Development dependencies are inventoried but are not all distributed in REA's npm package. |
| NativeAOT parser | REA's derived parser uses the MIT-licensed [ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot/tree/effeb734fc570c32650f88b159608979dc7b423e) work of Washi and the NativeAOT layout definitions in [dotnet/runtime](https://github.com/dotnet/runtime/tree/a3b2d40328be0be3f8dd43f0e5cc925b8827b044). Preserve [Washi's MIT notice](licenses/nativeaot-parser-MIT.txt) and the [.NET Foundation MIT notice](licenses/dotnet-runtime-MIT.txt). |
| EVM analysis | `evmole@0.9.3`, MIT, copyright 2023 Maxim Andreev. The published npm archive omits its license document; REA retains the [original upstream license](third_party/evmole/LICENSE) and [provenance](third_party/evmole/README.md). It is also included in the runtime notices. |
| Windows native addon | The release addon is compiled with MinGW-w64 and statically links GCC runtime libraries. Preserve the [MinGW-w64 runtime notices](licenses/MinGW-w64-runtime.txt), [GPLv3 text](licenses/GPL-3.0.txt), and [GCC Runtime Library Exception 3.1](licenses/GCC-Runtime-Library-Exception-3.1.txt). The exception governs eligible compiled combinations; it does not relicense the runtime itself under MIT. The Node-API build headers are from Node 22.19.0; its [original license and component notices](licenses/Node-22.19.0-LICENSE.txt) are retained. Node itself is supplied by the user. Snapshot provenance and release-toolchain review requirements are in [licenses/README.md](licenses/README.md). |
| Dinosaur website example | Selected excerpts from [wayou/t-rex-runner](https://github.com/wayou/t-rex-runner/tree/5455bfa408ec6b707c7300ff194b7390733a766d), derived from Chromium's dinosaur game. The website retains the [original BSD-3-Clause notices](website/public/assets/licenses/dinosaur.txt), including Chromium authors and wayou. This asset is not part of the npm runtime. |
Two locked runtime releases, [`clean-git-ref@2.0.1`](https://github.com/TheSavior/clean-git-ref/tree/bf3c8afc8032bcdc6a53634c18b601dc6df478fb) (declared Apache-2.0) and
[`minimisted@2.0.1`](https://github.com/kt3k/minimisted/tree/95fdfb8d6efb9546c1117854fe3a58105f6332ce) (declared MIT), omit a full license/copyright document in their
published archives and inspected upstream release sources. Their runtime notice
entries preserve the declared license and author metadata and include a clearly
labeled standard license text. These are documented upstream notice gaps, not
recovered copyright notices or a claim that attribution has been fully verified.
The optional `frida@17.23.2` npm archive and tagged Node binding source also
omit a standalone license/copyright document. Its declared
`LGPL-2.0 WITH WxWindows-exception-3.1` license is recorded with unchanged
[Frida licensing text](licenses/frida-17.23.2-COPYING.txt) and the
[GNU Library GPL v2](licenses/LGPL-2.0.txt). These are licensing supplements,
not recovered Node binding or prebuild-component copyright notices.
## Open-source engine credits and licenses
We thank the maintainers and contributors of the projects below. Their
decompilers, binary-analysis engines, debuggers, parsers, and capture tools
provide capabilities used through REA's integrations. Each row identifies the
upstream project, its own license, and its role in REA. In particular, JADX
provides the Android decompiler, while jadx-headless-mcp provides the separate
headless integration used by REA; both projects deserve credit.
These acknowledgments are voluntary thanks. Required upstream copyright and
NOTICE texts remain in their original form and are not replaced by this
summary. Credits do not imply endorsement or change any license.
REA's adapters do not grant, replace, or waive these licenses. The upstream
executables and their dependency graphs are supplied separately. Git submodules
pin inspection sources; they are not included in the npm package.
| Project | License and source | REA relationship |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra 12.1.4. Copies of its [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
| Wakaru | [Apache-2.0](https://github.com/pionxzh/wakaru/blob/8219bf5016a063d7fb215101a8c45d5959ec733f/LICENSE) | v1.14.0; pinned source and separately obtained JavaScript reconstruction tooling. |
| WABT | [Apache-2.0](https://github.com/WebAssembly/wabt/blob/ff0ef7e0009402740c805a9744c09b05be063e48/LICENSE) | v1.0.42; external WebAssembly tools; [integration provenance](third_party/wabt/README.md). |
| pwntools | [MIT, with GPL/BSD component qualifications](https://github.com/Gallopsled/pwntools/blob/3ad86ec5adab1abf9781ca7552a6f5d7cff7a33c/LICENSE-pwntools.txt) | 4.15.0; caller-supplied offline ELF analysis package. |
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
| pwndbg | [MIT](third_party/pwndbg/LICENSE.md) | 2026.09.15, commit `55596aa676a2756efb8d67034f31675170eabe48`; separately obtained debugger extension. |
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
| mitmproxy | [MIT](https://github.com/mitmproxy/mitmproxy/blob/6c09d56e4c29a92f5ad01b03199977584b8ea14f/LICENSE) | 12.2.3; separately supplied network capture proxy. |
| Android Debug Bridge (adb) | [Apache-2.0](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/NOTICE) | User-supplied Android device tool. A complete Android SDK download has its own [SDK license terms](https://developer.android.com/studio/terms); adb's source license does not replace them. |
| ILSpy / ICSharpCode.Decompiler | [MIT](https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs) | Optional separately installed .NET decompiler; not a redistributed REA dependency. |
Ghidra's original notice credits software developed at the National Security
Agency. It explains that US-government-created portions are not subject to US
copyright protection, while other portions retain their authors' rights. Its
modules include additional third-party licenses, and standalone programs under
`GPL/` have GPLv3 terms. The three retained top-level documents do not replace
the module notices and license directories in a complete Ghidra distribution.
REA is not affiliated with or endorsed by the NSA.
## Commercial tools and bridges
**You must legally obtain the tools and the rights needed for your use.** REA
does not include an IDA Pro, Hex-Rays decompiler, Hopper, or JEB license, license
key, or activation entitlement. A permitted trial/demo may be used only within
its official terms. Cracked copies, stolen keys, unauthorized sharing, and
bypassing activation or license limits are not authorized by REA.
| Project / product | Applicable terms and distinction |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IDA Pro and Hex-Rays decompilers | Proprietary Hex-Rays software governed by the [EULA](https://hex-rays.com/eula), purchase agreement, and applicable license scheme. Obtain rights for the required product, user, machine, decompiler, and automation interfaces. Availability of an API or a bridge does not grant hosting, service, or additional-user rights. |
| ida-pro-mcp | The bridge's [MIT license](https://github.com/mrexodia/ida-pro-mcp/blob/main/LICENSE) belongs to Duncan Ogilvie. It licenses the bridge code, not IDA or Hex-Rays products. Its [README](https://github.com/mrexodia/ida-pro-mcp#prerequisites) requires IDA Pro and explicitly states that IDA Free is unsupported. Follow the prerequisites for the version you actually install. The bridge is separately supplied, not bundled by REA. |
| Hopper Disassembler | Proprietary Cryptic Apps software under its [license agreement](https://www.hopperapp.com/license_agreement.html). Personal and computer licenses have different permitted-user and installation rules; an official evaluation trial is available. REA setup may obtain the official installer after approval, but this grants no paid license or activation entitlement. |
| JEB | Proprietary PNF Software product under its [EULA](https://www.pnfsoftware.com/jeb/LICENSE.TXT) and edition-specific agreement. Check user/seat/server rights and whether your edition permits automation, backend/service integration, and disclosure of generated output. The published EULA restricts public or third-party disclosure of decompiled code, reconstructed artifacts, and analysis reports unless expressly permitted by the license type. |
The vendor's applicable agreement controls. These summaries do not interpret
your individual contract or expand the rights it grants. Verify permissions
before publishing analysis output, sharing tool access, or operating a service.
Vendor terms above were checked on 2026-10-12.
## Runtimes, development tooling, and fixtures
REA uses a caller-supplied Node.js runtime ([MIT and component notices](https://github.com/nodejs/node/blob/main/LICENSE)).
Optional workflows may also use Python ([PSF and component licenses](https://docs.python.org/3/license.html)),
OpenJDK ([GPLv2 with Classpath Exception and component notices](https://openjdk.org/legal/)),
Electron ([MIT and component notices](https://github.com/electron/electron/blob/main/LICENSE)),
or a Chromium-based browser ([BSD-3-Clause and third-party licenses](https://chromium.googlesource.com/chromium/src/+/main/LICENSE)).
These external installations are not licensed or bundled by REA's MIT license.
Development, test, and website npm packages are included in the locked inventory.
GitHub Actions, operating-system utilities, downloaded verification fixtures,
and their runtimes retain the terms supplied by their publishers. Fixture
identities and provenance are recorded under `scripts/fixtures/` and the
existing `third_party/` records; downloaded executable fixtures are not tracked
or included in the npm package. This notice does not claim to inventory every
dependency of a caller's operating system, agent, or separately installed tool.
## Maintaining and redistributing notices
Refresh the dependency inventory and runtime notice collection when the lockfile
changes. Review the actual versions and distributed contents, retain the
upstream license and copyright texts, and account for optional platform
packages. See [licenses/README.md](licenses/README.md) for source provenance,
collection scope, and known upstream notice gaps.
The npm package includes this document, the `licenses/` directory, and the
existing redistributed license records under `third_party/`. If you additionally
bundle an external engine or modify upstream code, review that actual
distribution and retain its full applicable licenses, copyright notices,
NOTICE files, modification notices, and source obligations. REA's inventory is
not a substitute for those obligations.
+123 -11
View File
@@ -3,8 +3,14 @@ import com.google.gson.*;
import io.modelcontextprotocol.kotlin.sdk.server.*;
import io.modelcontextprotocol.kotlin.sdk.types.*;
import jadx.api.*;
import jadx.api.plugins.input.data.ICodeReader;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.insns.InsnIndexType;
import jadx.api.plugins.input.insns.Opcode;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.nodes.*;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
@@ -79,11 +85,17 @@ class ReaJadxBridge {
ordered.sort(Comparator.comparing(MethodNode::getAlias));
List<MethodSnapshot> methods = ordered.stream().map(method -> {
String name = method.getAlias();
String shortId = method.getMethodInfo().getShortId();
String rawName = method.getMethodInfo().getName();
if (!shortId.startsWith(rawName + "("))
throw new IllegalStateException("JADX method ID has no raw-name prefix: " + shortId);
String descriptor = shortId.substring(rawName.length());
String signature = name + "(" + method.getArgTypes().stream()
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + method.getReturnType().toString();
return new MethodSnapshot(name, method.getMethodInfo().getShortId(), Map.of(
return new MethodSnapshot(name, shortId, Map.of(
"name", name, "signature", signature,
"dex_descriptor", descriptor,
"is_constructor", method.isConstructor(), "def_pos", 0));
}).toList();
List<Map<String, Object>> fields = node.getFields().stream()
@@ -126,6 +138,71 @@ class ReaJadxBridge {
return result;
}
private JadxSession.SmartCode methodBody(JadxSession current, ClassSnapshot cls,
MethodSnapshot selected, JavaMethod method) {
if (!selected.shortId().equals("<clinit>()V"))
return current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
// Java code generation can fold all or part of <clinit> into field declarations.
// Its method text cannot establish coverage, even when a static block remains.
JadxSession.MethodSmali smali = current.getMethodSmali(cls.node(), method.getName());
if (!smali.getFound() || smali.getBlocks().size() != 1)
throw new IllegalArgumentException("Static initializer disassembly requires one exact method: " + selected.shortId());
return new JadxSession.SmartCode(
TextUtilKt.truncateToBytes(smali.getBlocks().get(0), current.getMaxSourceBytes()),
"smali", true, List.of("static_initializer_disassembly"));
}
// The local transport admits 8 MiB frames. Reserve 1 KiB for the fixed
// JSON-RPC/CallToolResult envelope and generated numeric request id, and
// account for JSON text being escaped again inside TextContent.
private static final int MAX_XREF_TEXT_BYTES = 8 * 1024 * 1024 - 1024;
private static int escapedJsonBytes(Object value) {
return JSON.toJson(JSON.toJson(value)).getBytes(StandardCharsets.UTF_8).length;
}
private static final class InvokeBudget {
long remaining;
InvokeBudget(long remaining) { this.remaining = Math.max(0, remaining); }
boolean admit(Map<String, Object> site) {
// Check a conservative two-layer escape bound before serialization.
long bound = 256;
for (Object value : site.values())
if (value instanceof String text) bound += 12L * text.length();
if (bound > remaining) return false;
long bytes = escapedJsonBytes(site) - 2L + 1L;
if (bytes > remaining) return false;
remaining -= bytes;
return true;
}
}
private record DirectInvokes(List<Map<String, Object>> sites, boolean exhaustive) {}
/** Raw DEX invokes within one JADX using method; offsets are code units from its code start. */
private static DirectInvokes directInvokes(JavaMethod source, MethodInfo target, InvokeBudget budget) {
MethodNode node = source.getMethodNode();
ICodeReader code = node.getCodeReader();
if (code == null) return null;
List<Map<String, Object>> sites = new ArrayList<>();
boolean[] exhaustive = {true};
code.visitInstructions(insn -> {
insn.decode();
if (insn.getIndexType() != InsnIndexType.METHOD_REF) return;
Opcode opcode = insn.getOpcode();
if (!opcode.name().startsWith("INVOKE_")) return;
IMethodRef ref = insn.getIndexAsMethod();
ref.load();
if (!MethodInfo.fromRef(node.root(), ref).getRawFullId().equals(target.getRawFullId())) return;
Map<String, Object> site = Map.of(
"dex_entry", node.getInputFileName(),
"source_raw_method_id", node.getMethodInfo().getRawFullId(),
"dex_code_unit_offset", insn.getOffset(),
"dex_method_index", insn.getIndex(),
"opcode", opcode.name());
if (budget.admit(site)) sites.add(site);
else exhaustive[0] = false;
});
return new DirectInvokes(sites, exhaustive[0]);
}
private CallToolResult handle(String name, JsonObject args) {
if (name.equals("rea_jvm_status")) return json(Map.of(
"engine_reported_version", BuildInfo.VERSION,
@@ -150,7 +227,7 @@ class ReaJadxBridge {
int index = integer(args, "overload_index", 0);
if (index >= overloads.size()) throw new InputFailure("overload_index", "overload_index out of range: " + index);
JavaMethod method = method(cls, overloads.get(index));
JadxSession.SmartCode code = current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
JadxSession.SmartCode code = methodBody(current, cls, overloads.get(index), method);
return json(Map.of("class_name", cls.summary().get("full_name"),
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
"overload_index", index, "overload_count", overloads.size(),
@@ -159,17 +236,52 @@ class ReaJadxBridge {
}
List<JavaNode> uses;
String target = string(args, "class_name");
MethodSnapshot selectedMethod = null;
int overloadIndex = 0;
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
else {
List<MethodSnapshot> overloads = methods(cls, args);
if (overloads.size() != 1) throw new InputFailure("method_name", "Method references require a unique overload");
target += "." + overloads.get(0).name();
uses = method(cls, overloads.get(0)).getUseIn();
if (overloads.size() != 1 && !args.containsKey("overload_index"))
throw new InputFailure("overload_index", "Select one of " + overloads.size() + " method overloads");
overloadIndex = integer(args, "overload_index", 0);
if (overloadIndex >= overloads.size())
throw new InputFailure("overload_index", "overload_index out of range: " + overloadIndex);
selectedMethod = overloads.get(overloadIndex);
target += "." + selectedMethod.name();
uses = method(cls, selectedMethod).getUseIn();
}
// REA consumes all references without materializing source line information.
List<Map<String, Object>> items = uses.stream().map(node -> current.describeUsage(node, false)).toList();
return json(Map.of("target", target, "total", uses.size(), "count", items.size(),
"resolve_line", false, "items", items));
MethodInfo targetInfo = selectedMethod == null ? null : method(cls, selectedMethod).getMethodNode().getMethodInfo();
List<Map<String, Object>> items = uses.stream().map(node -> {
Map<String, Object> item = new LinkedHashMap<>(current.describeUsage(node, false));
if (targetInfo != null && node instanceof JavaMethod) {
item.put("direct_invokes", List.of());
item.put("direct_invokes_exhaustive", false);
}
return item;
}).toList();
Map<String, Object> result = new LinkedHashMap<>(Map.of("target", target,
"total", uses.size(), "count", items.size(), "resolve_line", false, "items", items));
if (selectedMethod != null) {
result.put("overload_index", overloadIndex);
result.put("reported_signature", selectedMethod.summary().get("signature"));
}
if (targetInfo != null) {
InvokeBudget budget = new InvokeBudget(MAX_XREF_TEXT_BYTES - escapedJsonBytes(result));
for (int index = 0; index < uses.size(); index++) {
if (!(uses.get(index) instanceof JavaMethod source)) continue;
DirectInvokes invokes = directInvokes(source, targetInfo, budget);
Map<String, Object> item = items.get(index);
if (invokes == null) {
item.remove("direct_invokes");
item.remove("direct_invokes_exhaustive");
} else {
item.put("direct_invokes", invokes.sites());
item.put("direct_invokes_exhaustive", invokes.exhaustive());
}
}
}
return json(result);
}
public static void main(String[] args) throws Exception {
@@ -203,7 +315,7 @@ class ReaJadxBridge {
List<String> fields = switch (name) {
case "list_classes" -> List.of("offset", "limit");
case "get_method_body" -> List.of("class_name", "method_name", "overload_index");
case "get_xrefs_to_method" -> List.of("class_name", "method_name");
case "get_xrefs_to_method" -> List.of("class_name", "method_name", "overload_index");
default -> List.of("class_name");
};
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
@@ -211,8 +323,8 @@ class ReaJadxBridge {
String description = switch (name) {
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
case "get_method_body" -> "Source for an exact class and stable same-name overload index, with smali fallback.";
default -> "Complete static references to an exact class or unique method; source lines are unresolved.";
case "get_method_body" -> "Source for an exact class and stable same-name overload index; static initializers use smali to preserve folded field initialization.";
default -> "Complete static using-node inventory for an exact class or selected method overload; one node may contain multiple invoke instructions, and source lines are unresolved.";
};
Tool narrowed = new Tool(name, new ToolSchema(null, new JsonObject(properties),
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
+816
View File
@@ -0,0 +1,816 @@
"""REA's experimental local bridge plugin for Cutter.
Install this file in Cutter's user plugin directory. The bridge is REA-owned
IPC, not an upstream Cutter transport. It binds to loopback and requires a
per-process random token stored in a user-only descriptor file.
"""
from __future__ import annotations
import json
import hashlib
import hmac
import os
import secrets
import socket
import stat
import tempfile
import threading
import time
import uuid
from pathlib import Path
from typing import Any
if os.name == "nt":
import ctypes
from ctypes import wintypes
import cutter
try:
from PySide6.QtCore import QObject, Signal
except ImportError:
from PySide2.QtCore import QObject, Signal
MAX_FRAME_BYTES = 16 * 1024 * 1024
SOCKET_TIMEOUT_SECONDS = 20
AUTH_FRAME_TIMEOUT_SECONDS = 1
MAX_DESCRIPTOR_BYTES = 64 * 1024
MAX_COMPLETION_RECEIPTS = 64
def _windows_api() -> tuple[Any, Any]:
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
kernel32.GetCurrentProcess.restype = wintypes.HANDLE
kernel32.LocalFree.argtypes = [wintypes.HLOCAL]
kernel32.LocalFree.restype = wintypes.HLOCAL
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
kernel32.CloseHandle.restype = wintypes.BOOL
kernel32.GetFileAttributesW.argtypes = [wintypes.LPCWSTR]
kernel32.GetFileAttributesW.restype = wintypes.DWORD
kernel32.CreateDirectoryW.argtypes = [wintypes.LPCWSTR, wintypes.LPVOID]
kernel32.CreateDirectoryW.restype = wintypes.BOOL
kernel32.CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD,
wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE]
kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID]
kernel32.WriteFile.restype = wintypes.BOOL
kernel32.FlushFileBuffers.argtypes = [wintypes.HANDLE]
kernel32.FlushFileBuffers.restype = wintypes.BOOL
kernel32.MoveFileExW.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD]
kernel32.MoveFileExW.restype = wintypes.BOOL
kernel32.DeleteFileW.argtypes = [wintypes.LPCWSTR]
kernel32.DeleteFileW.restype = wintypes.BOOL
advapi32.OpenProcessToken.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)]
advapi32.OpenProcessToken.restype = wintypes.BOOL
advapi32.GetTokenInformation.argtypes = [wintypes.HANDLE, ctypes.c_int, wintypes.LPVOID,
wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)]
advapi32.GetTokenInformation.restype = wintypes.BOOL
advapi32.ConvertSidToStringSidW.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.LPWSTR)]
advapi32.ConvertSidToStringSidW.restype = wintypes.BOOL
advapi32.GetNamedSecurityInfoW.argtypes = [wintypes.LPWSTR, ctypes.c_int, wintypes.DWORD,
ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
ctypes.POINTER(wintypes.LPVOID)]
advapi32.GetNamedSecurityInfoW.restype = wintypes.DWORD
advapi32.GetSecurityDescriptorControl.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.WORD),
ctypes.POINTER(wintypes.DWORD)]
advapi32.GetSecurityDescriptorControl.restype = wintypes.BOOL
advapi32.EqualSid.argtypes = [wintypes.LPVOID, wintypes.LPVOID]
advapi32.EqualSid.restype = wintypes.BOOL
advapi32.GetAclInformation.argtypes = [wintypes.LPVOID, wintypes.LPVOID, wintypes.DWORD, ctypes.c_int]
advapi32.GetAclInformation.restype = wintypes.BOOL
advapi32.GetAce.argtypes = [wintypes.LPVOID, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID)]
advapi32.GetAce.restype = wintypes.BOOL
advapi32.IsValidSid.argtypes = [wintypes.LPVOID]
advapi32.IsValidSid.restype = wintypes.BOOL
advapi32.GetLengthSid.argtypes = [wintypes.LPVOID]
advapi32.GetLengthSid.restype = wintypes.DWORD
advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.argtypes = [
wintypes.LPCWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.DWORD),
]
advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.restype = wintypes.BOOL
return kernel32, advapi32
def _windows_user_sid_text() -> str:
kernel32, advapi32 = _windows_api()
token = wintypes.HANDLE()
if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
raise ctypes.WinError(ctypes.get_last_error())
try:
required = wintypes.DWORD()
advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
if required.value == 0:
raise ctypes.WinError(ctypes.get_last_error())
buffer = ctypes.create_string_buffer(required.value)
if not advapi32.GetTokenInformation(token, 1, buffer, required, ctypes.byref(required)):
raise ctypes.WinError(ctypes.get_last_error())
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
class TOKEN_USER(ctypes.Structure):
_fields_ = [("User", SID_AND_ATTRIBUTES)]
sid = ctypes.cast(buffer, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
text = wintypes.LPWSTR()
if not advapi32.ConvertSidToStringSidW(sid, ctypes.byref(text)):
raise ctypes.WinError(ctypes.get_last_error())
try:
return text.value
finally:
kernel32.LocalFree(text)
finally:
kernel32.CloseHandle(token)
def _verify_windows_private_path(path: Path, directory: bool) -> None:
kernel32, advapi32 = _windows_api()
attributes = kernel32.GetFileAttributesW(str(path))
if attributes == 0xFFFFFFFF:
raise ctypes.WinError(ctypes.get_last_error())
if attributes & 0x400 or bool(attributes & 0x10) != directory:
raise RuntimeError("Cutter bridge path is a reparse point or has the wrong object type")
class ACL_SIZE_INFORMATION(ctypes.Structure):
_fields_ = [("AceCount", wintypes.DWORD), ("AclBytesInUse", wintypes.DWORD), ("AclBytesFree", wintypes.DWORD)]
class ACE_HEADER(ctypes.Structure):
_fields_ = [("AceType", wintypes.BYTE), ("AceFlags", wintypes.BYTE), ("AceSize", wintypes.WORD)]
class SID_HEADER(ctypes.Structure):
_fields_ = [("Revision", wintypes.BYTE), ("SubAuthorityCount", wintypes.BYTE),
("IdentifierAuthority", wintypes.BYTE * 6)]
class ACCESS_ALLOWED_ACE(ctypes.Structure):
_fields_ = [("Header", ACE_HEADER), ("Mask", wintypes.DWORD), ("SidStart", wintypes.DWORD)]
owner = wintypes.LPVOID()
dacl = wintypes.LPVOID()
descriptor = wintypes.LPVOID()
result = advapi32.GetNamedSecurityInfoW(
str(path), 1, 0x1 | 0x4, ctypes.byref(owner), None, ctypes.byref(dacl), None, ctypes.byref(descriptor),
)
if result != 0:
raise ctypes.WinError(result)
try:
control = wintypes.WORD()
revision = wintypes.DWORD()
if not advapi32.GetSecurityDescriptorControl(descriptor, ctypes.byref(control), ctypes.byref(revision)):
raise ctypes.WinError(ctypes.get_last_error())
if not control.value & 0x1000 or not dacl:
raise RuntimeError("Cutter bridge ACL must be present and protected from inheritance")
token = wintypes.HANDLE()
if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
raise ctypes.WinError(ctypes.get_last_error())
try:
required = wintypes.DWORD()
advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
token_user = ctypes.create_string_buffer(required.value)
if not advapi32.GetTokenInformation(token, 1, token_user, required, ctypes.byref(required)):
raise ctypes.WinError(ctypes.get_last_error())
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
class TOKEN_USER(ctypes.Structure):
_fields_ = [("User", SID_AND_ATTRIBUTES)]
current_sid = ctypes.cast(token_user, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
if not advapi32.EqualSid(owner, current_sid):
raise RuntimeError("Cutter bridge path owner does not match the current user")
finally:
kernel32.CloseHandle(token)
info = ACL_SIZE_INFORMATION()
if not advapi32.GetAclInformation(dacl, ctypes.byref(info), ctypes.sizeof(info), 2) or info.AceCount != 2:
raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
expected_user = _windows_user_sid_text()
user_allowed = False
system_allowed = False
for index in range(info.AceCount):
ace_pointer = wintypes.LPVOID()
if not advapi32.GetAce(dacl, index, ctypes.byref(ace_pointer)):
raise ctypes.WinError(ctypes.get_last_error())
header = ctypes.cast(ace_pointer, ctypes.POINTER(ACE_HEADER)).contents
if header.AceType != 0 or header.AceSize < ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD) + ctypes.sizeof(SID_HEADER):
raise RuntimeError("Cutter bridge ACL contains a malformed or unsupported ACE")
ace_address = ctypes.cast(ace_pointer, ctypes.c_void_p).value
if ace_address is None:
raise RuntimeError("Cutter bridge ACL returned a null ACE")
sid_pointer = ctypes.c_void_p(ace_address + ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD))
sid_header = ctypes.cast(sid_pointer, ctypes.POINTER(SID_HEADER)).contents
sid_size = ctypes.sizeof(SID_HEADER) + sid_header.SubAuthorityCount * ctypes.sizeof(wintypes.DWORD)
if sid_size > header.AceSize - ctypes.sizeof(ACE_HEADER) - ctypes.sizeof(wintypes.DWORD) or not advapi32.IsValidSid(sid_pointer) or advapi32.GetLengthSid(sid_pointer) != sid_size:
raise RuntimeError("Cutter bridge ACL contains an invalid SID")
ace = ctypes.cast(ace_pointer, ctypes.POINTER(ACCESS_ALLOWED_ACE)).contents
sid_text = wintypes.LPWSTR()
if ace.Mask != 0x001F01FF or ace.Header.AceFlags != 0x03:
raise RuntimeError("Cutter bridge ACL contains an unsupported ACE")
if not advapi32.ConvertSidToStringSidW(sid_pointer, ctypes.byref(sid_text)):
raise ctypes.WinError(ctypes.get_last_error())
try:
if sid_text.value == expected_user:
user_allowed = True
elif sid_text.value == "S-1-5-18":
system_allowed = True
else:
raise RuntimeError("Cutter bridge ACL allows an unrelated principal")
finally:
kernel32.LocalFree(sid_text)
if not user_allowed or not system_allowed:
raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
finally:
kernel32.LocalFree(descriptor)
def _windows_security_attributes() -> tuple[Any, Any]:
kernel32, advapi32 = _windows_api()
sid = _windows_user_sid_text()
sddl = f"O:{sid}G:{sid}D:P(A;OICI;FA;;;{sid})(A;OICI;FA;;;SY)"
descriptor = wintypes.LPVOID()
if not advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, 1, ctypes.byref(descriptor), None):
raise ctypes.WinError(ctypes.get_last_error())
class SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = [("nLength", wintypes.DWORD), ("lpSecurityDescriptor", wintypes.LPVOID), ("bInheritHandle", wintypes.BOOL)]
attributes = SECURITY_ATTRIBUTES(ctypes.sizeof(SECURITY_ATTRIBUTES), descriptor, False)
return kernel32, (descriptor, attributes)
def _ensure_windows_bridge_directory(directory: Path) -> None:
directory.parent.mkdir(parents=True, exist_ok=True)
if not directory.exists():
kernel32, (descriptor, attributes) = _windows_security_attributes()
try:
if not kernel32.CreateDirectoryW(str(directory), ctypes.byref(attributes)):
error = ctypes.get_last_error()
if error != 183:
raise ctypes.WinError(error)
finally:
kernel32.LocalFree(descriptor)
_verify_windows_private_path(directory, True)
def _ensure_posix_bridge_directory(directory: Path) -> None:
directory.mkdir(mode=0o700, parents=True, exist_ok=True)
if not hasattr(os, "O_NOFOLLOW") or not hasattr(os, "O_DIRECTORY"):
raise RuntimeError("Cutter bridge directory requires no-follow directory handles")
descriptor = os.open(directory, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
directory_info = os.fstat(descriptor)
if directory_info.st_uid != os.getuid():
raise RuntimeError("Cutter bridge directory must be owned by the current user")
os.fchmod(descriptor, 0o700)
directory_info = os.fstat(descriptor)
if stat.S_IMODE(directory_info.st_mode) != 0o700:
raise RuntimeError("Cutter bridge directory must have mode 0700")
finally:
os.close(descriptor)
def _write_windows_descriptor(directory: Path, destination: Path, payload: bytes) -> None:
if len(payload) > MAX_DESCRIPTOR_BYTES:
raise RuntimeError("Cutter bridge descriptor exceeds its byte limit")
kernel32, (security_descriptor, attributes) = _windows_security_attributes()
temporary = directory / f".{destination.name}.{uuid.uuid4().hex}.tmp"
handle = wintypes.HANDLE()
try:
handle = kernel32.CreateFileW(str(temporary), 0x40000000 | 0x00020000 | 0x00040000,
0x1 | 0x2 | 0x4, ctypes.byref(attributes), 1, 0x80, None)
if handle == wintypes.HANDLE(-1).value:
raise ctypes.WinError(ctypes.get_last_error())
_verify_windows_private_path(temporary, False)
if payload:
data = ctypes.create_string_buffer(payload)
written = wintypes.DWORD()
if not kernel32.WriteFile(handle, data, len(payload), ctypes.byref(written), None) or written.value != len(payload):
raise ctypes.WinError(ctypes.get_last_error())
if not kernel32.FlushFileBuffers(handle):
raise ctypes.WinError(ctypes.get_last_error())
kernel32.CloseHandle(handle)
handle = wintypes.HANDLE()
if not kernel32.MoveFileExW(str(temporary), str(destination), 0x1 | 0x8):
raise ctypes.WinError(ctypes.get_last_error())
finally:
if handle and handle != wintypes.HANDLE(-1).value:
kernel32.CloseHandle(handle)
kernel32.DeleteFileW(str(temporary))
kernel32.LocalFree(security_descriptor)
def _bridge_directory() -> Path:
configured = os.environ.get("REA_CUTTER_BRIDGE_DIR")
if configured:
return Path(configured).expanduser()
local_app_data = os.environ.get("LOCALAPPDATA")
if os.name == "nt" and local_app_data:
return Path(local_app_data) / "REA" / "CutterBridge"
return Path.home() / ".cache" / "rea" / "cutter-bridge"
class _RequestDispatcher(QObject):
dispatch = Signal(object, object)
def __init__(self) -> None:
super().__init__()
self.dispatch.connect(self._handle)
self.generation = 0
self.completed_commands: dict[str, str] = {}
self._receipt_lock = threading.Lock()
self.current_file = self._read_current_file()
try:
self.cutter_version = str(cutter.core().getVersionInformation())
except Exception:
self.cutter_version = None
@staticmethod
def _read_current_file() -> str | None:
try:
value = cutter.core().currentlyOpenFile()
return str(value) if value else None
except Exception:
return None
def _handle(self, request: dict[str, Any], reply: dict[str, Any]) -> None:
kind = request.get("kind")
request_id = request.get("request_id")
if kind in ("command", "ack"):
try:
valid_request_id = (
isinstance(request_id, str)
and str(uuid.UUID(request_id)) == request_id.lower()
)
except (ValueError, AttributeError):
valid_request_id = False
if not valid_request_id:
reply.update({
"ok": False,
"error": "invalid-command-request-id",
"execution_state": "not_started",
})
request["_event"].set()
return
if kind == "ack":
with self._receipt_lock:
acknowledged = self.completed_commands.pop(request_id, None) is not None
reply.update({"ok": True, "acknowledged": acknowledged})
request["_event"].set()
return
if request["_cancelled"].is_set():
reply.update({
"ok": False,
"error": "request-cancelled-before-execution",
"execution_state": "not_started",
})
if kind == "command":
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
self._refresh_document_identity()
if kind == "status":
with self._receipt_lock:
completed_commands = [
{"request_id": receipt_id, "execution_state": state}
for receipt_id, state in self.completed_commands.items()
]
reply.update({
"ok": True,
"session_id": request.get("session_id"),
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"completed_commands": completed_commands,
})
request["_event"].set()
return
if kind != "command":
reply.update({"ok": False, "error": "unsupported-request-kind"})
request["_event"].set()
return
if request.get("expected_generation") != self.generation:
reply.update({
"ok": False,
"error": "active-document-generation-changed",
"execution_state": "not_started",
})
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
command = request.get("command")
if not isinstance(command, str) or not command.strip():
reply.update({
"ok": False,
"error": "invalid-command",
"execution_state": "not_started",
})
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
reply["execution_state"] = "running"
try:
output = cutter.cmdj(command) if request.get("json") is True else cutter.cmd(command)
command_error = None
except Exception as error:
output = None
command_error = str(error)
self._refresh_document_identity()
if command_error is None:
reply.update({
"ok": True,
"output": output,
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"execution_state": "complete",
})
self._record_command_completion(request_id, "complete")
else:
reply.update({
"ok": False,
"error": command_error,
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"execution_state": "unknown",
"message": "The command raised an exception; it may have produced partial effects. Do not retry automatically.",
})
self._record_command_completion(request_id, "failed")
request["_event"].set()
def _record_command_completion(
self, request_id: str, execution_state: str
) -> None:
with self._receipt_lock:
if request_id in self.completed_commands or len(self.completed_commands) < MAX_COMPLETION_RECEIPTS:
self.completed_commands[request_id] = execution_state
def can_accept_command(self) -> bool:
with self._receipt_lock:
return len(self.completed_commands) < MAX_COMPLETION_RECEIPTS
def _refresh_document_identity(self) -> None:
current_file = self._read_current_file()
if current_file != self.current_file:
self.current_file = current_file
self.generation += 1
class _BridgeServer(threading.Thread):
def __init__(self, dispatcher: _RequestDispatcher) -> None:
super().__init__(name="rea-cutter-bridge", daemon=True)
self.dispatcher = dispatcher
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(("127.0.0.1", 0))
self.server.listen(8)
self.server.settimeout(0.5)
self.port = self.server.getsockname()[1]
self.session_id = str(uuid.uuid4())
self.token = secrets.token_urlsafe(32)
self.poisoned = False
self.connection_ordinal = 0
self.directory = _bridge_directory()
self.descriptor = self.directory / f"cutter-{os.getpid()}-{self.session_id}.json"
def publish(self) -> None:
if os.name == "nt":
_ensure_windows_bridge_directory(self.directory)
else:
_ensure_posix_bridge_directory(self.directory)
if os.name == "nt" and (self.directory.is_symlink() or not self.directory.is_dir()):
raise RuntimeError("Cutter bridge directory must be a real directory")
payload = {
"session_id": self.session_id,
"pid": os.getpid(),
"host": "127.0.0.1",
"port": self.port,
"token": self.token,
"document_generation": self.dispatcher.generation,
"current_file": self.dispatcher.current_file,
"cutter_version": self.dispatcher.cutter_version,
"identity_status": "partial",
}
if os.name == "nt":
_write_windows_descriptor(self.directory, self.descriptor, json.dumps(payload).encode("utf-8"))
return
descriptor, temporary_path = tempfile.mkstemp(prefix=f".{self.descriptor.name}.", dir=self.directory)
try:
os.fchmod(descriptor, 0o600)
descriptor_info = os.fstat(descriptor)
if descriptor_info.st_uid != os.getuid() or stat.S_IMODE(descriptor_info.st_mode) & 0o077:
raise RuntimeError("Cutter bridge descriptor must be owned by the current user with mode 0600")
with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
stream.write(json.dumps(payload))
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary_path, self.descriptor)
except Exception:
try:
os.close(descriptor)
except OSError:
pass
try:
os.unlink(temporary_path)
except OSError:
pass
raise
def run(self) -> None:
try:
self.publish()
except Exception:
self.server.close()
return
while True:
try:
connection, _ = self.server.accept()
except socket.timeout:
if getattr(self, "stopping", False):
break
continue
except OSError:
break
self.connection_ordinal += 1
connection_ordinal = self.connection_ordinal
with connection:
connection.settimeout(AUTH_FRAME_TIMEOUT_SECONDS)
authenticated = False
try:
hello = self._receive_frame(connection)
if not isinstance(hello, dict) or hello.get("kind") != "rea-auth-v2" or not isinstance(hello.get("nonce"), str) or len(hello["nonce"]) != 64:
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
client_nonce = hello["nonce"]
server_nonce = f"{connection_ordinal:016x}{secrets.token_hex(24)}"
server_proof = hmac.new(self.token.encode(), f"rea-cutter-server-v2:{self.session_id}:{client_nonce}:{server_nonce}".encode(), hashlib.sha256).hexdigest()
self._send(connection, {"session_id": self.session_id, "nonce": server_nonce, "proof": server_proof})
authenticated_frame = self._receive_frame(connection)
request_bytes = authenticated_frame.get("request") if isinstance(authenticated_frame, dict) else None
expected_proof = hmac.new(self.token.encode(), f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:{request_bytes}".encode(), hashlib.sha256).hexdigest()
if not isinstance(request_bytes, str) or not isinstance(authenticated_frame.get("proof"), str) or not hmac.compare_digest(authenticated_frame["proof"], expected_proof):
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
request = json.loads(request_bytes)
if not isinstance(request, dict) or request.get("session_id") != self.session_id:
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
authenticated = True
connection.settimeout(SOCKET_TIMEOUT_SECONDS)
if self.poisoned and request.get("kind") not in ("status", "ack"):
self._send_authenticated(connection, {
"ok": False,
"error": "session-poisoned-after-ui-timeout",
"execution_state": "not_started",
}, client_nonce, server_nonce)
continue
if (
request.get("kind") == "command"
and not self.dispatcher.can_accept_command()
):
self._send_authenticated(connection, {
"ok": False,
"error": "completion-receipt-capacity-exceeded",
"execution_state": "not_started",
}, client_nonce, server_nonce)
continue
request["session_id"] = self.session_id
event = threading.Event()
cancelled = threading.Event()
reply: dict[str, Any] = {}
request["_event"] = event
request["_cancelled"] = cancelled
self.dispatcher.dispatch.emit(request, reply)
if not event.wait(SOCKET_TIMEOUT_SECONDS):
cancelled.set()
self.poisoned = True
self._send_authenticated(connection, {
"ok": False,
"error": "ui-thread-request-timeout",
"execution_state": "unknown",
"message": "The UI operation may still complete after this timeout; restart Cutter before issuing further commands.",
}, client_nonce, server_nonce)
else:
if request.get("kind") == "status" and reply.get("ok") is True:
reply["completed_connection"] = f"{connection_ordinal - 1:016x}"
self._send_authenticated(connection, reply, client_nonce, server_nonce)
except socket.timeout:
if authenticated:
self._send_authenticated(connection, {
"ok": False,
"error": "request-timeout",
"execution_state": "unknown",
"message": "The command may have produced partial or persistent effects. Do not retry automatically.",
}, client_nonce, server_nonce)
except Exception as error:
self._send(connection, {"ok": False, "error": str(error)})
self.server.close()
try:
self.descriptor.unlink(missing_ok=True)
except OSError:
pass
@staticmethod
def _receive_frame(connection: socket.socket) -> dict[str, Any]:
data = bytearray()
deadline = time.monotonic() + AUTH_FRAME_TIMEOUT_SECONDS
while b"\n" not in data:
if len(data) >= MAX_FRAME_BYTES:
raise ValueError("frame-limit-exceeded")
remaining = deadline - time.monotonic()
if remaining <= 0:
raise socket.timeout("authentication frame timed out")
connection.settimeout(remaining)
part = connection.recv(min(65536, MAX_FRAME_BYTES + 1 - len(data)))
if not part:
raise ValueError("incomplete authentication frame")
data.extend(part)
line, remainder = bytes(data).split(b"\n", 1)
if len(line) > MAX_FRAME_BYTES or remainder:
raise ValueError("invalid framed request")
value = json.loads(line)
return value if isinstance(value, dict) else {}
def _send_authenticated(self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str) -> None:
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
value = self._truncated_response(value)
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
# This fixed-size fallback is checked through the same signed framing path.
value = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output": None,
"output_truncated": True,
"message": "The result exceeded the bridge frame limit; command completion state is preserved.",
}
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
return
try:
connection.sendall(encoded)
except OSError:
pass
def _encode_authenticated_response(self, value: dict[str, Any], client_nonce: str, server_nonce: str) -> bytes | None:
if not _within_response_limit(value):
return None
prefix = b'{"response":"'
suffix = b'","proof":"' + (b"0" * 64) + b'"}\n'
frame_size = len(prefix) + len(suffix)
response_parts: list[bytes] = []
escaped_parts: list[bytes] = []
for fragment in json.JSONEncoder(ensure_ascii=False, separators=(",", ":")).iterencode(value):
raw = fragment.encode("utf-8")
escaped = json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
frame_size += len(escaped)
if frame_size > MAX_FRAME_BYTES:
return None
response_parts.append(raw)
escaped_parts.append(escaped)
proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
proof.update(f"rea-cutter-response-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode())
for part in response_parts:
proof.update(part)
return prefix + b"".join(escaped_parts) + b'","proof":"' + proof.hexdigest().encode() + b'"}\n'
@staticmethod
def _truncated_response(value: dict[str, Any]) -> dict[str, Any]:
response: dict[str, Any] = {
"ok": value.get("ok") is True,
"output": None,
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command result exceeded the bridge frame limit; completion state is preserved and output was omitted.",
}
for key in ("current_file", "document_generation", "cutter_version", "identity_status"):
if key in value:
response[key] = value[key]
if value.get("ok") is not True and isinstance(value.get("error"), str):
response["error"] = value["error"]
return response
@staticmethod
def _send(connection: socket.socket, value: dict[str, Any]) -> None:
if not _within_response_limit(value):
value = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
}
parts: list[bytes] = []
encoded_size = 0
for part in json.JSONEncoder(ensure_ascii=False).iterencode(value):
encoded_part = part.encode("utf-8")
encoded_size += len(encoded_part)
if encoded_size > MAX_FRAME_BYTES:
fallback = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
}
parts = [json.dumps(fallback, separators=(",", ":")).encode("utf-8")]
break
parts.append(encoded_part)
encoded = b"".join(parts)
try:
connection.sendall(encoded + b"\n")
except OSError:
# A disconnected caller must not terminate the bridge thread or
# prevent later commands from being served.
pass
def stop(self) -> None:
self.stopping = True
try:
self.server.close()
except OSError:
pass
class ReaCutterPlugin(cutter.CutterPlugin):
name = "REA Local Bridge"
description = "Local REA analysis bridge for the active Cutter session"
version = "1.0"
author = "REA"
def __init__(self) -> None:
super().__init__()
self._dispatcher: _RequestDispatcher | None = None
self._bridge: _BridgeServer | None = None
def setupPlugin(self) -> None:
return
def setupInterface(self, main: Any) -> None:
self._dispatcher = _RequestDispatcher()
self._bridge = _BridgeServer(self._dispatcher)
self._bridge.start()
def terminate(self) -> None:
if self._bridge is not None:
self._bridge.stop()
self._bridge.join(timeout=2)
self._bridge = None
self._dispatcher = None
def create_cutter_plugin() -> ReaCutterPlugin:
return ReaCutterPlugin()
def _within_response_limit(value: Any) -> bool:
pending = [value]
seen: set[int] = set()
size = 0
while pending:
current = pending.pop()
if isinstance(current, str):
size += 2
if current.isascii():
size += len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
else:
size += 4 * len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
elif isinstance(current, dict):
identity = id(current)
if identity in seen:
return False
seen.add(identity)
size += 2 + 4 * len(current)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current.keys())
pending.extend(current.values())
elif isinstance(current, (list, tuple)):
identity = id(current)
if identity in seen:
return False
seen.add(identity)
size += 2 + 2 * len(current)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current)
else:
size += 16
if size > MAX_FRAME_BYTES:
return False
return True
@@ -0,0 +1,38 @@
import java.util.Map;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.util.headless.HeadlessScript;
/** Disable the named auto-analyzers before default auto-analysis. */
public final class ReaGhidraAnalysisOptions extends HeadlessScript {
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
if (arguments.length != 1 || arguments[0].isBlank()) {
throw new IllegalArgumentException(
"ReaGhidraAnalysisOptions requires one comma-separated analyzer-name argument");
}
String[] names = arguments[0].split(",");
AutoAnalysisManager analysis = AutoAnalysisManager.getAnalysisManager(currentProgram);
Map<String, String> options = getCurrentAnalysisOptionsAndValues(currentProgram);
for (String requestedName : names) {
monitor.checkCancelled();
String name = requestedName.trim();
if (analysis.getAnalyzer(name) == null || !options.containsKey(name)) {
throw new IllegalArgumentException("Unknown Ghidra analyzer for this program: " + name);
}
}
for (String requestedName : names) {
monitor.checkCancelled();
setAnalysisOption(currentProgram, requestedName.trim(), "false");
}
options = getCurrentAnalysisOptionsAndValues(currentProgram);
for (String requestedName : names) {
monitor.checkCancelled();
String name = requestedName.trim();
if (!"false".equals(options.get(name))) {
throw new IllegalStateException("Ghidra analyzer remained enabled: " + name);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
// Compile the no-op activator with REA's scripts. Otherwise Ghidra generates
// one afterward and opens a second Java compiler and file manager.
public final class ReaGhidraBundleActivator implements BundleActivator {
@Override
public void start(BundleContext context) {
}
@Override
public void stop(BundleContext context) {
}
}
+176
View File
@@ -0,0 +1,176 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.data.AbstractFloatDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DoubleDataType;
import ghidra.program.model.data.FloatDataType;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.data.IntegerDataType;
import ghidra.program.model.data.ParameterDefinition;
import ghidra.program.model.data.TypeDef;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.pcode.XmlEncode;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* Soft-float o32 passes and returns float/double values exactly where same-size integers go.
* Ghidra's MIPS32 compiler specifications model only hard-float o32, so every typed float
* prototype (functions, externals, thunks via their targets, and function-pointer definitions)
* would otherwise read $f12/$f14/$f0. Derive a program-specific prototype model from the
* program's own default o32 model with its floating-point entries and rules removed, install it
* as a specification extension, and assign it to every callable whose formal types use floats.
*/
public final class ReaGhidraMipsSoftFloat extends GhidraScript {
static final String REPORT_KEY = "REA MIPS soft-float storage";
static final String POLICY = "o32-soft-float-prototype-v1";
static final String PROTOTYPE = "__rea_soft_float";
private static final Pattern FPU_REGISTER = Pattern.compile("f\\d+(_\\d+)?");
@Override
public void run() throws Exception {
String processor = currentProgram.getLanguage().getProcessor().toString();
if (!processor.equals("MIPS") || currentProgram.getDefaultPointerSize() != 4)
throw new IllegalStateException(
"REA soft-float storage requires a 32-bit MIPS program, not " + processor);
CompilerSpec spec = currentProgram.getCompilerSpec();
PrototypeModel base = spec.getDefaultCallingConvention();
new SpecExtension(currentProgram)
.addReplaceCompilerSpecExtension(softFloatPrototype(base, spec), monitor);
PrototypeModel soft = currentProgram.getCompilerSpec().getCallingConvention(PROTOTYPE);
if (soft == null)
throw new IllegalStateException("REA soft-float prototype was not installed");
requireIntegerStorage(
soft.getStorageLocations(
currentProgram,
new DataType[] {
FloatDataType.dataType, IntegerDataType.dataType, DoubleDataType.dataType,
FloatDataType.dataType, DoubleDataType.dataType
},
false),
"soft-float prototype self-check");
int functions = 0;
List<Function> callables = new ArrayList<>();
currentProgram.getFunctionManager().getFunctions(true).forEach(callables::add);
currentProgram.getFunctionManager().getExternalFunctions().forEach(callables::add);
for (Function function : callables) {
// A thunk's signature is its target's; the target is in this list.
if (function.isThunk() || !usesFloat(function)) continue;
if (function.hasCustomVariableStorage()) function.setCustomVariableStorage(false);
function.setCallingConvention(PROTOTYPE);
List<VariableStorage> storage = new ArrayList<>();
storage.add(function.getReturn().getVariableStorage());
for (Parameter parameter : function.getParameters()) storage.add(parameter.getVariableStorage());
requireIntegerStorage(storage.toArray(VariableStorage[]::new), function.getName(true));
functions += 1;
}
int definitions = 0;
Iterator<DataType> types = currentProgram.getDataTypeManager().getAllDataTypes();
while (types.hasNext()) {
if (!(types.next() instanceof FunctionDefinition definition) || !usesFloat(definition))
continue;
definition.setCallingConvention(PROTOTYPE);
definitions += 1;
}
// Written last: any failure above leaves no report, and REA refuses the session.
currentProgram.getOptions(Program.PROGRAM_INFO).setString(
REPORT_KEY,
"{\"policy\":\"" + POLICY + "\",\"functions\":" + functions +
",\"definitions\":" + definitions + "}");
println("REA MIPS soft-float prototype: functions=" + functions + " definitions=" + definitions);
}
/** The default o32 model without floating-point registers or float-consuming rules. */
private static String softFloatPrototype(PrototypeModel base, CompilerSpec spec) throws Exception {
XmlEncode encoder = new XmlEncode();
base.encode(encoder, spec.getPcodeInjectLibrary());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Document document =
factory.newDocumentBuilder().parse(new InputSource(new StringReader(encoder.toString())));
Element root = document.getDocumentElement();
if (!root.getTagName().equals("prototype"))
throw new IllegalStateException("Unexpected default prototype encoding " + root.getTagName());
root.setAttribute("name", PROTOTYPE);
List<Node> removed = new ArrayList<>();
collect(root.getElementsByTagName("pentry"), "metatype", "float", null, removed);
collect(root.getElementsByTagName("consume"), "storage", "float", "rule", removed);
if (removed.isEmpty())
throw new IllegalStateException("Default prototype has no floating-point entries to remove");
for (Node node : removed) node.getParentNode().removeChild(node);
StringWriter xml = new StringWriter();
var transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(document), new StreamResult(xml));
return xml.toString();
}
/** Select matching elements, or their nearest `ancestor` element when one is named. */
private static void collect(
NodeList nodes, String attribute, String value, String ancestor, List<Node> selected) {
for (int index = 0; index < nodes.getLength(); index += 1) {
Element element = (Element) nodes.item(index);
if (!value.equals(element.getAttribute(attribute))) continue;
Node target = element;
while (ancestor != null && target != null && !ancestor.equals(target.getNodeName()))
target = target.getParentNode();
if (target != null && !selected.contains(target)) selected.add(target);
}
}
private static void requireIntegerStorage(VariableStorage[] storage, String owner) {
for (VariableStorage item : storage) {
if (item.isBadStorage() || item.isUnassignedStorage())
throw new IllegalStateException("REA soft-float storage unresolved for " + owner);
List<Register> registers = item.getRegisters();
if (registers == null) continue; // stack-only storage
for (Register register : registers) {
if (FPU_REGISTER.matcher(register.getName()).matches())
throw new IllegalStateException(
"REA soft-float storage still uses " + register.getName() + " for " + owner);
}
}
}
private static boolean usesFloat(Function function) {
if (isFloat(function.getReturn().getFormalDataType())) return true;
for (Parameter parameter : function.getParameters()) {
if (isFloat(parameter.getFormalDataType())) return true;
}
return false;
}
private static boolean usesFloat(FunctionDefinition definition) {
if (isFloat(definition.getReturnType())) return true;
for (ParameterDefinition argument : definition.getArguments()) {
if (isFloat(argument.getDataType())) return true;
}
return false;
}
private static boolean isFloat(DataType type) {
DataType current = type;
while (current instanceof TypeDef typeDef) current = typeDef.getBaseDataType();
return current instanceof AbstractFloatDataType;
}
}
+49
View File
@@ -0,0 +1,49 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
/** Seed the caller-supplied raw-binary entry before default auto-analysis. */
public final class ReaGhidraPrepareRaw extends GhidraScript {
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
if (arguments.length != 2) {
throw new IllegalArgumentException(
"Raw binary preparation requires the committed language and entry address"
);
}
String expectedLanguage = arguments[0];
String entryHex = arguments[1];
if (!currentProgram.getLanguageID().getIdAsString().equals(expectedLanguage) ||
!currentProgram.getExecutableFormat().equals("Raw Binary")) {
throw new IllegalArgumentException(
"Raw binary preparation requires the committed language and BinaryLoader import"
);
}
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(entryHex);
MemoryBlock block = entry == null ? null : currentProgram.getMemory().getBlock(entry);
boolean fileStart = false;
if (block != null) {
// Processor-defined zero page/stack blocks can precede the imported file.
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
if (source.getFileBytes().isPresent() && source.getFileBytesOffset() == 0 &&
entry.equals(source.getMinAddress())) {
fileStart = true;
break;
}
}
}
if (!fileStart) {
throw new IllegalArgumentException(
"Raw binary import must map file offset 0 at the caller-supplied entry"
);
}
addEntryPoint(entry);
if (!disassemble(entry) || createFunction(entry, "entry") == null) {
throw new IllegalStateException("Raw binary entry cannot be decoded as a function");
}
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA raw binary prepared", true);
}
}
+97 -68
View File
@@ -644,7 +644,7 @@ def _analyze_function(document, params):
"end": _hex(_basic_block_end(procedure, block)),
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
})
pseudo = _pseudocode(document, procedure) or ""
pseudo = _pseudocode(document, procedure)
assembly_lines = _assembly(procedure).splitlines()
callers = sorted((_procedure_identity(item) for item in procedure.getAllCallerProcedures()), key=lambda item: int(item["address"], 16))
callees = sorted((_procedure_identity(item) for item in procedure.getAllCalleeProcedures()), key=lambda item: int(item["address"], 16))
@@ -706,7 +706,7 @@ def _analyze_function(document, params):
"signature": procedure.signatureString(),
"locals": _procedure_locals(procedure),
},
"pseudocode": pseudo,
"pseudocode": pseudo or "",
"assembly": assembly_lines,
"comments": comments,
"callers": callers, "callees": callees,
@@ -718,6 +718,12 @@ def _analyze_function(document, params):
"basic_blocks": blocks,
"native_api": None,
"native_value_flow": None,
"facet_availability": {
"pseudocode": {"available": True} if pseudo is not None else _unavailable("Hopper did not supply pseudocode."),
**{facet: {"available": True} for facet in (
"assembly", "comments", "callers", "callees", "references", "strings_names", "cfg"
)},
},
"limitations": [
"Native CallReference classifications are observed where available; detailed reference flags remain unknown.",
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
@@ -1016,73 +1022,87 @@ def _dispatch(method, params):
def _serve_connection(connection):
"""Serve one capability-authenticated NDJSON connection."""
file = connection.makefile("rwb")
while True:
line = file.readline()
if not line:
break
request_id = None
authenticated = False
should_stop = False
try:
should_stop = False
try:
while True:
line = file.readline()
if not line:
break
request_id = None
authenticated = False
should_stop = False
try:
request = json.loads(line.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as error:
raise InvalidRequestError("Invalid bridge request JSON") from error
if not isinstance(request, dict) or set(request) != {"id", "token", "method", "params"}:
raise InvalidRequestError("Invalid bridge request shape")
request_id = request["id"]
if isinstance(request_id, bool) or not isinstance(request_id, int) or request_id < 0:
raise InvalidRequestError("Invalid bridge request id")
if (
not isinstance(request["method"], str) or not request["method"]
or not isinstance(request["params"], dict)
):
raise InvalidRequestError("Invalid bridge method or parameters")
if (
not isinstance(request["token"], str) or not request["token"].isascii()
or not hmac.compare_digest(request["token"], REA_TOKEN)
):
raise PermissionError("Invalid bridge capability")
authenticated = True
_write_message(file, {"id": request_id, "event": {
"type": "progress",
"phase": "hopper_bridge",
"completed": 0,
"total": 1,
"message": "Hopper bridge started request",
}})
result = _dispatch(request["method"], request["params"])
should_stop = request["method"] == "shutdown_document" or (
request["method"] == "shutdown" and not result.get("cleanup_required", False)
)
safe_result = _json_safe(result)
_write_message(file, {"id": request_id, "event": {
"type": "progress",
"phase": "hopper_bridge",
"completed": 1,
"total": 1,
"message": "Hopper bridge completed request",
"terminal": True,
}})
response = {"id": request_id, "result": safe_result}
except Exception as error:
diagnostic = _safe_diagnostic(error)
if authenticated:
try:
request = json.loads(line.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as error:
raise InvalidRequestError("Invalid bridge request JSON") from error
if not isinstance(request, dict) or set(request) != {"id", "token", "method", "params"}:
raise InvalidRequestError("Invalid bridge request shape")
request_id = request["id"]
if isinstance(request_id, bool) or not isinstance(request_id, int) or request_id < 0:
raise InvalidRequestError("Invalid bridge request id")
if (
not isinstance(request["method"], str) or not request["method"]
or not isinstance(request["params"], dict)
):
raise InvalidRequestError("Invalid bridge method or parameters")
if (
not isinstance(request["token"], str) or not request["token"].isascii()
or not hmac.compare_digest(request["token"], REA_TOKEN)
):
raise PermissionError("Invalid bridge capability")
authenticated = True
_write_message(file, {"id": request_id, "event": {
"type": "diagnostic",
"error": diagnostic,
"type": "progress",
"phase": "hopper_bridge",
"completed": 0,
"total": 1,
"message": "Hopper bridge started request",
}})
response_id = request_id if (
isinstance(request_id, int)
and not isinstance(request_id, bool)
and request_id >= 0
) else 0
response = {"id": response_id, "error": diagnostic}
_write_message(file, response)
if should_stop:
break
file.close()
connection.close()
result = _dispatch(request["method"], request["params"])
should_stop = request["method"] in ("shutdown", "shutdown_document") and (
result.get("shutdown") is True and (
(result.get("document_closed") is True and result.get("analysis_stopped") is True)
or (result.get("document_closed") is False and result.get("document_retained") is True)
)
)
safe_result = _json_safe(result)
_write_message(file, {"id": request_id, "event": {
"type": "progress",
"phase": "hopper_bridge",
"completed": 1,
"total": 1,
"message": "Hopper bridge completed request",
"terminal": True,
}})
response = {"id": request_id, "result": safe_result}
except Exception as error:
diagnostic = _safe_diagnostic(error)
if authenticated:
_write_message(file, {"id": request_id, "event": {
"type": "diagnostic",
"error": diagnostic,
}})
response_id = request_id if (
isinstance(request_id, int)
and not isinstance(request_id, bool)
and request_id >= 0
) else 0
response = {"id": response_id, "error": diagnostic}
_write_message(file, response)
if should_stop:
break
except (ConnectionError, OSError):
# A lost client cannot confirm document closure or revoke this run.
pass
finally:
try:
file.close()
except OSError:
# Closing a failed writer can repeat the same disconnected-pipe error.
pass
connection.close()
return should_stop
def _write_message(file, message):
@@ -1124,8 +1144,17 @@ def _run():
os.chmod(REA_SOCKET, 0o600)
server.listen(1)
try:
connection, _ = server.accept()
_serve_connection(connection)
while True:
try:
connection, _ = server.accept()
except socket.timeout:
break
connection.settimeout(None)
if _serve_connection(connection):
break
# Match REA's cleanup handshake budget without pinning Hopper's
# Python thread indefinitely after a client disappears.
server.settimeout(REA_RECONNECT_TIMEOUT_SECONDS)
finally:
server.close()
if os.path.exists(REA_SOCKET):
+1 -1
View File
@@ -93,7 +93,7 @@ def project_record(state, secrets):
safe_bytes, credential_url = transport_value(value, path, transport_url)
content_base64 = base64.b64encode(value).decode("ascii")
digest = hashlib.sha256(value).hexdigest()
hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest for secret in secrets)
hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest or secret in str(len(value)) for secret in secrets)
safe_text = None
try:
safe_text = safe_bytes.decode("utf-8", errors="strict")
+76 -34
View File
@@ -112,6 +112,11 @@ enum RunTokenReadError: Error {
case ambiguousEnvironmentBoundary
}
struct ParsedRunToken {
let runId: String?
let environmentEntryCount: Int
}
@_silgen_name("csops")
private func csops(
_ pid: Int32,
@@ -122,19 +127,67 @@ private func csops(
private let csOpsStatus: UInt32 = 0
private let csPlatformBinary: UInt32 = 0x0400_0000
private let csRestrict: UInt32 = 0x0000_0800
/**
* Whether the target image is an Apple platform binary. Current macOS omits
* the environment of platform binaries from KERN_PROCARGS2 for every caller,
* so this distinguishes a policy-withheld environment from a process whose
* environment is genuinely absent or transiently unreadable.
*/
func isPlatformBinary(pid: Int32) -> Bool {
/** Read the target's current code-signing flags when csops permits inspection. */
private func codeSigningFlags(pid: Int32) -> UInt32? {
var flags: UInt32 = 0
let result = withUnsafeMutablePointer(to: &flags) { pointer in
csops(pid, csOpsStatus, UnsafeMutableRawPointer(pointer), MemoryLayout<UInt32>.size)
}
return result == 0 && (flags & csPlatformBinary) != 0
return result == 0 ? flags : nil
}
/** Derive one process-token observation from parser facts and native flags. */
func processRunTokenObservation(
pid: Int32,
parse: Result<ParsedRunToken, RunTokenReadError>,
codeSigningFlags: () -> UInt32?
) -> RunTokenObservation {
switch parse {
case .success(let parsed):
if let runId = parsed.runId {
return RunTokenObservation(pid: pid, state: "readable", run_id: runId, reason: nil)
}
// A non-empty environment without REA's token proves this candidate is
// unrelated. CS_RESTRICT cannot turn that readable fact into uncertainty.
if parsed.environmentEntryCount > 0 {
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
}
let flags = codeSigningFlags()
if let flags, (flags & csPlatformBinary) != 0 {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
if let flags, (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
case .failure(let failure):
switch failure {
case .environmentUnavailable:
let flags = codeSigningFlags()
if let flags, (flags & csPlatformBinary) != 0 {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
if let flags, (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return unavailable(pid: pid, reason: "environment_unavailable")
case .ambiguousEnvironmentBoundary:
if let flags = codeSigningFlags(), (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
case .duplicateToken:
return unavailable(pid: pid, reason: "duplicate_run_token")
case .invalidTokenEncoding:
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
case .appleVectorUnavailable:
return unavailable(pid: pid, reason: "apple_vector_unavailable")
case .invalidBuffer:
return unavailable(pid: pid, reason: "malformed_procargs")
}
}
}
private func readRunToken(pid: Int32, pointerSize: Int) -> RunTokenObservation {
@@ -151,33 +204,19 @@ private func readRunToken(pid: Int32, pointerSize: Int) -> RunTokenObservation {
return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
}
bytes = Array(bytes.prefix(size))
let parse: Result<ParsedRunToken, RunTokenReadError>
do {
let runId = try token(in: bytes, pointerSize: pointerSize)
// A complete empty environment is readable for an ordinary image. Apple
// platform images can expose the same layout after the kernel withholds
// their environment, so they remain explicitly unverified without a token.
if runId == nil && isPlatformBinary(pid: pid) {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
return RunTokenObservation(pid: pid, state: "readable", run_id: runId, reason: nil)
} catch RunTokenReadError.duplicateToken {
return unavailable(pid: pid, reason: "duplicate_run_token")
} catch RunTokenReadError.invalidTokenEncoding {
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
} catch RunTokenReadError.environmentUnavailable {
return unavailable(
pid: pid,
reason: isPlatformBinary(pid: pid)
? "platform_binary_environment_withheld"
: "environment_unavailable"
)
} catch RunTokenReadError.appleVectorUnavailable {
return unavailable(pid: pid, reason: "apple_vector_unavailable")
} catch RunTokenReadError.ambiguousEnvironmentBoundary {
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
parse = .success(try parseProcessEnvironment(in: bytes, pointerSize: pointerSize))
} catch let failure as RunTokenReadError {
parse = .failure(failure)
} catch {
return unavailable(pid: pid, reason: "malformed_procargs")
parse = .failure(.invalidBuffer)
}
return processRunTokenObservation(
pid: pid,
parse: parse,
codeSigningFlags: { codeSigningFlags(pid: pid) }
)
}
func readRunTokens(pids: [Int32]) -> [RunTokenObservation] {
@@ -201,7 +240,10 @@ func readRunTokens(pids: [Int32]) -> [RunTokenObservation] {
}
}
func token(in bytes: [UInt8], pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size) throws -> String? {
func parseProcessEnvironment(
in bytes: [UInt8],
pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size
) throws -> ParsedRunToken {
guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBuffer }
let argc = bytes.withUnsafeBytes { $0.loadUnaligned(as: Int32.self).littleEndian }
guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBuffer }
@@ -277,7 +319,7 @@ func token(in bytes: [UInt8], pointerSize: Int = MemoryLayout<UnsafeRawPointer>.
found = decoded
}
}
return found
return ParsedRunToken(runId: found, environmentEntryCount: environmentEntries.count)
}
private func readString(
+5
View File
@@ -206,6 +206,11 @@ def inspect_elf(path, cache):
"visibility": raw.st_other.visibility, "section_index": raw.st_shndx,
})
if h.sh_type in ("SHT_REL", "SHT_RELA"):
if h.sh_size % h.sh_entsize != 0:
raise LayoutFailure(
"format",
f"ELF relocation section {index} size is not a multiple of its {h.sh_entsize}-byte entries.",
)
if image.header.e_type == "ET_REL" and h.sh_info >= len(all_sections):
raise LayoutFailure("format", f"ELF relocation section {index} references missing target section {h.sh_info}.")
symbol_table = None
+31 -10
View File
@@ -11,6 +11,12 @@ import sys
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
# Linux ET_CORE machine identifiers whose PRSTATUS layout is interpreted with
# unchanged pwntools 4.15.0 ctypes. Both share pr_cursig@12, pr_pid@32 and
# pr_reg@112; only the register window differs: amd64 is 27 x 8 bytes for a
# 336-byte descriptor, AArch64 is 34 x 8 bytes for a 392-byte descriptor.
CORE_MACHINES = (62, 183)
class CoreFailure(Exception):
def __init__(self, reason, message):
@@ -51,12 +57,27 @@ def inspect_core(path, cache):
# Other declared OSABIs must never select Linux note ctypes.
if content[7] not in (0, 3) or content[8] != 0:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires System V or GNU/Linux OSABI with ABI version zero; selected OSABI=" + str(content[7]) + ", ABI version=" + str(content[8]) + ".")
if int.from_bytes(content[16:18], "little") != 4 or int.from_bytes(content[18:20], "little") != 62:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires x86-64 ET_CORE.")
machine = int.from_bytes(content[18:20], "little")
if int.from_bytes(content[16:18], "little") != 4 or machine not in CORE_MACHINES:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires ET_CORE for x86-64 or AArch64; selected ET_TYPE=" + str(int.from_bytes(content[16:18], "little")) + ", e_machine=" + str(machine) + ".")
from elftools.common.exceptions import ELFError
from elftools.common.utils import struct_parse
from elftools.elf.elffile import ELFFile
from pwnlib.elf.datatypes import elf_prstatus_amd64, elf_siginfo_64, user_regs_struct_amd64
from pwnlib.elf.datatypes import (
elf_prstatus_aarch64,
elf_prstatus_amd64,
elf_siginfo_64,
user_regs_struct_aarch64,
user_regs_struct_amd64,
)
if machine == 62:
profile_label = "x86-64"
architecture = "x86_64-little-endian"
prstatus_type, registers_type = elf_prstatus_amd64, user_regs_struct_amd64
else:
profile_label = "AArch64"
architecture = "aarch64-little-endian"
prstatus_type, registers_type = elf_prstatus_aarch64, user_regs_struct_aarch64
try:
image = ELFFile(io.BytesIO(content))
header = image.header
@@ -107,17 +128,17 @@ def inspect_core(path, cache):
if owner != b"CORE\0":
continue
if note.n_type == "NT_PRSTATUS":
if len(descriptor) < ctypes.sizeof(elf_prstatus_amd64):
raise CoreFailure("format", "Linux amd64 NT_PRSTATUS descriptor is truncated.")
status = elf_prstatus_amd64.from_buffer_copy(descriptor)
if len(descriptor) < ctypes.sizeof(prstatus_type):
raise CoreFailure("format", "Linux " + profile_label + " NT_PRSTATUS descriptor is truncated.")
status = prstatus_type.from_buffer_copy(descriptor)
registers = []
for name, kind in user_regs_struct_amd64._fields_:
relative = elf_prstatus_amd64.pr_reg.offset + getattr(user_regs_struct_amd64, name).offset
for name, kind in registers_type._fields_:
relative = prstatus_type.pr_reg.offset + getattr(registers_type, name).offset
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
elif note.n_type == "NT_SIGINFO":
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
raise CoreFailure("format", "Linux amd64 NT_SIGINFO descriptor is truncated.")
raise CoreFailure("format", "Linux " + profile_label + " NT_SIGINFO descriptor is truncated.")
signal = elf_siginfo_64.from_buffer_copy(descriptor)
number = ctypes.c_int32(signal.si_signo).value
code = ctypes.c_int32(signal.si_code).value
@@ -125,7 +146,7 @@ def inspect_core(path, cache):
signals.append({"note_index": note_index, "number": number, "code": code, "errno": ctypes.c_int32(signal.si_errno).value,
"fault_address": hex(signal.sigfault_addr) if known else None,
"fault_address_meaning": "recorded-sigsegv-address" if known else "unknown", "thread_association": "unknown"})
return {"format": "elf-core", "architecture": "x86_64-little-endian", "target_execution": "not-performed", "live_process_identity": "unknown",
return {"format": "elf-core", "architecture": architecture, "target_execution": "not-performed", "live_process_identity": "unknown",
"segments": segments, "notes": notes, "note_padding": padding, "threads": threads, "signals": signals,
"note_interpretation_completeness": "unknown",
"limitations": ["The initial Linux note profile accepts System V/unspecified or GNU/Linux OSABI with ABI version zero; an unspecified OSABI alone does not prove operating-system origin.",
+18 -4
View File
@@ -6,6 +6,7 @@ export default defineConfig({
"Reverse engineer binaries, applications, and runtime behavior with one CLI and MCP server.",
lang: "en-US",
base: "/rea/",
srcExclude: ["readme/**"],
themeConfig: {
nav: [
{ text: "Get started", link: "/installation" },
@@ -17,6 +18,7 @@ export default defineConfig({
text: "Getting started",
items: [
{ text: "Installation and setup", link: "/installation" },
{ text: "Client configuration", link: "/client-configuration" },
{ text: "CLI and Evidence", link: "/cli" },
{ text: "Agent prompts", link: "/mcp-prompts" },
{ text: "Roadmap", link: "/roadmap" },
@@ -26,6 +28,8 @@ export default defineConfig({
text: "Investigation guides",
items: [
{ text: "Native binaries", link: "/native-investigation" },
{ text: "Go build metadata", link: "/go-binaries" },
{ text: "Rust dependencies", link: "/rust-binaries" },
{
text: "JavaScript and Electron",
link: "/javascript-artifact-reconstruction",
@@ -35,16 +39,19 @@ export default defineConfig({
link: "/javascript-application-workflows",
},
{ text: "Browser observation", link: "/browser-observation" },
{
text: "Web network captures",
link: "/web-network-captures",
},
{ text: "Electron observation", link: "/electron-observation" },
{ text: "Process capture", link: "/process-capture" },
{ text: ".NET assemblies", link: "/managed-code-analysis" },
{ text: "Android applications", link: "/android-analysis" },
{ text: "Apple applications", link: "/apple-application-analysis" },
{ text: "Firmware", link: "/firmware-analysis" },
{
text: "Reconstruction readiness",
link: "/reconstruction-readiness",
},
{ text: "SQLite databases", link: "/sqlite-databases" },
{ text: "Packet captures", link: "/packet-captures" },
{ text: "Signal recordings", link: "/signal-recordings" },
],
},
{
@@ -54,7 +61,9 @@ export default defineConfig({
{ text: "IDA", link: "/ida-provider" },
{ text: "Ghidra on Windows", link: "/windows-ghidra-p0" },
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
{ text: "Ghidra raw binary", link: "/raw-binary" },
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
{ text: "Ghidra PowerPC", link: "/ghidra-ppc" },
{ text: "Ghidra semantics", link: "/ghidra-provider" },
],
},
@@ -65,6 +74,11 @@ export default defineConfig({
{ text: "MCP contracts", link: "/mcp-contracts" },
{ text: "Tool design", link: "/tool-design" },
{ text: "Testing", link: "/testing" },
{ text: "Client verification", link: "/client-verification" },
{
text: "Reconstruction readiness",
link: "/reconstruction-readiness",
},
{ text: "Releasing", link: "/releasing" },
],
},
+18 -4
View File
@@ -88,7 +88,7 @@ existing analysis families directly, for example
| `list_adb_packages` | read | `pm list packages -f` with scope; base APK device paths |
| `resolve_adb_packages` | read | Case-insensitive package-name substring search with exact-match flag |
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, permission count |
| `list_adb_directory` | read | `ls -la`: kinds, permissions, owner, group, sizes, symlink targets |
| `list_adb_directory` | read | NUL-framed immediate children, including hidden entries: kinds, permissions, owner, group, sizes, symlink targets |
| `pull_adb_package` | read | Complete split APK set from `pm path`, each file digested |
| `pull_adb_file` | read | One absolute device path pulled and digested |
| `capture_adb_screen` | read | One `screencap -p` frame (exec-out, no device-side file) with dimensions |
@@ -102,13 +102,22 @@ existing analysis families directly, for example
## Interpret the result
- **Directory names retain whitespace and newlines.** Symlink targets do too,
including trailing newlines. Records containing invalid UTF-8 remain as
labelled hex in `unparsed_lines` and make directory coverage partial.
- **Inferences carry their basis.** Device `kind` reports
`kind_basis` (serial prefix or USB transport); APK `role` reports
`role_basis: file_name`; `emulator_observed` is the build's own
`ro.kernel.qemu` value, `null` when absent.
- **Device paths are not host paths.** `base_apk_device_path` values live on
the device filesystem. Only pulls and captures produce host-local paths,
each paired with byte size and SHA-256 digest.
each paired with byte size and SHA-256 digest. A pulled file's host name is
the device path's final POSIX segment. REA preserves the original device
path and filename; a missing final segment or `.`/`..` is refused before
output creation. Windows hosts follow [Windows filename rules](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
while POSIX hosts retain valid POSIX filenames such as names with spaces or
colons. A package pull checks reported names against the destination
filesystem before pulling, and refuses names that would share a local path.
- **Splits are part of the answer.** `pm path` enumerates the base APK and
every installed split; pulls cover the whole set. System packages whose
APKs keep other names report the `unknown` role rather than guessing.
@@ -127,8 +136,8 @@ existing analysis families directly, for example
## Resource and lifecycle limits
- Caller values reach adb only as positional arguments in fixed vectors;
no tool accepts an arbitrary shell command line, and UI interaction
(`input`, `am`, `monkey`) is deliberately not exposed.
no tool accepts an arbitrary shell command line, `input`, or `monkey`.
The app/activity lifecycle tools use `am start -W` and `am force-stop`.
- Per-invocation budgets: client/devices 10-20 s, shell reads 20-60 s with
64 KiB-8 MiB capture caps (logcat 4 MiB, screen 32 MiB), transfers and
bugreports 600 s. Exceeded budgets report `resource_constraint`; stopped
@@ -139,6 +148,11 @@ existing analysis families directly, for example
- Cancellation during a package pull preserves caller-selected output. The
error includes partial observations identifying completed artifacts and the
interrupted local path; remaining APKs were not attempted.
- Bugreports collect into isolated staging and publish with exclusive creation;
filename collisions select the next numeric suffix. If publication fails, the
completed archive remains in staging. The error's partial observation gives
its recoverable `local_path`, the original `adb_reported_path`, and an unknown
digest status. Later operations and shutdown preserve this retained evidence.
- Failed package acquisition rollback retains its directory identity and retries
cleanup before later work and during shutdown. Cleanup refuses a replacement
directory. Terminal shutdown stops admission and drains active operations.
+105 -18
View File
@@ -9,13 +9,64 @@ This family is present on repository main and in npm 4.1.0. Check the
[released package boundary](installation.md#released-package-and-main) and the
connected server's tool list before selecting it.
## Optional package delivery
The MCP `inspect_android_package` request keeps complete output when `detail` is
omitted or `"complete"`. Select `detail: "summary"` explicitly to receive all
package, SDK and permission declarations, engine identity and class/resource
counts without decoded manifest text or duplicate raw provider replies. A
retaining session keeps the complete original Evidence and returns a distinct
linked `inspect_analysis_view` summary. This changes delivery, not JADX analysis.
Use the returned `normalized_result.parent_evidence_id` with
`inspect_analysis_view`, source kind `retained-evidence`, and
`view: {kind: "android", facet: "manifest", offset: 0, limit: 4096}` to read the
exact decoded manifest without reopening the APK. Offsets and limits count UTF-16
code units; follow `coverage.next_offset`. The page preserves `source_status` and
`reported_total_bytes`: exhausting retained text does not make a partial decoded
manifest complete. Complete source Evidence remains exportable under the existing
session lifetime. Servers without retention reject summary before starting JADX.
One-shot CLI output remains complete. Older servers require the existing complete
request after schema discovery. APK authenticity and runtime behavior remain
unverified.
## Supply tools
Use a **full JDK 17 or newer** (including its compiler module) and
**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
source-file mode against the unmodified engine JAR. REA does not install Java,
an Android SDK, JADX, a device service or an emulator. Set the path in the shell
or MCP server environment:
[**jadx-headless-mcp 0.7.1**](https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar).
Verify the downloaded JAR's SHA-256 against
[REA's pinned release metadata](https://github.com/morluto/rea/blob/main/src/android/JadxRelease.ts)
before using it. The following download and verification commands work for npm
package consumers as well as repository checkouts; the repository-only
`npm run fixtures:android` helper is not included in the npm package.
On macOS or Linux, choose a destination and verify the pinned digest:
```sh
mkdir -p "$HOME/.local/share/rea/android" &&
cd "$HOME/.local/share/rea/android" &&
curl -fL -o jadx-headless-mcp-0.7.1-all.jar https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar &&
if command -v sha256sum >/dev/null; then
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
else
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | shasum -a 256 -c -
fi &&
export REA_JADX_MCP_JAR="$PWD/jadx-headless-mcp-0.7.1-all.jar"
```
On Windows, run the equivalent in PowerShell:
```powershell
$jar = Join-Path $HOME '.local\share\rea\android\jadx-headless-mcp-0.7.1-all.jar'
New-Item -ItemType Directory -Force (Split-Path $jar) | Out-Null
Invoke-WebRequest -Uri 'https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar' -OutFile $jar
if ((Get-FileHash -Algorithm SHA256 $jar).Hash.ToLowerInvariant() -ne '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75') { throw 'JADX JAR SHA-256 does not match REA pinned metadata' }
$env:REA_JADX_MCP_JAR = $jar
```
REA runs its packaged Java metadata bridge in source-file mode against the
unmodified engine JAR. REA does not install Java, an Android SDK, JADX, a device
service or an emulator. Set the path in the shell or MCP server environment:
```sh
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
@@ -65,6 +116,7 @@ rea inspect-android-method /path/Example.apk example.MainActivity onCreate
rea inspect-android-method /path/Example.apk example.MainActivity select --overload-index 1
rea trace-android-references /path/Example.apk example.MainActivity
rea trace-android-references /path/Example.apk example.MainActivity --method-name onCreate
rea trace-android-references /path/Example.apk example.MainActivity --method-name select --overload-index 1
```
Equivalent MCP methods are `inspect_android_package`, `search_android_classes`,
@@ -85,15 +137,22 @@ Every request includes `path`. Class/method operations use `class_name`,
```
Use `inspect_android_class` to choose an overload. REA rejects an ambiguous
method request rather than silently selecting the first match. The engine's
method reference operation cannot select overloads, so REA reports that boundary
as unsupported when multiple same-name methods exist.
method request rather than silently selecting the first match. Method reference
queries accept the same `overload_index`; the bridge resolves the selected
method node and the result reports its index and displayed signature. These
indices are tied to the current artifact and engine, not stable across builds.
The engine's smali fallback also joins same-name overloads; REA rejects that
fallback for an overloaded method instead of attributing all bodies to one
selected overload. Native/abstract methods with no decompiled body report
`body_status: "not_available"` without inferring which implementation flag caused it.
Static initializers (`<clinit>`) return smali with the
`static_initializer_disassembly` marker. Java decompilation can move their
instructions into field declarations, leaving an empty or incomplete method
body. The selected method's disassembly preserves those instructions and retains
the same source byte limit and explicit truncation reporting.
## Interpret the result
- Package inspection retains decoded manifest XML, summary declarations,
@@ -101,19 +160,37 @@ selected overload. Native/abstract methods with no decompiled body report
upstream engine and should be checked against the retained XML when needed.
Signature verification is explicitly `not_performed`.
- Class search consumes all pages and checks their counts. An empty `query`
inventories all class names; class names are decompiler representations.
inventories all class-name rows; class names are decompiler representations.
Repeated names remain repeated in `matches` so the inventory row count and
producer evidence stay visible. `duplicate_names` identifies matching names
with multiple rows and their occurrence counts; a name alone does not
distinguish which provider class node a later lookup selects.
- Class inventories read parsed metadata without generating source and return
full reported display types, fields and inner class names. Synthetic members
that code generation hides can appear in the inventory; it stays consistent
after method decompilation within the retained session.
Exact DEX descriptors are unknown. Overload indices are tied to this artifact
and engine, not stable identities across different builds.
Exact DEX prototypes are reported from frozen JADX method metadata as
`dex_descriptor`, for example `(I)V`. Display names can be JADX aliases;
the descriptor alone is not a complete raw DEX method ID. Overload indices
are tied to this artifact and engine, not stable identities across builds.
- Method inspection reports Java or smali, fallback markers and complete/partial
returned text. Decompiled source is a derived representation. A complete text
result does not establish complete semantics or a byte-identical reconstruction.
- Reference results are incoming static relationships between provider nodes.
Source lines and DEX instruction offsets remain unknown. They are not observed
runtime calls.
`reference_unit=using_node` means `total` counts containing JADX nodes, not
individual invoke instructions; two calls from one method can appear as one
entry. `coverage=complete` covers this node inventory only. For a method
reference whose using node has raw code, `direct_invokes` lists matching DEX
invoke instructions with their DEX entry, raw source method ID, code-unit
offset, method index and opcode. An empty array means no direct invoke was
matched in that node; `null` means raw code was unavailable or the using node
was not a method. This list does not establish complete call-site coverage
outside JADX's using-node inventory or through reflection and dynamic loading.
A virtual or interface invoke also does not establish its runtime implementation.
Source lines and the containing node's own `dex_offset` remain unknown.
Method-reference results also carry the selected `dex_descriptor`;
class-reference results report `null`.
These relationships are not observed runtime calls.
- Original requests, APK SHA-256, actual engine JAR SHA-256 and raw producer
responses remain in Evidence. Source revision is populated only for bytes
matching the audited release JAR.
@@ -179,9 +256,11 @@ REA never writes to the original APK or launches target code.
On Windows, stdin writes preserve stream backpressure and run outside the Node
event loop. Cancellation terminates the retained job, including descendants,
before workspace cleanup. An abrupt CLI/server process termination closes the
job and stops Java, but cannot run JavaScript workspace cleanup; temporary
workspace files can remain after that forced exit.
before workspace cleanup. On Windows, a detached REA helper owns the private
workspace's native directory handle. An abrupt CLI/server owner termination
closes the Java job and the helper's input pipe; the helper then removes the
workspace by handle. The real Windows lane checks both effects. If the helper
also dies or the host loses power before it finishes, private files can remain.
If Java cannot start the bridge, REA reports the selected executable and retained
startup diagnostics. Select a full JDK through `JAVA_HOME` or `PATH`, and use
@@ -244,12 +323,20 @@ The original APK and host Java configuration remain unchanged.
The same lane runs on Windows x64 with the bundled native controls and a full
JDK. It checks CLI/MCP results, real MCP cancellation and disconnect, and Java
exit after abrupt CLI owner termination. Windows forced termination is checked
separately from the POSIX CLI SIGTERM handler; it does not claim a structured
cancellation result or workspace removal after the owner is forcibly killed.
exit and workspace removal after abrupt CLI owner termination. Windows forced
termination is checked separately from the POSIX CLI SIGTERM handler. A
synthetic two-owner test confirms that one owner's exit does not remove the
other owner's workspace. The lane does not claim a structured cancellation
result or power-loss recovery.
The download script verifies fixed SHA-256 values, reuses matching files and
refuses to overwrite a different existing file. APK/JAR files live under ignored
`_reference/` and are excluded from package contents. The lane exercises actual
REA CLI and MCP results with the real engine, separately from synthetic protocol
regressions. It needs no Gradle build, emulator, Android SDK or Ghidra.
Direct invoke sites retain exact DEX code-unit offsets separately from each
containing node's unknown `dex_offset`. `direct_invokes_exhaustive: false`
means instruction rows were bounded by the local bridge frame budget; `null`
means the producer did not establish instruction coverage. Using-node totals
and coverage do not imply complete instruction evidence.
+8
View File
@@ -22,6 +22,14 @@ reports the resolved launcher, its selection source, and its version. A
missing launcher reports `capability_unavailable` with the
`REA_APKTOOL_COMMAND` remediation rather than attempting an installation.
On Windows, a `.bat` or `.cmd` launcher (such as Chocolatey's `apktool.bat`)
runs through an explicit `cmd.exe /d /s /c` call. That call has no AutoRun,
no delayed expansion, and quotes every argument. A launcher path that
contains `"`, `%`, `^`, `!`, `&`, `|`, `<` or `>` is reported as
`not_configured` instead of being passed to `cmd.exe`. If REA's temporary
decode paths contain those characters, decoding through a batch launcher
reports `capability_unavailable`.
## Answer an analyst question
```sh
+1 -1
View File
@@ -174,7 +174,7 @@ Resolution follows dyld:
- `@rpath` tries each `LC_RPATH` of the loading image first, then those of the
image that loaded it, up to the process root.
- Dependencies load dependents-first in load-command order, and each image
loads once per process. A later request whose install name matches an
loads once per process. A later @rpath request whose install name matches an
already loaded image reuses it.
- Symlinks such as `Versions/Current` are followed inside the analyzed root.
An escape through `..` or an absolute link is `escapes-target`.
+18 -2
View File
@@ -6,7 +6,7 @@ graph TD
end
subgraph "Production Composition"
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading"]
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading<br/>static WASM/EVM descriptors: named contracts, factories, retryable lifetimes"]
end
subgraph "Adapters"
@@ -27,6 +27,7 @@ graph TD
subgraph "Provider Layers"
PWNTOOLS["src/native/pwntools/ + src/native/pwndbg/<br/>BYO offline ELF/core adapters<br/>owned snapshots/Python and optional core-only GDB<br/>unchanged upstream parsers, ABI structures and mapping API"]
WASM["src/wasm/<br/>caller-supplied WABT; immutable artifact inspection<br/>owned process supervision and retryable workspace cleanup"]
EVM["src/evm/<br/>unchanged EVMole/WASM in bounded owned worker<br/>explicit carrier parsing and inferred interface adapter"]
HOPPER["src/hopper/<br/>Hopper launch & Unix-socket protocol<br/>BridgeLauncher, HopperClient, protocol"]
GHIDRA["src/ghidra/<br/>Ghidra discovery, profile & function analysis<br/>snapshot, Unix/Windows transport, bounded lifecycle"]
@@ -37,6 +38,10 @@ graph TD
JS_CODEC["src/javascript/sourceMaps/<br/>pinned upstream map codec<br/>owned heap-limited Node process, complete point evidence"]
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
DOTNET["src/dotnet/<br/>Managed static metadata provider<br/>PE/CLI metadata inventory, members, IL"]
OBJDUMP["src/objdump/<br/>GNU objdump command profile"]
RIZIN["src/rizin/<br/>Static command profile and persistent debugger sessions"]
GDB["src/gdb/<br/>Persistent GDB/MI sessions<br/>owned process lifecycle and target state"]
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
REFERENCE["src/reference/<br/>Filesystem reference-source reader<br/>bounded traversal, validation, errors"]
@@ -49,7 +54,7 @@ graph TD
end
subgraph "Contracts Layer"
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, managed, browser, Electron, application, and session tools"]
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, reverse engineering, managed, browser, Electron, application, and session tools"]
ERRORS["src/contracts/errorSchemas.ts<br/>Shared analysis error schemas<br/>CLI diagnostics, generated error catalog"]
end
@@ -94,6 +99,9 @@ graph TD
APP -->|record callbacks| RECORDS
SERVER -->|narrow record ports| RECORDS
RECORDS --> DOMAIN
COMPOSITION --> WASM
WASM --> PROCESS
WASM --> DOMAIN
COMPOSITION --> PWNTOOLS
PWNTOOLS --> PROCESS
PWNTOOLS --> DOMAIN
@@ -118,6 +126,14 @@ graph TD
INSPECTOR --> DOMAIN
APP --> DOTNET
DOTNET --> DOMAIN
COMPOSITION --> OBJDUMP
OBJDUMP --> DOMAIN
COMPOSITION --> RIZIN
RIZIN --> DOMAIN
CLI --> GDB
SERVER --> GDB
GDB --> PROCESS
GDB --> DOMAIN
HOPPER --> HOPPER_BRIDGE
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
+9 -4
View File
@@ -98,10 +98,14 @@ section, symbol, mitigation or linkage views. The default `complete` detail and
CLI output are unchanged.
Complete results have a 32 MiB input, 64 MiB reply, 1 MiB combined diagnostics
and 30-second owned command deadline. The reply budget applies to the decoder
record; the CLI/MCP Evidence envelope adds copies and encoding overhead. The
and 30-second owned command deadline. `REA_PWNTOOLS_MAX_INPUT_BYTES` raises the
input bound alone, up to 1 GiB; the reply and deadline bounds are unchanged. The
reply budget applies to the decoder record; the CLI/MCP Evidence envelope adds
copies and encoding overhead. The
pinned MCP SDK defaults to a 10 MiB receive buffer. Large complete results need
a caller-configured `StdioClientTransport({ maxBufferSize: ... })`; the large
a caller-configured `StdioClientTransport({ maxBufferSize: ... })` and a matching
server `REA_MCP_MAX_RESPONSE_BYTES`. See [MCP delivery budgets](mcp-contracts.md#tool-results).
These settings affect delivery, not decoder coverage or limits. The large
extended-index verification uses 256 MiB and an explicit five-minute request
timeout in its separate opt-in MCP lane. REA does not silently truncate the result. Python lowers resource soft limits to at most 3 GiB virtual address space,
30 CPU seconds and 64 MiB file output. Inherited tighter soft/hard limits are
@@ -181,7 +185,8 @@ payload ranges are supported. The supported tree has three levels: type, name,
and language. Other resource payload formats remain opaque.
`max_file_bytes` defaults to 64 MiB (maximum 512 MiB); `max_entries` defaults to
4096 (maximum 65536). CLI options are `--max-file-bytes` and `--max-entries`.
16384, the largest stock Windows resource image measured with headroom
(maximum 65536). CLI options are `--max-file-bytes` and `--max-entries`.
Metadata is bounded to 8 MiB; exceeding any budget fails without truncation.
Large Evidence envelopes may require a caller-configured MCP transport receive
buffer because envelope copies add overhead. Requests honor cancellation during
+118 -5
View File
@@ -8,6 +8,10 @@ exported script's native imports under an explicit URL/import-map context.
REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.
For request/response bodies and traffic debugging, see
[Fiddler Everywhere captures and MCP](web-network-captures.md#reading-fiddler-captures-saz).
Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate MCP server.
## Shipped surfaces
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
@@ -24,6 +28,29 @@ REA can attach to a user-owned Chrome-family browser through the Chrome DevTools
Target discovery returns the complete in-scope target array in one result.
## Screenshot comparison memory budget
`compare_web_screenshots` admits both PNGs against a 256 MiB estimated
working-memory budget before inflating either image. This is a processing
buffer budget for this operation, not a PNG dimension limit, CDP transport
limit, or process RSS guarantee. Header-valid dimensions that exceed the
budget return `resource_constraint` with `details.resource: "memory"` and the
estimated and maximum working-set bytes. Invalid PNG structure remains
malformed input; encodings outside supported pixel-comparison coverage remain
unsupported-target errors.
The conservative estimate includes the two decoder-owned decoded input
buffers, the current image's concatenated IDAT buffer, Node zlib's default
16 KiB output chunk backing rounded up for retained chunks, the concatenated
inflate result while its output chunks are retained, one possible extra output
chunk before zlib checks `maxOutputLength`, unfiltered scanline storage,
RGB-to-RGBA expansion, and the first retained RGBA image while the second is
decoded. RGBA output aliases its unfiltered buffer and is counted once.
Admission uses checked integer arithmetic before image-sized allocation.
Caller-owned JSON/base64 strings, input parsing and validation, transport
buffers, garbage-collector behavior, and other runtime memory are outside this
estimate; the budget does not promise a bound on total process memory.
Electron `file://` pages use a separate provider and target boundary; see [electron-observation.md](electron-observation.md).
## Provider authorities
@@ -53,12 +80,74 @@ google-chrome \
REA does not launch, own, or terminate this browser. Use a dedicated profile and stop it yourself when the investigation is complete. Do not expose the debugging port on a non-loopback interface.
Other Chromium-family browsers, such as Chromium or Brave, work the same way
with the same two flags. On macOS, run the executable inside the application
bundle, for example
`"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"` or
`"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"`. Fedora installs
Chromium as `chromium-browser`.
REA needs the browser's HTTP discovery endpoint on a loopback port. A browser
driven over `--remote-debugging-pipe` or a private channel, for example by an
automation SDK, exposes no such endpoint, so `list_browser_targets` has nothing
to attach to. Connection failures report `endpoint_unreachable`; a reachable
HTTP service that returns a non-200 or non-JSON discovery response reports
`invalid_endpoint_response`.
Reproduce that flow in a Chromium-family browser started with
`--remote-debugging-port`.
In a minimal Linux container, headed Chromium under Xvfb can keep running while
its DevTools port never opens. Install the GLES, EGL, and Mesa DRI libraries
(on Fedora: `libglvnd-gles`, `mesa-libEGL`, and `mesa-dri-drivers`), then
restart the browser.
On Linux, a reachable but locked desktop keyring can stall Chrome before its
first navigation. For a dedicated investigation profile, add
`--password-store=basic` to bypass that keyring. Empty or placeholder frame URLs
then describe an unloaded page; REA continues to require an allowed, committed
URL. See [the startup investigation](https://github.com/morluto/rea/issues/1384#issuecomment-6093781686).
### Browsers without HTTP discovery
Some browsers expose a loopback browser WebSocket but no `/json/version` or
`/json/list`. Chrome 144 and later can enable remote debugging on a running
browser from `chrome://inspect/#remote-debugging`; this mode is reported to
serve only the browser socket. Since Chrome 136, `--remote-debugging-port` is
ignored for the default profile, so this is the remaining way to inspect a page
in an existing profile.
For such a browser, pass the browser socket instead of the HTTP endpoint:
```bash
rea list-browser-targets ws://127.0.0.1:9222/devtools/browser/<id>
```
Chrome writes the port and the `/devtools/browser/<id>` path to the
`DevToolsActivePort` file in its user data directory. REA does not read that
file for you.
- Only `list_browser_targets`, `inspect_web_page`, `analyze_web_bundle`,
`observe_web_session`, `discover_webmcp_tools`, and `capture_web_screenshot`
accept a browser socket. Website runtime attribution, Electron inspection,
and scenario attach mode still require HTTP discovery.
- Each call opens one WebSocket and uses it for discovery
(`Browser.getVersion`, `Target.getTargets`) and for the selected page.
Discovery must finish within the same 10-second deadline as HTTP discovery.
Chrome may ask you to approve each connection.
- The browser identifier controls access to the socket. Evidence records the
endpoint as `ws://127.0.0.1:9222/devtools/browser/[REDACTED]`.
- `browser.revision` is the socket's `Browser.getVersion` value, such as
`@e533e98…`. HTTP discovery reports `WebKit-Version`, such as
`537.36 (@e533e98…)`.
- `Target.getTargets` reports whether each target is attached. Chrome's
`/json/list` can omit it, so HTTP discovery may report `attached` as `null`.
- An existing profile can hold many unrelated pages. Target listing returns
each eligible page's URL and title, so pass `--allowed-origins` to list only
the origins you are investigating.
A browser driven only through `--remote-debugging-pipe` or a vendor's private
automation channel has neither endpoint, and REA cannot attach to it.
## Request boundary
CDP connections enforce the shared 64 MiB limit per WebSocket message. A larger
@@ -68,9 +157,13 @@ decoded strings or JSON objects.
Discovery supplies a literal loopback CDP endpoint and returns all eligible
page targets unless an exact HTTP(S) origin filter narrows the result. Follow-up
inspection requests select one target; their default scope is its current origin. `localhost`, private-LAN
addresses, HTTPS CDP endpoints, credentials, paths, queries, fragments, and
implicit ports are not valid CDP endpoints. Origin filters contain only scheme,
inspection requests select one target; their default scope is its current origin.
The endpoint is either `http://127.0.0.1:PORT` or a
[browser socket](#browsers-without-http-discovery),
`ws://127.0.0.1:PORT/devtools/browser/ID`; `[::1]` may replace `127.0.0.1`.
`localhost`, private-LAN addresses, HTTPS and `wss:` endpoints, credentials,
queries, fragments, implicit ports, and any other path are not valid CDP
endpoints. Origin filters contain only scheme,
host, and port; paths, credentials, queries, fragments, and wildcards are not
valid origins.
@@ -150,6 +243,7 @@ REA retains local observation data and removes only URL userinfo credentials, ex
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
- Unavailable or malformed storage inventories mark `storage_keys` unavailable, even without fingerprints. Comparisons suppress inferred key additions and removals while preserving modifications proven by complete fingerprints on both sides. Previously exported captures are not retroactively corrected; recapture them to obtain these coverage markers.
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Script metadata admission bounds retained identity and source-map strings to 8 MiB and 50,000 records. When a source-map URL cannot fit, the script identity remains and source-map coverage reports `resource_budget_exhausted`; rejected script observations are counted without retaining their IDs. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
Frames, resources, scripts, events, and workers outside the selected origin scope are excluded. An explicit `allowed_origins` list can include additional origins; excluded target details are counted without being exposed.
@@ -170,12 +264,31 @@ validation and resource bounds. Omitted script metadata makes its affected
section explicitly truncated.
Disallowed-origin entries are filtered before retention.
CDP discovery, WebSocket connection, and commands continue until the peer responds or the caller cancels the operation; REA has no internal byte, message-count, or pending-command quota for these exchanges. Without caller cancellation, a stalled peer can leave an operation waiting. On cancellation, REA closes only its own WebSocket, waiting up to one second before terminating it. Malformed unsolicited events and malformed replies to pending commands poison the connection so later commands fail closed. When a correlated reply reports a session ID, it must match the command's selected session. An optional method is reported unavailable only when the browser rejects it as unknown; other command rejections retain their reported failure.
CDP discovery, connection, and commands wait for the peer or caller cancellation;
a stalled peer can leave an operation waiting. WebSocket messages remain subject
to the [64 MiB limit](#request-boundary). REA does not impose a message-count or
pending-command quota. On cancellation, it closes only its own WebSocket,
waiting up to one second before terminating it. Malformed unsolicited events
and malformed replies to pending commands poison the connection so later
commands fail closed. A correlated reply's session ID must match the command's
selected session. An optional method is unavailable only when the browser
rejects it as unknown; other command rejections retain their reported failure.
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. Scripts are different: scripts the page parsed before REA attached are still reported under `scripts`. A page that loaded before attach can therefore list its scripts while `network.requests` lacks the requests that fetched them. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation has a 30-second deadline and a 64 MiB aggregate streamed-response budget across redirects. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
source-map items retain the script's `declared_url` separately from the observed
`effective_url`. successful maps use the latter as their source-resolution base.
failed fetches retain the last response URL observed within the approved origins;
`effective_url` is `null` when no response was observed. query parameters are
preserved, and both URLs count toward the inline evidence budget.
Current fetches emit a string or null; historical observations may omit
`effective_url` without changing their stored content or Evidence identity.
An observed URL must fit before its response body is read. If it cannot fit,
remaining map results are explicitly unknown; failed cancellation retains its
cleanup context and resource constraint.
## Non-goals and threat model
The browser provider deliberately does not expose generic CDP commands, `Runtime.evaluate`, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and returns an image only when requested. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
+13 -2
View File
@@ -262,8 +262,13 @@ Capturing more data alone does not establish a website change.
states. Captured bodies retain base64 bytes, byte count, media type, and SHA-256
of the retained bytes. Request bytes are those exposed by Playwright; a
`not_exposed` request body does not prove absence or complete multipart file
coverage. Response bytes are decoded by the browser, not the compressed wire
representation. No later fetch is substituted for the original response.
coverage. Response bytes are the result of Playwright's `Response.body()` API.
Playwright may satisfy that API with a credentialed follow-up load of the
request URL when CDP has no response body. Event and transaction references
identify the associated Playwright response object, but do not verify that the
retained bytes came from the original network transaction. Successful selected
response bodies include this limitation in the capture. These bytes are not
the compressed wire representation.
Headers preserve producer order and duplicates. Authorization,
Proxy-Authorization, Cookie, and Set-Cookie values are null with
`redacted: true`; ordinary values remain intact. Declared UTF-8 secret values
@@ -307,3 +312,9 @@ artifacts carry their normalized before/after digests and capture states.
Every changed or unknown artifact record is returned inline for each aligned
step. Missing, truncated, or mismatched capture coverage remains unknown
instead of being treated as equality.
`scenario.event_families` records the sorted, de-duplicated `capture.events`
selection. Captures with different selections report event evidence as unknown
with a recovery instruction, even when no selected family emitted an event.
Captures written before this field existed omit it, so their event evidence is
also unknown.
+97 -4
View File
@@ -37,8 +37,64 @@ module page. See
[JavaScript application workflows](javascript-application-workflows.md) for
views, feature traces and comparisons that take the same saved Evidence.
## Managed assemblies (.NET)
For a .NET PE/CLI assembly, inspect its identity and deployment form before
inspecting members. These commands read metadata and Common Intermediate
Language (CIL) without loading or executing the assembly and require no native
analysis engine:
```bash
rea inspect-managed-artifact /absolute/path/Assembly.dll --json
rea inspect-managed-members /absolute/path/Assembly.dll --json
rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser --json
```
Replace `Example.Parser` with an exact `full_name` from the member inspection,
or a declaring type name you already know. Omit `--type` to inspect all members,
or repeat it to select several types.
Selection limits decoding to the selected types; member references remain an
assembly-wide inventory, so the output can still be large.
Choose the workflow from the artifact's contents rather than its `.exe` or
`.dll` extension. The managed parser requires PE/CLI metadata; it does not
unpack .NET single-file hosts or decode IL2CPP metadata. NativeAOT method bodies
require native analysis. See the [managed-code guide](managed-code-analysis.md)
for supported deployment forms, selection semantics, and coverage limits.
## SQLite snapshots
For a local database snapshot, use `rea inspect-sqlite-database PATH --json`.
Add `--table NAME --row-limit 20` to read a bounded sample from an ordinary
table. This command does not require a native analysis provider. See
[SQLite database snapshots](sqlite-databases.md) for database/WAL identity,
source-file handling and row-value semantics.
## Native analysis
Inspect embedded Go build metadata without installing Go or configuring a
disassembler:
```bash
rea inspect-go-binary /absolute/path/to/program --json
```
The shared CLI/MCP workflow reports compiler version, modules, replacements,
build settings, original bytes and SHA-256. See [Go binaries](go-binaries.md)
for supported containers and absent or malformed metadata.
Inspect an embedded cargo-auditable Rust dependency list without installing
Rust or configuring a disassembler:
```bash
rea inspect-rust-binary /absolute/path/to/program --json
```
The same workflow reports package names, versions, sources, the root package,
dependency edges, and the section's file range. A missing `.dep-v0` section is
null metadata, not proof that the binary has no dependencies. See
[Rust binaries](rust-binaries.md).
Configure [Hopper or Ghidra](installation.md#hopper), or the
[IDA adapter](ida-provider.md), before analyzing a native target. Substitute
your target, search text and function name or address in these examples:
@@ -57,9 +113,10 @@ a function dossier; `decompile` returns pseudocode. For assembly instructions
alone, use `rea instructions`. A macOS `.app` bundle can be supplied directly.
Run `rea --help` or a command's `--help` for arguments and output options.
The [generated catalog](product-catalog.json) lists all CLI commands and MCP
The [generated catalog](mcp-contracts.md#generated-catalog) lists all CLI commands and MCP
tools. Provider-specific target support is described in the
[native guide](native-investigation.md), [DOS guide](ghidra-dos.md), and
[native guide](native-investigation.md), [DOS guide](ghidra-dos.md),
[raw binary guide](raw-binary.md), and
[Windows Ghidra guide](windows-ghidra-p0.md).
## Choose a provider
@@ -113,6 +170,10 @@ an exact result when the target bytes, operation, parameters, provider and
settings match. Mutations and cursor-dependent calls are excluded from the
cache. Snapshot files are local and use owner-only permissions.
Dispatching a metadata write invalidates the active session's snapshot cache,
even if its reply fails: the provider may already have applied the write.
Close and reopen the target before importing or saving another snapshot.
Snapshots retain eligible target-scoped question histories in full, including
mutation Evidence recorded while another target was active. Observations and
related questions must still belong to the saved target. If those dependencies
@@ -125,7 +186,9 @@ rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/pat
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
```
An exact CLI cache hit is read before starting a provider process.
An exact CLI cache hit is normally read before starting a provider process.
On Windows, Ghidra first admits the selected source through its native snapshot
owner, so replay can start that provider.
In MCP, `open_binary` accepts `snapshot_path` to import a snapshot atomically
for its matching target; an MCP provider may still start before returning a
cached result. `close_binary` accepts `snapshot_path` and optional
@@ -163,10 +226,40 @@ Import an older source tree to compare with the current artifact:
```bash
rea import-reference-source /absolute/path/to/source
rea import-reference-source /absolute/path/to/source --detail summary --outline-depth 2 --json
```
The default (`--detail full`) returns the complete `HistoricalSourceGraph`.
Choose `--detail summary` for a `HistoricalSourceSummary` of that same committed
inventory: its `HistoricalSourceManifest` contains the full graph and root
digests, with provenance, VCS facts, languages, manifest paths, entry and
relationship counts, parse-failure counts, file/directory states, exclusions
and limitations. The summary is a presentation view, not a graph accepted by
source-to-bundle comparison; keep the full output for that workflow.
`--outline-depth` requires `--detail summary` and accepts a nonnegative safe
integer. Depth 1 shows root children, depth 2 (the default) also shows their
children, and depth 0 omits all outline entries. Paths retain canonical Unicode
code-point order. `outline.omitted_entry_count` counts only inventoried entries
hidden by the chosen depth; it does not count unexamined children of excluded or
unreadable directories, or change `inventory_state`. Exclusions and source
limitations remain separate from this presentation omission.
`file_sizes.known_bytes` is the exact sum of known inventoried file sizes as
decimal text, so large sums cannot lose integer precision. Unknown sizes count
in `file_sizes.unknown_file_count`; excluded or unexamined paths are not included
in that total. A zero known-byte sum therefore does not establish an empty source
tree. Full output retains individual file hashes, relationships and parse-failure
diagnostics. Summary mode performs the same import and cleanup, then projects
the committed result without rereading source files. It reduces the selected
output, not the amount of source examined. A shallow tree or many exclusions can
still produce a large summary; depth 0 removes the outline but preserves the
other facts. No fixed output-size or runtime improvement is promised.
The import records hashes and metadata for the supplied files, separately
from observations of the current app. It honors the source tree's `.gitignore`
from observations of the current app. For a directory inside a Git checkout,
it records the HEAD of the nearest enclosing repository; whether the working
tree matches that commit remains unknown. It honors the source tree's `.gitignore`
and applies default exclusions for common generated and dependency paths such
as `node_modules/`, `dist/`, and `*.log`. Pattern-based exclusions record the
matched pattern and whether it came from project, default, caller, or sensitive
+372
View File
@@ -0,0 +1,372 @@
# Client configuration and troubleshooting
Use this reference when a client's configuration paths, profiles, trust settings,
or schema limits affect REA. Start with [installation and setup](installation.md)
for the reviewed setup transaction and [client IDs](installation.md#supported-agents).
The versioned verification notes below describe their stated runs; live service
acceptance is verified only where explicitly recorded.
## Fiddler Everywhere alongside REA
Connect Fiddler Everywhere's MCP server separately for HTTP traffic debugging,
preserving your REA client entry. See the
[setup instructions and public skills](web-network-captures.md#fiddler-everywhere-mcp-and-public-skills).
## GitHub Copilot CLI
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata can block ordinary
chat with REA's full input-schema profile before sending a model request
(`compaction_static_context_blocked`; [#1554](https://github.com/morluto/rea/issues/1554)).
For the native offline OpenAI-compatible BYOK adapter, full-profile chat and
analysis also passed with a distinct `COPILOT_PROVIDER_MODEL_ID` such as
`rea-gpt-4.1`, `COPILOT_PROVIDER_WIRE_MODEL=gpt-4.1`, and that distinct ID selected
through `copilot --model rea-gpt-4.1`. The local fixture requested
`COPILOT_PROVIDER_MAX_PROMPT_TOKENS=1000000`; configure the limit supported by
your backend. This keeps every full schema and the actual wire model intact.
It does not establish the effective client budget or fix the built-in-ID path.
The existing compact profile passed native chat and analysis with the complete
tool inventory on Linux using a loopback model fixture. After setup, add
`"REA_MCP_INPUT_SCHEMA_PROFILE": "compact"` to the `env` object of the
`mcpServers.rea` entry in Copilot's `mcp-config.json`, preserving its other
settings, then restart the client. Compact advertisements can reduce nested
schema constraints; REA still validates complete canonical inputs. Full-profile
`gpt-5.4` metadata is also verified locally. Live model API acceptance and
Windows remain unverified; see [native client verification](testing.md).
## Claude Desktop
Setup registers `mcpServers.rea` in `claude_desktop_config.json`: under
`~/Library/Application Support/Claude` on macOS, under
`$XDG_CONFIG_HOME/Claude` (default `~/.config/Claude`) on Linux, and on Windows
in the file the installed app reads:
- Without Claude Desktop's MSIX package storage, setup uses
`%APPDATA%\Claude\claude_desktop_config.json`.
- The MSIX build (package family `Claude_pzs8sxrjxfjjc`, installed under
`C:\Program Files\WindowsApps\Claude_<version>_x64__pzs8sxrjxfjjc`)
virtualizes AppData writes. Once it has saved its configuration, it reads
the package-private copy
`%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json`
and ignores the `%APPDATA%` file. Setup, doctor and uninstall use that
private copy when it exists. Before the package has saved one, they use the
`%APPDATA%` file it still reads; with neither file, setup creates the
private copy.
- If `%LOCALAPPDATA%\Packages` holds a `Claude_<publisher ID>` package from
another publisher, or cannot be read, REA cannot establish which file Claude
Desktop reads. Setup, doctor and uninstall report that instead of falling
back to `%APPDATA%`. Doctor then reports `claude_desktop` as invalid, and
uninstall stops before removing anything for any client.
Quit Claude Desktop fully before approving setup; closing its window is not
enough. A running Claude Desktop can rewrite its configuration from memory and
drop the registration. This was observed with the Windows MSIX build. Start
Claude Desktop again after setup. If the package saves its private copy after
setup planned to write the `%APPDATA%` file, setup makes no change and asks you
to rerun it.
A process that the MSIX app started, such as a terminal in Claude Desktop's
Code tab, sees the package's private files at their `%APPDATA%` paths even
though it reports no package identity. Setup compares one of those files at
both paths. When it finds that redirected view, the app is running, so setup
and uninstall refuse to change Claude Desktop's configuration; rerun them from
a terminal that Claude Desktop did not start. If the package has no private
file to compare, setup and uninstall cannot rule that out and also refuse; open
Claude Desktop once if it has never run, quit it fully, then rerun. Doctor
reads the same file from either kind of process.
The same process also writes everything under `%APPDATA%` and
`%LOCALAPPDATA%` into the package's private storage, where other apps never
read it. From that redirected view, setup and uninstall refuse to change other
clients' files there (VS Code, Trae, Devin and Hermes) and skill directories
there. `npm install -g` run from such a process installs into the package's
`LocalCache\Roaming\npm`, which other processes cannot see at the `%APPDATA%`
path. Setup refuses to register a launcher stored in any MSIX package's
private storage. Install REA from an ordinary terminal and rerun setup there.
Uninstall removes REA's entry from both the private copy and the `%APPDATA%`
file, including registrations that earlier releases wrote to `%APPDATA%`.
REA recognizes only the MSIX package. A non-MSIX Claude Desktop installed
beside it, which would read the `%APPDATA%` file, is not detected.
The package identity, both file locations, the redirected view, and these
setup and doctor results were observed on Windows 11 with the MSIX build
2.31226.1.0, using read-only `doctor` and `setup --dry-run` runs both inside
and outside the app. On that host, setup run from an ordinary terminal with
the app quit wrote the package-private copy; after the app restarted, the
entry was still present, doctor reported it aligned, and Claude Desktop's MCP
log showed REA starting and answering `tools/list`. Uninstall against the real
app is unverified.
## Claude Code
Claude Code normally defers MCP tool definitions through tool search. It
[turns tool search off](https://code.claude.com/docs/en/mcp#configure-tool-search)
when `ANTHROPIC_BASE_URL` points to a non-first-party host, such as DeepSeek's
Anthropic-compatible endpoint. It then sends every REA tool's name, description
and input schema with every request
([#2019](https://github.com/morluto/rea/issues/2019)). On main at 04089d5be,
with 172 tools, that payload measured about 496 KB with the default profile and
about 290 KB with `compact`. By the o200k and cl100k tokenizers, that is roughly
120k and 68k tokens. Compaction cannot remove tool definitions. If they nearly
fill the window Claude Code assumes, the context refills after every compaction
and Claude Code reports that autocompact is thrashing. To avoid this:
- Make Claude Code assume the model's actual context window. DeepSeek's Claude
Code guide selects `deepseek-flash[1m]`; the `[1m]` suffix makes Claude Code
assume a one-million-token window. For other unrecognized model IDs, set
`CLAUDE_CODE_MAX_CONTEXT_TOKENS` to the window the provider documents.
- If the endpoint forwards `tool_reference` blocks, set `ENABLE_TOOL_SEARCH=true`
to restore deferred loading. DeepSeek's Anthropic API guide does not list
`tool_reference` among its supported content blocks, so this setting may cause
its requests to fail.
- To shrink the upfront catalog, add
`"REA_MCP_INPUT_SCHEMA_PROFILE": "compact"` to the `env` object of the
`mcpServers.rea` entry in `~/.claude.json` (`$CLAUDE_CONFIG_DIR/.claude.json`
when that variable is set), then restart Claude Code. Setup keeps this
override.
The sizes above come from REA's own `tools/list`. A live Claude Code session
against DeepSeek's API is unverified.
## Kimi Code
Setup registers `rea` in `~/.kimi-code/mcp.json`, or
`$KIMI_CODE_HOME/mcp.json` when set, while preserving unrelated settings and
creating a `.rea.backup` before updating an existing file. Kimi Code infers
stdio from the `command` entry, so setup does not add a `type` field. The
registration includes `startupTimeoutMs = 30000` and `toolTimeoutMs = 120000`.
Kimi Code also discovers personal skills in `~/.agents/skills` and project-local
`.agents/skills`, so no separate skill root is required; setup still installs
and verifies the matching skill files through the normal shared-skill flow.
Start a new Kimi Code session to pick up MCP and skill changes.
## Pi
For [Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md),
setup writes a `type: "stdio"` entry under `mcpServers.rea` in the user-level
`~/.pi/agent/mcp.json` and installs the bundled skill under `~/.agents/skills`.
Use `rea setup --client pi --dry-run --json` to inspect the plan, then
`rea setup --client pi --yes --json` to approve it. Check the registration with
`rea doctor --client pi --json`. Pi reads `mcp.json` as strict JSON, like its
own loader: comments, trailing commas, a byte-order mark, or an empty existing
file are invalid, and setup reports them instead of rewriting the file. Repair
such a file to plain JSON first, then rerun setup.
Setup follows Pi's `PI_CODING_AGENT_DIR`: unset or empty selects the default;
`~` and `~/` expand to the home directory; absolute paths and file URLs select
that directory; relative paths stay relative to the command's working directory.
On Windows, native paths, `~\`, and Git Bash/MSYS, Cygwin, and WSL drive paths
are normalized as Pi does. Run REA and Pi from the same working directory when
using a relative override. Pi has no profiles: `OMP_PROFILE`, `PI_PROFILE`,
`PI_CONFIG_DIR`, and OMP's `enabledServers`/`disabledServers` lists do not affect
Pi registration. Doctor accepts an omitted `type` for stdio but reports a
non-stdio type as invalid; `enabled: false` stays disabled.
REA configures only the user scope. Pi also reads `.pi/mcp.json` in trusted
projects; a same-name project entry can replace the user entry or override its
enabled state and exposure. Check that file if the user registration is aligned
but unavailable in a project. An extension that registers `/mcp` can replace
Pi's built-in MCP support and use its own configuration instead. Run `/reload`
in an existing Pi session after changing servers outside the session.
## Qwen Code
For Qwen Code, setup registers `rea` in the `mcpServers` table of the
user-level `~/.qwen/settings.json`, or `$QWEN_HOME/settings.json` when configured.
Setup and doctor follow Qwen Code's tilde and working-directory-relative home
overrides. Qwen Code also discovers personal skills in `~/.agents/skills`,
where setup installs the bundled workflow.
## Antigravity
Antigravity 2.0 discovers personal skills in `~/.gemini/config/skills`.
Setup installs its selected skill copy there.
## Gemini CLI
Gemini CLI uses `$GEMINI_CLI_HOME/.gemini/settings.json` when that home override
is nonempty, including its shared personal skills at
`$GEMINI_CLI_HOME/.agents/skills`. Setup and doctor check MCP allow/exclude
policies from system defaults, user settings, trusted workspace settings, and
system overrides. Workspace trust follows `GEMINI_CLI_TRUST_WORKSPACE`,
`GEMINI_RESTRICTED_MODE`, the effective `security.folderTrust.enabled` setting,
and the selected `trustedFolders.json` (including
`GEMINI_CLI_TRUSTED_FOLDERS_PATH`). Settings in an untrusted workspace do not
participate. A workspace or system `mcpServers.rea` replaces the entire user
entry; setup stops if the winning definition conflicts with its planned entry.
Review those files manually: setup never edits administrator/workspace settings
or grants workspace trust. Trust supplied only by a running IDE is outside this
standalone CLI check.
## Codex and Devin
Codex treats an unset or empty `CODEX_HOME` as `~/.codex`. Devin honors
`XDG_CONFIG_HOME` for its `devin/mcp_config.json` on non-Windows systems.
## OMP
For OMP, setup writes a `type: "stdio"` entry to the user-level
`~/.omp/agent/mcp.json`. It follows `PI_CONFIG_DIR` and
`PI_CODING_AGENT_DIR` (relative to the setup working directory), and the profile selected by `OMP_PROFILE` or
`PI_PROFILE` (`~/.omp/profiles/<name>/agent/mcp.json`). Setup also removes
`rea` from that file's `disabledServers` list, which would otherwise hide the
registration. Doctor treats an `enabled: false` entry as active when
`enabledServers` lists `rea`, as OMP does, unless `disabledServers` also lists
it. Run setup under each profile that should load REA.
## Hermes
For Hermes, setup writes a `mcp_servers.rea` entry to `config.yaml`, preserving
comments and unrelated settings. It resolves that file from `HERMES_HOME`, else
from the platform default Hermes itself uses — `%LOCALAPPDATA%\hermes` on
Windows, `~/.hermes` elsewhere — including any `HERMES_DATA_DIR_SUFFIX`. Hermes
connects an entry unless `enabled` is false, so setup states `enabled: true`.
Hermes personal skills install under the active Hermes home's `skills` directory.
Setup, doctor and uninstall follow the root's `active_profile` selection, as
plain Hermes CLI commands do. An explicit `HERMES_HOME` pointing to
`profiles/<name>` selects that named profile directly. An invalid, missing or
deleted sticky profile is reported instead of writing into the default home;
repair the profile or run `hermes profile use default` before retrying.
REA follows `HERMES_HOME` whitespace trimming, environment-variable expansion,
and home expansion before resolving that profile. Setup previews the resolved
configuration and skill destinations; doctor and uninstall use those same paths.
## Qoder CLI
For [Qoder](https://docs.qoder.com/cli/mcp-reference), setup writes
`mcpServers.rea` into the user-level `~/.qoder/settings.json` (the
`QODER_CONFIG_DIR` override is honored). Entries carry `command`, `args`, and
`env`; stdio is the default transport. Qoder CLI discovers personal skills
from `~/.qoder/skills` (following `QODER_CONFIG_DIR` when set), so setup
installs its skill copy there.
## Factory Droid
For [Factory Droid](https://docs.factory.com/harness/mcp), setup writes
`mcpServers.rea` into the user-level `~/.factory/mcp.json`. Entries carry
`command`, `args`, and `env`. Droid discovers personal skills from
`~/.factory/skills` ([skills](https://docs.factory.com/harness/skills)), so
setup installs its skill copy there.
## Trae
For [Trae](https://docs.trae.ai/ide/add-mcp-servers), setup writes
`mcpServers.rea` into the user-scope `mcp.json` under Trae's VS Code-style
user directory (`%APPDATA%\Trae\User` on Windows,
`~/Library/Application Support/Trae/User` on macOS, and
`~/.config/Trae/User` elsewhere). Entries carry `command`, `args`, and
`env`; stdio is the default transport. Trae discovers personal skills from
`~/.trae/skills` (`%userprofile%\.trae\skills` on Windows), so setup
installs its skill copy there.
The user-level MCP paths follow the editor's VS Code layout. Trae's official
documentation describes the raw JSON editor without specifying those paths;
loading this registration in Trae remains unverified.
## ZCode
For [ZCode](https://zcode.z.ai/en/docs/mcp-services), setup registers `rea` in the nested `mcp.servers` table of the
user-level `~/.zcode/cli/config.json`. Entries carry `command`, `args`, and
`env` without a transport discriminator. ZCode discovers personal skills from
`~/.zcode/skills` rather than the shared directory, so setup installs its
skill copy there.
ZCode reads the user-scope `~/.agents/mcp.json` fallback only while the native
`.zcode` config defines no servers at all, with no merging. When setup writes
the first native server into an empty table, it copies the fallback's
`mcpServers` entries into the native table so they keep loading, and leaves
the fallback file untouched. Existing REA environment overrides are preserved
when refreshing its command. A malformed or unreadable fallback file fails setup instead of
being silently suppressed; repair or remove it and rerun. ZCode disables a
server with `enable: false` on its entry; doctor reports such a registration
as stale, and rerunning setup removes the flag to repair it. Native configuration
uses strict JSON; setup refuses comments, trailing commas and a BOM rather than
writing a registration ZCode cannot load.
## DeepSeek Harness
DeepSeek Harness registers MCP clients through a Cordis YAML patch sequence
rather than a `mcpServers` object. `rea setup --client dsh` appends (or
replaces) the REA entry in the home patch `$DSH_HOME/cordis.patch.yml`
(`~/.dsh/cordis.patch.yml` when `DSH_HOME` is unset), preserving unrelated
patches and comments. `rea doctor --client dsh` checks that same entry id.
`rea uninstall` removes it by scanning supported clients; uninstall takes no
`--client` flag. The home patch applies to every Harness profile, so the
registration is written once and no per-profile edit is needed.
Setup, doctor and uninstall resolve `DSH_HOME` and `DSH_AGENTS_HOME` as Harness
does: unset, empty or whitespace-only values use `~/.dsh` and `~/.agents`,
respectively. Bare `~`, `~/` and `~\` expand to the user home; other relative
values resolve against the command's working directory. Nonblank values retain
their original spaces, and the agents root is independent of the Harness home.
Run REA and Harness from the same working directory when using relative overrides.
The written entry is:
```yaml
- insert:
- id: rea-mcp
name: "@deepseek-ai/dsh-mcp-client"
config:
serverName: rea
transport: stdio
command: /absolute/path/to/node
args: ["/absolute/path/to/rea/scripts/rea.mjs", mcp]
```
Setup preserves caller-owned Cordis expressions and replaces launcher fields
with literal values that doctor can verify. It refuses to edit an expression
that produces the entire REA config, or a group-targeted REA insertion. Use a
literal config and a root-level insertion as shown above.
For a global npm installation, the launcher is under
`$(npm root -g)/rea-agents/scripts/rea.mjs`. Restart Harness after setup; it
exposes tools as `mcp__rea__<tool_name>`. Run `rea setup --skill --dry-run`,
review the destination, then `rea setup --skill --yes` to install the workflow
in `~/.agents/skills` (`$DSH_AGENTS_HOME/skills` when configured); Harness
discovers that directory, and its native `skill` tool can load
`reverse-engineer-anything`.
REA's optional [native client verification](testing.md#end-to-end-integration-and-golden-evidence)
checks headless chat, skill loading, all tool input schemas and actual analysis
using the installed Harness and a local model fixture. This establishes the
Harness integration, with live DeepSeek API compatibility remaining unverified.
See the [Harness MCP guide](https://github.com/deepseek-ai/deepseek-harness/blob/639ed015397290b3745d163aafe02ffee4aa3f84/packages/mcp/mcp-client/README.md)
for its native configuration contract.
## Other client settings
For OpenCode, setup writes the V1 `mcp.rea` entry, which OpenCode V1 and V2
both load. If the configuration already uses OpenCode V2's native
`mcp.servers` table, setup registers REA there instead and replaces any earlier
`mcp.rea` entry from REA. Setup honors `OPENCODE_CONFIG_DIR` and
`OPENCODE_CONFIG`, preferring the later-loaded JSONC file when both JSON and
JSONC exist. The ordinary XDG global configuration still participates when a
custom directory is selected. Doctor checks the merged user registration; uninstall removes
REA-owned entries from every applicable user file so an older entry cannot
become active again. Malformed applicable files block setup before changes.
Grok Build loads `[mcp_servers.rea]` from `$GROK_HOME/config.toml`, or from
`~/.grok/config.toml` when `GROK_HOME` is unset or empty. Setup edits that server
table, `[mcp_servers.rea.env]`, and a root `disabled_mcp_servers` entry that
names `rea`. It sets `startup_timeout_sec = 30` and leaves every other name
in that list. The shared skill installed under `~/.agents/skills` is already
on Grok Build's skill path.
Grok Bot (`grok_bot`) is detected from `~/.grokbot`, or from `SAND_DATA_ROOT`
when that value is an absolute path. A relative or empty `SAND_DATA_ROOT`
stays on `~/.grokbot`. That directory is not the connector store. Grok Bot
keeps connectors in the signed-in account and runs them on its hosted
computer. It does not import `mcp.json` from the data directory, and it does
not attach a stdio server running on this machine. Setup does not call the
account connector API, does not write a registration file, and does not report
the data directory as aligned. Ask the Grok Bot chat to add a custom MCP
server named `rea` that runs on the Bot's computer:
```bash
npx -y rea-agents@<version> mcp
```
Do not put credentials in that command or its arguments. `rea doctor --client grok_bot`
reports this manual step. `rea uninstall` does not remove the account connector.
+538
View File
@@ -0,0 +1,538 @@
# Client and agent verification
Use these optional lanes to verify an installed client or evaluate an agent's
REA workflow. [Testing REA](testing.md) provides the normal development commands
and provider lanes. Each record below states its host, client version, model
fixture, and remaining coverage; a native-client run with a loopback model does
not establish live model-service acceptance.
## End-to-end, integration and golden evidence
The optional `verify:qwen-client`, `verify:pi-client`, and `verify:hermes-client`
lanes require an installed native client. Select its executable with
`REA_VERIFY_QWEN_COMMAND`, `REA_VERIFY_PI_COMMAND`, or `REA_VERIFY_HERMES_COMMAND`.
They configure disposable profiles through the public REA CLI, preserve caller
settings and backups, and exercise real stdio MCP calls and skill loading. The
`call` mode checks complete catalog discovery and actual JavaScript Evidence;
pass `-- chat` to check plain chat with REA enabled. Qwen also reads the full
result that its client offloads, and Pi exercises default codemode execution.
Use `REA_VERIFY_RUNTIME_ROOT` to select a production-only installed REA package.
Receipts retain client versions, result digests, host coverage and owned-process
cleanup. These lanes use a deterministic loopback model and do not prove live
model-provider compatibility. Pi and Hermes remain POSIX-only. Qwen, Pi and Hermes
bind both `HOME` and `USERPROFILE` to the disposable account. Qwen and Pi discover
the shared personal skill through their default search paths without an explicit
skill setting; Hermes discovers it through the selected `HERMES_HOME`.
Set `REA_VERIFY_HERMES_STICKY_PROFILE=1` to exercise native Hermes selection of
a named sticky profile with `HERMES_HOME` still pointing to its root.
The Qwen lane also supports native Windows (verified with Qwen Code 0.25.1 and
Node 24.19.0 for both `chat` and `call`). With Node 24.18.0, the same `call`
journey reached full Evidence readback but the client aborted during exit with
`UV_HANDLE_CLOSING`; that run remains a failure, not a compatibility claim.
Select the npm install's
`node_modules/@qwen-code/qwen-code/cli-entry.js` with `REA_VERIFY_QWEN_COMMAND`,
because `execFile` cannot launch the `qwen.cmd` shim directly. A selected `.js`,
`.mjs` or `.cjs` entry point runs under the verifier's Node. Windows path checks
use native separators; persisted output is read through Qwen's real shell tool
with a shell-independent encoded Node payload. Windows receipts report process
lineage as unavailable when the verifier has no retained native Job Object lease.
`verify:gemini-client` requires an installed Gemini CLI (verified with
`@google/gemini-cli@0.63.0` on POSIX and native Windows); select it with
`REA_VERIFY_GEMINI_COMMAND`. A selected `.js`, `.mjs` or `.cjs` entry point runs
under the verifier's Node. On Windows, select the npm install's
`node_modules/@google/gemini-cli/bundle/gemini.js`, because only a `gemini.cmd`
shim reaches `PATH`.
The optional lane uses native `GEMINI_CLI_HOME` discovery in an isolated
Git project, checks setup plans, backups and idempotence, activates the installed
personal skill, validates all forwarded input JSON Schemas against their declared
dialect, forwards the complete REA catalog and checks full JavaScript
Evidence for a Unicode path. Use `-- chat` for ordinary chat with REA enabled,
or `REA_VERIFY_RUNTIME_ROOT` for a production-only installed REA package.
The local fixture exercises the real Gemini API adapter, including its
`untrusted_context` tool-result envelope. Token counts are synthetic; live
Google API compatibility remains unverified. The lane disables
the client's memory-based relaunch to preserve the caller's Node heap budget.
`verify:opencode-client` requires an installed OpenCode (verified with
`opencode-ai@1.18.35`); select it with `REA_VERIFY_OPENCODE_COMMAND`.
The optional POSIX lane configures isolated XDG roots and `OPENCODE_CONFIG_DIR`,
checks setup plans, backups, preserved JSONC comments and idempotence, activates
the installed skill, validates all forwarded input schemas and verifies complete
JavaScript Evidence for a Unicode path. JSONC is the default fixture; set
`REA_VERIFY_OPENCODE_CONFIG_FORMAT=json` for JSON. Use `-- chat` for ordinary
chat or `REA_VERIFY_RUNTIME_ROOT` for a production-only installed package.
The native core runs with external plugins disabled through `OPENCODE_PURE`;
the local OpenAI-compatible fixture has a caller-declared one-million-token
context and synthetic usage. Skills use an explicit isolated `skills.paths`
directory, so default OS-home discovery remains unverified. Live model APIs and
native Windows are also unverified.
`verify:claude-client` requires installed Claude Code (verified with
`@anthropic-ai/claude-code@2.1.296` on POSIX and native Windows); select it
with `REA_VERIFY_CLAUDE_COMMAND`. On Windows, select
`claude.exe`: npm installs put only a `claude.cmd` shim on `PATH`, and the real
executable is under `node_modules/@anthropic-ai/claude-code/bin`.
The optional lane uses native `CLAUDE_CONFIG_DIR` discovery, an independent
Git workspace, user settings and default built-in tools. It preserves caller
preferences and backups, checks idempotent setup, waits through the native
`WaitForMcpServers` tool when discovery is pending, and validates the complete
catalog and forwarded input schemas. Call mode loads the personal skill through
native `Skill`, checks its full body and verifies named-schema JavaScript Evidence
for a Unicode path. Use `-- chat` for ordinary chat with REA enabled, or
`REA_VERIFY_RUNTIME_ROOT` for a production-only installed package. The local
Anthropic Messages/SSE fixture preserves native resource-hint envelopes in its
request artifacts. Usage is synthetic; live Anthropic API and bare-mode
personal-skill activation remain unverified by this lane.
`verify:copilot-client` requires installed GitHub Copilot CLI (verified with
`@github/copilot@1.0.95`); select it with `REA_VERIFY_COPILOT_COMMAND`.
The optional POSIX lane uses native `COPILOT_HOME` discovery, an independent
Git workspace, guarded setup plans, preserved registrations, backups and
idempotence. Native `skill add` registers the isolated installed skill; call
mode loads its full body, validates all forwarded input schemas and checks
named-schema JavaScript Evidence for a Unicode path. When Copilot spills a
large MCP result, the model requests native `view` with `forceReadLargeFiles`
and validates the complete returned Evidence rather than its preview.
Use `-- chat` for ordinary
chat or `REA_VERIFY_RUNTIME_ROOT` for a production-only installed package.
The native offline BYOK adapter uses a loopback OpenAI completions/SSE fixture
and `gpt-5.4` model metadata; model inference and token usage are synthetic.
Set `REA_VERIFY_COPILOT_MODEL` to examine another client model ID and
`REA_VERIFY_COPILOT_WIRE_MODEL` for its backend model name (defaults to the client
ID). Every actual HTTP request's model is checked and recorded separately.
Both `HOME` and `USERPROFILE` point to the disposable account; native `skill add`
still selects the installed skill directory explicitly.
The same native client passed full-profile chat and analysis with the actual
wire model `gpt-4.1` under a distinct BYOK model ID:
```bash
REA_VERIFY_COPILOT_MODEL=rea-gpt-4.1 REA_VERIFY_COPILOT_WIRE_MODEL=gpt-4.1 npm run verify:copilot-client -- chat
REA_VERIFY_COPILOT_MODEL=rea-gpt-4.1 REA_VERIFY_COPILOT_WIRE_MODEL=gpt-4.1 npm run verify:copilot-client -- call
```
These runs retain full canonical input/output schemas, all tools, installed-skill
loading and complete Evidence readback. They use the fixture's requested
one-million-token BYOK prompt setting; effective capacity and live API acceptance
remain unknown. With the built-in `gpt-4.1` model ID, the verified client still
blocks the full input-schema profile before HTTP with
`compaction_static_context_blocked`, even when requesting a larger BYOK prompt
capacity. The effective capacity is unknown. The default verifier preserves
the complete catalog and full schemas. To verify the existing compact profile:
```bash
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- chat
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- call
```
Both native workflows passed with the complete tool inventory. The verifier selects the
profile only in its disposable Copilot registration and records it in the
receipt. Compact advertisements can reduce nested validation structure; the
server still validates complete canonical inputs. This is a tested alternate
workflow, not a fix for the full-profile client/model limit tracked in #1554.
Live model APIs, native Windows and default OS-home skill discovery remain
unverified.
`verify:grok-client` requires installed Grok Build (verified with the official
Linux x64 1.0.50 binary); select it with `REA_VERIFY_GROK_COMMAND`. This optional
POSIX lane isolates `GROK_HOME`, an independent Git workspace and additional
skill roots, disables foreign configuration discovery, guards setup writes,
preserves unrelated registrations/backups and checks idempotence. Call mode
loads the full skill through native `read_file`, queries all REA names and input
schemas through native `search_tool`, and calls analysis through `use_tool`.
If discovery reports `partial`, the native agent diagnoses REA registration and
retries discovery. No startup-timeout override or fixed readiness delay is used.
A larger Unicode-path fixture exercises native result offloading: the full
retained Evidence is validated against its named schema, then native terminal
queries recover the selected export facts, subject and full artifact digest in
the next model request. This verifies useful artifact recovery without claiming
that every offloaded graph fact enters model context. Native line-number and
truncation envelopes remain in the request artifacts. Use `-- chat` for ordinary
chat, or `REA_VERIFY_RUNTIME_ROOT` for a production-only installed package.
The loopback OpenAI completions/SSE custom model declares a one-million-token
context and synthetic usage. Live xAI APIs, native Windows and default OS-home
skill discovery remain unverified.
`verify:commandcode-client` requires installed Command Code (verified with npm
1.79.2); select it with `REA_VERIFY_COMMANDCODE_COMMAND`. Auth, MCP tokens and
sessions use the actual OS home, so provision a disposable POSIX account rather
than overriding `HOME`. Set `REA_VERIFY_COMMANDCODE_ACCOUNT_HOME` to that account's
actual home and create `.rea-client-verification` there with exactly
`Disposable REA client verification account` followed by a newline. The lane
owns that account's `.commandcode` configuration and shared REA skill. Build the
runtime as the checkout owner, then run `node scripts/verify-commandcode-client.mjs`
as the disposable account; the npm shortcut also needs a writable checkout.
The verifier does not create accounts or install clients. It guards setup
targets, preserves an unrelated registration and backups, checks idempotence,
and verifies native default shared-skill discovery and complete activation.
Default deferred-schema delivery stays enabled: the native prompt advertises
every REA tool, exact-name `search_tools` returns every input schema, and a native
shell query recovers the saved catalog's count and digest when the client spills
it. Actual analysis of a Unicode-path fixture delivers full named-schema Evidence
to the next model request. Use `-- chat` for ordinary chat or
`REA_VERIFY_RUNTIME_ROOT` for a production-only installed package. The loopback
OpenAI completions/SSE model is a keyless BYOK endpoint declaring a million-token
context with synthetic usage. A synthetic account-key value satisfies the
client's print-mode gate; native local-only mode refuses hosted API calls.
Updates, telemetry and cron are disabled. Hosted authentication, live models
and Windows remain unverified.
`verify:omp-client` requires installed OMP (verified with the official Linux x64
18.8.7 binary); select it with `REA_VERIFY_OMP_COMMAND`. This optional POSIX lane
isolates the default agent, global config and XDG roots, guards setup targets,
preserves unrelated registrations and backups, and checks idempotence. It
compares native rejection of an invalid profile with REA's refusal to plan
fallback writes. OMP's default `xd://` interface mounts the complete REA catalog
as devices: call mode reads the complete installed skill and tool documentation,
then dispatches analysis through native `write`. It checks full named-schema
JavaScript Evidence with a Unicode path in the next model request. Device
metadata and documentation are distinct from forwarding all JSON schemas as
model functions. Native print-mode MCP readiness uses its defaults.
Use `-- chat` for ordinary chat or `REA_VERIFY_RUNTIME_ROOT` for a production-only
installed package. The loopback OpenAI completions/SSE model fixture declares a
one-million-token context and synthetic usage. Live model APIs, native Windows,
named-profile native execution and default OS-home skill discovery are
unverified; skill discovery uses an explicit isolated custom directory.
`verify:deepseek-client` requires an installed DeepSeek Harness (`dsh`;
verified with `@deepseek-ai/dsh@0.2.0-rc.2`) and Git. Set
`REA_VERIFY_DEEPSEEK_COMMAND` to select its executable. This optional POSIX lane
seeds caller model settings and a stale Cordis registration in an isolated
`DSH_HOME`, then runs public `setup --client dsh` dry-run, setup, repeated setup,
and scoped doctor. It checks the backup and unrelated patches before booting
Harness with the generated registration. After native execution it uninstalls
through the public CLI and checks that only caller patches remain. `HOME` and
`USERPROFILE` point to the disposable account. The native default
`~/.agents/skills` search path discovers
the installed skill without a `DSH_AGENTS_HOME` override. A separate Git root prevents inherited project skills from
masking a missing installation. The default `call` mode checks skill loading,
complete catalog discovery, the actual forwarded input schemas and regexes,
and full JavaScript Evidence for a Unicode path. Use `-- chat` for ordinary
chat with REA enabled, or `REA_VERIFY_RUNTIME_ROOT` for an installed package.
The fixture uses Harness's custom OpenAI adapter and the one-million-token
context capacity declared by its default DeepSeek Flash model; it does not
establish live DeepSeek API or native Windows coverage. Receipts include
request artifacts, result digests and owned-process lineage.
The DSH acceptance journey also exercises public setup, approval, doctor,
generated-launcher MCP startup and uninstall without an installed Harness.
That journey proves REA workflow behavior, not native Cordis expression
evaluation. Filesystem regressions retain tagged values and unrelated rows
across alias dependencies and check refusal of unsafe YAML without mutation.
Full E2E tests invoke the production command dispatcher and real providers,
without fake launchers, runners or responses. `verify:keyed-archive` writes
Foundation binary, XML, and integer archives using one run-owned Swift module
cache. It checks CLI and separate stdio MCP parity, pagination, escaping-path
rejection, an XML graph golden, and numeric meaning for safe controls, unsafe
integers, boxed numbers, and integral-valued reals. `verify:asset-catalog`
compiles source-owned colors with
`actool`, invokes real `assetutil`, then checks CLI/MCP results, exact catalog
digest, every raw metadata field, pagination and malformed input rejection.
Neither artifact workflow requires Hopper or launches it. Both run in macOS CI.
`verify:macos-bundle` needs only macOS with Command Line Tools. It compiles a
source-owned app with `clang`: a versioned framework, XPC services, an app
extension, a login item, a privileged helper, launchd plists, and a helper tool,
signed ad hoc. It packs the app as a directory, a `ditto` ZIP, and an APFS DMG,
then checks that `inspect-artifact` plus `project-apple-application-graph`
report the same bundle anatomy for all three through the CLI, with stdio MCP
parity. It also checks that the DMG is detached afterwards. The same app
covers `trace-dylib-resolution`:
- each resolution status and finding, with CLI/MCP parity;
- for every traced image, dependencies, rpaths, and install names equal to
`otool -l`;
- for the main executable and an XPC service, a predicted load order equal to
the images dyld actually loads under `DYLD_PRINT_LIBRARIES`.
The lane also compiles executable/library pairs with empty embedded directory,
versioned-path, and suffix settings. It checks present and removed dependencies
through CLI and MCP, and compares actual `DYLD_PRINT_SEARCHING` diagnostics for
root-level candidates. These cases distinguish an empty search directory from
an empty versioned scan or a suffix that only repeats the original path.
It runs in macOS CI.
Prepare the native inspector before cases that measure producer output or exit
behavior; keep startup deadlines and cancellation in distinct cases. Run real
process-capture verification separately from package or build checks. On macOS,
new npm processes can become token-unreadable after changing their display
title and prevent verified cleanup during a capture's ownership sweep.
MCP SDK transport tests with recording providers remain integration tests.
They are useful for schema drift and failure projection but do not prove that
Hopper, Ghidra or another substituted engine works. `verify:package` proves
packaging/install behavior and fake-provider integration; use the corresponding
real-provider lanes for engine claims. Packed-bridge checks verify shipped files and Python syntax.
Real Apple dispatch and Interface
Builder verifiers currently prove format integration through production readers.
`verify:hopper` exercises an installed Hopper through the production stdio MCP
server and CLI. It checks source-owned call chains, CFG edges, references and
complete large inventories, then probes unnamed bookmarks, annotation clearing,
batch validation before mutation, malformed addresses and regexes, segment-end
partial reads, and synthetic file-offset rejection. Advertised schemas are
validated in their JSON Schema dialect and successful replies are checked against
their advertised output schemas. Navigation checks cover interior-object cursor
readback and mapped-memory boundaries. Annotation probes verify invalid native
text and duplicate destinations/names before mutation, preserve unselected label
owners, and exercise explicit batch label swaps. Function locals retain observed
names and stack displacements. Graph probes check symbol/interior-address parity
and a source-owned recursive cycle; literal tracing preserves complete queries
and whitespace. Disposable binary copies prove that switching and closing
actually removes the native document, and that CLI byte, function-dossier,
literal-trace results and invalid-address diagnostics agree with MCP.
No provider is mocked in this lane.
Real search probes cover Unicode names, literal metacharacters, case and regex
modes, annotation cache invalidation, complete native fragments of long literals
checked against byte reads, escaped UTF-8/control text, byte-preserving Latin-1
decoding, and Hopper's UTF-16 symbol-name truncation boundary. It verifies native
CallReference classifications across reference and dossier results, retains long
string fragment metadata in dossiers, and exercises pathological regex deadline
and cancellation followed by successful requests in the same native session. When the macOS
Objective-C fixture is present, the lane also verifies native UTF-16 string objects
and their inferred encodings against their actual bytes. Native terminal calls
are checked across reference, instruction, assembly, block-range and procedure-length
projections; block endpoints are normalized using actual native membership.
Exact Objective-C names also exercise named CLI selectors for function, instruction,
decompilation, reference and search operations. A literal `--help` trace query proves
that selector data is preserved independently of global CLI flags.
Unmapped annotation destinations and
oversized later batch names fail before any earlier edit is applied.
`verify:hopper:deadlines` checks the native client's optional request deadlines
on macOS with an owned source-built fixture. Zero and elapsed deadlines must
leave native comments unchanged, including when a synchronous progress observer
delays timer dispatch. It also observes a short analysis deadline and verifies
subsequent wire recovery and clean shutdown. Caller timeout does not interrupt
Hopper's synchronous native operation. Socket boundary tests deterministically
cover active and queued expiry, late replies and timer cleanup.
The Linux demo lane remains a separate `verify:hopper:linux` command. Hopper
6.4.2's ELF loader did not recognize the original 4,119-byte literal or three
non-ASCII byte strings in the measured fixtures. The lane retains complete raw
byte checks for those literals through source-owned symbols. Recognition of the
original full-length literal is bound to its exact source address; its decoded
object coverage remains explicitly unverified on Linux even when recognized.
Absent non-ASCII recognition also reports decoded-object coverage as unverified.
Recognized non-ASCII and control-text objects must still match their source
bytes, encoding, filters and searches.
A separate 548-byte ASCII literal requires native fragmentation, shortened
provider display, complete decoded fragments and dossier provenance on Linux;
the macOS lane retains the original full-length recognition requirement. These
are observed fixture/build capabilities, not universal Hopper length limits.
Its lifecycle checks attempt competing CLI launches for the active target and a
different target, requiring rejection before either can change the owning
MCP session's documents or procedures. Lease boundary tests also preserve
unresponsive or malformed live endpoints while allowing confirmed stale
sockets to recover.
`verify:setup:linux` exercises installation on a clean supported Linux runner as
root. Use a disposable VM: this lane installs Hopper and its Linux runtime
dependencies through the system package manager. Set `REA_VERIFY_SERVER_COMMAND`
to the CLI entrypoint being verified. The command builds and checks source-owned
ELF fixtures before testing the read-only Hopper setup plan, approved
installation, scoped doctor, real CLI/MCP analysis, rejection of a modified
launcher, idempotent setup without requesting a reinstall, and owned child
cleanup. Before installation and after teardown, a clean-runner process scan
also rejects surviving Hopper demo helpers or Xvfb groups, including orphans
whose launcher exited. This scan observes processes without signaling them.
Hopper detection compares executable file identity through `/proc`, covering
alternate or configured launcher paths even after an invoked symlink is removed.
The dedicated runner must have no unrelated Hopper/Xvfb sessions. It requires
the repository's pinned development toolchain and an
existing host C compiler, `file`, `nm`, and `strings`. Use `verify:hopper:linux`
instead when Hopper is already installed.
`verify:hopper:fat` is a separate macOS lane requiring installed Hopper and the
existing Xcode clang/lipo toolchain. It compiles arm64/x86-64 thin executables and
one- and two-slice FAT32 containers, verifies exact/interior address mappings
against bytes in the original files, checks CLI/MCP parity, and checks owned
runtime cleanup. Source byte changes, removal, permission denial (for non-root
callers), and nonregular replacement must retain native partial mapping facts,
reject unverified original-file coordinates,
and recover after restoration. Single-slice FAT cases also relocate the slice
without changing its loaded bytes. `verify:hopper:fat64` additionally checks
FAT64 preparation through Hopper's native Mach-O loader, source-container
mappings, profile identity,
malformed and ambiguous slice rejection, and temporary-image lifetime. Both
lanes have been verified on Hopper 6.1.0-demo; this establishes REA's prepared
FAT64 workflow, not native FAT64-loader support.
Cross-architecture fixture compilation is not required by `verify:hopper`.
Golden tests use immutable captured text inputs with producer/source provenance
under `tests/fixtures/golden/`. Expected results are reviewed for the semantic
claim; capture commands do not automatically approve new expected outputs.
Do not call handcrafted utility output or synthetic binary builders real-data
goldens. Keep unsupported binary layouts and malformed boundaries as targeted
regressions until a real fixture establishes equivalent coverage.
`verify:browser` exercises source-map failure isolation and expanded-output limits through the compiled CLI and stdio MCP with real Chrome. It also submits five 2 MiB source-map annotations, checks retained script identities and explicit map omissions, and closes its owned fixture target. Replacement and document-reset behavior has separate producer-boundary coverage.
The raw Chrome fixture launchers use `--password-store=basic` so Linux startup
does not wait for an unlock prompt from a reachable but locked desktop keyring.
Such a stall leaves the initial page unloaded with empty or placeholder CDP URLs;
it does not establish permission to capture that page. See
[the keyring diagnosis](https://github.com/morluto/rea/issues/1384#issuecomment-6093781686).
`verify:browser` also captures a source-owned noise canvas as a real PNG above
8 MiB through the CLI and stdio MCP, with complete byte/digest parity and real PNG
decoding. Its SDK client explicitly permits the larger inline JSON response;
this lane does not establish large image-comparison request transport coverage.
The scenario checks read locale, timezone, device scale and viewport dimensions
from actual page JavaScript through CLI and stdio MCP DOM captures. They also
verify that attached-page emulation is released after success, initialization
failure, action failure and cancellation while the external target stays open.
`verify:browser:network` is a focused real-browser lane for transaction identity,
selected request/response bytes, binary and compressed responses, duplicate
headers, credential and declared-secret redaction, redirects, streaming cutoff,
CLI/MCP parity, and owned-profile cleanup. Set `REA_BROWSER_EXECUTABLE` to an
installed Chrome-family browser. An optional script argument selects an already
installed package's `scripts/rea.mjs` entry point for packaged-artifact checks.
The complete `verify:browser` lane includes these same checks.
After building, `verify:browser:dom` checks empty and HTML-whitespace form
destinations against a native Chrome DOM-property oracle through CLI and stdio
MCP. It requires `REA_BROWSER_EXECUTABLE` and accepts an optional installed REA
entrypoint. The full browser lane includes the same public-adapter assertions
before other fixtures navigate the selected page.
`verify:browser:scripts` checks active script capture → exact-byte export →
existing static JavaScript analysis through CLI and stdio MCP, including
manifest readback, competing query variants, and resolved relative imports.
It uses an installed browser and accepts an optional installed REA entrypoint.
The complete `verify:browser` lane also exercises passive script export through
both public adapters. See [website script export](website-script-export.md).
`verify:browser:modules` compares CLI and stdio MCP traces against an independent
real Chromium module-loading fixture: import-map scopes, package prefixes,
null/backtracking rejection, query/fragment identities, repeated module instances,
lazy and computed unknowns, exact source/map identities and no implicit refetch.
Set `REA_BROWSER_EXECUTABLE`; the optional script argument selects an installed
package entrypoint. The complete `verify:browser` lane and existing conditional
Chrome CI include this verifier. See [module relationships](website-module-trace.md).
## Agent evaluation and conformance records
Evaluate native, JavaScript, managed and browser investigation tasks through a
real local Codex CLI with:
```bash
npm run verify:agent
```
The lane runs scenarios sequentially in standalone Codex (`--no-daemon`) with a
disposable HOME and CODEX_HOME under `~/.cache/rea-agent-evaluations/`. Set
`REA_AGENT_EVAL_ROOT` to choose another parent directory outside the OS temporary
directory: current Codex refuses helper aliases under `/tmp`, preventing shell
commands and skill reads even if MCP works. Each owned run directory is removed
after verification unless fixture retention is requested. The lane
installs the packaged skill and MCP registration through public `rea setup`,
rather than copying a skill into a project that also inherits the caller's
skills and configuration. Only authentication is copied, when present, and it
is removed even with `REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
rules, plugins or history is copied; shell snapshots and additional agents are
disabled. Existing API-key environment authentication remains available.
The native missing-provider scenario preapproves only `open_binary` and
`close_binary` in the disposable client so it reaches REA's actual provider
boundary instead of stopping at Codex approval. This does not validate a real
native engine or authorize arbitrary runtime execution.
Select a smaller run with `REA_AGENT_EVAL_SCENARIOS`, a comma-separated list of
scenario IDs. `asar-zh` exercises the packaged desktop rubric from a Chinese
request. `javascript-module-view` exercises summary analysis, module paging and
item inspection in a tree with unrelated modules; its routing and text checks
remain heuristic, so review the retained transcript for selector choice and
coverage claims. Set `REA_CODEX_CLI` to an installed Codex executable and
`REA_AGENT_EVAL_MODEL` to an account-supported model. Use an external resource
limit for the whole process tree when testing on a constrained host; a Node
heap limit alone does not bound client and child-process memory.
`missing-target` leaves desktop fixtures visible without selecting one in the
request. It requires zero REA calls and a structured request for an app name or
artifact path. Its `expectedFirstTool` is `null`, and
`targetClarificationPassed` grades that limited routing outcome independently
of analysis prose heuristics. Analyzing a nearby example cannot pass this case.
The packaged desktop, parser-comparison, and managed scenarios assess a closed set of
known fixture claims. Each final answer must be a strict JSON manifest containing
exactly the requested claim IDs, values, Evidence IDs, and Evidence authority and
confidence. Golden values are not included in the prompt. The evaluator checks
exact values against both the fixture oracle and authenticated successful REA
results; an Evidence ID by itself is insufficient. Missing, duplicate, extra,
contradictory, incorrectly sourced, or unsupported claims fail the scenario.
The desktop rubric covers the exposed bridge API and members, renderer and main
IPC operations, and the resolved preload. It binds its Evidence to the exact
packaged artifact path and SHA-256. The parser rubric checks the precise heading
depth addition, discriminant, complete comparison counts, and the explicit limit
on runtime semantics. Its comparison must link to the two delivered source
analyses and use the requested module and export selectors. Boundary tests package
the same source-owned desktop fixture and compare the same source-owned parser
files through actual MCP tool results, then verify that fabricated answers fail.
The managed rubric checks the exact public top-level type set and the CLI entry
point's declaring type and method name. The exact type-set claim requires an
unfiltered member inventory: `selection` must be null and the Evidence parameters
must omit `type_names`. A selected inventory can still ground the entry-point
claim. Both inspections must describe the freshly built DLL's SHA-256. Entry-point
resolution additionally requires complete artifact and member metadata, matching
non-null module MVIDs, and a unique method at the header's metadata token. These are
static metadata claims, not runtime behavior;
compiler-generated member counts, token values, and MVIDs are not golden answers.
Per-scenario `factualCorrectness` is `passed`, `failed`, or `not_assessed` within
the `configured_fixture_claims` scope. A pass establishes only the configured
claims, not unrestricted factual correctness. Native, browser,
navigation-context, and address-context scenarios have no factual rubric and
remain `not_assessed`.
The closed factual scenarios request complete producer Evidence on the initial
inspection or analysis so their source selectors can authenticate all configured facts.
Summary/page/item workflows are exercised separately by `javascript-module-view`;
its heuristic gate does not establish full-producer factual correctness.
All scenarios retain routing, workflow, validation, repetition, process-exit,
and token-use gates. Configured factual scenarios additionally require a factual
pass. Their answer-text heuristics are diagnostic and do not affect the gate.
Validation counts include complete text-only REA `invalid_request` envelopes
and failed SDK calls whose arguments violate the named current REA contract.
Client approval rejections and provider unavailability remain distinct failures;
truncated JSON previews are not parsed into invented error codes.
Repeated `binary_session` observations separated by a successful `open_binary`
or `close_binary` are treated as lifecycle verification. An unchanged retry,
including one after a failed lifecycle call, still counts as repetition.
Scenarios without a rubric retain the legacy text-heuristic gate.
`answerTermCoverageMet` checks case-insensitive substrings,
`epistemicCuePresent` checks keywords, and `finalCitesEvidence` checks only an ID's
presence. These metrics can still accept fabricated prose and must not be read
as factual assessments. Set `REA_AGENT_EVAL_TRANSCRIPT_DIR` to retain complete
tool results and final answers for review.
Report schema version 4 adds negative routing scenarios with nullable
`expectedFirstTool` and a `targetClarificationPassed` outcome. Consumers must
allow an expected absence of REA calls; this does not make an analysis scenario
pass without its required tools. Version 3 changed the top-level
`factualCorrectness` from a constant
string to an assessment summary with status, scope, and assessed/passed/failed/
not-assessed scenario counts. Scenario records include the factual assessment and
configured claim IDs. The evaluation scope is
`routing_workflow_and_configured_fixture_claims`. Update report consumers for
these changes. The narrower heuristic field names introduced in version 2 remain:
`answerHeuristicsMet`, `epistemicCuePresent`, `answerTermCoverageMet`, and
`requiredAnswerTermGroups`.
Regenerate the managed conformance manifest and Evidence completion ledger from
live verification results, or check them for drift:
```bash
npm run evidence:generate
npm run evidence:check
```
The records preserve unsupported and unverified coverage as explicit unknowns.
Run the matching real-tool prerequisites described in this guide.
+68 -1
View File
@@ -35,10 +35,22 @@ license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md)
and whitespace. Decoded-byte SHA-256 identifies actual selected EVM bytes.
Neither digest is the Ethereum Keccak code hash.
- Full bytecode, upstream function offsets and original producer representation
are returned inline. Offsets are byte positions in decoded bytecode.
are returned inline by default. Offsets are byte positions in decoded bytecode.
- Upstream also returns terminal metadata during selector recovery. Its reported
values remain in `raw_result`; integer precision and non-text-key coverage are
unknown in the JS binding. Full bytecode retains the original encoded metadata.
- `bytecode.metadata` independently interprets a plausible terminal CBOR root
map from the selected bytes. Integers use decimal strings; byte strings use hex,
and scalar keys retain their types. The recorded offset/length addresses the
original bytecode, so summary delivery does not copy an opaque CBOR payload.
Nested containers have partial scalar coverage; unsupported or malformed
candidates report `not-decoded` with a diagnostic. Decoding is bounded to 64
container levels and the terminal payload's encoded size.
- An unavailable trailer does not prove metadata absence or Solidity origin.
Compiler-version bytes/text are metadata declarations, not verified compiler,
source-file or deployed-contract identity. Historical omission parses as a null
unknown facet without changing the original Evidence record. Metadata/source
URLs are not fetched.
- Selectors, argument strings and state mutability are **inferences**. Function
names/signatures are not looked up. Optimized code may produce different
mutability candidates; recovered types are not a verified source ABI.
@@ -47,6 +59,49 @@ license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md)
- Analysis is offline: no target runtime execution, implicit chain/RPC/explorer
lookup, wallet action, transaction broadcast or network request.
## Selected interface delivery and retained views
Existing CLI calls and MCP calls with omitted or `complete` detail keep the
complete inline result. On a retaining MCP server, select `detail: "summary"`
when the question needs every inferred function and its identity/uncertainty,
without bytecode text or duplicated raw stdout:
```json
{
"name": "inspect_evm_interface",
"arguments": {
"path": "/artifacts/runtime.hex",
"encoding": "hex",
"detail": "summary"
}
}
```
The result is distinct derived Evidence linked to its complete parent. Its
`normalized_result.summary` preserves every producer-returned selector, decoded
byte offset, dispatch, argument/mutability string including null, carrier/decoded
identities, original limitations and explicit unknowns. Payload `evidence_kind`
remains `inferred`. It preserves stderr/truncation and records stdout text length.
Complete-within-view coverage covers returned candidates; discovery completeness
remains unknown even for an empty or exhausted collection. Analysis still runs;
delivery selection does not save interpreter work or change source parameters.
Use `normalized_result.parent_evidence_id` with `inspect_analysis_view` source
kind `retained-evidence`. EVM view kind `evm` pages `functions` by original row
order, or `bytecode_hex`, `stdout`, `stderr` by UTF-16 code units. Hex pages preserve
the original lowercase text without a prefix; two characters represent one decoded
byte. A text-page offset differs from a function's decoded-byte offset. Complete
parent Evidence remains accessible through the normal ledger/export workflow;
reading views does not rerun analysis or change its identity. Existing record
retention and close semantics are unchanged.
Portable historical complete EVM Evidence can use source kind `inline` without
a provider. A fixed server without Evidence retention rejects selected summary
delivery before starting analysis and continues to support complete delivery.
Discover the actual advertised schema before sending `detail` to an older server;
use its complete operation when an equivalent workflow is required. The one-shot
CLI keeps its existing complete result.
## Resource and lifecycle limits
Inputs are limited to 4 MiB, complete replies to 16 MiB and combined diagnostic
@@ -82,3 +137,15 @@ The real verification lane traces representative raw/hex CLI workflows and check
that only declared Node/prlimit launchers and process ownership inspection run.
Socket traces permit local stdio socket pairs and reject attempted network requests.
These observations verify those fixtures; they are not a general network sandbox.
For the independent metadata parser, `npm run verify:solidity:metadata` uses an
existing caller-selected solc 0.8.30 module:
```sh
REA_VERIFY_SOLC_MODULE=/absolute/path/to/solc/index.js npm run verify:solidity:metadata
```
It compiles the owned fixture with unoptimized, viaIR, content-hash-free and
CBOR-free settings, verifies the observed trailer facts and reports local decode
timings. No contract is executed. This lane does not establish the complete
Linux EVM provider's CLI/MCP, resource-limit or cleanup behavior.
+4 -2
View File
@@ -123,8 +123,10 @@ Select a verified native child for the existing tools:
```
Then use `analyze_function`, `inspect_native_api` or the other native primitives.
Native architecture and executable validation still apply. This milestone does
not add MIPS/PPC/RISC-V decompilation, raw flash load profiles, bootloader/device
Native architecture and executable validation still apply. Generic
[MIPS ELF](ghidra-mips.md) and [RISC-V ELF](ghidra-riscv.md) lanes are separate
documents with their own admission boundaries. This milestone does
not add PPC decompilation, raw flash load profiles, bootloader/device
tree semantics, vulnerability scanning, emulation or hardware runtime capture.
## Resource and lifecycle policy
+189
View File
@@ -0,0 +1,189 @@
# Flutter build identification
REA identifies a Flutter app's build facts by **pure parsing** of its APK:
the Dart VM snapshot hash, per-ABI library digests, the engine's GNU
build-id, and any labeled version strings. No engine, no tool installation,
no execution, and no external lookups — this family answers "which Dart SDK
build is this, and what exactly is in the payload?" before any deeper
native analysis.
This is the triage step for Flutter reversing: the snapshot hash is the
artifact that maps to a Dart SDK release through an external dataset (such
as the one reFlutter maintains), and knowing the SDK version is what makes
SDK-layout-specific snapshot parsers applicable.
## Answer an analyst question
```sh
rea identify-flutter-build /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk --abi armeabi-v7a
```
The equivalent MCP methods are `identify_flutter_build` (with `path`) and
`inspect_dart_aot` (with `path` and an optional `abi`; the default prefers
arm64-v8a, then x86_64, then x86, then armeabi-v7a, then the remaining
ABIs alphabetically).
## Optional retained AOT delivery
MCP `inspect_dart_aot` keeps complete output when `detail` is omitted or
`"complete"`. Explicit `detail: "summary"` returns the selected target/ABI/library
identity, all snapshot headers, structural coverage, string-pool coverage and
original lower-bound counts without the string lists. It retains complete source
Evidence and returns a distinct linked `inspect_analysis_view` summary. Analysis
and the caller's ABI choice are unchanged; one-shot CLI output stays complete.
Use `normalized_result.parent_evidence_id` as the retained source for
`inspect_analysis_view`, with `view: {kind: "dart-aot", collection: "package_uris",
offset: 0, limit: 64}`. Collections are `package_uris`, `dart_uris` and
`class_like_tokens`. Pages preserve the original strings and order, selected ABI,
library identity and source coverage. `returned_counts` describe producer-returned
rows; counts ending in `_at_least` remain lower bounds. Exhausting a page sequence
does not make a partial pool complete or turn byte-pattern tokens into typed
classes/functions. Views do not reopen the APK. Original Evidence remains
exportable under its existing session lifetime. Summary requires a retaining
server; older servers keep the complete request after schema discovery.
## What the AOT inspection returns
`inspect_dart_aot` reads one ABI's `libapp.so` in full and reports:
- **Snapshot sections** — the four `_kDart*Snapshot*` symbols located
through the ELF **dynamic symbol table** (ELF32 and ELF64, little-endian,
every read bounds-checked), each with file offset, size, and — for data
sections, which carry the `f5f5dcdc` magic — the parsed header kind and
length (the stored length excludes the four magic bytes). Data headers
whose declared snapshot length does not fit their symbol retain their
observed fields and report partial coverage. Complete structural coverage
currently requires kind 3, the FullAOT value in the supported
[Dart 2.14 layout](https://github.com/dart-lang/sdk/blob/2.14.0/runtime/vm/snapshot.h).
Kind numbers vary across Dart releases; other values are preserved with
partial coverage, without guessing their interpretation.
Address resolution examines every overlapping `PT_LOAD` interpretation,
including partial overlaps. Equivalent mappings are coalesced; conflicting
mappings leave affected symbols unresolved and qualify coverage. Dynamic
hash, symbol and string tables use the same mapping checks.
A payload without exported snapshot symbols is refused rather
than guessed at.
- **The snapshot hash** from the header at the start of each exported data
symbol. Complete hash coverage requires a valid header from both the VM
and isolate; duplicate records inside one symbol cannot stand in for the
other's header. Aliased or overlapping data symbols report partial coverage.
- **A string-pool projection from the isolate data section**: distinct
`package:` source URIs (the app's own and its dependencies' Dart files —
a dependency inventory), `dart:` SDK URIs (which SDK libraries the app
uses), and identifier-shaped tokens, each bounded with counts and
`coverage: "partial"` when truncated. On the reference target this counts
756 package URIs and 201 dart URIs on arm64-v8a and x86_64, and 212 / 48
on armeabi-v7a, retaining up to 500 of each in the response.
`libapp.coverage` describes structural snapshot checks;
`string_pool.coverage` describes the bounded projection. Overall coverage
is partial when either facet is partial.
## Interpret the result
- **`flutter_detected`** is true when any `lib/<abi>/libapp.so` exists. A
negative answer reports complete coverage: absence is an answer.
- **`snapshot_hash`** is the Dart build hash observed 20 bytes after the
snapshot magic (`f5 f5 dc dc`) inside `libapp.so`. Identification scans
byte patterns; AOT inspection reads the exported VM and isolate headers.
`snapshot_hash_sources` counts the valid records observed by the chosen
operation, and when they **disagree** the distinct values are
kept in `snapshot_hash_candidates` with `snapshot_hash: null` and
`coverage: "partial"` rather than silently picking one.
- **`elf_header_valid`** checks the supported little-endian ELF32/ELF64
header, table entry widths, and full table extents against the library's
actual byte length. Truncated, malformed, or unsupported headers report
false and partial coverage while retaining byte-scanned hashes. This
header check does not establish valid table contents or exported symbols.
- **`libflutter` facts** are per-ABI observations: byte size, SHA-256
digest, GNU build IDs from ELF-declared `PT_NOTE` / `SHT_NOTE` ranges,
and any _labeled_ strings the build
carries. Release engine builds often strip the `Dart VM version` string —
absence is reported as `null`, not guessed. Android clang/LLVM toolchain
lines are reported verbatim as build provenance.
`build_id_candidates` preserves distinct IDs. `build_id` is populated only
when all declared note ranges were examined and one ID was found;
conflicting IDs or malformed/bounded note metadata make coverage partial.
Matching bytes outside declared note ranges cannot supply a build ID.
- Labeled strings must fit their complete grammar. `dart_version_capped`
and `toolchain_capped` independently report labels omitted by grammar or
projection bounds; either makes identification coverage partial. Values
split across read chunks are retained without publishing truncated prefixes.
- **`coverage: "partial"`** also appears when an ABI carries `libapp.so`
without `libflutter.so` (or vice versa).
- The APK itself is digested before parsing, so `target.sha256` binds the
observation to the exact input.
## Limits
- **Owned snapshots.** Both operations copy the admitted APK into a
provider-owned, read-only snapshot before digesting and parsing, so a
concurrently modified original cannot mix into one observation; failed
reader closure and snapshot cleanup are retained and retried on `close()`.
The snapshot is removed only after all owned readers and file descriptors
close successfully. Copying pins one admitted descriptor and compares its
identity, size, mode and timestamps after the copy. Source permission
failures remain `access_denied`, and source identity changes remain
`artifact_changed`. Provider-owned storage failures report a
`resource_constraint` with their native I/O cause. Cancellation is checked
during copying, metadata/symbol walks, string scanning, and after cleanup.
- **Bounded reads.** APK snapshots are bounded at 2 GiB, including while
copying. Identification permits 768 MiB of cumulative library extraction;
declared sizes are checked before extraction. Library reads are bounded at 256 MiB each and ZIP
metadata at 8 MiB; the AOT image buffer is allocated at exactly the
declared size before reading. Exceeded budgets report
`resource_constraint`.
- **Engine notes.** Engine libraries are spooled into the owned root. ELF
headers, note tables and note payloads share an 8 MiB metadata budget;
up to 64 distinct build IDs and 1024 distinct nonempty note ranges are retained.
The range cap bounds bookkeeping and separate positional reads even for tiny
overlapping declarations. Exact duplicates share one read and empty ranges
consume no inventory slots. Exceeding a bound preserves the candidates examined
from retained ranges and reports partial coverage with `build_id: null`.
- **Metadata and candidate capacity.** Both operations refuse duplicate
relevant library paths. Inventory is limited to 32 ABIs and 1024 bytes per
relevant library path, independently of compressed size. Identification
refuses more than 64 distinct snapshot hashes with `resource_constraint`
rather than silently dropping conflicting evidence.
- **Selected ABI.** AOT inspection applies the library budget to the selected
ABI after selection; an oversized unselected library does not block it.
Undefined dynamic-symbol imports are not treated as snapshot exports.
Section-stripped images require a bounded symbol count from `DT_HASH` or
`DT_GNU_HASH`; layouts without either are refused. Symbol names and their
terminators must fit their linked string table.
- REA performs **no hash-to-SDK lookup**: mapping the snapshot hash to a
Dart SDK release requires an external dataset and is deliberately out of
scope for this family.
- The AOT string pool is a **byte-pattern projection, not deserialized
cluster semantics**: typed class and function recovery requires an
SDK-specific parser (the snapshot layout changes per release — exactly
why the hash is the key), and `class_like_tokens` include incidental
matches. JEB's GUI Dart unit and native analysis of `libapp.so` cover
some ground today.
## Real verification
`npm run verify:flutter -- --apk PATH [--plain-apk PATH]` runs both tools
through compiled CLI and stdio MCP, compares Evidence, and checks every ABI.
Use `--expectations scripts/fixtures/flutter-gallery-expectations.json` with
the Gallery 2.9.2 release APK to check exact per-ABI engine labels,
URI counts and the snapshot hash against its pinned SHA-256. The verifier
requires complete structural snapshot coverage on every ABI and explicitly
reports the bounded pool projection as partial/unverified when truncated.
Other APKs need their own expectations file to verify exact engine labels
and URI counts; otherwise those facets are reported unverified.
The optional plain APK checks negative detection. Record: Flutter Gallery 2.9.2 (112 MB, three
ABIs) — every ABI reports the same agreeing hash
`65817c30a78bb44c3dc3771876b6010a` from two snapshot sections, per-ABI
engine build-ids, no labeled Dart version (as expected for a release
engine), and the Android clang toolchain line; a plain aapt2-built APK
reports not-detected with complete coverage. The suite uses self-contained
synthetic ELF images and scanner payloads; it does not read private local files.
The Gallery workflow and all 147 focused Flutter tests also pass on macOS Sonoma
14.8.9 x86-64 and Windows 11 x64 build 26300.9457 guests with Node 24.18.0,
including the conflicting-load and note-range-limit CLI/MCP regressions. Windows
verification runs under a non-administrator account. Verifiers use a shared
temporary parent so each CLI/MCP process can own and release its own snapshots.
+13
View File
@@ -0,0 +1,13 @@
# Frida instrumentation
REA exposes Frida device and process discovery, attach or spawn sessions, and caller-authored JavaScript instrumentation through the CLI and MCP. The Node bindings are an optional package dependency and load only when a Frida operation runs. REA does not install Frida server on a target.
For remote devices, pass a host with an optional port (`host[:port]`; bracket IPv6 addresses) and any token, certificate, origin, or keepalive options on that operation. The keepalive interval is measured in seconds, matching Frida Core 17.23.2. Authentication values are held only for the active call or session and are excluded from Evidence. Do not embed credentials in the endpoint address.
MCP exposes `list_frida_devices`, `list_frida_processes`, `start_frida_session`, `load_frida_script`, `resume_frida_session`, `unload_frida_script`, `frida_session_status`, and `close_frida_session`. A spawned process starts paused so scripts can load first. Closing unloads session scripts and detaches. If a spawn remains paused, REA resumes it only after script cleanup or session detach confirms the instrumentation is released; if both cleanup paths fail, the target stays paused and the cleanup error is retained. REA never kills the target. MCP sessions end with the server process. The one-shot CLI command `instrument-with-frida` accepts one inline script or script file, observes messages for a bounded duration, and returns Evidence with the source hash and captured messages.
Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. Server shutdown stops admitting new Frida operations and waits for admitted startup, discovery, and queued work before releasing their resources. A failed cleanup remains reported and can be retried by closing the server again, rather than treating an unregistered session as fully released.
Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. A cancelled script load on an existing session releases only its new script before queued operations can run; the session and previously loaded scripts remain available. Once the provider commits a successful load, later request cancellation does not unload that script; use `unload_frida_script` to release it. If script cleanup fails, the script remains owned and a paused target cannot resume until the pending instrumentation is released. Queued script loads, resumes, and unloads check cancellation before dispatching native effects. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
The [Frida Core API](https://frida.re/docs/frida-core-api/) documents device management, sessions, and script loading; the [JavaScript API](https://frida.re/docs/javascript-api/) documents target-side instrumentation. [Luma](https://luma.frida.re/) is Frida's standalone GUI; REA does not automate or control it. `npm run verify:frida` requires a Linux host with the optional Frida binding and uses a short-lived local Node.js fixture created by the verifier. Add `-- --address HOST:PORT --pid PID` to run the separate remote attach lane against a caller-selected target. Mock tests do not establish real device/server compatibility.
+7 -4
View File
@@ -130,8 +130,9 @@ range's file offset and analysis address; it does not locate the first differing
byte inside that range. Mapping ends are **inclusive**, unlike the exclusive ends
from `list_segments`. Appended overlays remain in source-file identity and are
excluded from initialized module coverage. Uninitialized allocations are reported;
their contents and DOS runtime semantics are not verified. Formats other than MZ and explicit COM return
`unsupported` with measured observations, not a successful MZ verification.
their contents and DOS runtime semantics are not verified. Independent verification
also covers explicit [raw-binary imports](raw-binary.md); other formats return
`unsupported` with measured observations.
`read_bytes` returns initialized provider memory, including loader fixups; an
unmapped or uninitialized byte ends the read with an explicit returned length and
@@ -150,8 +151,10 @@ For a packed executable, importing the original can analyze its unpacking
stub. Analyze a separately prepared unpacked copy to inspect the original
program's recovered code. Retain both artifact digests and the transformation
evidence; REA never silently replaces the admitted target. Appended overlays
are separate from the initialized MZ module. Raw COM files and DOS extenders
without an admitted MZ real-mode entry are outside this import boundary.
are separate from the initialized MZ module. Headerless COM requires the
[explicit COM interpretation](#headerless-com-images); REA does not guess it
from the filename or unrecognized bytes. DOS extenders without an admitted
MZ real-mode entry remain outside this import boundary.
DOS MZ is available through Ghidra, with no Hopper support claim. It is outside
the Windows native PE P0 boundary. macOS x64 DOS verification remains unverified.
+185 -17
View File
@@ -3,7 +3,7 @@
This lane extends native ELF analysis; it does not execute MIPS code or rehost
firmware. It advances the generic-MIPS portion of
[#718](https://github.com/morluto/rea/issues/718), not the whole firmware roadmap.
PSP/Allegrex remains a separate specialization tracked in
PSP/Allegrex has a [separate static-ELF profile](ghidra-psp.md) tracked in
[#1330](https://github.com/morluto/rea/issues/1330).
## Admission boundary
@@ -13,13 +13,36 @@ retains class, byte order, `e_type`, raw `e_flags`, and inspected ABI declaratio
Recognition does not mean every provider or MIPS variant is supported. Existing
PE, managed and Mach-O architecture sets are unchanged.
The first Ghidra lane admits ELF32 `ET_EXEC`, little or big endian, with explicit
o32 and `EF_MIPS_ARCH_32R2` flags and no machine-specific or ASE encoding flags.
These are header declarations, not proof that every instruction conforms to
that ISA. ELF64, n32/n64, unspecified ABI, MIPS16/microMIPS, machine-specific
variants, shared/relocatable objects and PSP PRX are not covered by this lane.
Their family may be identified, but provider selection returns the specific
unsupported constraint rather than guessing a compatible profile.
The Ghidra lane admits ELF32 `ET_EXEC` and `ET_REL`, little or big endian, with
explicit o32 and `EF_MIPS_ARCH_32R2` flags and no machine-specific or ASE
encoding flags. These are header declarations, not proof that every instruction
conforms to that ISA. ELF64, n32/n64, unspecified ABI, microMIPS,
machine-specific variants and PSP PRX are not covered by this standard-instruction
lane. MIPS16 shared images have the separate bounded profile below; other shared
or compressed images remain unsupported. Their family may be identified, but provider selection returns the
specific unsupported constraint rather than guessing a compatible profile.
A second admission covers recordless legacy firmware images
([#1620](https://github.com/morluto/rea/issues/1620)): ELF32 `ET_EXEC` headers
declaring `EF_MIPS_ARCH_32` or `EF_MIPS_ARCH_32R2` with an unspecified ABI
field, `EF_MIPS_ABI2` clear, and an _inspected_ absence of any ABI flags
record (a target whose record was merely not observed stays refused). For
these images o32 is an analysis assumption derived from the ELF32/ET_EXEC
structure, recorded as `mips_abi_basis` in the profile and stated as a
limitation in Evidence, never a declared fact; floating-point ABI semantics
stay unverified. Ghidra's own header auto-selection picks a 64-bit language
for exactly these files, so the lane pins `-processor MIPS:<LE|BE>:32:default`
through a `provider-pinned-v1` profile selection and checks the
bridge-reported `language_id` after import. A caller-selected
`REA_GHIDRA_LANGUAGE_ID`/`REA_GHIDRA_COMPILER_SPEC_ID` still wins over the
pin; combining the pinned profile with an environment override is a typed
refusal. A header that declares an ABI field but carries no record is still
refused. Two further boundaries apply: an inspection that could not examine
every ABI declaration source (for example a carved ELF whose section table
extends past the captured bytes without a program-header record) does not
establish the recordless admission, and an `EF_MIPS_ARCH_32` header that
_declares_ an ABI is refused rather than relabeled, because the declared-ABI
lane is verified for `EF_MIPS_ARCH_32R2` headers only.
Explicit `EF_MIPS_FP64` and `EF_MIPS_NAN2008` declarations are refused. Their
absence is insufficient: Clang can emit identical `e_flags` for FP32, FP64 and
@@ -39,19 +62,125 @@ absent or supply a default ABI.
The field layout follows
[`Elf_MIPS_ABIFlags_v0`](https://github.com/bminor/glibc/blob/master/elf/elf.h).
Provider admission requires record version 0, MIPS32r2, 32-bit GPR/CPR1, no CPR2,
and a double-precision or FPXX ABI declaration with no ISA extension, ASE or
unknown general flags. Missing records remain unknown rather than defaulting
to a compatible ABI, and this initial lane refuses them. Unknown record values
are retained for diagnosis but do not gain provider support. These checks bind
the declared interpretation; the integer-only fixture does not establish full
Provider admission on the declared-ABI lane above requires record version 0,
MIPS32r2, 32-bit GPRs, no CPR2, no ISA extension, ASE or unknown general
flags, and one of two floating-point declarations: 32-bit CPR1 with a
double-precision or FPXX ABI, or no CPR1 with the soft-float ABI. Other
combinations, including single-float, FP64 and unspecified ABIs, are refused.
Missing records remain unknown rather than defaulting to a compatible ABI, and
the declared-ABI lane refuses them; the recordless legacy admission above is
the only path that admits a recordless image. Unknown record
values are retained for diagnosis but do not gain provider support. These checks
bind the declared interpretation; the fixtures do not establish full
floating-point behavioral conformance.
### MIPS16 shared images
A separate profile admits ELF32 `ET_DYN`, either byte order, declaring explicit
o32, MIPS32r2 and exactly the MIPS16 ASE. Machine variants, n32, FP64 and NAN2008
remain refused. Source inspection must examine the complete dynamic relocation
declarations through uniquely mapped file-backed `PT_LOAD` ranges. This profile
verifies `R_MIPS_NONE` and symbol-index-zero `R_MIPS_REL32`; section declarations
must describe that same dynamic table. Unavailable section headers and additional
section relocation tables (including `--emit-relocs` output) remain unsupported.
Symbol-bearing REL32,
other relocation types, RELA and PLT declarations remain unsupported. A real
negative control shows that Ghidra can normalize a symbol-bearing MIPS16 function
pointer to an even address, losing the source mode tag. REA refuses that shape
rather than rewriting the source or silently repairing the analysis result.
The profile pins the endian-matched `MIPS:<LE|BE>:32:default` processor and default
compiler specification, and checks the bridge-reported language after import.
The generic processor preserves mixed MIPS32/MIPS16 code through per-address
`ISA_MODE` context; a global `16e` processor would impose one mode across the
image. A caller-selected incompatible processor or compiler specification is
explicitly refused without replacing the requested choice.
An observed ABI flags record must agree with the header and MIPS16 ASE, and
must declare the same verified floating-point combinations as the standard
lane. With no observed record, every ABI declaration table must have been
completely examined. The header still declares o32, but floating-point ABI
semantics remain unknown and are stated in Evidence; selecting the default
compiler specification does not establish floating-point storage. Partial
section tables do not prove record absence. The existing soft-float prototype
policy applies when the source explicitly declares soft-float.
Returned addresses are analysis coordinates, not runtime load addresses.
`inspect_native_load_image` preserves original relocation bytes, current loaded
memory bytes and provider status. External GOT bindings remain unverified
without their defining images. The provider's `modified_sha256` describes its
ephemeral relocated image and can differ from `original_sha256`; it does not
mean that the caller's artifact was written. `inspect_native_instruction` retains the program
language variant in `mode` and reports actual per-address `ISA_MODE` separately
in `decoder_context`, including null when unavailable. Artifact bytes and digests
remain unchanged; this workflow does not execute the shared library.
The optional real-provider check builds eight source-owned GNU controls, covering
both byte orders, mixed MIPS32/MIPS16 code, declared and absent ABI records, and
the rejected symbol-bearing and additional section relocations. It compares source instruction bytes,
function symbols and the local function-pointer tag with actual CLI/MCP results,
checks context through a snapshot round trip, and verifies process cleanup.
It requires caller-supplied GNU MIPS cross-binutils and Ghidra; no fixture target
is executed or patched after linking. Floating-point behavior of compressed
functions is not established by these integer controls.
```bash
REA_MIPS_AS=/absolute/path/to/mips-linux-gnu-as \
REA_MIPS_LD=/absolute/path/to/mips-linux-gnu-ld \
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra \
npm run verify:ghidra:mips-shared
```
MIPS header and ABI record facts are committed in the analysis profile so
interpretation changes invalidate profile-bound snapshots. The support-lane
identity is versioned independently of file identity. Evidence, lifecycle
outputs and saved snapshots preserve the `mips` family. Hopper and IDA adapters
do not implicitly gain MIPS support. Windows Ghidra P0 remains PE x86/x86-64 only.
do not implicitly gain MIPS support. Windows Ghidra P0 does not admit MIPS targets.
### Soft-float prototype
Ghidra's MIPS32 compiler specifications model hard-float o32 only: a typed
`float` or `double` parameter is bound to `$f12`/`$f14` and returned in `$f0`.
Soft-float o32 passes those values in integer registers and stack slots, so a
typed prototype (for example from DWARF) would make the decompiler read FPU
registers the code never writes and drop the real dataflow. That applies to
functions, external functions and indirect calls through typed function
pointers alike.
For soft-float targets REA commits
`mips_soft_float_storage: o32-soft-float-prototype-v1` in the analysis profile
and runs `ReaGhidraMipsSoftFloat.java` after auto-analysis. The script encodes
the program's own default o32 prototype, removes its floating-point parameter
and return entries and the rules that consume floating-point storage, and
installs the result as the program-specific `__rea_soft_float` prototype through
a Ghidra specification extension. Deriving it keeps the byte-order-specific
aggregate rules and the hidden struct-return pointer. The script then assigns
that prototype, with dynamic storage, to every non-thunk function and external
function, and to every function-pointer definition, whose declared types use
`float` or `double`. Thunks follow their targets. Types are kept.
The script fails, and writes no report, if the derived prototype still places a
float in an FPU register or any reassigned function does. It records the
function and definition counts last; REA requires that report exactly when the
profile commits the policy and refuses the session otherwise, before serving any
operation. Session Evidence carries the policy and counts as limitations.
Callables without typed float signatures keep Ghidra's default prototype, under
which Ghidra already infers integer-register parameters. Hard-float profiles do
not gain the policy, so their digests are unchanged.
### Relocatable objects
`ET_REL` objects, such as Linux kernel modules (`.ko`), are imported by Ghidra's
ELF loader, which assigns each allocated section an analysis address and applies
the object's REL relocations there. Undefined symbols are placed in Ghidra's
`EXTERNAL` block: a thunk for code, a label for data. These addresses are
analysis coordinates, not the addresses a kernel or linker would choose, and
session Evidence says so. `inspect_native_load_image` reports each relocation
with Ghidra's status; HI16 entries report `PARTIAL` because Ghidra applies them
together with their paired LO16. Relocatable targets commit
`mips_support_lane: elf32-rel-o32-arch32r2-standard-v1`, separate from the
executable lane, whose identity executable targets keep. Kernel modules are
usually soft-float and therefore also use the soft-float storage policy.
## Use
@@ -70,7 +199,10 @@ REA's ELF parser.
## Verification
The source-owned freestanding fixture is `tests/conformance/c/mips.c`. The
### Executables
The source-owned freestanding fixture is `tests/conformance/c/mips.c`; the
soft-float variant also links `tests/conformance/c/mips-soft-float.c`. The
optional cross-target lane requires a caller-supplied Clang with MIPS targets,
LLD, GNU `readelf`, Ghidra and its compatible JDK; ordinary host-native checks
do not acquire these tools. `REA_MIPS_CLANG` selects Clang and
@@ -81,7 +213,8 @@ These are test-time prerequisites, not REA runtime providers.
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:mips
```
The lane compiles both byte orders and never executes the targets. GNU readelf
The lane compiles both byte orders with hard-float and soft-float ABIs and never
executes the targets. GNU readelf
independently supplies header/ABI facts and symbol addresses, compared with the
production REA target resolver before provider startup. Its version, bounded
command output and selected tool path are retained in the verification report.
@@ -104,9 +237,44 @@ all program-header payloads. It checks the actual Ghidra language, entry-address
decompilation, distinct artifact digest and missing-section limitations through
CLI Evidence and an MCP-produced snapshot. Caller-selected Ghidra heap limits
are passed to these child workflows.
For soft-float fixtures the lane also checks, through `inspect_native_api`, the
exact parameter and return types and storage of float, double, mixed (pair
alignment and stack spill) and struct-returning functions; that a caller's
decompilation receives every source float constant without FPU inputs,
including an indirect call through a typed function pointer; and that the
snapshot profile and Evidence retain the policy and outcome counts.
Pseudocode presence is a liveness check, not proof of semantic equivalence.
Instruction decoding and reader agreement likewise do not establish runtime,
delay-slot execution or complete floating-point semantics. Assertion regressions
reject incorrect byte order, constants, destinations and loaded identities.
Parser/profile tests are narrower checks, not substitutes for this real-provider
run. Report the actual lanes run and leave unexecuted verification pending.
### Relocatable objects
This lane has its own command and fixture:
```bash
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:mips-relocatable
```
It compiles `tests/conformance/c/mips-relocatable.c` with `-c -G0` in both byte
orders, plus a little-endian soft-float `-mlong-calls` variant matching Linux
module options. GNU readelf independently supplies sections, symbols and REL
entries. For every relocation in an allocated section, the lane reads the
loaded word through `read_bytes` and requires it to encode `S + A`: `S` from
Ghidra's own section placement (`list_segments`) or `EXTERNAL` placement, `A`
from the original file bytes. HI16/LO16 are checked as the effective address
each LO16 forms with each HI16 it resolves, including several HI16s before one
LO16, so a missing signed-low carry fails and a HI16 without a following LO16
fails. The lane also requires matching Ghidra relocation entries and statuses,
section file offsets and sizes, coverage of a shared HI16, a LO16 resolving
several pending HI16s (from an assembly block, since Clang does not emit that
from C), a LO16 carry, external code and data and `R_MIPS_32` words, direct
calls from the entry
function, the relocatable support lane in the snapshot profile, the
relocatable limitation in Evidence and an unchanged source object. The fixture
also calls `powf`, an external whose float prototype comes from Ghidra's C
library archive; every variant must decompile `powf(1.5,2.5)` without FPU
inputs, and the soft-float variant must report that the soft-float prototype
covered it.
+25 -7
View File
@@ -17,10 +17,14 @@ submodule and is a separate, more capable workflow.
The parser currently handles x64 PE32+ and RTR 9.1, including bounded
DEHYDRATED_DATA processing, MethodTable relationships, virtual slots, and
frozen strings. Other RTR versions and PE architectures are reported as
unsupported or partial. The matching .NET 8.0.22 fixture is source-built and
the parser's Windows-host Ghidra workflow has not yet had a real Windows-host
run. This does not establish Windows Ghidra import or decompiler behavior.
Windows Ghidra P0 has no database mutation authority. A PE/CLI or ReadyToRun
unsupported or partial. The matching .NET 8.0.22 fixture is source-built.
On a Windows host, Windows Ghidra P0 runs the parser without a JAR. Metadata,
MethodTable relationships, virtual slots and frozen strings come from the
parser report, and `analyze_function` decompiles slot targets. Tools that read
the Ghidra database do not see rehydrated bytes (see below), because only the
optional adapter writes them into the database and Windows Ghidra P0 has no
database mutation authority. A PE/CLI or ReadyToRun
assembly should use `inspect_managed_artifact`; it is not NativeAOT.
## Build and configure
@@ -57,7 +61,9 @@ returns `observations.metadata_recovery` inline from the captured source snapsho
including the RTR header, MethodTable addresses, frozen strings, derived overlay
identity, and examined/recovered counts. Its enclosing `unsupported` load-image
status means independent PE load-image verification was not performed; it does
not mean the metadata parser failed or the executable failed to load.
not mean the metadata parser failed or the executable failed to load. Without
the optional adapter, each recovered type's `type` is `null`, because no Ghidra
DataType exists; select it by `address`.
Pass a returned MethodTable address to `inspect_native_data_type`. A direct PE
query also derives the snapshot metadata from the authenticated Ghidra session
@@ -67,8 +73,12 @@ The database result remains `unavailable` when Ghidra has no DataType at that
address; its `metadata_recovery.source` is `read-only-derived-overlay` and
carries related type, interface, slot, and target observations. Generated
address identities do not claim original managed names. The parser does not
merge frozen literals into Ghidra's database string inventory. Follow slot
targets with `analyze_function` and ordinary cross-references.
merge frozen literals into Ghidra's database string inventory: read them from
`metadata_recovery.frozen_strings`, not `search_strings`. Slot targets come
from the parser report; on Windows P0 without the adapter, a `read_bytes` of
a slot in the rehydrated region returns no bytes. The Linux adapter route
verifies a complete slot read and its target. Follow slot targets with `analyze_function` and ordinary
cross-references.
The built-in CLI path uses a MethodTable address returned by the parser or
another observation:
@@ -129,3 +139,11 @@ compiler/linker symbols and raw directory bytes provide the oracle. Stripped
fixtures exercise heuristic detection. Tests do not execute the resulting
NativeAOT binaries. Fixture generation and adapter compilation are optional and
never prerequisites of ordinary native analysis or setup.
The real MCP lane runs only on a Linux x64 host with the adapter. The
Windows-host behavior above was checked manually over MCP on Windows 11 x64
(non-elevated, Ghidra 12.1.4, JDK 21, no JAR) with the source-built win-x64
symbols fixture. The format, RTR version and frozen strings matched the fixture
oracle; the derived MethodTable, its base type, its interface and a virtual
slot reaching the overridden method matched the linker map; and pseudocode was
readable. No CI lane repeats this check.
+90
View File
@@ -0,0 +1,90 @@
# PowerPC ELF analysis through Ghidra
This lane extends native ELF analysis to 32-bit big-endian PowerPC; it does
not execute PowerPC code. It is the first milestone of
[#1741](https://github.com/morluto/rea/issues/1741). GameCube/Wii DOL
executables, PPC64 and other PowerPC variants are separate follow-ups.
## Admission boundary
ELF `EM_PPC` (20) is retained as the provider-neutral `powerpc` family. The
target also retains class, byte order, `e_type` and raw `e_flags`. Recognition
does not mean every provider or PowerPC variant is supported. `EM_PPC64` (21)
is not recognized as this family.
The Ghidra lane admits ELF32 `ET_EXEC`, big endian. ELF64, little-endian,
relocatable and shared objects are refused with a specific unsupported
constraint. `EF_PPC_EMB` declares an embedded variant and is refused; the
remaining `e_flags` bits only declare relocation conventions and do not change
instruction decoding.
## Bound language
Ghidra's ELF loader selects `PowerPC:BE:32:e500:default` for a plain `EM_PPC`
header with `e_flags` 0. e500 is the embedded Book-E variant whose SPE unit
replaces the classic floating-point unit, so it misinterprets ordinary
750-family code. Nothing in the header distinguishes the two.
The lane therefore imports with `PowerPC:BE:32:default` and compiler spec
`default`, commits that choice in the analysis profile as
`language_selection: "provider-pinned-v1"`, and checks the language Ghidra
reports after import. A different language set through
`REA_GHIDRA_LANGUAGE_ID` (and optionally `REA_GHIDRA_COMPILER_SPEC_ID`)
replaces the lane default and is admitted against the installed language
catalog as usual; for example `PowerPC:BE:32:e500` for genuine e500 code.
PowerPC header facts are committed in the analysis profile so interpretation
changes invalidate profile-bound snapshots. The support-lane identity is
versioned independently of file identity. Evidence, lifecycle outputs and saved
snapshots preserve the `powerpc` family. Hopper and IDA adapters do not
implicitly gain PowerPC support. Windows Ghidra P0 remains PE x86/x86-64 only.
## Use
With a caller-installed Ghidra and compatible JDK on a supported Linux/macOS host:
```bash
rea analyze /absolute/path/to/ppc.elf --provider ghidra --json
rea function /absolute/path/to/ppc.elf function_name --provider ghidra --json
```
MCP uses the existing tools, including `open_binary` with an absolute path and
`provider_id: "ghidra"`. No PowerPC-specific tool catalog or emulator is
introduced.
## Verification
The source-owned freestanding fixture is `tests/conformance/c/ppc.c`. The
optional cross-target lane requires a caller-supplied Clang with the PowerPC
target, LLD, GNU `readelf`, Ghidra and its compatible JDK; ordinary host-native
checks do not acquire these tools. `REA_PPC_CLANG` selects Clang and
`REA_PPC_READELF` selects GNU readelf (for example
`/opt/homebrew/opt/binutils/bin/readelf` on macOS). These are test-time
prerequisites, not REA runtime providers.
```bash
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:ppc
```
The lane compiles the fixture for a 750-family core (`-mcpu=750`) and never
executes it. GNU readelf independently supplies header facts and symbol
addresses, compared with the production REA target resolver before provider
startup. Its version, bounded command output and selected tool path are
retained in the verification report. The parser deliberately recognizes only
this fixture's GNU output, rejecting missing, ambiguous or unexpected fields
rather than substituting defaults.
Existing public MCP operations check Ghidra's **observed** language and
compiler specification and loaded-file hashes; an e500 import fails the lane.
The source-owned `ppc-probe.S` supplies fixed `addi` and conditional-branch
encodings: verification checks bytes, length, immediate and direct destination,
independent of decompiler variable names. Global and string bytes are checked at
independently reported symbol addresses.
CLI/MCP discovery and decompilation, direct calls, target/profile/Evidence
identity, snapshot round-trip and final process cleanup remain covered.
Pseudocode presence is a liveness check, not proof of semantic equivalence.
Instruction decoding and reader agreement do not establish runtime or complete
floating-point semantics. Parser/profile tests are narrower checks, not
substitutes for this real-provider run. Report the actual lanes run and leave
unexecuted verification pending.
+127 -3
View File
@@ -41,6 +41,100 @@ does not prove text is absent. Both list and search Evidence carry this
limitation. When an address is known, use `read_bytes` and `xrefs` to inspect
bytes and references.
## PE debug-symbol coverage
The post-analysis handshake reports Ghidra's PDB Program Information attributes:
the referenced file, GUID, hexadecimal age, legacy signature, version, and
`PDB Loaded` state. Absent attributes remain null. PE Evidence carries a
limitation when a PDB reference is present but Ghidra reports it as not loaded,
or its load state is unknown. A bridge without this report leaves PDB coverage
unknown. A reported loaded state is a Ghidra observation, not an independent
PDB identity or complete symbol-coverage attestation, and retains an inline
coverage caveat. Handshake PDB strings share a 4096 UTF-16-unit serialization
budget (at most 24 KiB after JSON escaping); whole attributes that do not fit
are omitted with an explicit resource limitation, rather than truncated.
Original names and types may be missing even when auto-analysis completes.
An empty name search does not establish that a function is absent; use
`list_procedures` and address-based analysis to inspect recovered functions.
REA imports a private executable copy and does not admit a caller-selected
PDB companion. This reporting does not search symbol servers, copy a sibling
PDB, or change analyzer settings.
## Loaded addresses and file offsets
`rea analyze <path> --provider ghidra --json` and MCP `binary_overview` report
the observed loaded `image_base` in `normalized_result`. They retain each
segment's reported `image_base` and `address_space`. A provider that does not
report a base, or whose segments disagree, omits the overview field, following
the existing contract on main. REA does not substitute zero or the first
segment's address. All reported bases and address spaces remain visible in the
complete segment inventory, including conflicting observations.
`function` and most MCP procedure selectors use procedure names or
provider analysis addresses. Ghidra `decompile` / MCP `procedure_pseudo_code`
also accept source-file byte offsets. Existing symbols and analysis addresses
take precedence; a `0x`-prefixed number that resolves to no procedure falls back
to its observed source mapping. Use `--file-offset=0x...` on the CLI or
`procedure: "file-offset:0x..."` through MCP to select file coordinates explicitly.
Multiple mapping candidates fail as ambiguous rather than selecting a bank or
address space. A successful conversion retains the file offset, mapped address
and procedure entry in raw Evidence and `locations`.
If an offset maps to loaded target bytes but no procedure starts at or contains
the mapped address, the failure preserves both the original file offset and
analysis address. This differs from an unmapped offset and does not recommend
retrying the same offset syntax.
If the function resolves but the backend fails or cancels decompilation, the
failure retains the selector, mapped address and procedure entry without
changing the backend failure code.
Both mapping directions require a complete original `FileBytes` source whose
SHA-256 matches the admitted target. Auxiliary files and partial source objects
remain visible in the load-image inventory but do not establish offsets in the
original target. Ghidra's object-local offsets are not silently treated as
original-file coordinates. Original-source identity is checked once per source
object per session; analysis-memory edits do not change the original bytes.
ELF virtual addresses and file offsets are
different coordinates. Ghidra can rebase a PIE ELF during import: in the
source-owned verification fixture, ELF entry `0x1013` becomes Ghidra procedure
`0x101013` with image base `0x100000`. That example is not a general conversion
rule.
Use `list_procedures` to select an observed procedure address. To relate file
bytes to loaded memory, call MCP `inspect_native_load_image` or:
```sh
rea search <path> . --kind procedures --mode regex --provider ghidra --json
rea inspect-native-load-image <path> --provider ghidra --json
rea decompile <path> <observed-procedure-address> --provider ghidra --json
rea decompile <path> --file-offset=0x1013 --provider ghidra --json
rea address-to-file-offset <path> <observed-procedure-address> --provider ghidra --json
```
The load-image result preserves source-file indexes, file offsets, address
spaces, and inclusive mapping ranges. Follow the mapping for the selected
source file and region; adding `image_base` to an arbitrary file offset is not
valid. For a linear byte-addressed, file-backed mapping, a file offset `F` in
`[mapping.file_offset, mapping.file_offset + mapping.length)` maps to
`mapping.start + (F - mapping.file_offset)` in the reported `address_space`.
If several mappings cover the same file offset, keep those candidates distinct
and select the procedure from the observed inventory. Uninitialized regions may
have no file backing. ELF mappings are observed
without independent format-specific load-image verification, as the result's
status and limitations state.
An unsuccessful numeric procedure lookup reports the current Ghidra image base
and gives the CLI commands above alongside the corresponding MCP operations.
Repeat any target-format, architecture, and entry-point options used to open the
target. Other procedure operations keep their analysis-address semantics;
file-offset fallback applies only to decompilation.
Overview workflow revision 3 binds this output to snapshot identity. Older
serialized overview Evidence remains readable; it is not replayed as a current
overview merely because its source artifact is unchanged.
## Admitted function-analysis semantics
| Concern | Ghidra contract |
@@ -57,9 +151,10 @@ bytes and references.
Function extents and instruction completeness refer to Ghidra's analyzed body.
An inferred no-return call can terminate that body before subsequent bytes.
Pseudocode, assembly, procedure info, instruction windows, and dossiers report
observed terminal call sites in Evidence limitations, including resolved callee
names, addresses, and FunctionManager flags when available. Instruction windows
Pseudocode, assembly, procedure info, instruction windows, callee and caller
reads, and dossiers report observed terminal call sites in Evidence limitations,
including resolved callee names, addresses, and FunctionManager flags when
available. Instruction windows
and dossiers also retain these warnings in their result limitations. An
unresolved callee or unestablished terminal-flow basis remains explicit. These
observations qualify the analysis; they do not prove that a no-return flag is
@@ -103,3 +198,32 @@ It accepts the same architecture and DLL flags and verifies the selected PE role
in the analysis profile.
All lanes require matching native controls; package startup alone establishes
only package compatibility.
## Masked byte search
`search_bytes` (`rea search-bytes PATH '48 8B ?? ?? 89' --provider ghidra`)
searches initialized analyzed memory, including loader fixups. Supply hexadecimal
byte pairs, with `??` for any byte. An optional equal-length hexadecimal `mask`
selects bits: `pattern: "a0"`, `mask: "f0"` matches bytes from `a0` through `af`.
Wildcards remain unconstrained even with an explicit mask. At least one bit
must be constrained.
Optional `start` and `end` addresses are inclusive and select one address space.
A match must end at or before `end`, so `end` set inside a match excludes it.
Without bounds, loaded memory blocks are eligible, in Ghidra address order.
Blocks that are not loaded (file parts such as `.symtab`) and overlay blocks are
listed in `searched.skipped_not_loaded` and are not searched; pass `start` or
`end` in their address space to search them.
Overlapping matches are included; matches never cross block boundaries.
Each match reports its address, segment, unique source file offset (or null),
and containing procedure (or null).
Pages default to 100 matches and accept 1 to 1000. Pass `next_cursor` as `cursor`
with the same target, profile, pattern, mask and bounds to continue. `complete`
means no further matches remain. `searched.blocks` and `searched.bytes` describe
the inclusive memory ranges examined for this page, including lookahead for the
next match. Coverage can overlap between pages; do not sum it as unique memory.
`searched.skipped_uninitialized` lists eligible uninitialized blocks on every
page. These blocks are never searched. Cancellation returns a typed error,
discards the active Ghidra database, and does not return a successful partial page.
Hopper byte search remains unavailable until its implementation is verified.
+105
View File
@@ -0,0 +1,105 @@
# PSP static ELF analysis through Ghidra
This specialization builds on [generic MIPS support](ghidra-mips.md) for
[#1330](https://github.com/morluto/rea/issues/1330). It reuses REA's existing
CLI/MCP, Ghidra bridge, Evidence and snapshot workflows. Allegrex instructions,
PSP loading and calling conventions remain owned by
[ghidra-allegrex](https://github.com/kotcrab/ghidra-allegrex).
## Initial boundary
The initial profile is limited to Linux x64, little-endian ELF32 `ET_EXEC`,
`EM_MIPS` and the PSPSDK declaration `e_flags=0x10a23001`. Those bits declare
MIPS II, Allegrex, EABI32 and NOREORDER; they are not proof of every instruction's
semantics. The profile also requires an inspected version-0 MIPS ABI flags
record declaring ISA level 2/revision 0, 32-bit GPR and CPR1, no CPR2,
single-precision hard float (`fpAbi=2`), no ISA extension or ASEs,
`flags1=1` and `flags2=0`. These are the declarations independently reported by
PSPDEV v20261001's GNU readelf 2.44 and its 24-byte section dump. Missing,
uninspected, contradictory and other producer declarations are refused.
The PSP profile is not an exception to the generic MIPS32r2/o32 policy; that
separate gate is unchanged. Profile revision v2 records this corrected boundary.
PRX, relocatable/shared images, PBP/ISO/CSO containers, runtime emulation and live
code overlays are outside this first profile. Other hosts and flag combinations
are not silently admitted. File names do not identify a PSP target.
## Bring your own compatible extension
Install the extension in the selected Ghidra distribution at
`$GHIDRA_INSTALL_DIR/Ghidra/Extensions/ghidra-allegrex`, with its
`Module.manifest`, `extension.properties`, language files and loader JARs.
User-home installations are not searched because REA uses an isolated analysis
home. REA never downloads, installs, rebuilds or vendors the extension.
The published compatibility experiment used Ghidra **12.1.3**, extension
**v21.4**, PSPDEV **v20261001** and JDK 21. New public-provider results must be
reported separately from that historical bridge experiment. The extension's
`version` property declares its compatible **Ghidra version**, not its release
tag; the property must match the selected installation. Other same-version
builds are not described as independently verified merely because they pass
this metadata check. A malformed or unloadable extension still fails real import.
REA fingerprints installed extension files before resolving the analysis
profile and checks that identity again immediately before launch. Symlinks,
nonregular files, missing components and incompatible metadata are refused.
Inspection is bounded to 1,024 entries, 16 directory levels and 64 MiB, with
streamed file hashing and 64 KiB properties. Rebuilds or content changes yield a
different profile; an old snapshot is not silently reused with the new profile.
This is content identity, not a signature or a sandbox for installed code.
Do not modify the Ghidra installation while a session is running.
## Ordinary public interfaces
```bash
rea analyze /absolute/path/to/psp.elf --provider ghidra --json
rea function /absolute/path/to/psp.elf function_name --provider ghidra --json
```
MCP uses `open_binary` with an absolute path and `provider_id: "ghidra"`, then
ordinary inventory and function operations. The loader is explicitly
`PspElfLoader`, language `Allegrex:LE:32:default`, compiler `default`. The
handshake must confirm the language and compiler before serving observations.
Missing prerequisites return `PSP_EXTENSION_UNAVAILABLE` with an installation
remedy; no provider fallback or automatic installation occurs.
Evidence retains the `mips` architecture family and its PSP-specific profile,
including extension content identity. It also carries the VFPU and static-only
limitations. The extension does not model VFPU prefix effects completely;
nonempty decompiled code is not proof of correct vector behavior. Existing
session-local metadata editing does not change executable bytes or turn a
snapshot into a persistent Ghidra project.
## Verification
Use the existing real MIPS verifier with its PSP option. Required tools are
caller-supplied `make`, `psp-config`, `psp-gcc`, `psp-readelf`, `psp-objdump`,
`PSPDEV`, `GHIDRA_INSTALL_DIR` and a compatible `JAVA_HOME`.
```bash
npm run build:cached
node scripts/verify-real-ghidra-mips.mjs --psp
# In a disposable installation without Allegrex, test actual public refusals:
node scripts/verify-real-ghidra-mips.mjs --psp-missing-extension
# Generic MIPS must remain generic with Allegrex installed:
npm run verify:ghidra:mips
```
The generic verifier also exercises a section-truncated firmware fixture. That
check uses generic MIPS symbols and language expectations and runs only in generic
mode; the PSP report explicitly records carved-PSP coverage as not run.
The maintained PSP fixture builds from `tests/conformance/psp/`, never executes,
and is independent of commercial game data. PSP GNU tools supply ELF/ABI,
raw ABI-record, symbol and encoding observations; REA's parsed target and saved
snapshot must agree with the independent ABI declaration. Public CLI/MCP must
recover known functions, a direct
call, the immediate `0x1234`, BITREV, a global word and a marker. The verifier
checks observed loader/language, original bytes, Evidence, snapshot reopening,
and an A-B-A target switch with different global contents at the same address.
A snapshot for A must not open B. Cleanup must finish before success is reported.
These checks are selected static facts, not full behavioral equivalence, VFPU
semantics or arbitrary PSP compatibility. Independently published PSP binaries
are supplemental calibration targets after payload classification and license
review; unsupported PRX/PBP must not count as a successful static-ELF case.
+105
View File
@@ -0,0 +1,105 @@
# RISC-V ELF analysis through Ghidra
This lane extends native ELF analysis for `EM_RISCV` targets. It does not
execute RISC-V code, emulate a boot sequence, or rehost firmware. It addresses
[#2063](https://github.com/morluto/rea/issues/2063) and follows the same
lane shape as [generic MIPS support](ghidra-mips.md).
## Admission boundary
ELF `EM_RISCV` (243) is retained as the provider-neutral `riscv` family. The
target also retains the raw ELF declarations that decide a lane: class, byte
order, `e_type`, and `e_flags`. Recognition does not mean every provider or
RISC-V variant is supported. Existing PE, managed and Mach-O architecture sets
are unchanged. Headerless inputs use the separate [raw-binary admission
boundary](raw-binary.md); raw RISC-V images are not supported.
The Ghidra lane admits little-endian ELF32 and ELF64 `ET_EXEC` images that
declare `EF_RISCV_FLOAT_ABI_DOUBLE` in `e_flags`, with no `EF_RISCV_RVE`,
`EF_RISCV_TSO`, or any bit outside `EF_RISCV_RVC | EF_RISCV_FLOAT_ABI`. These are
header declarations, not proof that every instruction conforms to that ISA.
Provider selection returns the specific unsupported constraint rather than
guessing a compatible profile:
| Declaration | Outcome |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Little-endian ELF64 `ET_EXEC`, double-float ABI | admitted, `RISCV:LE:64:default` + `gcc` |
| Little-endian ELF32 `ET_EXEC`, double-float ABI | admitted, `RISCV:LE:32:default` + `gcc` |
| Big-endian | refused; Ghidra 12.1.4 ships no big-endian RISC-V language |
| `ET_REL`, `ET_DYN`, other `e_type` | refused with the declared value |
| `EF_RISCV_RVE` | refused; the embedded 16-register profile (x0–x15) needs its own processor specification |
| `EF_RISCV_TSO` | refused; the total-store-order memory model is unverified here |
| Soft, single, or quad float ABI | refused; the pinned generic compiler specification models double-float |
| Reserved or newer `e_flags` bits | refused with the offending bits named |
`e_type` is the only load-shape declaration this lane reads. A fixed-address
`ET_EXEC` declaration does **not** establish static linkage: `PT_INTERP` and
dynamic dependencies are not inspected, and the session records that boundary as
a limitation rather than inferring a load model.
## Why the language is pinned
Ghidra's header-based selection is not reliable for RV32. The 12.1.4
`riscv.opinion` pins `variant="default"` for the 64-bit constraint and leaves the
32-bit constraint unpinned with the comment that the loader "must choose between
'default' and 'AndeStar' variants". Measured against the installed release tree:
| Fixture | Ghidra auto-selection |
| ----------------------------------------------- | ----------------------------- |
| RV64 ELF, `e_flags 0x5` (RVC, double-float ABI) | `RISCV:LE:64:default:gcc` |
| RV32 ELF, `e_flags 0x5`, no Andes declaration | `RISCV:LE:32:AndeStar_v5:gcc` |
So an ordinary RV32 image lands on an Andes processor model unless the language
is stated. REA therefore pins `RISCV:LE:32:default` or `RISCV:LE:64:default`
with the `gcc` compiler specification per class, records
`language_selection: provider-pinned-v1`, and refuses a caller-selected
`REA_GHIDRA_LANGUAGE_ID` or `REA_GHIDRA_COMPILER_SPEC_ID` that disagrees with the
pin without changing the requested override. An override that matches the pin is
accepted and recorded as `configured-v1`.
## Evidence
The lane identity is `elf32-exec-le-double-float-generic-v1` or
`elf64-exec-le-double-float-generic-v1`, and `riscv_elf` retains `elf_class`,
`byte_order`, `type`, and the raw `flags` alongside the read-only booleans
`float_abi_double`, `rvc_declared`, `rve_declared`, and `tso_declared`. Those
fields report declarations the header makes; none of them is evidence of the
original compiler, of instruction conformance, or of a floating-point calling
convention beyond the ABI the pinned language models.
## Verification and limits
Ghidra 12.1.4 PUBLIC with a 64-bit JDK 21+ is required, and on macOS the release
tree ships no host decompiler binary, so `Decompiler/os/<platform>/decompile` and
`sleigh` must come from your own build; see
[installation](installation.md#ghidra).
Run `npm run verify:ghidra:riscv` with an existing Ghidra 12.1.4 installation,
JDK 21+, and a RISC-V-capable Clang, LLD and LLVM readelf. The verifier acquires
no tools and never executes target code. Select installed tools explicitly when
they are not on `PATH`:
```sh
GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC \
JAVA_HOME=/path/to/jdk-21 \
REA_RISCV_CLANG=/path/to/llvm/bin/clang \
REA_RISCV_LLD=/path/to/lld/bin/ld.lld \
REA_RISCV_READELF=/path/to/llvm/bin/llvm-readelf \
npm run verify:ghidra:riscv
```
The lane builds the committed freestanding C and assembly sources into RV32 and
RV64 `ET_EXEC` fixtures. Independent LLVM readelf observations establish ELF
declarations and symbol addresses. Public CLI and stdio MCP workflows verify
function enumeration, decompilation, pinned language/compiler identity, decoded
probe bytes, global data, and direct calls. It validates advertised MCP output
schemas and both JSON representations, saves and reopens snapshots, rejects
header-mutated soft-float and unsupported high-bit declarations, and checks
unchanged source bytes and owned cleanup. Failures retain the diagnostic
workspace; successful runs remove it and report the observed process lineage.
Module tests cover admission and malformed headers. Call-graph completeness,
string inventories, dynamic linkage, Andes or RVE/TSO images, and big-endian
targets are **not** established by this lane. `.riscv.attributes` ISA extensions
are not inspected; the generic language pin remains an analysis convention.
+99
View File
@@ -0,0 +1,99 @@
# Go binary build metadata
`inspect_go_binary` / `inspect-go-binary` answers which Go compiler version,
modules, replacements and build settings are embedded in one selected local
executable or library. It reads the file without running it and requires no
Go installation, active binary target, Hopper, Ghidra or IDA.
```bash
rea inspect-go-binary /absolute/path/to/application --json
```
The CLI resolves relative paths against the caller's working directory. MCP
requires an absolute filesystem path and preserves that exact selection:
```json
{
"name": "inspect_go_binary",
"arguments": { "path": "/artifacts/application" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, byte length,
container format, architecture, word size and byte order. A recognized
`build_info` record contains:
- `go_version`, preserved as embedded UTF-8 text rather than forced into a
semantic version, or `null` when the embedded bytes are not UTF-8;
- `go_version_bytes_base64`, preserving the exact compiler-string bytes;
- exact file offsets and byte lengths for the header, compiler string and module
string;
- `module_text`, the decoded module-information body, or `null` when the complete
body cannot be represented as UTF-8;
- `module_bytes_base64`, the original module-string bytes including Go framing;
- `module.path`, the embedded package path;
- `module.main`, the main module's path, version, checksum and optional
replacement;
- `module.dependencies`, with the same fields and their original order;
- `module.settings`, retaining ordered key/value entries and duplicate keys;
- `module.unparsed_lines`, `module.unparsed_line_bytes_base64` and
`module.complete`, so unfamiliar, malformed, conflicting or undecodable records
do not disappear behind a complete result.
Missing checksums are `null`; empty fields and development/local replacement
versions remain unchanged. Build settings such as `GOOS`, `GOARCH` or `vcs.revision`
are reported when present. Their absence does not establish a build choice.
Go strings may contain arbitrary bytes. Non-UTF-8 records remain available as
base64 while other decodable module records are still reported; the reader does
not insert replacement characters or reject the entire artifact for those
strings.
## Supported boundary
The built-in TypeScript reader supports ELF, PE and thin Mach-O images with Go's
inline or historical pointer build-info encoding. Source ranges refer to the
original stable file snapshot, including pointer-backed strings. It does not
scan arbitrary byte strings as compiler metadata or interpret a fat Mach-O
container as one selected architecture.
Historical moduleless Go binaries can store their empty module-string header in
declared zero-initialized virtual memory rather than file-backed bytes. The reader
recognizes this only when the complete optional module header lies in that memory
without a conflicting file mapping. Its module contents are empty and
`module_location` has zero bytes, anchored at the build-info field containing the
module-header pointer. Compiler strings and other unmapped pointers remain strict
file-backed reads.
`build_info: null` means no recognized record was found in the inspected image.
It does not prove that the binary is not Go. Stripping, old builds, deliberate
removal and obfuscation can limit metadata. A malformed supported container or
recognized build-info record returns an input-format failure. Unsupported
containers receive a separate unsupported-target failure.
Symlinks and nonregular files are rejected. The shared stable reader detects
replacement and observed in-place changes during acquisition. Missing files,
host permission denial, changed artifacts and cancellation retain distinct
errors. The selected file is never modified; this inspection creates no
temporary files, starts no subprocesses and accesses no network endpoint.
Resource guards bound the selected file to 256 MiB, aggregate embedded strings
to 1 MiB, structural table decoding to 16 MiB and the complete normalized metadata
report to 8 MiB. Oversized results fail without partial success. MCP also applies
its [response transport budget](mcp-contracts.md), which counts both text and
structured content; a report within the metadata limit can still require export
from retained Evidence. These limits are not operating-system memory or CPU
isolation.
Embedded metadata is evidence of file content. It does not authenticate a
compiler, dependency, commit or supply chain, and it does not establish runtime
behavior. Function names, source mappings, types and decompiled code are outside
this tool's coverage. Use the [native workflow](native-investigation.md) when
those questions require a disassembler.
## Real producer verification
`npm run verify:go:binary` exercises compiler-built fixtures through the public
CLI and MCP, comparing their metadata with Go's standard `debug/buildinfo`
reader. The verification lane requires Go; normal inspection does not.
+79
View File
@@ -0,0 +1,79 @@
# Static HarmonyOS package inventory
REA inventories HarmonyOS application packages through the shared hardened
ZIP artifact reader in `src/artifacts/`, then projects the authenticated
inventory into an execution-free application graph. This is the first
increment of the HarmonyOS roadmap (#1431): package identification and
component inventory only.
Recognized root formats:
- `.hap` — installable application module
- `.hsp` — runtime shared package
- `.app` — distribution App Pack (HAP/HSP children are listed by path). The
App Pack classification applies to verified-ZIP-magic root inputs only; a
nested ZIP member ending in `.app` inside another package, such as an IPA,
keeps the generic `zip` classification.
HarmonyOS `.har` shared libraries are intentionally not classified by suffix:
`.har` also names HTTP Archive JSON, so a Harmony shared archive requires
verified ZIP bytes and is deferred with an explicit limitation.
## Usage
No external SDK, toolchain, or engine is required. Inventory the package and
project the graph; CLI and MCP use the same workflow and return
artifact-bound Evidence inline:
```sh
rea inspect-artifact /path/to/Example.hap --format json > inspection.json
node --input-type=module -e 'import { readFileSync } from "node:fs"; console.log(JSON.stringify({ inventory_evidence: [JSON.parse(readFileSync("inspection.json", "utf8"))] }));' > projection-input.json
rea project-harmony-application-graph projection-input.json --format json
```
The MCP tool `project_harmony_application_graph` takes the same
`inventory_evidence` input. The projection reports:
- exact component paths, artifact IDs, and hashes (manifests, Ark bytecode,
resources, native libraries, JavaScript, signing)
- a packaging-model hint: Stage is claimed only from a `module.json` entry,
FA only from `config.json`, otherwise `unknown`
- FA resources under `assets/<module>/resources/` as well as Stage `resources/`
- unavailable component paths with unknown IDs and hashes, including encrypted entries; these make projection coverage partial
- App Pack child package paths without recursive inventory
- runtime-family hints (`ark`, `native`, `javascript`) from paths and formats
- path-based N-API bridge hypotheses between bytecode and `libs/<abi>` native
libraries
Every claim is path-presence only. Manifest semantics (module identity,
abilities, entry points, permissions, dependencies), Ark bytecode contents and
versions, resource resolution, signing verification, and observed native
boundaries are out of scope for this increment and stay in the projection's
`limitations` array until a dedicated HarmonyOS provider exists. REA fails
closed on packages whose inventory cannot be authenticated.
## Verification lane
`npm run fixtures:harmony` downloads the SHA-256-pinned 1.0.1 HAP release
from the MIT-licensed [harmony-next-pipeline source repository](https://github.com/ohosvscode/harmony-next-pipeline)
into ignored `_reference/harmony-integration/`. The pin records the source
revision, release asset ID, size, and digest. The downloaded artifact is not
redistributed; bundled component licenses have not been independently audited.
```sh
npm run fixtures:harmony
REA_HARMONY_TEST_HAP=_reference/harmony-integration/entry-default-unsigned.hap \
npm run verify:harmony
```
The lane exercises the compiled CLI and actual SDK-connected stdio MCP:
`inspect_artifact` authenticity, projection determinism, Stage-model path hints,
exact manifest/bytecode/resource component sets, empty native and bridge sets,
complete-inventory coverage, and CLI/MCP parity. It checks that this fixture has
no META-INF signing entries; that does not establish whether the package is signed.
The release artifact's compiler/SDK versions and reproducible source build have
not been independently established. This lane does not complete the pinned
toolchain acceptance in #1431 and does not verify Ark decoding or signatures.
Set `REA_HARMONY_ENTRYPOINT` to an installed package's `scripts/rea.mjs` to run
the same checks against that package; the default lane uses the repository build.
+183
View File
@@ -0,0 +1,183 @@
# Verify real HTTP/3 MITM captures
This optional Linux laboratory checks native mitmproxy captures through REA's
public CLI and MCP. It changes no runtime contracts or adapters and establishes
only the specific producer, mode and clients exercised here.
## Prepare a disposable guest
The exercised image is [Omarchy 4.0.4, build 440](https://nightly.omarchy.org/stable/4.0.4/440/omarchy-4.0.4.qcow2),
SHA-256 `43412b896fe3d730e373a51a8acf99343d7c3e6dee78aa9f9bd7fca181127264`.
Download it without installing guest packages on the host, check its digest,
and use a **new qcow2 overlay** with that file as a read-only backing image.
The matching [boot artifacts](https://nightly.omarchy.org/stable/4.0.4/440/unpacked/)
include the kernel, initrd, cmdline and upstream vm-boot recipe. Use a fresh SSH
key, loopback forwarding and guest disk; do not reuse another investigation's
writable overlay or host trust configuration. Shut the guest down before
switching overlays. Preserve the image and boot artifact hashes with evidence.
This sealed guest already supplies cURL 8.22.0 with OpenSSL 3.6.4, ngtcp2 1.25.0,
nghttp3 1.18.0 and nghttp2 1.70.0. **No guest rolling package upgrade is needed.**
Run `curl --version` and verify HTTP2 and HTTP3 features before starting. A stock
cURL from another distribution may omit HTTP/3. For a different guest, building
cURL requires a compatible TLS library, ngtcp2 and nghttp3; follow the
[official build instructions](https://curl.se/docs/http3.html), install to a guest
private prefix, and record exact source revisions and build inputs. That alternative
has not been exercised by this laboratory; it is not the pinned reproduction path.
Use a fresh VM overlay over a sealed Linux image. Use user-mode networking with
SSH forwarding bound to host loopback; keep host directories unmounted. Install
and build dependencies inside the guest, then remove its default route before
generating traffic. Host trust, routes and installed packages stay unchanged.
Record the image digest, REA source revision and these dependency versions:
- REA's `.nvmrc`, `package.json#packageManager` and locked npm dependencies.
- Official Linux x86_64 mitmproxy **12.2.3** standalone archive. SHA-256:
`2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5`.
- A private Python virtual environment using the complete pinned graph in
`scripts/fixtures/http3-lab-requirements.txt`. Save `pip freeze` and build/download logs.
- OpenSSL and cURL advertising HTTP3 and HTTP2 in `curl --version`.
The verified guest used Python 3.14.7, cURL 8.22.0, ngtcp2 1.25.0 and nghttp3
1.18.0. Other dependency profiles need verification. Back up the guest's
`/etc/hosts` before adding `127.0.0.1 lab.test`. Reserve guest TCP/UDP loopback
ports 18443 and 19443. These changes belong only to the disposable guest.
From the guest REA checkout, use its pinned Node/npm toolchain:
```sh
npm ci
npm run build:cached
python3 -m venv /absolute/lab/venv
/absolute/lab/venv/bin/pip install --no-deps -r scripts/fixtures/http3-lab-requirements.txt
/absolute/lab/venv/bin/pip check
/absolute/lab/venv/bin/pip freeze > /absolute/lab/python-requirements.txt
```
Before removing the guest default route, download the official mitmproxy archive
and verify it, then extract to the guest's private tools directory:
```sh
mkdir -p /absolute/lab/tools
curl --fail --location https://downloads.mitmproxy.org/12.2.3/mitmproxy-12.2.3-linux-x86_64.tar.gz \
--output /absolute/lab/mitmproxy.tar.gz
printf '%s %s\n' 2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5 \
/absolute/lab/mitmproxy.tar.gz | sha256sum --check --strict
tar -xzf /absolute/lab/mitmproxy.tar.gz -C /absolute/lab/tools
/absolute/lab/tools/mitmdump --version
```
Pin the REA revision with `git rev-parse HEAD` or archive SHA-256. Build using
the exact `.nvmrc` and npm version; do not copy a different checkout's `dist`.
For stronger offline dependency reproduction, preserve downloaded Python
archives, their digests and built wheels along with the image and toolchain.
The requirement versions pin the runtime graph, not every transient build tool;
source builds and alternate interpreters are not guaranteed byte-identical.
## Run the narrow profile
Select absolute executables and a new evidence directory. The producer refuses
to reuse a directory. No certificates are installed in a system trust store;
clients and proxy receive explicit paths to freshly generated test CAs.
```sh
REA_MITMDUMP_COMMAND=/absolute/lab/tools/mitmdump \
REA_HTTP3_PYTHON_COMMAND=/absolute/lab/venv/bin/python \
npm run verify:web:http3 -- /absolute/evidence/run-1
```
The producer sends three concurrent binary POSTs on one HTTP/3 connection to the
same URL through mitmproxy reverse mode to an aioquic HTTP/3 origin. The origin
waits for all complete requests, then dispatches responses in reverse order of
completion at the origin. QUIC stream send/receive orders need not coincide. A cURL HTTP/3-only
request checks a second client implementation. Mitmproxy's real save addon
writes `normal.mitm`; no native flows are synthesized.
A second proxy run disables HTTP/3. cURL `--http3` falls back to HTTP/2, reaching
an HTTP/1.1 TLS test origin. `fallback.mitm` must retain those lower protocols,
so a successful HTTP request cannot masquerade as an HTTP/3 success.
## Acceptance and evidence
Each execution creates `verifications/<run-UUID>/` with its own diagnostics.
`verification-latest.json` belongs to the most recently registered attempt and
changes atomically from `running` to `passed` or `failed`. Registration and
the ownership check/replacement use the same SQLite transaction lock. An older
attempt finishing later keeps its own reports without replacing the marker.
The `.verification-lock.sqlite` pathname must remain in place while attempts
may run; SQLite releases the lock on connection close or process death.
Lock acquisition waits for the current owner without a timeout or lease
stealing. The verifier has per-command deadlines, but no overall run deadline;
a live stalled lock owner can therefore delay publication until it exits.
Only a successful attempt writes its own `verification.json`, after
all cleanup and input-integrity checks. `result.json` also records failures and
the original error separately from cleanup errors. Older successes remain in
identified history; a legacy root `verification.json` is archived before a rerun.
A killed verifier may leave `running`, which must never be interpreted as success.
Capture inputs live in `captures/`. To inspect them again without new traffic:
```sh
REA_MITMDUMP_COMMAND=/absolute/lab/tools/mitmdump \
npm run verify:web:http3 -- /absolute/evidence/run-1 --inspect-existing
```
Acceptance for the current run:
- Every positive native record reports HTTP/3, UDP, ALPN h3 and successful TLS
timestamps on both legs. Connection IDs, producer TLS labels, exact TLS setup
timestamps and connection errors match the UUID-matched producer observation
on each leg. Endpoint handshakes independently observe QUIC v1.
- Native flow UUIDs remain distinct. Three endpoint streams share a connection
on each leg. Origin dispatch reverses completed receipt. Client and capture
ordering is observed, while association is verified independent of their order.
- CLI and MCP agree. Request/response bytes, lengths, SHA-256, ordered duplicate
headers, method, scheme, authority and path match independent observations.
Readable header names and values, request URL fields and ALPN also match;
non-UTF-8 header bytes retain a null display value rather than replacement text.
- Actual MCP advertised schemas validate, Evidence preserves source digests and
provider provenance, and the complete returned Evidence matches the retained MCP session records.
- The fallback capture reports client HTTP/2 over TCP and origin HTTP/1.1, with
cURL independently reporting version 2. Input files remain unchanged.
- Every cleanup action is attempted on success and failure, and final lineage
contains no live descendants. Recorded producer groups are inspected separately
after token-verified cleanup, including after their launcher has exited.
Retain captures, endpoint/producer JSONL, proxy diagnostics, cURL output, manifest,
CLI/MCP output, advertised schema and session bundle. Export public certificates
and dependency/source provenance separately. Do not commit evidence, private
keys, dependencies or VM disks. Shut down the guest after exporting evidence.
The reverse proxy can preserve authority `lab.test:18443` while recording origin
port 19443. Mitmproxy's serialized flow/connection UUIDs are not stream IDs.
Endpoint stream IDs are scoped observations; this test invents no cross-leg
mapping. Mitmproxy 12.2.3's `QUICv1` label alone does not prove negotiation: the
fixture checks the negotiated `_quic._version` in pinned aioquic 1.2.0.
Coverage is native capture, Linux reverse mode, QUIC v1 and these clients.
Empty/absent/incomplete/redacted body semantics, cancellations, resets,
certificate failures, malformed captures, HAR and qlog are separate increments.
Local/WireGuard, browsers, QUIC v2, migration, 0-RTT, mTLS, replay, sustained load
and loss tolerance remain unverified by this profile. Passing it does not
establish general HTTP/3 support in REA.
Official references: [mitmproxy protocol support](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/docs/src/content/concepts/protocols.md),
[HTTP/3 implementation](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/mitmproxy/proxy/layers/http/_http3.py),
[serialized HTTP state](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/mitmproxy/http.py),
[aioquic configuration](https://github.com/aiortc/aioquic/blob/1.2.0/src/aioquic/quic/configuration.py).
## Verifier regressions
After building REA, run these inside the same isolated guest:
```sh
node --test scripts/lib/http3-verifier-lifecycle.test.mjs scripts/lib/http3-capture-assertions.test.mjs
/absolute/lab/venv/bin/python scripts/fixtures/http3-lab.test.py
```
These cover different QUIC stream completion orders, stale success reports,
multiple failed closes, separately owned process groups and partially acquired
resources, plus producer TLS fact mismatches on both legs and altered readable
fields with unchanged binary evidence. They test verifier
semantics; they do not add HTTP/3 provider coverage.
+55 -8
View File
@@ -1,9 +1,18 @@
# IDA Pro provider
# IDA provider
REA adapts an existing [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) server into its native analysis tools. Install and configure IDA, Hex-Rays, Python, and upstream MCP using that repository's instructions. REA does not install, upgrade, activate, or license them, and does not bundle another IDA server.
REA adapts an existing IDA MCP server into its native analysis tools. Existing IDA Pro profiles use [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp); an optional native bridge for IDA Pro and IDA Free is described below. Install and configure your selected engine and upstream MCP using their instructions. REA does not install, upgrade, activate, or license them, and does not bundle another IDA server.
Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP_CONFIG` to that file, then select `ida` once through `REA_ANALYSIS_PROVIDER`, the CLI's `--provider ida`, or MCP's `open_binary.provider_id`. No provider startup occurs during discovery.
## Supported upstream versions
Each mode accepts one upstream tool profile, checked against `tools/list` before any analysis runs. Upstream's `main` branch has since moved to 2.0.0, which renamed the GUI tools and dropped `idb_close`, so it serves neither profile. Installing from that branch fails at startup with the missing tool names in the error diagnostics.
| Mode | Upstream profile | Known working |
| -------- | ------------------- | ------------------------------------------------------ |
| attached | legacy GUI tools | 1.4.0 |
| headless | database supervisor | builds serving `idb_open`, `idb_list`, and `idb_close` |
## Analyze an already-open GUI target
The attached profile supports upstream **1.4.0 legacy tools**, including `get_metadata`, `list_functions`, and `decompile_function`. Copy the upstream registration's `command`, `args`, and optional `env` into a JSON file. An existing `mcpServers` object with an `ida-pro-mcp` entry is also accepted. For example:
@@ -32,6 +41,34 @@ Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
## Optional native IDA MCP bridge
IDA Pro and IDA Free can use the separate [ida-free-mcp native plugin](https://github.com/0xshlomil/ida-free-mcp) through an explicit attached registration. Install and start that plugin using its upstream instructions, open the original binary in the IDA GUI, and wait for analysis. The native MCP 1.1.2 release binds `0.0.0.0` without a listen-address setting. Restrict inbound access with the host firewall or use an upstream build that binds only to loopback before starting it. REA's loopback URL restriction does not change the plugin's listener. REA does not install IDA, build the plugin, activate a license, or launch the GUI.
```json
{
"mode": "attached",
"protocol": "native",
"url": "http://127.0.0.1:13337/mcp"
}
```
Set `REA_IDA_MCP_CONFIG` to this file and select `ida` using the same CLI/MCP commands above. Omitting `protocol` preserves existing IDA Pro registrations. Native mode requires `resources/read` for `ida://idb/metadata` and its recorded input SHA-256; it never substitutes a digest of an unrelated local file. The native resource's `path` describes the IDB in the upstream protocol, not the original input path. REA retains it and checks database identity and image base before and after requests.
This experimental profile provides function/string inventory and search, function lookup, assembly, GUI pseudocode, callees, address xrefs, instruction windows, and function dossiers. Direct callers remain unavailable: native code/data xref labels do not prove call edges. It requires neither IDAPython nor idalib. Native headless registrations are rejected.
Native string observations use upstream C-string rendering; other representations remain unknown. Literal searches request conservative candidates and apply REA's original case and Unicode matching rules. Regex searches retain the full scan because native byte-oriented regex semantics cannot replace REA's Unicode regex semantics. All candidate pages are examined, and upstream search errors remain failures.
Native callee resolution batches unique queries and checks each reply's query identity. Results remain live: a recorded input hash does not establish database revision, so cross-request analysis caches cannot prove freshness. For focused questions, use pseudocode, callees, or instruction-window tools; a function dossier requests additional facets and returns more Evidence.
IDA Free's cloud decompiler needs network access and may request consent. Follow its licensing and supported architecture limits; see [IDA Free](https://hex-rays.com/ida-free). Decompilation moves GUI focus and the upstream plugin closes existing pseudocode tabs before requesting fresh output. REA advertises UI/network effects and closes only its MCP connection, preserving the open database. Rendered pseudocode has upstream traversal limits and does not prove complete source recovery. Cloud/widget failures remain explicit failures; retry after the GUI finishes or dismiss its prompt.
Native MCP 1.1.2 can navigate in the previously focused pseudocode tab when switching functions. Use upstream 1.1.3 or a source build including [the navigation correction](https://github.com/0xshlomil/ida-free-mcp/commit/6adf2c615a2a03661059673e9c136ef28d0dc778), which focuses the disassembly view **before** jumping to the requested entry. This REA profile does not repair stale GUI output. Verification must include two different functions before claiming reliable function switching. The initial Linux verification used a locally corrected 1.1.2 plugin; upstream 1.1.3 has not been verified by that run.
Native tool observations retain actual tool names and complete JSON text. Upstream `structuredContent` can be a shortened preview even while text contains the full result. REA reads the full native representation and rejects missing/malformed text instead of treating previews as complete observations.
The legacy producer moves the active GUI as a side effect: assembly and raw function instructions jump to the function, and successful pseudocode opens or reuses a pseudocode view. A function dossier does both. Native GUI decompilation also changes the active view. `binary_session` reports these operations with `effects.may_show_ui: true`; headless operations and the other attached operations report `false`.
## Give REA a target for headless analysis
The headless profile supports upstream's **database supervisor** API: `idb_open`, `idb_list`, `idb_close`, explicit `database` arguments, and modern analysis tools. Legacy 1.4.0 `idalib-mcp`, which accepts one initial filename and serves SSE, does not implement this profile. Follow the [upstream headless instructions](https://github.com/mrexodia/ida-pro-mcp#headless-idalib-session-model) for an installation with the supervisor API.
@@ -56,10 +93,12 @@ rea function 'D:\samples\program.exe' main --json
Headless REA and the configured supervisor must share the host filesystem and native path syntax. Run REA on Windows when the supervisor runs on Windows. The attached profile can span WSL and a Windows GUI because it binds recorded input hashes instead of asking the GUI to open a WSL path.
REA copies the admitted binary into a private temporary directory, checks its digest, and requests an owned worker with `force_headless`. POSIX workspaces use mode `0700`; Windows uses the matching package's native private-DACL boundary on a local NTFS temporary directory. It injects the returned database ID into analysis requests. It verifies the active owned worker and input path rather than adopting a user database. On close or failed startup, it releases its session with `save: false`, verifies that it is absent from upstream session discovery, and removes its private input and sidecars. An unverified release retains the workspace and reports incomplete cleanup.
REA copies the admitted binary into a private temporary directory, checks its digest, and requests an owned worker with `force_headless`. POSIX workspaces are created atomically with owner-only permissions, respecting the process umask; Windows uses the matching package's native private-DACL boundary on a local NTFS temporary directory. It injects the returned database ID into analysis requests. It verifies the active owned worker and input path rather than adopting a user database. On close or failed startup, it releases its session with `save: false`, verifies that it is absent from upstream session discovery, and removes its private input and sidecars. An unverified release retains the workspace and reports incomplete cleanup.
One REA session owns one database. Stdio registrations default `IDA_MCP_MAX_WORKERS` to `1`; the explicit upstream `--max-workers 1` also bounds a supervisor. Database size and engine memory depend on the target and upstream; REA does not impose an engine memory limit. The original binary is never executed or overwritten. Optional `workspaceRoot` selects an existing absolute temporary-directory parent on the REA host. Ensure it has space for the input copy and IDA working files.
When a completed `idb_open` response reports success or is otherwise ambiguous, but no active owned private worker can be verified, REA retains the private workspace even if `idb_list` appears empty. A later close can retry verification if the original owned worker becomes discoverable. A completed explicit `idb_open` rejection retains the existing no-worker cleanup path. This is a local admission/ownership safeguard and **does not** provide a process-termination receipt or change the legacy reaped-worker protocol.
Headless cleanup uses the upstream database lifecycle. Abruptly terminating REA, its proxy, or the supervisor does not establish worker release. Prefer `close_binary` for MCP sessions; after an interrupted process, inspect upstream session inventory and retained workspace diagnostics before removing working files. This adapter does not claim Windows Job Object containment for the upstream detached workers.
## Connect an agent once
@@ -90,17 +129,19 @@ Use a package version that actually includes this adapter; repository main can b
{"name":"close_binary","arguments":{}}
```
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. A freshly opened headless worker can briefly report `status: "busy"` from `server_health`; REA re-reads the health every 100 ms until the worker answers, the caller cancels, or `timeoutMs` has elapsed since the first read. Only expiry while the worker is still busy produces a retryable `provider_timeout`; no further read is started once the deadline has passed. That error names the busy tool of the last observation, or `unknown` when the worker reported none or a value that is not tool-name-shaped. Cancellation during the wait produces `cancelled`. Any other unexpected health payload, including a busy payload with an invalid shape, remains `unreadable_output`. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <client>`. Setup includes that file reference in its reviewed plan and preserves it after approval; it does not copy upstream credentials into the agent configuration. `rea doctor --provider ida --json` validates the registration without starting IDA and explicitly leaves runtime compatibility and target binding for analysis.
## Coverage and evidence
Both profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
All profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. In the modern headless profile, targets that are not proven x86-only use processor-neutral `callgraph` edges instead of the x86 instruction checks in upstream `callees` (mrexodia/ida-pro-mcp#549). REA requests depth zero: the pinned upstream emits the root's outgoing edges without scanning child functions, then REA batches function-name lookup for those addresses. The graph covers recognized function destinations only; imports and other non-function targets remain unknown. A server without `callgraph` returns an empty relationship list with an explicit coverage limitation. Graph and path workflows retain those limitations alongside observed edges. A failed or inconsistent graph reply remains an error. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
The legacy profile additionally supports direct callers. Its producer may report call-site or interior addresses; REA resolves the reported function symbol to its canonical entry. The modern profile does not prove direct callers from its code/data xref labels, so that capability is unavailable and the dossier marks it unknown.
The modern headless profile also supports `resolve_containing_procedure` and `list_documents`. An address resolves through upstream `lookup_funcs` to the canonical entry of the function that contains it; the body stays unavailable like other IDA identities. When no function contains it, REA reads the database-scoped minimal `survey_binary` and reports `not_in_procedure` for an address inside one of its segments (ends are exclusive) and `outside_segments` otherwise. A missing, truncated or inconsistent survey fails the read instead of guessing a reason. `list_documents` returns the single admitted program path, so `inspect_address_context` and `trace_feature` need no explicit document. `trace_feature` marks a trace truncated, with a residual unknown for that address, when upstream caps the references it returned. The legacy and native profiles do not declare these operations.
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. `binary_overview`, whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
The legacy profile additionally supports direct callers. Its producer may report call-site or interior addresses; REA resolves the reported function symbol to its canonical entry. The modern and native profiles do not prove direct callers from its code/data xref labels, so that capability is unavailable and the dossier marks it unknown.
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. `binary_overview`, whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and requested GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
`provider.version` identifies the REA adapter; the profile explicitly records `version_scope: "rea-ida-adapter"`. The MCP handshake's `serverInfo` is separate and does not establish IDA or Python distribution versions. Raw observations preserve producer metadata and output. Attached hashes identify the recorded original input; headless identity uses the digest-verified private copy and observed worker input path. Neither proves source-code recovery or an unmodified analysis database.
@@ -108,4 +149,10 @@ Complete body ranges, typed reference edges, CFG, referenced data, source locals
Run `npm run verify:ida -- --target /absolute/path/to/program --procedure main` with a configured upstream server. It exercises production CLI and MCP tools, validates their output contracts and Evidence, checks original-input preservation and session cleanup, and keeps local target data out of its console summary. Supply `--package-root` to verify an extracted or installed package. See [testing](testing.md) for the distinction between adapter tests and real-provider evidence.
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux/macOS headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
Native verification requires `--second-procedure other_function --second-marker unique_second_body_text`; these options remain optional for the other profiles. The marker must occur in the second function's pseudocode and be absent from the first; the verifier also checks that returning to the first restores its text.
The verifier requires the resolved procedure in both inventory and procedure search, then compares address, assembly, pseudocode, instruction windows and relationship results with the CLI/MCP dossier. Supply `--expected-string known_literal` to check a known positive string inventory/search result and `--expected-xref 0xSOURCE` to check a known reference source address. Choose those oracles independently from the operation under test. Without them, string and xref correctness are listed in `unverified`; valid output shapes alone do not establish those behaviors. Callee parity does not independently establish semantic completeness. Native callee successes must report an explicit truncation marker; missing markers remain protocol failures.
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. The headless workflow has also passed on macOS arm64 with IDA Professional 9.1 (build 9.1.250226) against the same upstream commit `c133c3853faa111a9b00ee615c013b720d0c4acd`, using a source-built arm64 Mach-O fixture; Mach-O C symbols carry a leading underscore, so the procedure name is `_rea_fixture_add`. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
Native Windows verification uses IDA Pro 9.3 with the public two-function fixture and Windows `whoami.exe`, using upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c` with a loopback-listener change. It covers production CLI/MCP parity, distinct-function switching, identity and connection cleanup. See the [correctness and performance measurements](https://github.com/morluto/rea/blob/main/experiments/analysis-efficiency/ida-native-windows.md). This follow-up did not run Windows IDA Free or native macOS.

Some files were not shown because too many files have changed in this diff Show More