rea-agents 2.0.0 → 2.1.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 (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -0,0 +1,262 @@
1
+ import { posix } from "node:path";
2
+ import { addAstContainsEdge, artifactLocalIdentity, } from "./JavaScriptArtifactGraphContext.js";
3
+ import { astObservationEvidence, completeReconstructionCoverage, partialReconstructionCoverage, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
4
+ import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
5
+ /** Create one path-scoped source-module identity for every analyzed source file. */
6
+ export const addJavaScriptSourceModules = (context) => {
7
+ for (const analyzed of context.analysis.files) {
8
+ const { file, semantic } = analyzed;
9
+ const asset = context.assetNodes.get(file.path);
10
+ const program = semantic?.ir.scopes.find(({ kind }) => kind === "program");
11
+ if (asset === undefined || semantic === null || program === undefined)
12
+ continue;
13
+ const exports = semantic.ir.moduleLinks
14
+ .flatMap(({ exportedName }) => exportedName === null ? [] : [exportedName])
15
+ .sort(compareCodePoints);
16
+ const retainedExports = [...new Set(exports)].slice(0, 128);
17
+ const source = context.accumulator.addNode({
18
+ kind: "javascript-module",
19
+ identity: artifactLocalIdentity(file.sha256, "source-module", `${file.container_sha256}:${file.path}`),
20
+ observations: [
21
+ {
22
+ label: file.path,
23
+ properties: {
24
+ semantic_role: "source-module",
25
+ path: file.path,
26
+ container_sha256: file.container_sha256,
27
+ logical_module_key: file.path,
28
+ module_format: moduleFormat(file.path, semantic.ir),
29
+ import_relationships: semantic.ir.moduleLinks.filter(({ kind }) => kind === "import" || kind === "require").length,
30
+ export_relationships: exports.length,
31
+ export_names: retainedExports,
32
+ omitted_export_names: Math.max(0, exports.length - retainedExports.length),
33
+ },
34
+ evidence: astObservationEvidence({
35
+ sha256: file.sha256,
36
+ path: file.path,
37
+ range: program.location,
38
+ operation: "recover-source-module",
39
+ coverage: semanticCoverage(semantic),
40
+ limitations: semantic.ir.limitations,
41
+ }),
42
+ },
43
+ ],
44
+ });
45
+ context.sourceModuleNodes.set(file.path, source);
46
+ addAstContainsEdge(context, {
47
+ source: asset,
48
+ target: source,
49
+ file,
50
+ range: program.location,
51
+ coverage: semanticCoverage(semantic),
52
+ properties: { logical_module_key: file.path },
53
+ });
54
+ }
55
+ };
56
+ /** Compose bounded CommonJS and ESM binding relationships across artifact files. */
57
+ export const addJavaScriptModuleRelationships = (context) => {
58
+ for (const analyzed of context.analysis.files) {
59
+ const { file, semantic } = analyzed;
60
+ const source = context.sourceModuleNodes.get(file.path);
61
+ if (semantic === null || source === undefined)
62
+ continue;
63
+ for (const link of semantic.ir.moduleLinks) {
64
+ const input = { context, file, semantic, source, link };
65
+ if (isExportLink(link))
66
+ addExportRelationship(input);
67
+ else if (link.specifier !== null)
68
+ addImportRelationship(input, source, {
69
+ specifier: link.specifier,
70
+ importedPath: link.importedName === null ? [] : [link.importedName],
71
+ });
72
+ }
73
+ }
74
+ };
75
+ const addExportRelationship = (input) => {
76
+ const { context, file, semantic, source, link } = input;
77
+ if (link.exportedName === null)
78
+ return;
79
+ const exported = context.accumulator.addNode({
80
+ kind: "javascript-module",
81
+ identity: artifactLocalIdentity(file.sha256, "module-export", `${file.container_sha256}:${file.path}:${link.exportedName}`),
82
+ observations: [
83
+ {
84
+ label: `${file.path}:${link.exportedName}`,
85
+ properties: {
86
+ semantic_role: "export-binding",
87
+ module_path: file.path,
88
+ relationship_kind: link.kind,
89
+ exported_name: link.exportedName,
90
+ local_name: link.localName,
91
+ imported_name: link.importedName,
92
+ declared_specifier: link.specifier,
93
+ },
94
+ evidence: astObservationEvidence({
95
+ sha256: file.sha256,
96
+ path: file.path,
97
+ range: link.location,
98
+ operation: "recover-module-export",
99
+ coverage: semanticCoverage(semantic),
100
+ limitations: semantic.ir.limitations,
101
+ }),
102
+ },
103
+ ],
104
+ });
105
+ context.accumulator.addEdge({
106
+ source_node_id: source.node_id,
107
+ target_node_id: exported.node_id,
108
+ relation: "exposes",
109
+ properties: relationshipProperties(link, null),
110
+ evidence: relationshipEvidence(input, "expose-module-export", []),
111
+ });
112
+ const origin = moduleOriginForExport(semantic.ir, link);
113
+ if (origin !== null)
114
+ addImportRelationship(input, exported, origin);
115
+ };
116
+ const addImportRelationship = (input, source, origin) => {
117
+ const target = resolveModuleTarget(input, origin.specifier);
118
+ input.context.accumulator.addEdge({
119
+ source_node_id: source.node_id,
120
+ target_node_id: target.node.node_id,
121
+ relation: "imports",
122
+ properties: relationshipProperties(input.link, target, origin),
123
+ evidence: relationshipEvidence(input, "resolve-semantic-module-relationship", target.limitations, target.status === "resolved"
124
+ ? "high"
125
+ : target.status === "external"
126
+ ? "medium"
127
+ : "low"),
128
+ });
129
+ };
130
+ const resolveModuleTarget = (input, specifier) => {
131
+ const resolution = resolveArtifactPathByContext({
132
+ declaredPath: specifier,
133
+ sourcePath: input.file.path,
134
+ context: "module-specifier",
135
+ files: input.context.filesByPath,
136
+ moduleKind: input.link.kind === "require" || input.link.kind === "commonjs-export"
137
+ ? "require"
138
+ : "import",
139
+ });
140
+ const path = resolution.resolved_path;
141
+ const file = path === null ? undefined : input.context.filesByPath.get(path);
142
+ const node = path === null
143
+ ? undefined
144
+ : (input.context.sourceModuleNodes.get(path) ??
145
+ input.context.fileNodes.get(path));
146
+ if (path !== null && file !== undefined && node !== undefined)
147
+ return {
148
+ node,
149
+ path,
150
+ file,
151
+ status: "resolved",
152
+ limitations: resolution.limitations,
153
+ };
154
+ return {
155
+ node: unresolvedModuleNode(input, specifier, resolution.resolution_status),
156
+ path: null,
157
+ file: null,
158
+ status: resolution.resolution_status,
159
+ limitations: resolution.limitations,
160
+ };
161
+ };
162
+ const unresolvedModuleNode = (input, specifier, status) => input.context.accumulator.addNode({
163
+ kind: "javascript-module",
164
+ identity: artifactLocalIdentity(input.file.sha256, "unresolved-semantic-module", `${status}:${specifier}`),
165
+ observations: [
166
+ {
167
+ label: specifier,
168
+ properties: {
169
+ semantic_role: "module-reference",
170
+ declared_specifier: specifier,
171
+ resolution_status: status,
172
+ },
173
+ evidence: staticInferenceEvidence({
174
+ sha256: input.file.sha256,
175
+ path: input.file.path,
176
+ range: input.link.location,
177
+ operation: "retain-unresolved-semantic-module",
178
+ coverage: semanticCoverage(input.semantic),
179
+ confidence: "low",
180
+ limitations: input.semantic.ir.limitations,
181
+ }),
182
+ },
183
+ ],
184
+ });
185
+ const moduleOriginForExport = (ir, link) => {
186
+ if (link.specifier !== null)
187
+ return {
188
+ specifier: link.specifier,
189
+ importedPath: link.importedName === null ? [] : [link.importedName],
190
+ };
191
+ if (link.localName === null)
192
+ return null;
193
+ const program = ir.scopes.find(({ kind }) => kind === "program");
194
+ const binding = ir.bindings.find(({ name, scopeId }) => name === link.localName && scopeId === program?.scopeId);
195
+ return binding?.provenance.status === "module" &&
196
+ binding.provenance.origins.length === 1
197
+ ? (binding.provenance.origins[0] ?? null)
198
+ : null;
199
+ };
200
+ const relationshipProperties = (link, target, origin) => ({
201
+ module_link_kind: link.kind,
202
+ specifier: origin?.specifier ?? link.specifier,
203
+ imported_name: origin?.importedPath.at(-1) ?? link.importedName,
204
+ imported_path: origin?.importedPath ?? [],
205
+ local_name: link.localName,
206
+ exported_name: link.exportedName,
207
+ resolved_path: target?.path ?? null,
208
+ resolution_status: target?.status ?? null,
209
+ target_file_kind: target?.file?.kind ?? null,
210
+ target_json_status: target?.file?.kind === "json" ? jsonStatus(target.file) : null,
211
+ });
212
+ const relationshipEvidence = (input, operation, limitations, confidence = "high") => staticInferenceEvidence({
213
+ sha256: input.file.sha256,
214
+ path: input.file.path,
215
+ range: input.link.location,
216
+ operation,
217
+ coverage: semanticCoverage(input.semantic),
218
+ confidence,
219
+ limitations: [...input.semantic.ir.limitations, ...limitations],
220
+ });
221
+ const semanticCoverage = (semantic) => {
222
+ const limits = Object.entries(semantic.limits).map(([name, value]) => ({
223
+ name: `semantic-${camelToKebab(name)}`,
224
+ value,
225
+ unit: "items",
226
+ }));
227
+ if (semantic.ir.coverage.status === "complete")
228
+ return completeReconstructionCoverage(limits);
229
+ return partialReconstructionCoverage(limits, semantic.ir.coverage.omittedCount, semantic.ir.coverage.status === "truncated");
230
+ };
231
+ const moduleFormat = (path, ir) => {
232
+ const extension = posix.extname(path).toLowerCase();
233
+ if (extension === ".mjs")
234
+ return "esm";
235
+ if (extension === ".cjs")
236
+ return "commonjs";
237
+ const esm = ir.moduleLinks.some(({ kind }) => ["import", "export", "re-export"].includes(kind));
238
+ const commonJs = ir.moduleLinks.some(({ kind }) => ["require", "commonjs-export"].includes(kind));
239
+ return esm && commonJs
240
+ ? "mixed"
241
+ : esm
242
+ ? "esm"
243
+ : commonJs
244
+ ? "commonjs"
245
+ : "unknown";
246
+ };
247
+ const jsonStatus = (file) => {
248
+ if (!file.text.included)
249
+ return "unavailable";
250
+ try {
251
+ JSON.parse(file.text.value);
252
+ return "included";
253
+ }
254
+ catch {
255
+ return "invalid";
256
+ }
257
+ };
258
+ const isExportLink = (link) => link.kind === "export" ||
259
+ link.kind === "re-export" ||
260
+ link.kind === "commonjs-export";
261
+ const camelToKebab = (value) => value.replaceAll(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
262
+ const compareCodePoints = (left, right) => left < right ? -1 : left > right ? 1 : 0;
@@ -0,0 +1,16 @@
1
+ /** Build the exact permission request for one controlled replay operation. */
2
+ export const replayPermissionRequest = (input, policy) => ({
3
+ capability: "javascript_replay",
4
+ roots: [...input.left.modules, ...(input.right?.modules ?? [])].map(({ path }) => path),
5
+ executables: [
6
+ policy.nodePath,
7
+ policy.bubblewrapPath,
8
+ policy.systemdRunPath,
9
+ policy.systemctlPath,
10
+ policy.shellPath,
11
+ ],
12
+ environment_names: [],
13
+ network: "none",
14
+ mount: true,
15
+ operation_identity: `run_controlled_replay:${input.plan_digest ?? "plan"}`,
16
+ });
@@ -6,6 +6,7 @@ import { createEvidence } from "../domain/evidence.js";
6
6
  import { projectInputIssues } from "../domain/inputIssueProjection.js";
7
7
  import { jsonValueSchema } from "../domain/jsonValue.js";
8
8
  import { err, ok } from "../domain/result.js";
9
+ import { replayPermissionRequest } from "./JavaScriptReplayPermission.js";
9
10
  import { digestBytes, prepareReplayPlan, } from "./JavaScriptReplayPlanning.js";
10
11
  const OPERATION = "run_controlled_replay";
11
12
  const isAborted = (signal) => signal?.aborted === true;
@@ -18,13 +19,46 @@ export const runControlledReplay = async (dependencies, rawInput, options = {})
18
19
  };
19
20
  /** Plan or execute replay input already parsed by a trusted adapter. */
20
21
  export const runControlledReplayValidated = async (dependencies, input, options = {}) => {
22
+ const authorized = await authorizeReplay(dependencies, input, options.signal);
23
+ if (!authorized.ok)
24
+ return authorized;
25
+ const prepared = await prepareValidatedReplay(dependencies, input, options.signal);
26
+ if (!prepared.ok)
27
+ return prepared;
28
+ if (isAborted(options.signal))
29
+ return err(new AnalysisCancelledError(OPERATION));
30
+ if (input.mode === "plan")
31
+ return ok(asJson({
32
+ phase: "plan",
33
+ plan: prepared.value.publicPlan,
34
+ source_evidence: [],
35
+ evidence: null,
36
+ }));
37
+ if (input.plan_digest !== prepared.value.publicPlan.plan_digest)
38
+ return err(new ReplayPlanStaleError(input.plan_digest ?? "", prepared.value.publicPlan.plan_digest));
39
+ const exportContext = await authorizeReproducerExport(dependencies.authority, input, prepared.value);
40
+ if (!exportContext.ok)
41
+ return exportContext;
42
+ const executed = await executeValidatedReplay(dependencies, prepared.value, exportContext.value, options);
43
+ if (!executed.ok)
44
+ return executed;
45
+ try {
46
+ return ok(buildReplayOutput(prepared.value, executed.value.executed, executed.value.sourceEvidence));
47
+ }
48
+ catch (cause) {
49
+ return err(new AnalysisProtocolError(cause instanceof z.ZodError
50
+ ? "Controlled replay produced an invalid bounded result"
51
+ : "Controlled replay could not establish or clean up its sandbox", { cause }));
52
+ }
53
+ };
54
+ const authorizeReplay = async (dependencies, input, signal) => {
21
55
  if (!dependencies.policy.enabled)
22
56
  return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "controlled replay is disabled; configure exact roots and executables before enabling it"));
23
57
  if (dependencies.authority === undefined)
24
58
  return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "JavaScript replay permission policy is not configured"));
25
- if (isAborted(options.signal))
59
+ if (isAborted(signal))
26
60
  return err(new AnalysisCancelledError(OPERATION));
27
- const request = permissionRequest(input, dependencies.policy);
61
+ const request = replayPermissionRequest(input, dependencies.policy);
28
62
  const authorized = input.mode === "plan"
29
63
  ? await dependencies.authority.explain(request, "read")
30
64
  : await dependencies.authority.authorize(request, "read");
@@ -34,51 +68,51 @@ export const runControlledReplayValidated = async (dependencies, input, options
34
68
  : new AnalysisProtocolError(authorized.error.message, {
35
69
  cause: authorized.error,
36
70
  }));
37
- let prepared;
71
+ return ok(true);
72
+ };
73
+ const prepareValidatedReplay = async (dependencies, input, signal) => {
74
+ if (isAborted(signal))
75
+ return err(new AnalysisCancelledError(OPERATION));
38
76
  try {
39
- prepared = await prepareReplayPlan(input, dependencies.policy, dependencies.host);
77
+ return ok(await prepareReplayPlan(input, dependencies.policy, dependencies.host));
40
78
  }
41
79
  catch (cause) {
42
80
  return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, cause instanceof Error ? cause.message : "replay planning failed"));
43
81
  }
44
- if (isAborted(options.signal))
45
- return err(new AnalysisCancelledError(OPERATION));
46
- if (input.mode === "plan")
47
- return ok(asJson({
48
- phase: "plan",
49
- plan: prepared.publicPlan,
50
- source_evidence: [],
51
- evidence: null,
52
- }));
53
- if (input.plan_digest !== prepared.publicPlan.plan_digest)
54
- return err(new ReplayPlanStaleError(input.plan_digest ?? "", prepared.publicPlan.plan_digest));
55
- let canonicalExportPath;
56
- if (input.reproducer_export !== undefined) {
57
- if (input.reproducer_export.approved !== true)
58
- return err(new AnalysisInputError(`${OPERATION}:reproducer_export`));
59
- const exportAuthorized = await dependencies.authority.authorize({
60
- capability: "evidence_write",
61
- roots: [input.reproducer_export.path],
62
- executables: [],
63
- environment_names: [],
64
- network: "none",
65
- mount: false,
66
- operation_identity: `${OPERATION}:reproducer:${prepared.publicPlan.plan_digest}`,
67
- }, "write");
68
- if (!exportAuthorized.ok)
69
- return err(exportAuthorized.error instanceof PermissionRequiredError
70
- ? exportAuthorized.error
71
- : new AnalysisProtocolError(exportAuthorized.error.message, {
72
- cause: exportAuthorized.error,
73
- }));
74
- canonicalExportPath = exportAuthorized.value.request.roots[0];
75
- if (canonicalExportPath !== input.reproducer_export.path)
76
- return err(new AnalysisInputError(`${OPERATION}:reproducer_export:path`, {
77
- cause: new TypeError(`Use the canonical reproducer path: ${canonicalExportPath ?? "unavailable"}`),
82
+ };
83
+ const authorizeReproducerExport = async (authority, input, prepared) => {
84
+ if (input.reproducer_export === undefined)
85
+ return ok(undefined);
86
+ if (input.reproducer_export.approved !== true)
87
+ return err(new AnalysisInputError(`${OPERATION}:reproducer_export`));
88
+ if (authority === undefined)
89
+ return err(new AnalysisCapabilityUnavailableError("rea-javascript-replay", OPERATION, "JavaScript replay permission policy is not configured"));
90
+ const exportAuthorized = await authority.authorize({
91
+ capability: "evidence_write",
92
+ roots: [input.reproducer_export.path],
93
+ executables: [],
94
+ environment_names: [],
95
+ network: "none",
96
+ mount: false,
97
+ operation_identity: `${OPERATION}:reproducer:${prepared.publicPlan.plan_digest}`,
98
+ }, "write");
99
+ if (!exportAuthorized.ok)
100
+ return err(exportAuthorized.error instanceof PermissionRequiredError
101
+ ? exportAuthorized.error
102
+ : new AnalysisProtocolError(exportAuthorized.error.message, {
103
+ cause: exportAuthorized.error,
78
104
  }));
79
- }
80
- if (isAborted(options.signal))
81
- return err(new AnalysisCancelledError(OPERATION));
105
+ const canonicalExportPath = exportAuthorized.value.request.roots[0];
106
+ if (canonicalExportPath !== input.reproducer_export.path)
107
+ return err(new AnalysisInputError(`${OPERATION}:reproducer_export:path`, {
108
+ cause: new TypeError(`Use the canonical reproducer path: ${canonicalExportPath ?? "unavailable"}`),
109
+ }));
110
+ return ok({
111
+ path: canonicalExportPath,
112
+ includeSources: input.reproducer_export.include_sources === true,
113
+ });
114
+ };
115
+ const executeValidatedReplay = async (dependencies, prepared, exportContext, options) => {
82
116
  await options.progress?.report({
83
117
  phase: OPERATION,
84
118
  completed: 0,
@@ -98,106 +132,9 @@ export const runControlledReplayValidated = async (dependencies, input, options
98
132
  });
99
133
  if (executed.termination === "cancelled")
100
134
  return err(new AnalysisCancelledError(OPERATION));
101
- if (input.reproducer_export !== undefined &&
102
- canonicalExportPath !== undefined &&
103
- executed.cleanup.state === "complete") {
104
- const manifest = {
105
- schema_version: 1,
106
- plan: prepared.publicPlan,
107
- result: executed,
108
- sources: input.reproducer_export.include_sources
109
- ? {
110
- left: prepared.leftSources,
111
- ...(prepared.rightSources === undefined
112
- ? {}
113
- : { right: prepared.rightSources }),
114
- }
115
- : null,
116
- };
117
- const encoded = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
118
- try {
119
- await writeFileAtomic(canonicalExportPath, encoded, {
120
- mode: 0o600,
121
- });
122
- executed = replayExecutionResultSchema.parse({
123
- ...executed,
124
- reproducer: {
125
- state: "written",
126
- path: canonicalExportPath,
127
- sha256: digestBytes(encoded),
128
- },
129
- });
130
- }
131
- catch (cause) {
132
- executed = replayExecutionResultSchema.parse({
133
- ...executed,
134
- limitations: [
135
- ...executed.limitations,
136
- "Replay completed, but the separately approved reproducer export failed.",
137
- ],
138
- reproducer: {
139
- state: "failed",
140
- path: canonicalExportPath,
141
- error: cause instanceof Error
142
- ? cause.message
143
- : "unknown reproducer export failure",
144
- },
145
- });
146
- }
147
- }
148
- const leftCount = prepared.publicPlan.cases.length;
149
- const sourceEvidence = [
150
- createReplayEvidence(prepared, executed, "left", executed.outcomes.slice(0, leftCount)),
151
- ...(prepared.publicPlan.right === undefined
152
- ? []
153
- : [
154
- createReplayEvidence(prepared, executed, "right", executed.outcomes.slice(leftCount)),
155
- ]),
156
- ];
157
- const subject = prepared.publicPlan.left.modules[0];
158
- const evidence = createEvidence(subject === undefined
159
- ? undefined
160
- : {
161
- path: subject.canonical_path,
162
- sha256: subject.sha256,
163
- format: "javascript",
164
- }, {
165
- id: "rea-javascript-replay",
166
- name: "REA isolated JavaScript replay",
167
- version: "1",
168
- }, {
169
- predicateType: "javascript-controlled-replay",
170
- operation: OPERATION,
171
- parameters: {
172
- replay_plan: jsonValueSchema.parse(JSON.parse(JSON.stringify(prepared.publicPlan))),
173
- },
174
- result: jsonValueSchema.parse(JSON.parse(JSON.stringify(executed))),
175
- rawResult: jsonValueSchema.parse(JSON.parse(JSON.stringify({
176
- cases: prepared.publicPlan.cases,
177
- outcomes: executed.outcomes,
178
- stderr: executed.stderr,
179
- }))),
180
- confidence: executed.comparison === undefined ? "observed" : "derived",
181
- authority: "controlled-replay",
182
- environment: {
183
- id: prepared.publicPlan.plan_digest,
184
- platform: process.platform,
185
- architecture: process.arch,
186
- isolation: "container",
187
- },
188
- limitations: executed.limitations,
189
- locations: prepared.publicPlan.left.modules.map((module) => ({
190
- kind: "artifact-path",
191
- path: module.canonical_path,
192
- })),
193
- evidenceLinks: sourceEvidence.map(({ evidence_id: id }) => id),
194
- });
195
- return ok(asJson({
196
- phase: "execute",
197
- plan: null,
198
- source_evidence: sourceEvidence.map((item) => replayEvidenceSchema.parse(item)),
199
- evidence: replayEvidenceSchema.parse(evidence),
200
- }));
135
+ executed = await applyReproducerExport(executed, exportContext, prepared);
136
+ const sourceEvidence = buildSourceEvidence(prepared, executed);
137
+ return ok({ executed, sourceEvidence });
201
138
  }
202
139
  catch (cause) {
203
140
  return err(new AnalysisProtocolError(cause instanceof z.ZodError
@@ -205,6 +142,110 @@ export const runControlledReplayValidated = async (dependencies, input, options
205
142
  : "Controlled replay could not establish or clean up its sandbox", { cause }));
206
143
  }
207
144
  };
145
+ const applyReproducerExport = async (executed, exportContext, prepared) => {
146
+ if (exportContext === undefined || executed.cleanup.state !== "complete")
147
+ return executed;
148
+ const manifest = {
149
+ schema_version: 1,
150
+ plan: prepared.publicPlan,
151
+ result: executed,
152
+ sources: exportContext.includeSources
153
+ ? {
154
+ left: prepared.leftSources,
155
+ ...(prepared.rightSources === undefined
156
+ ? {}
157
+ : { right: prepared.rightSources }),
158
+ }
159
+ : null,
160
+ };
161
+ const encoded = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
162
+ try {
163
+ await writeFileAtomic(exportContext.path, encoded, {
164
+ mode: 0o600,
165
+ });
166
+ return replayExecutionResultSchema.parse({
167
+ ...executed,
168
+ reproducer: {
169
+ state: "written",
170
+ path: exportContext.path,
171
+ sha256: digestBytes(encoded),
172
+ },
173
+ });
174
+ }
175
+ catch (cause) {
176
+ return replayExecutionResultSchema.parse({
177
+ ...executed,
178
+ limitations: [
179
+ ...executed.limitations,
180
+ "Replay completed, but the separately approved reproducer export failed.",
181
+ ],
182
+ reproducer: {
183
+ state: "failed",
184
+ path: exportContext.path,
185
+ error: cause instanceof Error
186
+ ? cause.message
187
+ : "unknown reproducer export failure",
188
+ },
189
+ });
190
+ }
191
+ };
192
+ const buildSourceEvidence = (prepared, executed) => {
193
+ const leftCount = prepared.publicPlan.cases.length;
194
+ return [
195
+ createReplayEvidence(prepared, executed, "left", executed.outcomes.slice(0, leftCount)),
196
+ ...(prepared.publicPlan.right === undefined
197
+ ? []
198
+ : [
199
+ createReplayEvidence(prepared, executed, "right", executed.outcomes.slice(leftCount)),
200
+ ]),
201
+ ];
202
+ };
203
+ const buildReplayOutput = (prepared, executed, sourceEvidence) => {
204
+ const subject = prepared.publicPlan.left.modules[0];
205
+ const evidence = createEvidence(subject === undefined
206
+ ? undefined
207
+ : {
208
+ path: subject.canonical_path,
209
+ sha256: subject.sha256,
210
+ format: "javascript",
211
+ }, {
212
+ id: "rea-javascript-replay",
213
+ name: "REA isolated JavaScript replay",
214
+ version: "1",
215
+ }, {
216
+ predicateType: "javascript-controlled-replay",
217
+ operation: OPERATION,
218
+ parameters: {
219
+ replay_plan: jsonValueSchema.parse(JSON.parse(JSON.stringify(prepared.publicPlan))),
220
+ },
221
+ result: jsonValueSchema.parse(JSON.parse(JSON.stringify(executed))),
222
+ rawResult: jsonValueSchema.parse(JSON.parse(JSON.stringify({
223
+ cases: prepared.publicPlan.cases,
224
+ outcomes: executed.outcomes,
225
+ stderr: executed.stderr,
226
+ }))),
227
+ confidence: executed.comparison === undefined ? "observed" : "derived",
228
+ authority: "controlled-replay",
229
+ environment: {
230
+ id: prepared.publicPlan.plan_digest,
231
+ platform: process.platform,
232
+ architecture: process.arch,
233
+ isolation: "container",
234
+ },
235
+ limitations: executed.limitations,
236
+ locations: prepared.publicPlan.left.modules.map((module) => ({
237
+ kind: "artifact-path",
238
+ path: module.canonical_path,
239
+ })),
240
+ evidenceLinks: sourceEvidence.map(({ evidence_id: id }) => id),
241
+ });
242
+ return asJson({
243
+ phase: "execute",
244
+ plan: null,
245
+ source_evidence: sourceEvidence.map((item) => replayEvidenceSchema.parse(item)),
246
+ evidence: replayEvidenceSchema.parse(evidence),
247
+ });
248
+ };
208
249
  const createReplayEvidence = (prepared, executed, side, outcomes) => {
209
250
  const manifest = side === "left" ? prepared.publicPlan.left : prepared.publicPlan.right;
210
251
  const subject = manifest?.modules[0];
@@ -249,18 +290,3 @@ const createReplayEvidence = (prepared, executed, side, outcomes) => {
249
290
  });
250
291
  };
251
292
  const asJson = (value) => jsonValueSchema.parse(JSON.parse(JSON.stringify(controlledReplayOutputSchema.parse(value))));
252
- const permissionRequest = (input, policy) => ({
253
- capability: "javascript_replay",
254
- roots: [...input.left.modules, ...(input.right?.modules ?? [])].map(({ path }) => path),
255
- executables: [
256
- policy.nodePath,
257
- policy.bubblewrapPath,
258
- policy.systemdRunPath,
259
- policy.systemctlPath,
260
- policy.shellPath,
261
- ],
262
- environment_names: [],
263
- network: "none",
264
- mount: true,
265
- operation_identity: `${OPERATION}:${input.plan_digest ?? "plan"}`,
266
- });
@@ -26,6 +26,16 @@ const SUPPORTED_RELEASES = {
26
26
  file_hash: "92d780b61cab38eda95dc4112dee4fbeb0920f46",
27
27
  },
28
28
  };
29
+ /** Describe the release and package-manager commands without downloading or writing. */
30
+ export const linuxHopperInstallDisclosure = (family, isRoot) => {
31
+ const release = SUPPORTED_RELEASES[family];
32
+ return {
33
+ downloadUrl: release.filename,
34
+ expectedBytes: Number(release.file_length),
35
+ expectedSha1: release.file_hash,
36
+ commands: linuxPackageManagerCommands(family, `<verified-hopper.${family}>`, isRoot).map(({ executable, args }) => `${executable} ${args.join(" ")}`),
37
+ };
38
+ };
29
39
  /** Parse an os-release document without executing its shell syntax. */
30
40
  export const parseLinuxDistribution = (text) => {
31
41
  const fields = new Map();