rea-agents 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -28
- package/bridge/hopper_bridge.py +161 -12
- package/dist/application/AnalysisSnapshotCache.js +131 -0
- package/dist/application/AnalysisSnapshotFiles.js +31 -0
- package/dist/application/ArtifactInventory.js +84 -44
- package/dist/application/AuthorizedArtifactInventory.js +16 -0
- package/dist/application/BinarySession.js +66 -36
- package/dist/application/BinarySessionPort.js +1 -0
- package/dist/application/BoundedJsonFiles.js +109 -0
- package/dist/application/CommandShimReplay.js +136 -0
- package/dist/application/CrossVersionInventory.js +98 -0
- package/dist/application/CrossVersionInvestigation.js +295 -0
- package/dist/application/DirectAnalysis.js +115 -22
- package/dist/application/Doctor.js +30 -1
- package/dist/application/EvidenceBundleFiles.js +16 -101
- package/dist/application/EvidenceLedger.js +3 -2
- package/dist/application/InvestigationProviders.js +48 -0
- package/dist/application/InvestigationWorkspaceStore.js +214 -0
- package/dist/application/LinuxHopper.js +72 -48
- package/dist/application/ProcessCaptureAuthority.js +25 -0
- package/dist/application/ProcessCaptureCapability.js +23 -0
- package/dist/application/ProcessCaptureError.js +17 -0
- package/dist/application/ProcessCaptureLifecycle.js +287 -0
- package/dist/application/ProcessCheckpoints.js +129 -0
- package/dist/application/ProcessCli.js +128 -0
- package/dist/application/ProcessEvidence.js +38 -0
- package/dist/application/ProcessHarness.js +252 -272
- package/dist/application/ProcessNormalization.js +10 -1
- package/dist/application/ProcessOwnership.js +39 -1
- package/dist/application/ProcessSampling.js +39 -23
- package/dist/application/ReferenceSourceImportEntries.js +18 -3
- package/dist/application/ReferenceSourceImportTypes.js +32 -0
- package/dist/application/Setup.js +44 -96
- package/dist/application/SetupClients.js +19 -0
- package/dist/application/SetupInstallFailure.js +53 -0
- package/dist/application/SetupPlan.js +31 -0
- package/dist/application/SetupSkill.js +44 -0
- package/dist/application/TerminalRenderer.js +92 -0
- package/dist/application/Uninstall.js +33 -23
- package/dist/application/UnknownEvidence.js +33 -0
- package/dist/application/Upgrade.js +1 -1
- package/dist/application/runtime.js +2 -2
- package/dist/artifacts/ArtifactProvider.js +20 -11
- package/dist/artifacts/ArtifactReader.js +3 -1
- package/dist/artifacts/AsarArtifactReader.js +8 -1
- package/dist/artifacts/DirectoryArtifactReader.js +1 -0
- package/dist/artifacts/MachOSliceArtifactReader.js +1 -0
- package/dist/artifacts/NativeDmgArtifactReader.js +151 -0
- package/dist/artifacts/ZipArtifactReader.js +1 -0
- package/dist/cli.js +57 -32
- package/dist/cliEvidenceCommands.js +11 -12
- package/dist/cliInvestigationCommands.js +86 -0
- package/dist/cliOutput.js +41 -0
- package/dist/cliProcessCommands.js +29 -0
- package/dist/config.js +41 -24
- package/dist/contracts/artifactToolContracts.js +1 -0
- package/dist/contracts/investigationExamples.js +3 -3
- package/dist/contracts/processCaptureExample.js +50 -7
- package/dist/contracts/promptContracts.js +256 -0
- package/dist/contracts/sessionLifecycleInputs.js +11 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +13 -8
- package/dist/contracts/toolOutputSchemas.js +33 -4
- package/dist/domain/analysisSnapshot.js +149 -0
- package/dist/domain/changedBehavior.js +32 -9
- package/dist/domain/errors.js +186 -39
- package/dist/domain/evidence.js +4 -2
- package/dist/domain/evidenceBundle.js +28 -0
- package/dist/domain/hopperStartupFailure.js +55 -0
- package/dist/domain/investigationWorkspace.js +214 -0
- package/dist/domain/jsonValue.js +2 -0
- package/dist/domain/nativeInspection.js +3 -2
- package/dist/domain/processCapture.js +100 -293
- package/dist/domain/processCaptureValidation.js +124 -0
- package/dist/domain/processComparison.js +177 -32
- package/dist/domain/processScenario.js +370 -0
- package/dist/domain/reconstructionVerification.js +3 -3
- package/dist/domain/staticRuntimeCorrelation.js +3 -2
- package/dist/hopper/BridgeLauncher.js +47 -20
- package/dist/hopper/HopperClient.js +17 -2
- package/dist/hopper/HopperProvider.js +1 -0
- package/dist/identity.js +1 -0
- package/dist/main.js +47 -24
- package/dist/server/createServer.js +2 -0
- package/dist/server/promptCompletion.js +144 -0
- package/dist/server/registerEnhancedTools.js +2 -6
- package/dist/server/registerEvidenceTools.js +2 -8
- package/dist/server/registerFunctionComparisonTool.js +1 -1
- package/dist/server/registerInvestigationTools.js +31 -11
- package/dist/server/registerOfficialTools.js +6 -13
- package/dist/server/registerProcessComparisonTool.js +28 -14
- package/dist/server/registerPrompts.js +67 -0
- package/dist/server/registerSessionStatusTool.js +11 -0
- package/dist/server/registerSessionTools.js +55 -43
- package/dist/server/sessionEvidence.js +2 -8
- package/dist/server/sessionToolPolicies.js +2 -48
- package/dist/server/toolResult.js +7 -3
- package/install.sh +11 -11
- package/package.json +9 -3
- package/scripts/hopper-demo-x11.py +349 -0
- package/scripts/prepare-node-pty.mjs +35 -0
- package/scripts/rea.mjs +6 -3
- package/skills/rea-analysis/SKILL.md +27 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# REA: Reverse Engineer Anything
|
|
6
6
|
|
|
7
|
-
### One CLI and MCP server for
|
|
7
|
+
### One CLI and MCP server for agents to reverse engineer anything
|
|
8
8
|
|
|
9
9
|
**See a feature you like. Understand how it works, down to the binary level.**
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
See a feature in an app that you want in your own product? Give the app to your
|
|
27
|
+
See a feature in an app that you want in your own product? Give the app to your agent—even without its source code. With REA, the agent can investigate the feature, explain how it works, show its evidence, and build a version adapted to your stack and requirements.
|
|
28
28
|
|
|
29
29
|
REA gives agents one consistent way to investigate software. Today that includes deep native analysis through Hopper, complete function dossiers, reproducible Evidence v2 records, and controlled process capture. The longer-term toolkit extends the same agent workflow to packaged apps, JavaScript bundles, websites, APIs, protocols, mobile artifacts, firmware, runtime behavior, and differences between versions.
|
|
30
30
|
|
|
@@ -73,7 +73,7 @@ REA shows how it reached its conclusions. It does not claim to recover original
|
|
|
73
73
|
| | |
|
|
74
74
|
| ------------------------ | ------------------------------------------------------------------------------------ |
|
|
75
75
|
| **Built for agents** | Ask what an app does and let your agent inspect it instead of guessing. |
|
|
76
|
-
| **CLI and MCP** | Run the same reverse-engineering capabilities from your terminal or
|
|
76
|
+
| **CLI and MCP** | Run the same reverse-engineering capabilities from your terminal or agent. |
|
|
77
77
|
| **Complexity handled** | REA installs and manages the reverse-engineering tools behind the scenes. |
|
|
78
78
|
| **From insight to code** | Understand a feature, then build your own version in the same coding session. |
|
|
79
79
|
| **Local by design** | Analysis runs on your Mac. REA does not upload the app to a hosted analysis service. |
|
|
@@ -88,7 +88,7 @@ npm install --global rea-agents
|
|
|
88
88
|
rea setup
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
Installing the CLI does not update Homebrew, Node.js, npm, Hopper, or
|
|
91
|
+
Installing the CLI does not update Homebrew, Node.js, npm, Hopper, or agent configuration. `rea setup` detects what is already present, prints every proposed change, and asks before applying it.
|
|
92
92
|
|
|
93
93
|
REA detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, and Devin. Registrations are additive, backup-first, and read back after writing. You can safely rerun setup.
|
|
94
94
|
|
|
@@ -100,7 +100,7 @@ curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
|
|
100
100
|
|
|
101
101
|
Pass installer options after `bash -s --`, for example `--dry-run`, `--no-setup`, or `--version 1.0.0`. The curl wrapper never installs prerequisites or configures integrations itself. See [Installation and setup](docs/installation.md) for its exact mutation boundary.
|
|
102
102
|
|
|
103
|
-
### With
|
|
103
|
+
### With an agent — recommended
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
npx skills add morluto/rea
|
|
@@ -108,7 +108,7 @@ npx skills add morluto/rea
|
|
|
108
108
|
|
|
109
109
|
Ask your agent to set up REA. It will check your Mac, explain anything it needs to install, ask for approval, and guide you through system prompts. After setup, restart the agent if it asks you to load the full REA toolset.
|
|
110
110
|
|
|
111
|
-
Review the setup plan, approve it if appropriate,
|
|
111
|
+
Review the setup plan, approve it if appropriate, then describe the app or feature you want to understand. Hopper can run in its free demo mode; if it shows a first-run prompt, choose the demo or enter an existing license.
|
|
112
112
|
|
|
113
113
|
### From Terminal — no installation
|
|
114
114
|
|
|
@@ -118,7 +118,7 @@ npx -y rea-agents doctor
|
|
|
118
118
|
npx -y rea-agents analyze /Applications/Notes.app
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
Review the setup plan before confirming it. Restart a configured
|
|
121
|
+
Review the setup plan before confirming it. Restart a configured agent so it loads REA.
|
|
122
122
|
|
|
123
123
|
### From Terminal — install the `rea` command
|
|
124
124
|
|
|
@@ -157,13 +157,13 @@ If something is not working, run:
|
|
|
157
157
|
npx -y rea-agents doctor
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
`rea doctor --json` is read-only and distinguishes unsupported hosts, missing dependencies, a missing local analysis engine, configuration drift, and healthy checks.
|
|
160
|
+
`rea doctor --json` is read-only and distinguishes unsupported hosts, missing dependencies, a missing local analysis engine, configuration drift, and healthy checks. Paid-license activation is optional: on Linux, REA runs the supported Hopper demo build on a private Xvfb display and selects Hopper's offered demo mode for each analysis session.
|
|
161
161
|
|
|
162
162
|
### Linux installation and troubleshooting
|
|
163
163
|
|
|
164
|
-
On macOS, approved setup downloads Hopper's official DMG, verifies it, and installs the app into `~/Applications` without Homebrew or administrator privileges.
|
|
164
|
+
On macOS, approved setup downloads Hopper's official DMG, verifies it, and installs the app into `~/Applications` without Homebrew or administrator privileges. Hopper may show its demo or license prompt when first opened; no manual drag-and-drop is required.
|
|
165
165
|
|
|
166
|
-
On Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux, approved setup downloads the
|
|
166
|
+
On Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux, approved setup downloads the pinned official Hopper 6.4.2 package, restricts downloads to Hopper's public origin, verifies the published size and checksum, and invokes `apt-get`, `dnf`, or `pacman` to install Hopper and the Xvfb, Python, X11, and XTEST packages used by demo sessions. When REA is not already running as root, `pkexec` presents the system authorization prompt. REA never invokes `sudo`. Demo sessions run on an isolated 1280×1024 Xvfb display. REA verifies the exact supported Hopper binary, its owned process ancestry, the expected dialog geometry, and bridge state before selecting `Try the Demo`; any mismatch fails closed.
|
|
167
167
|
|
|
168
168
|
The normal Linux launcher is `/opt/hopper/bin/Hopper`. If Hopper was installed elsewhere:
|
|
169
169
|
|
|
@@ -178,7 +178,7 @@ If doctor reports a missing analysis engine even though the file exists, inspect
|
|
|
178
178
|
ldd /opt/hopper/bin/Hopper | grep 'not found'
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
Install the missing distribution packages and rerun `rea setup`.
|
|
181
|
+
Install the missing distribution packages and rerun `rea setup`. Linux demo automation requires `Xvfb`, Python 3, `libX11.so.6`, and `libXtst.so.6`; approved setup installs those direct runtime dependencies and does not interact with the user's desktop display. Hopper's free demo supports analysis with vendor-defined limits, and a paid license is optional. The curl installer places the `rea` command in `~/.local/bin` on Linux; add that directory to future shell `PATH` values if it is not already present.
|
|
182
182
|
|
|
183
183
|
REA defaults `HOPPER_LAUNCHER_PATH` to `/Applications/Hopper Disassembler.app/Contents/MacOS/hopper` on macOS and `/opt/hopper/bin/Hopper` on Linux. Explicit configuration always takes precedence.
|
|
184
184
|
|
|
@@ -191,24 +191,40 @@ rea uninstall --purge-data # also removes only ~/.rea/cache and ~/.rea/state
|
|
|
191
191
|
|
|
192
192
|
Uninstall preserves Hopper, Node.js, evidence, captures, external evidence roots, unrelated skills, and other MCP servers. It refuses malformed client configuration and never follows purge-data symlinks.
|
|
193
193
|
|
|
194
|
-
### CLI or
|
|
194
|
+
### CLI or agent?
|
|
195
195
|
|
|
196
|
-
| If you want to…
|
|
197
|
-
|
|
|
198
|
-
| Ask an agent to investigate an app and build a feature
|
|
199
|
-
| Inspect or decompile one part of an app from the Terminal
|
|
200
|
-
| Validate, canonicalize, or compare Evidence v2 bundles
|
|
201
|
-
|
|
|
196
|
+
| If you want to… | Use |
|
|
197
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
198
|
+
| Ask an agent to investigate an app and build a feature | Install the skill, then talk to your agent |
|
|
199
|
+
| Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
|
|
200
|
+
| Validate, canonicalize, or compare Evidence v2 bundles | `rea evidence-import`, `rea evidence-export`, or `rea compare` |
|
|
201
|
+
| Run or resume a persistent two-version artifact analysis | `rea investigate-versions` |
|
|
202
|
+
| Reuse immutable analysis results without relaunching a provider | Pass `--snapshot /approved/path/analysis.json` to a deep-analysis command |
|
|
203
|
+
| Import source as historical reference | `rea import-reference-source` |
|
|
204
|
+
| Capture or compare controlled process behavior | `rea capture-process` or `rea compare-process-captures` |
|
|
202
205
|
|
|
203
206
|
Filesystem evidence commands and MCP file tools are disabled until the operator approves absolute roots:
|
|
204
207
|
|
|
205
208
|
```bash
|
|
206
209
|
export REA_EVIDENCE_ROOTS_JSON='["/absolute/path/to/evidence"]'
|
|
210
|
+
export REA_INVESTIGATION_INPUT_ROOTS_JSON='["/absolute/path/to/releases"]'
|
|
207
211
|
rea evidence-import /absolute/path/to/evidence/bundle.json
|
|
208
212
|
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
|
209
213
|
rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
|
|
214
|
+
rea investigate-versions /absolute/path/to/releases/v1 /absolute/path/to/releases/v2 /absolute/path/to/evidence/releases.json --yes --workspace-name releases
|
|
210
215
|
```
|
|
211
216
|
|
|
217
|
+
`investigate-versions` inventories both versions, checkpoints their observed
|
|
218
|
+
Evidence, derives an artifact comparison, and records a changed-behavior report.
|
|
219
|
+
Both input paths must resolve beneath `REA_INVESTIGATION_INPUT_ROOTS_JSON`;
|
|
220
|
+
workspace files remain independently restricted by `REA_EVIDENCE_ROOTS_JSON`.
|
|
221
|
+
The workspace uses deterministic content identities and monotonic CAS-linked
|
|
222
|
+
revisions, so the same request resumes an interrupted run or reuses a completed
|
|
223
|
+
run without replacing earlier investigations. It currently compares static
|
|
224
|
+
artifact structure only; it does not execute either version, and its report
|
|
225
|
+
keeps every difference labeled as a behavior candidate. See
|
|
226
|
+
[Persistent investigation workspaces](docs/investigation-workspaces.md).
|
|
227
|
+
|
|
212
228
|
Historical source import requires a separate allowlist and never treats source as current behavioral authority:
|
|
213
229
|
|
|
214
230
|
```bash
|
|
@@ -218,6 +234,30 @@ rea import-reference-source /absolute/path/to/source
|
|
|
218
234
|
|
|
219
235
|
Exports never replace an existing file unless `--overwrite` is explicit. Imports are size/depth bounded, validate every Evidence v2 ID and manifest, and never execute bundle content.
|
|
220
236
|
|
|
237
|
+
Provider-neutral analysis snapshots persist successful, immutable REA calls and
|
|
238
|
+
their Evidence v2 records. They are exact caches rather than Hopper databases:
|
|
239
|
+
REA reuses an entry only when the binary digest, format, architecture, operation
|
|
240
|
+
parameters, loader arguments, and provider identity match. Custom Hopper loader
|
|
241
|
+
overrides disable snapshots because their provider configuration cannot be
|
|
242
|
+
replayed safely. Cursor-dependent and mutating calls are never cached. Snapshot
|
|
243
|
+
files can contain proprietary analysis results and local
|
|
244
|
+
paths, so REA keeps them local, writes them with owner-only permissions, and
|
|
245
|
+
requires a separate approved root:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
export REA_ANALYSIS_SNAPSHOT_ROOTS_JSON='["/absolute/path/to/analysis"]'
|
|
249
|
+
rea analyze /absolute/path/to/app --snapshot /absolute/path/to/analysis/app.json
|
|
250
|
+
# The same exact query can now be answered from the snapshot.
|
|
251
|
+
rea analyze /absolute/path/to/app --snapshot /absolute/path/to/analysis/app.json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Exact CLI evidence replays happen before any provider starts. In MCP sessions,
|
|
255
|
+
pass `snapshot_path` to `open_binary` to import a snapshot atomically while
|
|
256
|
+
opening its matching target; MCP providers may still start before a cached call
|
|
257
|
+
is replayed. Pass `snapshot_path` and, when required, `overwrite: true` to
|
|
258
|
+
`close_binary` to save atomically before Hopper resources are released. If the
|
|
259
|
+
save fails, REA deliberately leaves the session open.
|
|
260
|
+
|
|
221
261
|
## One prompt, a full investigation
|
|
222
262
|
|
|
223
263
|
```text
|
|
@@ -245,7 +285,7 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
|
|
|
245
285
|
- Reconstruct an app's authentication, storage, update, or networking flow.
|
|
246
286
|
- Recover enough structure to document an undocumented format or interface.
|
|
247
287
|
- Trace a suspicious behavior from a string or symbol to the code that implements it.
|
|
248
|
-
-
|
|
288
|
+
- Run, checkpoint, resume, and reuse a content-addressed artifact investigation across two versions.
|
|
249
289
|
- Turn recovered behavior into product features, tests, migration notes, ports, or interoperable replacements.
|
|
250
290
|
- Analyze Swift and Objective-C metadata without manually untangling every mangled symbol.
|
|
251
291
|
- Leave names, comments, and bookmarks in Hopper so human and agent analysis reinforce each other.
|
|
@@ -267,12 +307,13 @@ The public interface describes what the agent is trying to learn. Providers deci
|
|
|
267
307
|
REA is already useful for native application investigation on macOS:
|
|
268
308
|
|
|
269
309
|
- Open Mach-O, ELF, PE, `.app`, ZIP, APK, IPA, ASAR, plist, JavaScript, source-map, and Hopper database targets.
|
|
270
|
-
- Traverse content-addressed artifact graphs without extraction;
|
|
310
|
+
- Traverse content-addressed artifact graphs without extraction; on macOS, read-only DMG traversal additionally requires `native_mount_approved: true` and `REA_ARTIFACT_NATIVE_MOUNT_ENABLED=true`. Materialize only approved occurrences into absent output roots.
|
|
271
311
|
- Build bounded function dossiers with pseudocode, assembly, CFG edges, comments, calls, references, strings, and names.
|
|
272
312
|
- Search and trace features across symbols, strings, metadata, references, and call paths.
|
|
273
313
|
- Record every successful result as deterministic Evidence v2 with artifact and provider identity, confidence, authority, limitations, and locations.
|
|
274
314
|
- Export and import evidence bundles across sessions.
|
|
275
|
-
-
|
|
315
|
+
- Persist automatic cross-version artifact runs as canonical, lock-protected workspaces with tamper-evident revision commitments.
|
|
316
|
+
- Capture approved PTY scenarios as Process Capture v4 Evidence, including committed run manifests, raw and rendered terminal frames, scripted interactions, descendant settlement, named filesystem checkpoints, deterministic command shims, and loopback HTTP/WebSocket exchanges.
|
|
276
317
|
- Compare complete artifact inventories by stable path, content, metadata, and relations; incomplete evidence never implies equivalence.
|
|
277
318
|
- Compare explicit function dossiers across text, calls, references, strings, and address-normalized CFG topology with per-facet unknowns.
|
|
278
319
|
- Compare canonical Evidence bundles by exact membership, explicit observation pairs, and residual-unknown histories without turning omissions into behavioral absence.
|
|
@@ -282,6 +323,7 @@ REA is already useful for native application investigation on macOS:
|
|
|
282
323
|
- Verify finite behavioral and structural reconstruction specifications with pass, fail, and unknown kept distinct.
|
|
283
324
|
- Track residual unknowns through immutable CAS revisions, evidence-qualified resolution, contradictions, probes, and validated dependency relationships.
|
|
284
325
|
- With explicit `unknown_registry_approved: true`, record bounded trace/capture residuals, typed provider unavailability, and capture disagreements automatically.
|
|
326
|
+
- Start six [guided MCP workflows](docs/mcp-prompts.md) with live, session-aware completion for documents, procedures, providers, evidence, captures, artifact IDs, and active unknowns.
|
|
285
327
|
|
|
286
328
|
Hopper is the first provider, not the boundary of the project. Some current workflows still require Hopper and macOS; every evidence record identifies the provider and limitations behind its result.
|
|
287
329
|
|
|
@@ -296,15 +338,15 @@ REA is growing into a toolkit for understanding software across static artifacts
|
|
|
296
338
|
5. **Runtime observation** — approval-gated LLDB, Frida, system logs, process and filesystem observers, and native API tracing.
|
|
297
339
|
6. **More static-analysis providers** — native platform utilities first, followed by Ghidra, IDA/Hex-Rays, Binary Ninja, Rizin, LIEF, and other engines behind provider-neutral capabilities.
|
|
298
340
|
7. **More targets and platforms** — Windows-native providers and ConPTY verification, Linux parity, websites and APIs, mobile artifacts, firmware, document formats, and other software-defined systems.
|
|
299
|
-
8. **Differential reconstruction** —
|
|
341
|
+
8. **Differential reconstruction expansion** — add automatic function matching, protocol/UI comparison, controlled replay, residual-unknown planning, and reconstruction verification to persistent version runs.
|
|
300
342
|
|
|
301
343
|
Roadmap items describe direction, not shipped support. New providers must produce the same evidence and safety metadata as existing capabilities before they become part of the public workflow. Once REA has multiple optional toolchains, setup can become capability-selective; the consent rules for that future work are recorded in the [installation roadmap](docs/roadmap.md).
|
|
302
344
|
|
|
303
345
|
See the [static-analysis provider evaluation](docs/provider-evaluation.md) for the current research matrix and admission gate.
|
|
304
346
|
|
|
305
|
-
## Using REA with other
|
|
347
|
+
## Using REA with other agents
|
|
306
348
|
|
|
307
|
-
Setup currently configures Claude Desktop and Cursor automatically. Any
|
|
349
|
+
Setup currently configures Claude Desktop and Cursor automatically. Any agent that supports local MCP servers can use REA with the configuration below.
|
|
308
350
|
|
|
309
351
|
### Manual MCP configuration
|
|
310
352
|
|
|
@@ -319,11 +361,16 @@ Setup currently configures Claude Desktop and Cursor automatically. Any coding a
|
|
|
319
361
|
}
|
|
320
362
|
```
|
|
321
363
|
|
|
364
|
+
MCP clients that support prompts can also discover six ordered investigation
|
|
365
|
+
workflows through `prompts/list`. Their optional identifier arguments use the
|
|
366
|
+
current session for bounded `completion/complete` suggestions; see
|
|
367
|
+
[Guided MCP prompts and completion](docs/mcp-prompts.md).
|
|
368
|
+
|
|
322
369
|
## How it works
|
|
323
370
|
|
|
324
371
|
```mermaid
|
|
325
372
|
flowchart LR
|
|
326
|
-
Agent["
|
|
373
|
+
Agent["Agent"] --> REA["REA<br/>CLI + MCP"]
|
|
327
374
|
Terminal --> REA
|
|
328
375
|
REA --> Workspace["Investigation workspace<br/>evidence + artifacts + captures"]
|
|
329
376
|
Workspace --> Router["Capability router"]
|
|
@@ -338,7 +385,7 @@ flowchart LR
|
|
|
338
385
|
Artifact --> Target
|
|
339
386
|
```
|
|
340
387
|
|
|
341
|
-
The CLI and MCP server use the same application workflows and evidence contracts. A provider declares which capabilities it supports and the side effects those capabilities may have. Terminal commands are short-lived; an MCP session can retain an active target and evidence ledger across an investigation.
|
|
388
|
+
The CLI and MCP server use the same application workflows and evidence contracts. A provider declares which capabilities it supports and the side effects those capabilities may have. Terminal commands are short-lived; an MCP session can retain an active target and evidence ledger across an investigation. Approved persistent workspaces keep canonical Evidence and resumable run checkpoints across both process and session lifetimes.
|
|
342
389
|
|
|
343
390
|
## CLI
|
|
344
391
|
|
|
@@ -353,6 +400,7 @@ npx -y rea-agents function /Applications/Notes.app 0x1000
|
|
|
353
400
|
npx -y rea-agents xrefs /Applications/Notes.app 0x1000
|
|
354
401
|
npx -y rea-agents trace /Applications/Notes.app "offline"
|
|
355
402
|
npx -y rea-agents compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
|
|
403
|
+
npx -y rea-agents investigate-versions /path/to/v1 /path/to/v2 /absolute/path/to/evidence/releases.json --yes
|
|
356
404
|
npx -y rea-agents capabilities
|
|
357
405
|
npx -y rea-agents providers
|
|
358
406
|
```
|
|
@@ -389,10 +437,28 @@ environment allowlist in `REA_PROCESS_ALLOWED_ENV_JSON`. Because the current PTY
|
|
|
389
437
|
adapter uses host networking, it also requires
|
|
390
438
|
`REA_PROCESS_ALLOW_EXTERNAL_NETWORK=true`.
|
|
391
439
|
|
|
440
|
+
Capture a scenario or compare two saved Process Capture v4 Evidence records:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
rea capture-process ./scenario.json > authority.json
|
|
444
|
+
rea capture-process ./reconstruction.json > reconstruction.json
|
|
445
|
+
rea compare-process-captures authority.json reconstruction.json
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
The comparison reports each observed dimension separately and identifies the
|
|
449
|
+
first terminal, interaction, exit, filesystem, protocol, process, or shim
|
|
450
|
+
divergence. See [Process Capture v4](docs/process-capture.md) for scenario
|
|
451
|
+
fields, command-shim replay, checkpoint triggers, limits, and safety behavior.
|
|
452
|
+
|
|
392
453
|
If the native PTY backend is unavailable, install Xcode command-line tools and
|
|
393
454
|
run `npm run rebuild:native`. Linux source builds require Python, `make`, and a
|
|
394
455
|
C++ toolchain. Compatible packaged binaries do not require this rebuild.
|
|
395
456
|
|
|
457
|
+
ASAR inventory verifies Electron integrity metadata for both archive entries
|
|
458
|
+
and `.asar.unpacked` companion files. Integrity failures identify the logical
|
|
459
|
+
path, declared and calculated SHA-256 values, and whether the entry was
|
|
460
|
+
unpacked; REA does not silently accept the mismatched artifact.
|
|
461
|
+
|
|
396
462
|
## Security model
|
|
397
463
|
|
|
398
464
|
REA does not provide a hosted analysis service. Hopper communication uses an authenticated private local socket. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. REA is not a security sandbox: providers and launched targets run with the current user's permissions, and each capability reports its side effects and limitations. Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
|
|
@@ -423,7 +489,7 @@ No. Setup can install Hopper for you, but Hopper remains separate software with
|
|
|
423
489
|
<details>
|
|
424
490
|
<summary><strong>Does REA upload the app?</strong></summary>
|
|
425
491
|
|
|
426
|
-
REA has no hosted analysis service. Current providers analyze artifacts and capture behavior locally. Your
|
|
492
|
+
REA has no hosted analysis service. Current providers analyze artifacts and capture behavior locally. Your agent or model provider may have its own data policy, so review that separately.
|
|
427
493
|
|
|
428
494
|
</details>
|
|
429
495
|
|
|
@@ -437,7 +503,7 @@ No decompiler can guarantee the original source. REA gives an agent pseudocode,
|
|
|
437
503
|
<details>
|
|
438
504
|
<summary><strong>Which agents can use REA?</strong></summary>
|
|
439
505
|
|
|
440
|
-
Any
|
|
506
|
+
Any agent that can run a local MCP server can use the manual configuration. Setup currently detects and configures Claude Desktop and Cursor automatically.
|
|
441
507
|
|
|
442
508
|
</details>
|
|
443
509
|
|
package/bridge/hopper_bridge.py
CHANGED
|
@@ -20,6 +20,22 @@ MAX_SEARCH_PATTERN_LENGTH = 256
|
|
|
20
20
|
MAX_SEARCH_VALUE_LENGTH = 4096
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _session_document():
|
|
24
|
+
"""Find only the document opened for this authenticated REA session."""
|
|
25
|
+
target = os.path.realpath(REA_TARGET_PATH)
|
|
26
|
+
for document in Document.getAllDocuments():
|
|
27
|
+
paths = (document.getExecutableFilePath(), document.getDatabaseFilePath())
|
|
28
|
+
for path in paths:
|
|
29
|
+
if path and os.path.realpath(path) == target:
|
|
30
|
+
return document
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
MAX_REGEX_BACKTRACKING_PATHS = 10000
|
|
35
|
+
MAX_REGEX_CANDIDATE_LENGTH = 4096
|
|
36
|
+
MAX_REGEX_SEARCH_WORK_UNITS = 1000000
|
|
37
|
+
|
|
38
|
+
|
|
23
39
|
def _hex(value):
|
|
24
40
|
return "0x%x" % value
|
|
25
41
|
|
|
@@ -216,33 +232,150 @@ def _search_inventory(document, kind):
|
|
|
216
232
|
return inventory
|
|
217
233
|
|
|
218
234
|
|
|
235
|
+
def _checked_regex_paths(left, right, operation):
|
|
236
|
+
"""Apply one path-count operation without crossing the static work budget."""
|
|
237
|
+
if operation == "add":
|
|
238
|
+
exceeded = left > MAX_REGEX_BACKTRACKING_PATHS - right
|
|
239
|
+
result = left + right
|
|
240
|
+
else:
|
|
241
|
+
exceeded = right != 0 and left > MAX_REGEX_BACKTRACKING_PATHS // right
|
|
242
|
+
result = left * right
|
|
243
|
+
if exceeded or result > MAX_REGEX_BACKTRACKING_PATHS:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
"Regex exceeds the %d-path backtracking budget"
|
|
246
|
+
% MAX_REGEX_BACKTRACKING_PATHS
|
|
247
|
+
)
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _repeat_regex_paths(child_paths, minimum, maximum):
|
|
252
|
+
"""Count every bounded repetition path, including alternative child paths."""
|
|
253
|
+
paths = 0
|
|
254
|
+
repeated_paths = 1
|
|
255
|
+
for count in range(maximum + 1):
|
|
256
|
+
if count >= minimum:
|
|
257
|
+
paths = _checked_regex_paths(paths, repeated_paths, "add")
|
|
258
|
+
if count < maximum:
|
|
259
|
+
repeated_paths = _checked_regex_paths(
|
|
260
|
+
repeated_paths, child_paths, "multiply"
|
|
261
|
+
)
|
|
262
|
+
return paths
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _validate_regex_class(items):
|
|
266
|
+
"""Accept only constant-time character-class operations."""
|
|
267
|
+
allowed = {
|
|
268
|
+
sre_parse.CATEGORY,
|
|
269
|
+
sre_parse.LITERAL,
|
|
270
|
+
sre_parse.NEGATE,
|
|
271
|
+
sre_parse.RANGE,
|
|
272
|
+
}
|
|
273
|
+
if any(operation not in allowed for operation, _ in items):
|
|
274
|
+
raise ValueError("Regex operation is not supported by the bounded matcher")
|
|
275
|
+
|
|
276
|
+
|
|
219
277
|
def _validate_regex_node(node, inside_repeat=False):
|
|
220
|
-
"""
|
|
278
|
+
"""Return capped path and step bounds for Python regex evaluation."""
|
|
279
|
+
leaf_operations = {
|
|
280
|
+
sre_parse.ANY,
|
|
281
|
+
sre_parse.AT,
|
|
282
|
+
sre_parse.CATEGORY,
|
|
283
|
+
sre_parse.LITERAL,
|
|
284
|
+
sre_parse.NOT_LITERAL,
|
|
285
|
+
}
|
|
221
286
|
forbidden = {
|
|
222
287
|
sre_parse.ASSERT,
|
|
223
288
|
sre_parse.ASSERT_NOT,
|
|
224
289
|
sre_parse.GROUPREF,
|
|
225
290
|
sre_parse.GROUPREF_EXISTS,
|
|
226
291
|
}
|
|
292
|
+
for name in ("GROUPREF_IGNORE", "GROUPREF_LOC_IGNORE", "GROUPREF_UNI_IGNORE"):
|
|
293
|
+
operation = getattr(sre_parse, name, None)
|
|
294
|
+
if operation is not None:
|
|
295
|
+
forbidden.add(operation)
|
|
227
296
|
repeat_tokens = {sre_parse.MAX_REPEAT, sre_parse.MIN_REPEAT}
|
|
228
297
|
possessive = getattr(sre_parse, "POSSESSIVE_REPEAT", None)
|
|
229
298
|
if possessive is not None:
|
|
230
299
|
repeat_tokens.add(possessive)
|
|
300
|
+
atomic = getattr(sre_parse, "ATOMIC_GROUP", None)
|
|
301
|
+
|
|
302
|
+
paths = 1
|
|
303
|
+
steps = 0
|
|
231
304
|
for operation, argument in node:
|
|
232
305
|
if operation in forbidden:
|
|
233
306
|
raise ValueError("Regex lookarounds and backreferences are not supported")
|
|
234
|
-
if operation in
|
|
307
|
+
if operation in leaf_operations:
|
|
308
|
+
operation_paths = 1
|
|
309
|
+
operation_steps = 1
|
|
310
|
+
elif operation == sre_parse.IN:
|
|
311
|
+
_validate_regex_class(argument)
|
|
312
|
+
operation_paths = 1
|
|
313
|
+
operation_steps = 1
|
|
314
|
+
elif operation in repeat_tokens:
|
|
235
315
|
if inside_repeat:
|
|
236
316
|
raise ValueError("Nested regex repetitions are not supported")
|
|
237
317
|
minimum, maximum, child = argument
|
|
238
318
|
if maximum == sre_parse.MAXREPEAT or maximum > 1000:
|
|
239
|
-
raise ValueError(
|
|
240
|
-
|
|
319
|
+
raise ValueError(
|
|
320
|
+
"Unbounded or excessive regex repetitions are not supported"
|
|
321
|
+
)
|
|
322
|
+
child_paths, child_steps = _validate_regex_node(child, True)
|
|
323
|
+
operation_paths = _repeat_regex_paths(
|
|
324
|
+
child_paths, minimum, maximum
|
|
325
|
+
)
|
|
326
|
+
operation_steps = maximum * child_steps
|
|
241
327
|
elif operation == sre_parse.SUBPATTERN:
|
|
242
|
-
_validate_regex_node(
|
|
328
|
+
operation_paths, operation_steps = _validate_regex_node(
|
|
329
|
+
argument[-1], inside_repeat
|
|
330
|
+
)
|
|
243
331
|
elif operation == sre_parse.BRANCH:
|
|
332
|
+
operation_paths = 0
|
|
333
|
+
operation_steps = 0
|
|
244
334
|
for branch in argument[1]:
|
|
245
|
-
_validate_regex_node(
|
|
335
|
+
branch_paths, branch_steps = _validate_regex_node(
|
|
336
|
+
branch, inside_repeat
|
|
337
|
+
)
|
|
338
|
+
operation_paths = _checked_regex_paths(
|
|
339
|
+
operation_paths,
|
|
340
|
+
branch_paths,
|
|
341
|
+
"add",
|
|
342
|
+
)
|
|
343
|
+
operation_steps = max(operation_steps, branch_steps)
|
|
344
|
+
elif atomic is not None and operation == atomic:
|
|
345
|
+
operation_paths, operation_steps = _validate_regex_node(
|
|
346
|
+
argument, inside_repeat
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
raise ValueError("Regex operation is not supported by the bounded matcher")
|
|
350
|
+
paths = _checked_regex_paths(paths, operation_paths, "multiply")
|
|
351
|
+
steps += operation_steps
|
|
352
|
+
return paths, steps
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _bounded_regex_matcher(expression, backtracking_paths, steps_per_path):
|
|
356
|
+
"""Create a matcher with per-candidate and cumulative work bounds."""
|
|
357
|
+
remaining_work = MAX_REGEX_SEARCH_WORK_UNITS
|
|
358
|
+
work_per_character = backtracking_paths * max(steps_per_path, 1)
|
|
359
|
+
|
|
360
|
+
def matches(value):
|
|
361
|
+
nonlocal remaining_work
|
|
362
|
+
if not isinstance(value, str):
|
|
363
|
+
raise ValueError("Regex candidates must be strings")
|
|
364
|
+
if len(value) > MAX_REGEX_CANDIDATE_LENGTH:
|
|
365
|
+
raise ValueError(
|
|
366
|
+
"Regex candidate exceeds the %d-character safety limit"
|
|
367
|
+
% MAX_REGEX_CANDIDATE_LENGTH
|
|
368
|
+
)
|
|
369
|
+
required_work = work_per_character * max(len(value), 1)
|
|
370
|
+
if required_work > remaining_work:
|
|
371
|
+
raise ValueError(
|
|
372
|
+
"Regex search exceeds the %d-unit work budget"
|
|
373
|
+
% MAX_REGEX_SEARCH_WORK_UNITS
|
|
374
|
+
)
|
|
375
|
+
remaining_work -= required_work
|
|
376
|
+
return expression.search(value) is not None
|
|
377
|
+
|
|
378
|
+
return matches
|
|
246
379
|
|
|
247
380
|
|
|
248
381
|
def _search_page(document, kind, params):
|
|
@@ -268,11 +401,13 @@ def _search_page(document, kind, params):
|
|
|
268
401
|
else:
|
|
269
402
|
try:
|
|
270
403
|
parsed = sre_parse.parse(pattern)
|
|
271
|
-
_validate_regex_node(parsed)
|
|
404
|
+
backtracking_paths, steps_per_path = _validate_regex_node(parsed)
|
|
272
405
|
expression = re.compile(pattern, 0 if case_sensitive else re.IGNORECASE)
|
|
273
|
-
except re.error as error:
|
|
406
|
+
except (re.error, OverflowError) as error:
|
|
274
407
|
raise ValueError("Invalid regex pattern") from error
|
|
275
|
-
matches =
|
|
408
|
+
matches = _bounded_regex_matcher(
|
|
409
|
+
expression, backtracking_paths, steps_per_path
|
|
410
|
+
)
|
|
276
411
|
|
|
277
412
|
selected = []
|
|
278
413
|
total = 0
|
|
@@ -494,7 +629,21 @@ def _dispatch(method, params):
|
|
|
494
629
|
if method == "health":
|
|
495
630
|
return {"name": "REA Hopper bridge", "version": "1.0.0", "run_id": REA_RUN_ID}
|
|
496
631
|
if method == "shutdown":
|
|
497
|
-
|
|
632
|
+
document = _session_document()
|
|
633
|
+
if document is None:
|
|
634
|
+
return {"shutdown": True, "analysis_stopped": True, "document_closed": True}
|
|
635
|
+
if document.backgroundProcessActive():
|
|
636
|
+
document.requestBackgroundProcessStop()
|
|
637
|
+
if document.backgroundProcessActive():
|
|
638
|
+
document.waitForBackgroundProcessToEnd()
|
|
639
|
+
analysis_stopped = not document.backgroundProcessActive()
|
|
640
|
+
document.closeDocument()
|
|
641
|
+
document_closed = _session_document() is None
|
|
642
|
+
return {
|
|
643
|
+
"shutdown": True,
|
|
644
|
+
"analysis_stopped": analysis_stopped,
|
|
645
|
+
"document_closed": document_closed,
|
|
646
|
+
}
|
|
498
647
|
if method == "list_documents":
|
|
499
648
|
return [document.getDocumentName() for document in Document.getAllDocuments()]
|
|
500
649
|
if method == "current_document":
|
|
@@ -604,9 +753,9 @@ def _dispatch(method, params):
|
|
|
604
753
|
if method == "procedure_pseudo_code":
|
|
605
754
|
return procedure.decompile()
|
|
606
755
|
if method == "procedure_callers":
|
|
607
|
-
return
|
|
756
|
+
return sorted((_hex(item.getEntryPoint()) for item in procedure.getAllCallerProcedures()), key=lambda value: int(value, 16))
|
|
608
757
|
if method == "procedure_callees":
|
|
609
|
-
return
|
|
758
|
+
return sorted((_hex(item.getEntryPoint()) for item in procedure.getAllCalleeProcedures()), key=lambda value: int(value, 16))
|
|
610
759
|
if method == "procedure_info":
|
|
611
760
|
blocks = list(procedure.basicBlockIterator())
|
|
612
761
|
length = sum(max(0, block.getEndingAddress() - block.getStartingAddress()) for block in blocks)
|