rea-agents 2.0.0 → 2.2.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.
Files changed (273) hide show
  1. package/README.md +103 -38
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/AndroidApplicationService.js +17 -0
  4. package/dist/application/AppleApplicationService.js +17 -0
  5. package/dist/application/ArtifactExtraction.js +1 -1
  6. package/dist/application/ArtifactInventory/classify.js +42 -0
  7. package/dist/application/ArtifactInventory/hash.js +43 -0
  8. package/dist/application/ArtifactInventory/reader.js +41 -0
  9. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  10. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  11. package/dist/application/ArtifactInventory/types.js +10 -0
  12. package/dist/application/ArtifactInventory.js +9 -351
  13. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  14. package/dist/application/BinarySession.js +47 -270
  15. package/dist/application/BinarySessionExecution.js +28 -0
  16. package/dist/application/BinarySessionOpen.js +39 -0
  17. package/dist/application/BinarySessionRecords.js +149 -0
  18. package/dist/application/BinarySessionStatus.js +75 -0
  19. package/dist/application/CapabilityInventory.js +24 -2
  20. package/dist/application/ClientRegistrationStatus.js +11 -4
  21. package/dist/application/CrossVersionInventory.js +8 -2
  22. package/dist/application/CrossVersionInvestigation.js +1 -23
  23. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  24. package/dist/application/DirectAnalysis.js +47 -44
  25. package/dist/application/Doctor.js +156 -238
  26. package/dist/application/DoctorDiagnostics.js +180 -0
  27. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  28. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  29. package/dist/application/EnhancedToolPagination.js +22 -0
  30. package/dist/application/EnhancedToolTypes.js +4 -0
  31. package/dist/application/EnhancedTools.js +20 -37
  32. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  33. package/dist/application/InventoryProjectionEvidence.js +41 -0
  34. package/dist/application/InvestigationProviders.js +12 -0
  35. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  36. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  37. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  38. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  39. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  40. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  41. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  42. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  43. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  44. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  45. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  46. package/dist/application/JavaScriptJsonModules.js +39 -0
  47. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  48. package/dist/application/JavaScriptReplayPermission.js +16 -0
  49. package/dist/application/JavaScriptReplayService.js +182 -156
  50. package/dist/application/LinuxHopper.js +10 -0
  51. package/dist/application/MacHopper.js +22 -11
  52. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  53. package/dist/application/PermissionAuthority.js +106 -63
  54. package/dist/application/ProcessCaptureCapability.js +1 -1
  55. package/dist/application/ProcessCapturePermission.js +13 -0
  56. package/dist/application/ProcessCli.js +2 -12
  57. package/dist/application/ProcessHarness.js +1 -1
  58. package/dist/application/ProcessPairedExperiment.js +72 -0
  59. package/dist/application/ProjectPermissionStore.js +48 -9
  60. package/dist/application/RealHopperAssertions.js +76 -0
  61. package/dist/application/ReconstructionCoverageService.js +69 -0
  62. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  63. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  64. package/dist/application/Setup.js +148 -260
  65. package/dist/application/SetupClientConfiguration.js +224 -0
  66. package/dist/application/SetupClients.js +24 -2
  67. package/dist/application/SetupPlan.js +124 -12
  68. package/dist/application/SetupSkill.js +54 -36
  69. package/dist/application/SupportedClients.js +1 -0
  70. package/dist/application/Uninstall.js +12 -3
  71. package/dist/application/Upgrade.js +16 -1
  72. package/dist/application/WindowsCapabilities.js +86 -0
  73. package/dist/artifacts/AsarArtifactReader.js +11 -0
  74. package/dist/browser/BrowserProcessStartup.js +88 -0
  75. package/dist/browser/CdpCaptureDocuments.js +12 -8
  76. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  77. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  78. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  79. package/dist/browser/CdpCaptureEventState.js +1 -0
  80. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  81. package/dist/browser/CdpCaptureEvents.js +29 -496
  82. package/dist/browser/CdpObservationSession.js +51 -8
  83. package/dist/browser/CdpPageCapture.js +12 -133
  84. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  85. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  86. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  87. package/dist/browser/PngVisualDiff.js +9 -2
  88. package/dist/catalogIdentity.js +4 -0
  89. package/dist/cli/artifactCommands.js +82 -0
  90. package/dist/cli/coreAnalysisCommands.js +232 -0
  91. package/dist/cli/managedCommands.js +397 -0
  92. package/dist/cli/managedProjectGraphCommand.js +24 -0
  93. package/dist/cli/options.js +9 -0
  94. package/dist/cli/setupCommands.js +113 -0
  95. package/dist/cli/types.js +1 -0
  96. package/dist/cli/utilityCommands.js +122 -0
  97. package/dist/cli.js +10 -768
  98. package/dist/cliApplicationCommands.js +118 -4
  99. package/dist/cliBrowserAdvancedCommands.js +73 -16
  100. package/dist/cliBrowserCommands.js +39 -115
  101. package/dist/cliCommandNames.js +4 -0
  102. package/dist/cliElectronCommands.js +33 -62
  103. package/dist/cliEvidenceCommands.js +13 -2
  104. package/dist/cliInvestigationCommands.js +53 -10
  105. package/dist/cliObservationOptions.js +137 -0
  106. package/dist/cliPolicyCommands.js +80 -46
  107. package/dist/cliSetup.js +306 -0
  108. package/dist/config/environment.js +107 -0
  109. package/dist/config/parseConfig.js +228 -0
  110. package/dist/config/parsers.js +68 -0
  111. package/dist/config/permissions.js +30 -0
  112. package/dist/config/types.js +1 -0
  113. package/dist/config.js +1 -375
  114. package/dist/contracts/applicationToolContracts.js +134 -0
  115. package/dist/contracts/electronToolContracts.js +51 -3
  116. package/dist/contracts/managedToolContracts.js +8 -3
  117. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  118. package/dist/contracts/providerSelection.js +2 -0
  119. package/dist/contracts/sessionStatusContract.js +26 -0
  120. package/dist/contracts/toolContractTypes.js +12 -1
  121. package/dist/contracts/toolContracts.js +5 -12
  122. package/dist/contracts/toolEffects.js +14 -0
  123. package/dist/contracts/toolOutputSchemaGroups.js +274 -0
  124. package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
  125. package/dist/contracts/toolOutputSchemas.js +2 -550
  126. package/dist/domain/analysisErrorPresentation.js +234 -0
  127. package/dist/domain/analysisErrorProjection.js +284 -0
  128. package/dist/domain/androidApplication.js +206 -0
  129. package/dist/domain/appleApplication.js +221 -0
  130. package/dist/domain/artifactComparison.js +34 -15
  131. package/dist/domain/binaryTarget.js +87 -37
  132. package/dist/domain/boundedCartesianProjection.js +15 -0
  133. package/dist/domain/browserObservation.js +1 -285
  134. package/dist/domain/browserObservationSchemas.js +286 -0
  135. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  136. package/dist/domain/errors.js +4 -486
  137. package/dist/domain/functionComparison.js +4 -165
  138. package/dist/domain/functionComparisonDimensions.js +164 -0
  139. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  140. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  141. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  142. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  143. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  145. package/dist/domain/javascriptSemanticIr.js +30 -0
  146. package/dist/domain/javascriptSemanticProjection.js +366 -0
  147. package/dist/domain/javascriptSemanticState.js +40 -0
  148. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  149. package/dist/domain/javascriptSemanticValues.js +352 -0
  150. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  151. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  152. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  153. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  154. package/dist/domain/knownPageCoverage.js +21 -0
  155. package/dist/domain/managedApplicationGraph.js +14 -364
  156. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  157. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  158. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  159. package/dist/domain/managedArtifact.js +16 -8
  160. package/dist/domain/managedMemberComparison.js +37 -391
  161. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  162. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  163. package/dist/domain/managedNativeVerification.js +27 -389
  164. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  165. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  166. package/dist/domain/managedReconstruction.js +82 -69
  167. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  168. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  169. package/dist/domain/permissionPolicy.js +2 -0
  170. package/dist/domain/processComparison.js +19 -0
  171. package/dist/domain/processPairedExperiment.js +128 -0
  172. package/dist/domain/reconstructionCoverage.js +387 -0
  173. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  174. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  175. package/dist/domain/replayMachine.js +316 -0
  176. package/dist/domain/replayMachineRuntime.js +215 -0
  177. package/dist/domain/runtimeCharacterization.js +85 -0
  178. package/dist/domain/runtimeIdentification.js +210 -0
  179. package/dist/domain/webBundleAnalyzer.js +3 -409
  180. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  181. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  182. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  183. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  184. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  185. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  186. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  187. package/dist/dotnet/ManagedMemberRows.js +120 -0
  188. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  189. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  190. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  191. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  192. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  193. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  194. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  195. package/dist/dotnet/ManagedPeReader.js +59 -28
  196. package/dist/evaluation/CodexAgentEval.js +132 -0
  197. package/dist/generatedPackageMetadata.js +2 -2
  198. package/dist/ghidra/GhidraClient.js +76 -91
  199. package/dist/ghidra/GhidraClientStartup.js +29 -0
  200. package/dist/ghidra/GhidraClientWire.js +34 -0
  201. package/dist/ghidra/GhidraDefaults.js +1 -1
  202. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  203. package/dist/ghidra/GhidraInstallation.js +20 -15
  204. package/dist/ghidra/GhidraLauncher.js +93 -29
  205. package/dist/ghidra/GhidraProvider.js +61 -161
  206. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  207. package/dist/ghidra/GhidraSessionValues.js +2 -0
  208. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  209. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  210. package/dist/ghidra/GhidraTransport.js +71 -0
  211. package/dist/identity.js +4 -2
  212. package/dist/main/messages.js +5 -0
  213. package/dist/main/reload.js +49 -0
  214. package/dist/main/shutdown.js +25 -0
  215. package/dist/main/startup.js +14 -0
  216. package/dist/main/state.js +26 -0
  217. package/dist/main/transport.js +61 -0
  218. package/dist/main/types.js +1 -0
  219. package/dist/main.js +35 -146
  220. package/dist/process/PrivateRuntimeRoot.js +3 -2
  221. package/dist/process/ProcessOwnership.js +35 -0
  222. package/dist/process/ProviderProcess.js +2 -0
  223. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  224. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  225. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  226. package/dist/replay/ReplayOutcome.js +95 -0
  227. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  228. package/dist/server/ProcessCaptureElicitation.js +157 -0
  229. package/dist/server/createServer.js +124 -56
  230. package/dist/server/javascriptApplicationResult.js +85 -0
  231. package/dist/server/registerApplicationTools/characterization.js +57 -0
  232. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  233. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  234. package/dist/server/registerApplicationTools/coverage.js +84 -0
  235. package/dist/server/registerApplicationTools/helpers.js +42 -0
  236. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  237. package/dist/server/registerApplicationTools/types.js +1 -0
  238. package/dist/server/registerApplicationTools.js +10 -112
  239. package/dist/server/registerBrowserTools.js +59 -83
  240. package/dist/server/registerElectronTools.js +48 -49
  241. package/dist/server/registerEnhancedTools.js +34 -42
  242. package/dist/server/registerEvidenceResources.js +56 -0
  243. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  244. package/dist/server/registerInvestigationTools/types.js +1 -0
  245. package/dist/server/registerInvestigationTools.js +83 -116
  246. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  247. package/dist/server/registerManagedTools.js +18 -1
  248. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  249. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  250. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  251. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  252. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  253. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  254. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  255. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  256. package/dist/server/registerManagedWorkflowTools.js +10 -274
  257. package/dist/server/registerOfficialTools.js +73 -59
  258. package/dist/server/registerSessionRecordTools.js +137 -0
  259. package/dist/server/registerSessionStatusTool.js +97 -26
  260. package/dist/server/registerSessionTools.js +60 -164
  261. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  262. package/dist/server/toolRegistrationOptions.js +101 -3
  263. package/dist/server/toolResult.js +15 -4
  264. package/package.json +11 -11
  265. package/skills/reverse-engineer-anything/SKILL.md +76 -0
  266. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  267. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  268. package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
  269. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  270. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
  271. package/scripts/prepare-node-pty.mjs +0 -35
  272. package/scripts/rebuild-native.mjs +0 -22
  273. package/skills/rea-analysis/SKILL.md +0 -222
package/README.md CHANGED
@@ -30,23 +30,24 @@
30
30
 
31
31
  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.
32
32
 
33
- REA gives agents one consistent way to investigate software. Today that includes deep native analysis and function dossiers through Hopper or bring-your-own Ghidra on Linux, execution-free managed PE/CLI triage, reproducible Evidence v2 records, controlled process capture, passive website and Electron observation, bounded JavaScript/source-map reconstruction, and a versioned domain graph for connecting JavaScript application layers without confusing static inference with runtime observation. The longer-term toolkit extends the same agent workflow to APIs, protocols, mobile artifacts, firmware, richer runtime behavior, and differences between versions.
33
+ REA gives agents one consistent way to investigate software. Today that includes deep native analysis and function dossiers through Hopper or bring-your-own Ghidra on Linux, plus an experimental Windows x64 Ghidra P0 for approved native PE applications; execution-free managed PE/CLI triage; reproducible Evidence v2 records; controlled process capture; passive website and Electron observation; bounded JavaScript/source-map reconstruction; and a versioned domain graph for connecting JavaScript application layers without confusing static inference with runtime observation. The longer-term toolkit extends the same agent workflow to APIs, protocols, mobile artifacts, firmware, richer runtime behavior, and differences between versions.
34
34
 
35
35
  Reverse engineering normally makes the operator choose a tool, learn its API, move evidence between programs, and decide what to inspect next. REA gives that work to the agent through commands, skills, structured results, and repeatable investigation workflows.
36
36
 
37
37
  ## Just ask your agent
38
38
 
39
- Install the REA skill:
39
+ Run setup once. Agent integration installs an aligned MCP registration and the
40
+ bundled routing skill together:
40
41
 
41
42
  ```bash
42
- npx skills add morluto/rea
43
+ npx rea-agents setup
43
44
  ```
44
45
 
45
46
  Then ask:
46
47
 
47
48
  ```text
48
- Use REA to understand how search works in the Notes app, show me the
49
- evidence, and build a similar feature for my project.
49
+ Understand how search works in the Notes app, show me the evidence, and build a
50
+ similar feature for my project.
50
51
  ```
51
52
 
52
53
  Notes is only an example. Name any app you want to understand, or ask the agent to start with an overview.
@@ -85,17 +86,41 @@ REA shows how it reached its conclusions. It does not claim to recover original
85
86
 
86
87
  ## Quick start
87
88
 
88
- ### Install the CLI — recommended
89
+ ### Run setup — recommended
89
90
 
90
91
  ```bash
91
- npm install --global rea-agents
92
- rea setup
92
+ npx rea-agents setup
93
93
  ```
94
94
 
95
- 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.
95
+ The npm package-runner prompt, when shown, approves downloading REA for this
96
+ invocation; it does not approve any setup changes. The REA wizard separately
97
+ shows its complete plan and asks before applying it. Setup does not update
98
+ Homebrew, Node.js, or npm. `npx rea-agents setup` opens with the work it
99
+ enables: investigate local apps from an agent, recover evidence through a
100
+ deep-analysis provider, and reuse REA's guided workflow. It summarizes the
101
+ detected agents, then asks which capabilities to set up: agent integration
102
+ (MCP plus the matching guided workflow) and—when needed—the Hopper provider.
103
+ Nothing is preselected. Choosing agent integration opens a second empty
104
+ checklist for the specific detected agents that should receive a registration.
105
+
106
+ REA keeps the journey inline so its history remains in the terminal. Selecting
107
+ a capability does not select every detected target or authorize a change.
108
+ Before anything changes, REA validates existing configuration, prints exact
109
+ paths and external effects, and asks for final approval with **No** as the
110
+ default. The screen keeps the available keys visible while you choose; Ctrl-C
111
+ and declining leave the system unchanged.
96
112
 
97
113
  REA detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, and Devin. It configures the first six when detected; Devin is reported but left unchanged because it has no documented local MCP configuration boundary. Registrations are additive, backup-first, and read back after writing. You can safely rerun setup.
98
114
 
115
+ Use `rea setup --dry-run` to inspect the plan, repeat `--client` to select exact
116
+ agents, and `--accessible` for sequential vertical prompts. Machine output
117
+ remains available through `--json`; prompt UI and progress go to stderr.
118
+
119
+ After a successful setup, REA reports the capabilities now ready to use and a
120
+ concrete next step, such as restarting a configured agent before asking it to
121
+ investigate an application. It does not claim an integration or provider is
122
+ ready unless setup and its final diagnostic check verified it.
123
+
99
124
  An optional curl wrapper installs the same CLI package and starts setup only when a terminal is available:
100
125
 
101
126
  ```bash
@@ -107,19 +132,21 @@ Pass installer options after `bash -s --`, for example `--dry-run`, `--no-setup`
107
132
  ### With an agent — recommended
108
133
 
109
134
  ```bash
110
- npx skills add morluto/rea
135
+ npx rea-agents setup
111
136
  ```
112
137
 
113
- Ask your agent to set up REA. It will check your supported host, 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.
138
+ Choose Agent Integration in the reviewed setup plan. REA installs the pinned MCP
139
+ registration and its matching routing skill as one transaction. After setup,
140
+ restart the configured agent so it loads the aligned integration.
114
141
 
115
142
  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.
116
143
 
117
144
  ### From Terminal — no installation
118
145
 
119
146
  ```bash
120
- npx -y rea-agents setup
121
- npx -y rea-agents doctor
122
- npx -y rea-agents analyze /Applications/Notes.app
147
+ npx rea-agents setup
148
+ npx -y rea-agents@latest doctor
149
+ npx -y rea-agents@latest analyze /Applications/Notes.app
123
150
  ```
124
151
 
125
152
  Review the setup plan before confirming it. Restart a configured agent so it loads REA.
@@ -146,10 +173,16 @@ an unrelated global package.
146
173
 
147
174
  Choose either the no-install commands or the global installation. You do not need both.
148
175
 
176
+ `npm install rea-agents` without `--global` installs `rea` only into the
177
+ current project's `node_modules/.bin`; it does not add `rea` to your shell
178
+ `PATH`. Use the `npx` commands above for one-off runs or `--global` when you
179
+ want a shell-visible `rea` command.
180
+
149
181
  ### Requirements
150
182
 
151
183
  - macOS 12 or newer
152
184
  - Ubuntu 24.04+, Fedora 41+, or 64-bit Arch Linux
185
+ - Windows x64 for the experimental, Ghidra-only native PE P0 boundary
153
186
  - Node.js 22.19+ or 24.11+ (including newer releases)
154
187
  - npm; REA does not require or install a particular npm version
155
188
 
@@ -158,7 +191,7 @@ Deep binary operations use [Hopper](https://www.hopperapp.com/), a separate desk
158
191
  If something is not working, run:
159
192
 
160
193
  ```bash
161
- npx -y rea-agents doctor
194
+ npx -y rea-agents@latest doctor
162
195
  ```
163
196
 
164
197
  `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.
@@ -188,7 +221,7 @@ REA defaults `HOPPER_LAUNCHER_PATH` to `/Applications/Hopper Disassembler.app/Co
188
221
 
189
222
  ### Ghidra read-only analysis provider
190
223
 
191
- The current Ghidra adapter supports the exact official Ghidra 12.1.2 release on Linux x64 with a 64-bit full JDK 21. Download and extract those projects yourself, then configure absolute paths:
224
+ The Ghidra adapter supports the exact official Ghidra 12.1.2 release with a 64-bit full JDK 21 on Linux x64. It also provides an experimental Windows x64 P0 limited to approved native x86-64 PE applications. Download and extract those projects yourself, then configure absolute paths:
192
225
 
193
226
  ```bash
194
227
  export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.2_PUBLIC
@@ -200,7 +233,9 @@ rea providers --json
200
233
 
201
234
  Doctor distinguishes missing configuration, a bad installation root, the wrong Ghidra or Java version, a JRE without `javac`, a missing `support/analyzeHeadless`, and an unsupported platform or architecture. Approved setup only copies the verified non-secret paths into detected MCP registrations; it does not modify the Ghidra installation or install/download Ghidra or Java.
202
235
 
203
- REA loads its packaged Java `HeadlessScript` with `-scriptPath`, imports the target into a mode-0700 temporary project, enables `-readOnly` and `-deleteProject`, caps auto-analysis at 300 seconds with two CPUs and a 2 GiB Java heap, and authenticates every request over a mode-0600 Unix socket. It isolates Ghidra home/cache/config/temp paths and removes the owned project, socket, process group, and runtime root on close, cancellation, timeout, or process exit.
236
+ On Windows, set the same variables in PowerShell and run `rea doctor --json`; automated `rea setup` and Hopper installation remain unavailable. The P0 target boundary rejects DLLs, managed PE files, non-x86-64 images, mutable/hostile inputs, and non-PE formats. See the [Windows Ghidra P0 operations guide](docs/windows-ghidra-p0.md) for registration, exact limitations, CI evidence, and acceptance gates.
237
+
238
+ REA loads its packaged Java `HeadlessScript` with `-scriptPath`, copies and digest-verifies the target in an ephemeral runtime, enables `-readOnly` and `-deleteProject`, caps auto-analysis at 300 seconds with two CPUs and a 2 GiB Java heap, and authenticates every request. Linux uses a mode-0600 Unix socket. Windows P0 uses token-authenticated IPv4 loopback and a token-free endpoint record because Node path-based IPC does not connect to Java AF_UNIX sockets on Windows. The bridge verifies Ghidra's imported-byte SHA-256 before serving any operation.
204
239
 
205
240
  The Ghidra adapter declares 18 direct and enhanced operations. Its ten inventory operations are `list_documents`, `list_procedures`, `list_strings`, `list_names`, `list_segments`, `address_name`, `procedure_address`, `resolve_containing_procedure`, `search_procedures`, and `search_strings`. It also admits `procedure_info`, `procedure_pseudo_code`, `procedure_assembly`, `procedure_callers`, `procedure_callees`, `procedure_references`, `xrefs`, and `analyze_function`. These capabilities enable the shared Swift/Objective-C inventory workflows, `binary_overview`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, `trace_feature`, and complete function dossiers. Default-space addresses are lowercase `0x` hexadecimal. Other spaces, including `EXTERNAL`, use `<percent-encoded-space>:0x<hex>`. Symbol results identify primary, dynamic, external, type, and source facts; procedures distinguish external functions and thunks; strings identify charset, missing-terminator state, byte length, and value truncation; memory-block ends are exclusive and permissions come directly from Ghidra.
206
241
 
@@ -208,6 +243,8 @@ The bridge serves operations only after auto-analysis completes. Each Program ow
208
243
 
209
244
  `npm run verify:ghidra` compiles source-owned x86-64 debug and stripped ELF, AArch64 ELF, x86-64 PE, and x86-64 Mach-O fixtures. Against real Ghidra 12.1.2 it validates every admitted operation, direct and indirect calls, imports/exports/thunks, typed references, strings/xrefs, multi-block CFG, cancellation, deadlines, concurrency, malformed inputs, and complete process/project cleanup. Set `REA_CC`, `REA_CLANG`, or `REA_LLD_LINK` only when the corresponding compiler command is not on `PATH`.
210
245
 
246
+ `npm run verify:ghidra:windows` uses a deterministic source-owned native x86-64 PE application and requires all 18 operations, target/snapshot/import digest linkage, authenticated loopback transport, and cleanup on a controlled Windows x64 Ghidra 12.1.2 runner. This proof does not establish Job Object ownership, private DACLs, or reparse-point-safe authority.
247
+
211
248
  To remove only REA-owned MCP registrations and the managed skill:
212
249
 
213
250
  ```bash
@@ -331,14 +368,14 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
331
368
  | Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
332
369
  | Browser observation | 8 | exact-origin CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, and visual evidence |
333
370
  | Electron analysis | 4 | passive root-confined observation, bounded static application mapping, and evidence-backed static/runtime reconciliation |
334
- | Application workflows | 3 | bounded cross-layer traces, unique-only version matching, and approved Linux-isolated extracted-module replay |
371
+ | Application workflows | 7 | bounded cross-layer traces, unique-only version matching, approved Linux-isolated extracted-module replay, managed-runtime characterization, and reconstruction coverage closure |
335
372
  | Workspace and observation | 18 | target lifecycle, Evidence v2 bundles, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
336
373
 
337
374
  The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
338
375
 
339
376
  ## Current status
340
377
 
341
- REA is already useful for native application, browser, and Electron investigation on supported macOS and Linux hosts:
378
+ REA is already useful for native application, browser, and Electron investigation on supported macOS and Linux hosts, plus the bounded Windows Ghidra P0 described above:
342
379
 
343
380
  - Open Mach-O, ELF, PE, `.app`, ZIP, APK, IPA, ASAR, plist, JavaScript, source-map, and generic analysis-database targets; Hopper remains the only adapter that accepts legacy `.hop` databases.
344
381
  - Discover deep-analysis candidates without starting them, choose deterministically, and retain one immutable provider/profile binding until an explicit switch or close; provider failures never trigger transparent fallback.
@@ -348,7 +385,11 @@ REA is already useful for native application, browser, and Electron investigatio
348
385
  - Reconstruct bounded static package, entrypoint, Webpack/Rspack module, import, worker, endpoint, storage, source-map, BrowserWindow, preload, contextBridge, IPC, utility-process, and native-add-on structure from an approved local directory or ASAR through `analyze_javascript_application` or `rea analyze-javascript-application`. The AST-only [application service](docs/javascript-artifact-reconstruction.md) never executes bootstrap code, pairs only unique exact literal IPC channels, and reports dynamic or ambiguous channels as unresolved.
349
386
  - Reconcile that static graph with existing passive web or Electron Evidence through `reconcile_javascript_runtime` or `rea reconcile-javascript-runtime`. Exact captured bytes outrank caller-declared file/URL mappings; target, frame, script, worker, cache, and asset ambiguity stays explicit, source-map authority stays separate, and a module resident in an observed bundle is never reported as executed. See [JavaScript static/runtime reconciliation](docs/javascript-runtime-reconciliation.md).
350
387
  - Trace a literal route, string, API, IPC channel, module, or native export through authenticated application Evidence with explicit traversal bounds, then hand exact native artifact digests and requested exports to retained Ghidra or Hopper Evidence without automatic provider switching. Compare application versions using unique-only digest, source-map, structural, and semantic tiers; duplicate, incomplete, and truncated matches stay unknown. See [cross-layer JavaScript application workflows](docs/javascript-application-workflows.md).
351
- - Classify PE/CLI managed artifacts with `inspect_managed_artifact` / `rea inspect-managed-artifact`, inspect bounded metadata members, signatures, method-body CIL hashes, exception regions, call edges, and field-access anchors with `inspect_managed_members` / `rea inspect-managed-members`, inventory declared ModuleRef/ImplMap/PInvoke and non-IL method boundary indicators with `inspect_managed_native_boundaries` / `rea inspect-managed-native-boundaries`, then compare two authenticated member observations with `compare_managed_members` / `rea compare-managed-members`. `verify_managed_native_boundaries` / `rea verify-managed-native-boundaries` checks managed P/Invoke declarations against authenticated native export or function Evidence while keeping verified, inferred, contradicted, and unresolved states distinct. The comparison remaps build-local tokens using unique CIL/signature and structural method-shape tiers, never names alone. `project_managed_application_graph` / `rea project-managed-application-graph` projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing. `import_managed_reconstruction` / `rea import-managed-reconstruction` admits user-supplied decompiler C#/IL/pseudocode as analyst inference only after exact artifact SHA-256, MVID, signature, and normalized IL commitments match. Separately, `plan_managed_runtime_correlation` / `rea plan-managed-runtime-correlation` can admit a default-disabled, permission-gated runtime-correlation plan locked to the same build evidence. These paths never load the assembly, resolve CLR dependencies, execute target code, run a decompiler, or translate managed tokens into native addresses; native-body bridge mapping and an actual runtime executor remain future managed-code contracts.
388
+ - Classify PE/CLI managed artifacts with `inspect_managed_artifact` / `rea inspect-managed-artifact`, inspect bounded metadata members, signatures, raw CIL hashes, limited decoded-instruction-tuple v1 hashes, separately reported exception regions, call edges, and field-access anchors with `inspect_managed_members` / `rea inspect-managed-members`, inventory declared ModuleRef/ImplMap/PInvoke and non-IL method boundary indicators with `inspect_managed_native_boundaries` / `rea inspect-managed-native-boundaries`, then compare two authenticated member observations with `compare_managed_members` / `rea compare-managed-members`. `verify_managed_native_boundaries` / `rea verify-managed-native-boundaries` checks managed P/Invoke declarations against authenticated native export or function Evidence while keeping verified, inferred, contradicted, and unresolved states distinct. The comparison treats build-local tokens as build-local and uses unique decoded-CIL/signature and structural method-shape tiers, never names alone; the v1 tuple hash does not itself resolve tokens or fully commit control flow. `project_managed_application_graph` / `rea project-managed-application-graph` projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing. `import_managed_reconstruction` / `rea import-managed-reconstruction` admits user-supplied decompiler C#/IL/pseudocode as analyst inference only after exact artifact SHA-256, MVID, signature, and the shipped v1 decoded-IL commitment match. Separately, `plan_managed_runtime_correlation` / `rea plan-managed-runtime-correlation` can admit a default-disabled, permission-gated runtime-correlation plan locked to the same build evidence. These paths never load the assembly, resolve CLR dependencies, execute target code, run a decompiler, or translate managed tokens into native addresses; full normalized-CIL v2 semantics, native-body bridge mapping, and an actual runtime executor remain future managed-code contracts.
389
+ - Configure `REA_ILSPY_CMD_PATH=/absolute/path/to/ilspycmd` only when you want
390
+ doctor and `verify:managed` to inspect a bring-your-own ILSpy command as a
391
+ real reconstruction oracle. REA does not install ILSpy and does not treat
392
+ decompiler text as canonical metadata or CIL observation.
352
393
  - 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.
353
394
  - Build bounded function dossiers with pseudocode, assembly, CFG edges, comments, calls, references, strings, and names.
354
395
  - Search and trace features across symbols, strings, metadata, references, and call paths.
@@ -418,17 +459,28 @@ Setup detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf,
418
459
 
419
460
  ### Manual MCP configuration
420
461
 
462
+ <!-- x-release-please-start-version -->
463
+
421
464
  ```json
422
465
  {
423
466
  "mcpServers": {
424
467
  "rea": {
425
468
  "command": "npx",
426
- "args": ["-y", "rea-agents", "mcp"]
469
+ "args": ["-y", "rea-agents@2.2.0", "mcp"]
427
470
  }
428
471
  }
429
472
  }
430
473
  ```
431
474
 
475
+ <!-- x-release-please-end -->
476
+
477
+ Persistent registrations should use one exact package version. `rea setup`
478
+ maintains that pin, upgrades the bundled skill at the same time, and gives Codex
479
+ a 30-second startup allowance for a cold package-runner start. An interactive
480
+ `rea upgrade` opens the updated setup plan after installing the new executable;
481
+ structured or non-interactive upgrades tell you to run that sync explicitly.
482
+ Restart clients whose approved registration changed.
483
+
432
484
  MCP clients that support prompts can also discover six ordered investigation
433
485
  workflows through `prompts/list`. Their optional identifier arguments use the
434
486
  current session for bounded `completion/complete` suggestions; see
@@ -464,20 +516,20 @@ The CLI and MCP server use the same application workflows and evidence contracts
464
516
  The agent workflow above is the easiest way to use REA. For a one-off overview from the Terminal:
465
517
 
466
518
  ```bash
467
- npx -y rea-agents analyze /Applications/Notes.app
468
- npx -y rea-agents inspect /Applications/Notes.app
469
- npx -y rea-agents inspect /Applications/Notes.app --detail detailed --limit 20
470
- npx -y rea-agents search /Applications/Notes.app "offline"
471
- npx -y rea-agents function /Applications/Notes.app 0x1000
472
- npx -y rea-agents xrefs /Applications/Notes.app 0x1000
473
- npx -y rea-agents trace /Applications/Notes.app "offline"
474
- npx -y rea-agents compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
475
- npx -y rea-agents investigate-versions /path/to/v1 /path/to/v2 /absolute/path/to/evidence/releases.json --yes
476
- npx -y rea-agents capabilities
477
- npx -y rea-agents providers
519
+ npx -y rea-agents@latest analyze /Applications/Notes.app
520
+ npx -y rea-agents@latest inspect /Applications/Notes.app
521
+ npx -y rea-agents@latest inspect /Applications/Notes.app --detail detailed --limit 20
522
+ npx -y rea-agents@latest search /Applications/Notes.app "offline"
523
+ npx -y rea-agents@latest function /Applications/Notes.app 0x1000
524
+ npx -y rea-agents@latest xrefs /Applications/Notes.app 0x1000
525
+ npx -y rea-agents@latest trace /Applications/Notes.app "offline"
526
+ npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
527
+ npx -y rea-agents@latest investigate-versions /path/to/v1 /path/to/v2 /absolute/path/to/evidence/releases.json --yes
528
+ npx -y rea-agents@latest capabilities
529
+ npx -y rea-agents@latest providers
478
530
  ```
479
531
 
480
- Run `npx -y rea-agents --help` for direct decompilation, bounded search and
532
+ Run `npx -y rea-agents@latest --help` for direct decompilation, bounded search and
481
533
  other options. `analyze` and `inspect` share the same overview workflow;
482
534
  `function`, `xrefs`, and `trace` return the same Evidence v2 envelopes as MCP.
483
535
 
@@ -566,6 +618,10 @@ environment allowlist in `REA_PROCESS_ALLOWED_ENV_JSON`. Because the current PTY
566
618
  adapter uses host networking, it also requires
567
619
  `REA_PROCESS_ALLOW_EXTERNAL_NETWORK=true`.
568
620
 
621
+ Set `REA_PROCESS_CAPTURE_AUTO_GRANT=false` to configure those process-capture
622
+ limits as a ceiling without implicitly granting them. This mode remains
623
+ fail-closed until a narrower grant is established.
624
+
569
625
  Capture a scenario or compare two saved Process Capture v4 Evidence records:
570
626
 
571
627
  ```bash
@@ -579,18 +635,22 @@ first terminal, interaction, exit, filesystem, protocol, process, or shim
579
635
  divergence. See [Process Capture v4](docs/process-capture.md) for scenario
580
636
  fields, command-shim replay, checkpoint triggers, limits, and safety behavior.
581
637
 
582
- If the native PTY backend is unavailable, install Xcode command-line tools and
583
- run `npm run rebuild:native`. Linux source builds require Python, `make`, and a
584
- C++ toolchain. Compatible packaged binaries do not require this rebuild.
638
+ REA installs a prebuilt PTY backend for supported macOS, Linux, and Windows
639
+ architectures. If the capability check reports that the backend is unavailable,
640
+ reinstall REA for the current platform and architecture.
585
641
 
586
642
  ASAR inventory verifies Electron integrity metadata for both archive entries
587
643
  and `.asar.unpacked` companion files. Integrity failures identify the logical
588
644
  path, declared and calculated SHA-256 values, and whether the entry was
589
- unpacked; REA does not silently accept the mismatched artifact.
645
+ unpacked; REA does not silently accept the mismatched artifact. If a supplied
646
+ ASAR declares unpacked companion bytes that are absent from the local artifact
647
+ set, REA keeps that occurrence as `unavailable` and continues analyzing the
648
+ embedded JavaScript instead of treating the missing native/resource bytes as
649
+ verified or absent.
590
650
 
591
651
  ## Security model
592
652
 
593
- REA does not provide a hosted analysis service. Hopper and Ghidra bridge communication uses authenticated private local sockets. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. Shipped providers, passive observers, and Process Capture are not security sandboxes: providers and launched targets run with the current user's permissions. Extracted JavaScript replay is a distinct Linux-only capability that fails closed unless Bubblewrap namespaces, architecture-checked seccomp, private runtime mounts, and delegated cgroup limits are available; it never inherits browser, Electron, or Process Capture authority. See [ADR-0002](docs/adr/0002-controlled-replay-authority-and-sandbox.md). Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
653
+ REA does not provide a hosted analysis service. Hopper and Linux Ghidra bridge communication uses authenticated private local sockets. Windows Ghidra P0 uses authenticated IPv4 loopback but does not claim named-pipe DACL or hostile-local-user isolation. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. Shipped providers, passive observers, and Process Capture are not security sandboxes: providers and launched targets run with the current user's permissions. Extracted JavaScript replay is a distinct Linux-only capability that fails closed unless Bubblewrap namespaces, architecture-checked seccomp, private runtime mounts, and delegated cgroup limits are available; it never inherits browser, Electron, or Process Capture authority. See [ADR-0002](docs/adr/0002-controlled-replay-authority-and-sandbox.md). Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
594
654
 
595
655
  ## FAQ
596
656
 
@@ -647,6 +707,11 @@ Any agent that can run a local MCP server can use the manual configuration. Setu
647
707
 
648
708
  See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, tests, and release instructions. Generated API documentation is available under [`docs/api`](docs/api/index.html).
649
709
 
710
+ `npm run verify:agent` runs brandless native, JavaScript-application, managed,
711
+ and browser prompts through a real local Codex CLI. Its JSON report measures
712
+ natural MCP use, first-tool routing, repeated calls, actual Codex token usage,
713
+ completion quality, and explicit treatment of authority and unknowns.
714
+
650
715
  ## Project links
651
716
 
652
717
  [npm](https://www.npmjs.com/package/rea-agents) · [Issues](https://github.com/morluto/rea/issues) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Hopper](https://www.hopperapp.com/) · [Ghidra](https://github.com/NationalSecurityAgency/ghidra)
@@ -7,15 +7,20 @@ import java.io.ByteArrayOutputStream;
7
7
  import java.io.IOException;
8
8
  import java.io.Reader;
9
9
  import java.math.BigInteger;
10
+ import java.net.InetAddress;
11
+ import java.net.InetSocketAddress;
10
12
  import java.net.StandardProtocolFamily;
11
13
  import java.net.UnixDomainSocketAddress;
12
14
  import java.nio.channels.Channels;
13
15
  import java.nio.channels.ServerSocketChannel;
14
16
  import java.nio.channels.SocketChannel;
15
17
  import java.nio.charset.StandardCharsets;
18
+ import java.nio.file.AtomicMoveNotSupportedException;
16
19
  import java.nio.file.Files;
17
20
  import java.nio.file.LinkOption;
18
21
  import java.nio.file.Path;
22
+ import java.nio.file.StandardCopyOption;
23
+ import java.nio.file.StandardOpenOption;
19
24
  import java.nio.file.attribute.PosixFilePermissions;
20
25
  import java.security.MessageDigest;
21
26
  import java.util.ArrayList;
@@ -67,7 +72,7 @@ import ghidra.program.model.symbol.Symbol;
67
72
  import ghidra.program.model.symbol.SymbolIterator;
68
73
 
69
74
  public final class ReaGhidraBridge extends HeadlessScript {
70
- private static final int BRIDGE_VERSION = 3;
75
+ private static final int BRIDGE_VERSION = 4;
71
76
  private static final int MAX_DESCRIPTOR_BYTES = 16 * 1024;
72
77
  private static final int MAX_REQUEST_CHARACTERS = 256 * 1024;
73
78
  private static final int MAX_RESPONSE_BYTES = 1024 * 1024;
@@ -85,9 +90,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
85
90
  private static final Gson GSON = new GsonBuilder().serializeNulls().create();
86
91
  private static final Set<String> DESCRIPTOR_KEYS = Set.of(
87
92
  "schema_version",
88
- "socket_path",
93
+ "transport",
94
+ "endpoint_path",
89
95
  "token",
90
96
  "run_id",
97
+ "target_sha256",
91
98
  "provider_version",
92
99
  "profile_digest"
93
100
  );
@@ -143,6 +150,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
143
150
  if (!Application.getApplicationVersion().equals(descriptor.providerVersion)) {
144
151
  throw new IllegalStateException("Ghidra provider version does not match the session");
145
152
  }
153
+ String importedSha256 = currentProgram.getExecutableSHA256();
154
+ if (importedSha256 == null ||
155
+ !constantTimeEquals(
156
+ importedSha256.toLowerCase(Locale.ROOT),
157
+ descriptor.targetSha256
158
+ )) {
159
+ throw new IllegalStateException(
160
+ "Ghidra imported-byte digest does not match the admitted target"
161
+ );
162
+ }
146
163
  try {
147
164
  initializeDecompiler();
148
165
  serve(descriptor);
@@ -156,22 +173,77 @@ public final class ReaGhidraBridge extends HeadlessScript {
156
173
  }
157
174
 
158
175
  private void serve(SessionDescriptor descriptor) throws Exception {
159
- Path socketPath = Path.of(descriptor.socketPath);
176
+ if (descriptor.transport.equals("unix-socket")) {
177
+ serveUnixSocket(descriptor);
178
+ return;
179
+ }
180
+ if (descriptor.transport.equals("authenticated-loopback-tcp")) {
181
+ serveLoopbackTcp(descriptor);
182
+ return;
183
+ }
184
+ throw new IllegalArgumentException("REA bridge transport is invalid");
185
+ }
186
+
187
+ private void serveUnixSocket(SessionDescriptor descriptor) throws Exception {
188
+ Path socketPath = Path.of(descriptor.endpointPath);
160
189
  Files.deleteIfExists(socketPath);
161
190
  try (ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
162
191
  server.bind(UnixDomainSocketAddress.of(socketPath));
163
192
  Files.setPosixFilePermissions(socketPath, PosixFilePermissions.fromString("rw-------"));
164
- try (SocketChannel client = server.accept();
165
- BufferedReader reader = new BufferedReader(Channels.newReader(client, StandardCharsets.UTF_8));
166
- BufferedWriter writer = new BufferedWriter(Channels.newWriter(client, StandardCharsets.UTF_8))) {
167
- serveClient(descriptor, reader, writer);
168
- }
193
+ acceptClient(server, descriptor);
169
194
  }
170
195
  finally {
171
196
  Files.deleteIfExists(socketPath);
172
197
  }
173
198
  }
174
199
 
200
+ private void serveLoopbackTcp(SessionDescriptor descriptor) throws Exception {
201
+ Path endpointPath = Path.of(descriptor.endpointPath);
202
+ Path pendingPath = endpointPath.resolveSibling(endpointPath.getFileName() + ".pending");
203
+ Files.deleteIfExists(endpointPath);
204
+ Files.deleteIfExists(pendingPath);
205
+ try (ServerSocketChannel server = ServerSocketChannel.open()) {
206
+ server.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0));
207
+ InetSocketAddress address = (InetSocketAddress) server.getLocalAddress();
208
+ JsonObject endpoint = new JsonObject();
209
+ endpoint.addProperty("schema_version", 1);
210
+ endpoint.addProperty("host", "127.0.0.1");
211
+ endpoint.addProperty("port", address.getPort());
212
+ Files.writeString(
213
+ pendingPath,
214
+ GSON.toJson(endpoint) + "\n",
215
+ StandardCharsets.UTF_8,
216
+ StandardOpenOption.CREATE_NEW,
217
+ StandardOpenOption.WRITE
218
+ );
219
+ try {
220
+ Files.move(pendingPath, endpointPath, StandardCopyOption.ATOMIC_MOVE);
221
+ }
222
+ catch (AtomicMoveNotSupportedException exception) {
223
+ Files.move(pendingPath, endpointPath);
224
+ }
225
+ acceptClient(server, descriptor);
226
+ }
227
+ finally {
228
+ Files.deleteIfExists(pendingPath);
229
+ Files.deleteIfExists(endpointPath);
230
+ }
231
+ }
232
+
233
+ private void acceptClient(
234
+ ServerSocketChannel server,
235
+ SessionDescriptor descriptor) throws Exception {
236
+ try (SocketChannel client = server.accept();
237
+ BufferedReader reader = new BufferedReader(
238
+ Channels.newReader(client, StandardCharsets.UTF_8)
239
+ );
240
+ BufferedWriter writer = new BufferedWriter(
241
+ Channels.newWriter(client, StandardCharsets.UTF_8)
242
+ )) {
243
+ serveClient(descriptor, reader, writer);
244
+ }
245
+ }
246
+
175
247
  private void serveClient(
176
248
  SessionDescriptor descriptor,
177
249
  BufferedReader reader,
@@ -270,6 +342,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
270
342
  "default_address_space",
271
343
  currentProgram.getAddressFactory().getDefaultAddressSpace().getName()
272
344
  );
345
+ target.addProperty("sha256", descriptor.targetSha256);
273
346
 
274
347
  JsonObject result = new JsonObject();
275
348
  result.addProperty("name", "REA Ghidra bridge");
@@ -1586,13 +1659,15 @@ public final class ReaGhidraBridge extends HeadlessScript {
1586
1659
  JsonParser.parseString(Files.readString(path, StandardCharsets.UTF_8)),
1587
1660
  DESCRIPTOR_KEYS
1588
1661
  );
1589
- if (requireInteger(object, "schema_version") != 1) {
1662
+ if (requireInteger(object, "schema_version") != 2) {
1590
1663
  throw new IllegalArgumentException("REA session descriptor version is invalid");
1591
1664
  }
1592
1665
  return new SessionDescriptor(
1593
- requireString(object, "socket_path"),
1666
+ requireString(object, "transport"),
1667
+ requireString(object, "endpoint_path"),
1594
1668
  requireString(object, "token"),
1595
1669
  requireString(object, "run_id"),
1670
+ requireSha256(object, "target_sha256"),
1596
1671
  requireString(object, "provider_version"),
1597
1672
  requireString(object, "profile_digest")
1598
1673
  );
@@ -1662,6 +1737,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
1662
1737
  return result;
1663
1738
  }
1664
1739
 
1740
+ private static String requireSha256(JsonObject object, String name) {
1741
+ String value = requireString(object, name);
1742
+ if (!value.matches("[a-f0-9]{64}")) {
1743
+ throw new IllegalArgumentException(name + " must be lowercase SHA-256");
1744
+ }
1745
+ return value;
1746
+ }
1747
+
1665
1748
  private static int requireInteger(JsonObject object, String name) {
1666
1749
  JsonElement value = object.get(name);
1667
1750
  if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber()) {
@@ -1795,9 +1878,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
1795
1878
  private record InstructionScan(List<Instruction> instructions, boolean truncated) {}
1796
1879
  private record TruncatedValue(String value, boolean truncated) {}
1797
1880
  private record SessionDescriptor(
1798
- String socketPath,
1881
+ String transport,
1882
+ String endpointPath,
1799
1883
  String token,
1800
1884
  String runId,
1885
+ String targetSha256,
1801
1886
  String providerVersion,
1802
1887
  String profileDigest
1803
1888
  ) {}
@@ -0,0 +1,17 @@
1
+ import { androidApplicationProjectionInputSchema, projectAndroidApplication, } from "../domain/androidApplication.js";
2
+ import { ANDROID_APPLICATION_PROVIDER } from "./InvestigationProviders.js";
3
+ import { projectInventoryEvidence } from "./InventoryProjectionEvidence.js";
4
+ const OPERATION = "project_android_application_graph";
5
+ /** Project authenticated APK inventory Evidence into Android application Evidence. */
6
+ export const projectAndroidApplicationEvidence = (rawInput) => {
7
+ return projectInventoryEvidence({
8
+ rawInput,
9
+ schema: androidApplicationProjectionInputSchema,
10
+ project: projectAndroidApplication,
11
+ operation: OPERATION,
12
+ predicateType: "rea.android-application-graph/v1",
13
+ provider: ANDROID_APPLICATION_PROVIDER,
14
+ subjectFormat: () => "apk",
15
+ protocolError: "Android application projection produced an invalid result",
16
+ });
17
+ };
@@ -0,0 +1,17 @@
1
+ import { appleApplicationProjectionInputSchema, projectAppleApplication, } from "../domain/appleApplication.js";
2
+ import { APPLE_APPLICATION_PROVIDER } from "./InvestigationProviders.js";
3
+ import { projectInventoryEvidence } from "./InventoryProjectionEvidence.js";
4
+ const OPERATION = "project_apple_application_graph";
5
+ /** Project authenticated IPA inventory Evidence into Apple application evidence. */
6
+ export const projectAppleApplicationEvidence = (rawInput) => {
7
+ return projectInventoryEvidence({
8
+ rawInput,
9
+ schema: appleApplicationProjectionInputSchema,
10
+ project: projectAppleApplication,
11
+ operation: OPERATION,
12
+ predicateType: "rea.apple-application-graph/v1",
13
+ provider: APPLE_APPLICATION_PROVIDER,
14
+ subjectFormat: () => "ipa",
15
+ protocolError: "Apple application projection produced an invalid result",
16
+ });
17
+ };
@@ -113,7 +113,7 @@ const createExtractionResult = (input, inventory, selected, extracted) => {
113
113
  });
114
114
  };
115
115
  const loadInventory = async (path, limits, selectedIds, signal) => {
116
- const snapshot = await scanArtifactInventory(path, limits, signal);
116
+ const snapshot = await scanArtifactInventory(path, limits, { signal });
117
117
  const occurrences = new Map();
118
118
  const neededNodes = new Set();
119
119
  collectOccurrences(snapshot.occurrences, selectedIds, occurrences, neededNodes);
@@ -0,0 +1,42 @@
1
+ import { open } from "node:fs/promises";
2
+ import { classifyArtifactPath } from "../ArtifactGraphConstruction.js";
3
+ const classifyContainerExtension = (path) => {
4
+ const lower = path.toLowerCase();
5
+ for (const format of ["asar", "ipa", "apk", "zip", "dmg", "pkg"])
6
+ if (lower.endsWith(`.${format}`))
7
+ return format;
8
+ return undefined;
9
+ };
10
+ export const classifyRoot = async (path, directory) => {
11
+ if (directory)
12
+ return "directory";
13
+ const extensionFormat = classifyContainerExtension(path);
14
+ if (extensionFormat !== undefined)
15
+ return extensionFormat;
16
+ const handle = await open(path, "r");
17
+ try {
18
+ const magic = Buffer.alloc(4);
19
+ const observed = await handle.read(magic, 0, magic.length, 0);
20
+ if (observed.bytesRead === 4 &&
21
+ magic[0] === 0x50 &&
22
+ magic[1] === 0x4b &&
23
+ [0x03, 0x05, 0x07].includes(magic[2] ?? -1) &&
24
+ [0x04, 0x06, 0x08].includes(magic[3] ?? -1))
25
+ return "zip";
26
+ if (observed.bytesRead === 4) {
27
+ const header = magic.readUInt32BE(0);
28
+ if ([0xcafebabe, 0xbebafeca, 0xcafebabf, 0xbfbafeca].includes(header))
29
+ return "mach-o-universal";
30
+ if ([0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe].includes(header))
31
+ return "mach-o";
32
+ if (magic.equals(Buffer.from([0x7f, 0x45, 0x4c, 0x46])))
33
+ return "elf";
34
+ }
35
+ if (observed.bytesRead >= 2 && magic[0] === 0x4d && magic[1] === 0x5a)
36
+ return "pe";
37
+ }
38
+ finally {
39
+ await handle.close();
40
+ }
41
+ return classifyArtifactPath(path).format;
42
+ };
@@ -0,0 +1,43 @@
1
+ import { createHash } from "node:crypto";
2
+ import { ArtifactReaderFailure, } from "../../artifacts/ArtifactReader.js";
3
+ import { streamChunkToBuffer } from "../../artifacts/StreamBytes.js";
4
+ export const abortIfNeeded = (signal) => {
5
+ if (signal?.aborted === true)
6
+ throw new ArtifactReaderFailure("cancelled", "Artifact inventory cancelled");
7
+ };
8
+ export const preflightEntry = (entry, limits) => {
9
+ if (entry.declaredSize !== null && entry.declaredSize > limits.maxEntryBytes)
10
+ throw new ArtifactReaderFailure("limit", `Entry exceeds byte limit: ${entry.path}`);
11
+ if (entry.declaredSize !== null &&
12
+ entry.compressedSize !== null &&
13
+ entry.compressedSize === 0 &&
14
+ entry.declaredSize > 0)
15
+ throw new ArtifactReaderFailure("limit", `Invalid compression ratio: ${entry.path}`);
16
+ if (entry.declaredSize !== null &&
17
+ entry.compressedSize !== null &&
18
+ entry.compressedSize > 0 &&
19
+ entry.declaredSize / entry.compressedSize > limits.maxCompressionRatio)
20
+ throw new ArtifactReaderFailure("limit", `Compression ratio exceeds limit: ${entry.path}`);
21
+ };
22
+ export const hashReadable = async (stream, maximum, signal) => {
23
+ const hash = createHash("sha256");
24
+ const prefixes = [];
25
+ let prefixBytes = 0;
26
+ let bytes = 0;
27
+ for await (const raw of stream) {
28
+ abortIfNeeded(signal);
29
+ const chunk = streamChunkToBuffer(raw);
30
+ bytes += chunk.length;
31
+ if (bytes > maximum) {
32
+ stream.destroy();
33
+ throw new ArtifactReaderFailure("limit", "Observed entry bytes exceed limit");
34
+ }
35
+ hash.update(chunk);
36
+ if (prefixBytes < 16) {
37
+ const selected = chunk.subarray(0, 16 - prefixBytes);
38
+ prefixes.push(selected);
39
+ prefixBytes += selected.length;
40
+ }
41
+ }
42
+ return { sha256: hash.digest("hex"), bytes, prefix: Buffer.concat(prefixes) };
43
+ };