rea-agents 1.7.0 → 2.0.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 (156) hide show
  1. package/README.md +38 -31
  2. package/dist/application/CapabilityInventory.js +18 -0
  3. package/dist/application/CrossVersionInvestigation.js +10 -6
  4. package/dist/application/Doctor.js +56 -6
  5. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  6. package/dist/application/ElectronBoundaryGraph.js +9 -0
  7. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  8. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  9. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  10. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  11. package/dist/application/EnhancedTools.js +38 -13
  12. package/dist/application/InvestigationProviders.js +30 -0
  13. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  14. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  15. package/dist/application/JavaScriptApplicationService.js +74 -0
  16. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  17. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  18. package/dist/application/JavaScriptArtifactAnalysis.js +7 -1
  19. package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
  20. package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
  21. package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
  22. package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
  23. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  24. package/dist/application/JavaScriptReplayService.js +266 -0
  25. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  26. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  27. package/dist/application/ManagedApplicationGraphService.js +55 -0
  28. package/dist/application/ManagedMemberComparisonService.js +122 -0
  29. package/dist/application/ManagedNativeVerificationService.js +39 -0
  30. package/dist/application/ManagedReconstructionService.js +58 -0
  31. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  32. package/dist/application/PermissionAuthority.js +1 -1
  33. package/dist/application/ProcessSampling.js +15 -11
  34. package/dist/application/ProjectPermissionStore.js +1 -0
  35. package/dist/application/runtime.js +2 -0
  36. package/dist/browser/CdpCaptureEvents.js +49 -0
  37. package/dist/browser/CdpElectronInspection.js +106 -162
  38. package/dist/browser/CdpElectronProvider.js +5 -1
  39. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  40. package/dist/browser/CdpElectronScripts.js +94 -0
  41. package/dist/browser/CdpElectronWorkers.js +56 -0
  42. package/dist/browser/CdpPageCapture.js +8 -4
  43. package/dist/catalogIdentity.js +4 -1
  44. package/dist/cli.js +388 -0
  45. package/dist/cliApplicationCommands.js +62 -0
  46. package/dist/cliCommandNames.js +13 -0
  47. package/dist/cliElectronCommands.js +103 -1
  48. package/dist/cliJsonInput.js +54 -0
  49. package/dist/config.js +83 -0
  50. package/dist/contracts/applicationToolContracts.js +91 -0
  51. package/dist/contracts/artifactToolContracts.js +5 -9
  52. package/dist/contracts/browserToolContracts.js +14 -62
  53. package/dist/contracts/electronToolContracts.js +45 -19
  54. package/dist/contracts/errorSchemas.js +1 -0
  55. package/dist/contracts/investigationExamples.js +0 -6
  56. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  57. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  58. package/dist/contracts/managedToolContracts.js +153 -0
  59. package/dist/contracts/managedWorkflowExamples.js +346 -0
  60. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  61. package/dist/contracts/nativeToolContracts.js +2 -6
  62. package/dist/contracts/promptContracts.js +18 -2
  63. package/dist/contracts/toolContractExamples.js +10 -25
  64. package/dist/contracts/toolContracts.js +47 -45
  65. package/dist/contracts/toolEffects.js +160 -0
  66. package/dist/contracts/toolOutputSchemas.js +46 -31
  67. package/dist/doctorRuntime.js +42 -1
  68. package/dist/domain/artifactComparison.js +3 -4
  69. package/dist/domain/browserObservation.js +3 -0
  70. package/dist/domain/bundleComparison.js +4 -4
  71. package/dist/domain/electronObservation.js +13 -0
  72. package/dist/domain/electronStaticAnalysis.js +9 -0
  73. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  74. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  75. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  76. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  77. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  78. package/dist/domain/errors.js +67 -1
  79. package/dist/domain/functionComparisonSchemas.js +3 -8
  80. package/dist/domain/inputIssueProjection.js +52 -0
  81. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  82. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  83. package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
  84. package/dist/domain/javascriptApplicationGraph.js +2 -1
  85. package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
  86. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  87. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  88. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  89. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  90. package/dist/domain/javascriptFeatureSeed.js +140 -0
  91. package/dist/domain/javascriptFeatureTrace.js +232 -0
  92. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  93. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  94. package/dist/domain/javascriptNativeHandoff.js +85 -0
  95. package/dist/domain/javascriptReplay.js +361 -0
  96. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  97. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  98. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  99. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  100. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  101. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  102. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  103. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  104. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  105. package/dist/domain/javascriptStaticAnalysis.js +31 -43
  106. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  107. package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
  108. package/dist/domain/javascriptStaticAnalysisState.js +6 -0
  109. package/dist/domain/managedApplicationGraph.js +556 -0
  110. package/dist/domain/managedArtifact.js +444 -0
  111. package/dist/domain/managedMemberComparison.js +565 -0
  112. package/dist/domain/managedNativeVerification.js +408 -0
  113. package/dist/domain/managedReconstruction.js +183 -0
  114. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  115. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  116. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  117. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  118. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  119. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  120. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  121. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  122. package/dist/dotnet/ManagedPeReader.js +157 -0
  123. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  124. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  125. package/dist/generatedPackageMetadata.js +2 -2
  126. package/dist/ghidra/GhidraProvider.js +6 -0
  127. package/dist/main.js +14 -0
  128. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  129. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  130. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  131. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  132. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  133. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  134. package/dist/server/createServer.js +52 -0
  135. package/dist/server/registerApplicationTools.js +115 -0
  136. package/dist/server/registerArtifactComparisonTool.js +16 -9
  137. package/dist/server/registerBrowserTools.js +33 -8
  138. package/dist/server/registerBundleComparisonTool.js +17 -20
  139. package/dist/server/registerElectronTools.js +45 -3
  140. package/dist/server/registerEnhancedTools.js +58 -13
  141. package/dist/server/registerEvidenceTools.js +5 -1
  142. package/dist/server/registerFunctionComparisonTool.js +14 -7
  143. package/dist/server/registerInvestigationTools.js +21 -17
  144. package/dist/server/registerManagedTools.js +6 -0
  145. package/dist/server/registerManagedWorkflowTools.js +277 -0
  146. package/dist/server/registerOfficialTools.js +8 -8
  147. package/dist/server/registerProcessComparisonTool.js +25 -47
  148. package/dist/server/registerSessionStatusTool.js +7 -7
  149. package/dist/server/registerSessionTools.js +51 -86
  150. package/dist/server/sessionEvidence.js +14 -19
  151. package/dist/server/toolInputValidation.js +77 -0
  152. package/dist/server/toolRegistrationOptions.js +79 -2
  153. package/dist/server/toolResult.js +25 -17
  154. package/package.json +12 -4
  155. package/scripts/rea.mjs +1 -1
  156. package/skills/rea-analysis/SKILL.md +59 -3
@@ -0,0 +1,242 @@
1
+ import { artifactLocalIdentity, javascriptAnalysisCoverage, } from "./JavaScriptArtifactGraphContext.js";
2
+ import { astObservationEvidence } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { collectElectronIpcRecords, unambiguousElectronIpcPairings, } from "./ElectronBoundaryAnalysis.js";
4
+ import { addElectronAstEdge, addElectronInferenceEdge, electronFindingSourceNode, electronObservationIdentity, electronRangeContains, electronRangeKey, } from "./ElectronBoundaryGraphContext.js";
5
+ /** Project IPC syntax, handler locations, validations, and exact pairings. */
6
+ export const addElectronIpcBoundaries = (context) => {
7
+ const state = {
8
+ context,
9
+ records: collectElectronIpcRecords(context.analysis),
10
+ channels: new Map(),
11
+ handlers: new Map(),
12
+ sources: new Map(),
13
+ };
14
+ for (const record of state.records)
15
+ addIpcRecord(state, record);
16
+ addValidationObservations(state);
17
+ addLiteralPairings(state);
18
+ };
19
+ const addIpcRecord = (state, record) => {
20
+ const { context } = state;
21
+ const coverage = coverageFor(context, record.file);
22
+ if (coverage === undefined)
23
+ return;
24
+ const source = electronFindingSourceNode(context, record.file, record.finding.module_key);
25
+ if (source === undefined)
26
+ return;
27
+ state.sources.set(record.key, source);
28
+ const channel = addChannelNode(context, record, coverage);
29
+ state.channels.set(record.key, channel);
30
+ if (record.finding.mode === "listen" || record.finding.mode === "handle") {
31
+ const handler = addHandlerNode({
32
+ context,
33
+ record,
34
+ source,
35
+ channel,
36
+ coverage,
37
+ });
38
+ state.handlers.set(record.key, handler);
39
+ return;
40
+ }
41
+ addElectronInferenceEdge(context, {
42
+ source,
43
+ target: channel,
44
+ file: record.file,
45
+ range: record.finding.location,
46
+ coverage,
47
+ relation: record.finding.mode === "invoke" ? "invokes" : "sends",
48
+ operation: "map-ipc-transmission",
49
+ properties: {
50
+ side: record.finding.side,
51
+ operation: record.finding.operation,
52
+ channel: record.finding.channel,
53
+ channel_expression: record.finding.channel_expression,
54
+ resolution: record.finding.channel === null ? "dynamic" : "literal",
55
+ },
56
+ });
57
+ };
58
+ const addChannelNode = (context, record, coverage) => {
59
+ const { finding, file } = record;
60
+ const identityKey = finding.channel === null
61
+ ? `dynamic:${record.key}`
62
+ : `literal:${finding.channel}`;
63
+ return context.accumulator.addNode({
64
+ kind: "ipc-channel",
65
+ identity: electronObservationIdentity(context, "electron-ipc-channel", identityKey),
66
+ observations: [
67
+ {
68
+ label: finding.channel ?? finding.channel_expression,
69
+ properties: {
70
+ side: finding.side,
71
+ operation: finding.operation,
72
+ mode: finding.mode,
73
+ channel: finding.channel,
74
+ channel_expression: finding.channel_expression,
75
+ resolution: finding.channel === null ? "dynamic" : "literal",
76
+ },
77
+ evidence: astObservationEvidence({
78
+ sha256: file.sha256,
79
+ path: file.path,
80
+ range: finding.location,
81
+ operation: "observe-ipc-channel-expression",
82
+ coverage,
83
+ limitations: finding.channel === null
84
+ ? [
85
+ "The channel expression is dynamic; its runtime value and pairings remain unknown.",
86
+ ]
87
+ : [],
88
+ }),
89
+ },
90
+ ],
91
+ });
92
+ };
93
+ const addHandlerNode = (input) => {
94
+ const { context, record, source, channel, coverage } = input;
95
+ const { finding, file } = record;
96
+ const handlerRange = finding.handler_location ?? finding.location;
97
+ const handler = context.accumulator.addNode({
98
+ kind: "ipc-handler",
99
+ identity: artifactLocalIdentity(file.sha256, "electron-ipc-handler", `${finding.side}:${finding.operation}:${finding.channel ?? finding.channel_expression ?? "[missing]"}:${electronRangeKey(handlerRange)}`),
100
+ observations: [
101
+ {
102
+ label: finding.channel,
103
+ properties: {
104
+ side: finding.side,
105
+ operation: finding.operation,
106
+ mode: finding.mode,
107
+ channel: finding.channel,
108
+ channel_expression: finding.channel_expression,
109
+ handler_kind: finding.handler_kind,
110
+ handler_location: finding.handler_location,
111
+ },
112
+ evidence: astObservationEvidence({
113
+ sha256: file.sha256,
114
+ path: file.path,
115
+ range: handlerRange,
116
+ operation: "observe-ipc-handler",
117
+ coverage,
118
+ }),
119
+ },
120
+ ],
121
+ });
122
+ addElectronAstEdge(context, {
123
+ source,
124
+ target: handler,
125
+ file,
126
+ range: handlerRange,
127
+ coverage,
128
+ relation: "contains",
129
+ operation: "locate-ipc-handler",
130
+ properties: { side: finding.side, operation: finding.operation },
131
+ });
132
+ addElectronInferenceEdge(context, {
133
+ source: handler,
134
+ target: channel,
135
+ file,
136
+ range: finding.location,
137
+ coverage,
138
+ relation: "handles",
139
+ operation: "map-ipc-handler-registration",
140
+ properties: {
141
+ channel: finding.channel,
142
+ channel_expression: finding.channel_expression,
143
+ resolution: finding.channel === null ? "dynamic" : "literal",
144
+ },
145
+ });
146
+ return handler;
147
+ };
148
+ const addLiteralPairings = (state) => {
149
+ for (const pairing of unambiguousElectronIpcPairings(state.records)) {
150
+ const source = state.sources.get(pairing.transmission.key);
151
+ const handler = state.handlers.get(pairing.handler.key);
152
+ const coverage = coverageFor(state.context, pairing.transmission.file);
153
+ if (source === undefined || handler === undefined || coverage === undefined)
154
+ continue;
155
+ addElectronInferenceEdge(state.context, {
156
+ source,
157
+ target: handler,
158
+ file: pairing.transmission.file,
159
+ range: pairing.transmission.finding.location,
160
+ coverage,
161
+ relation: pairing.transmission.finding.mode === "invoke" ? "invokes" : "sends",
162
+ operation: "pair-ipc-literal-channel",
163
+ properties: {
164
+ channel: pairing.transmission.finding.channel,
165
+ pairing_basis: "unique-exact-literal-channel",
166
+ handler_path: pairing.handler.file.path,
167
+ handler_operation: pairing.handler.finding.operation,
168
+ },
169
+ limitations: [
170
+ "Literal channel equality does not prove that the handler is registered or reachable at runtime.",
171
+ ],
172
+ });
173
+ }
174
+ };
175
+ const addValidationObservations = (state) => {
176
+ for (const analyzed of state.context.analysis.files) {
177
+ const { file, javascript } = analyzed;
178
+ if (javascript === null)
179
+ continue;
180
+ const coverage = javascriptAnalysisCoverage(javascript, state.context.input);
181
+ for (const validation of javascript.electron.sender_validations) {
182
+ const handlerRecord = containingHandler(state.records, file, validation);
183
+ const target = handlerRecord === undefined
184
+ ? electronFindingSourceNode(state.context, file, validation.module_key)
185
+ : state.handlers.get(handlerRecord.key);
186
+ if (target !== undefined)
187
+ addValidationObservation({
188
+ context: state.context,
189
+ file,
190
+ validation,
191
+ target,
192
+ coverage,
193
+ });
194
+ }
195
+ }
196
+ };
197
+ const addValidationObservation = (input) => {
198
+ const { context, file, validation, target, coverage } = input;
199
+ context.accumulator.addNode({
200
+ kind: target.kind,
201
+ identity: target.identity,
202
+ observations: [
203
+ {
204
+ label: `${validation.subject} validation candidate`,
205
+ properties: {
206
+ fact_kind: "sender-validation-candidate",
207
+ subject: validation.subject,
208
+ mechanism: validation.mechanism,
209
+ expected: validation.expected,
210
+ enforcement: validation.enforcement,
211
+ },
212
+ evidence: astObservationEvidence({
213
+ sha256: file.sha256,
214
+ path: file.path,
215
+ range: validation.location,
216
+ operation: "observe-ipc-sender-validation-candidate",
217
+ coverage,
218
+ limitations: [
219
+ "A comparison or string check is visible, but static syntax alone does not prove rejection, ordering, or complete sender validation.",
220
+ ],
221
+ }),
222
+ },
223
+ ],
224
+ });
225
+ };
226
+ const containingHandler = (records, file, validation) => records
227
+ .filter((record) => record.file.path === file.path &&
228
+ record.finding.handler_location !== null &&
229
+ electronRangeContains(record.finding.handler_location, validation.location))
230
+ .sort((left, right) => rangeMagnitude(left.finding.handler_location) -
231
+ rangeMagnitude(right.finding.handler_location))[0];
232
+ const rangeMagnitude = (range) => range === null
233
+ ? Number.MAX_SAFE_INTEGER
234
+ : (range.end.line - range.start.line) * 1_000_000 +
235
+ range.end.column -
236
+ range.start.column;
237
+ const coverageFor = (context, file) => {
238
+ const javascript = context.analysis.files.find(({ file: candidate }) => candidate.path === file.path)?.javascript;
239
+ return javascript === null || javascript === undefined
240
+ ? undefined
241
+ : javascriptAnalysisCoverage(javascript, context.input);
242
+ };
@@ -0,0 +1,86 @@
1
+ import { javascriptAnalysisCoverage, resolveArtifactPath, } from "./JavaScriptArtifactGraphContext.js";
2
+ import { astObservationEvidence } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { addElectronInferenceEdge, electronFindingSourceNode, electronObservationIdentity, electronRangeKey, } from "./ElectronBoundaryGraphContext.js";
4
+ /** Project JavaScript-requested native addon binding and re-export surfaces. */
5
+ export const addElectronNativeBoundaries = (context) => {
6
+ for (const analyzed of context.analysis.files) {
7
+ const { file, javascript } = analyzed;
8
+ if (javascript === null)
9
+ continue;
10
+ const coverage = javascriptAnalysisCoverage(javascript, context.input);
11
+ for (const value of javascript.electron.native_addon_bindings)
12
+ addNativeBinding({ context, file, value, coverage });
13
+ }
14
+ };
15
+ const addNativeBinding = (input) => {
16
+ const { context, file, value, coverage } = input;
17
+ const source = electronFindingSourceNode(context, file, value.module_key);
18
+ if (source === undefined)
19
+ return;
20
+ const resolved = resolveArtifactPath(value.specifier, file.path, context.filesByPath);
21
+ const addon = resolved === null ? undefined : context.fileNodes.get(resolved);
22
+ const binding = context.accumulator.addNode({
23
+ kind: "native-export",
24
+ identity: electronObservationIdentity(context, "javascript-requested-native-export", `${file.path}:${value.specifier}:${value.binding_kind}:${value.members.join("\0")}:${electronRangeKey(value.location)}`),
25
+ observations: [
26
+ {
27
+ label: value.members.join(", "),
28
+ properties: {
29
+ specifier: value.specifier,
30
+ binding_kind: value.binding_kind,
31
+ requested_members: value.members,
32
+ members_truncated: value.members_truncated,
33
+ resolved_path: resolved,
34
+ native_export_verification: "not-performed",
35
+ },
36
+ evidence: astObservationEvidence({
37
+ sha256: file.sha256,
38
+ path: file.path,
39
+ range: value.location,
40
+ operation: "observe-native-addon-binding",
41
+ coverage,
42
+ limitations: [
43
+ "JavaScript syntax identifies requested member names; it does not prove that the native binary exports or implements them.",
44
+ ],
45
+ }),
46
+ },
47
+ ],
48
+ });
49
+ addElectronInferenceEdge(context, {
50
+ source,
51
+ target: binding,
52
+ file,
53
+ range: value.location,
54
+ coverage,
55
+ relation: value.binding_kind === "re-export" ? "exposes" : "imports",
56
+ operation: "map-native-addon-binding",
57
+ properties: {
58
+ specifier: value.specifier,
59
+ binding_kind: value.binding_kind,
60
+ requested_members: value.members,
61
+ },
62
+ limitations: [
63
+ "Requested JavaScript members are not verified native exports.",
64
+ ],
65
+ });
66
+ if (addon === undefined || resolved === null)
67
+ return;
68
+ addElectronInferenceEdge(context, {
69
+ source: addon,
70
+ target: binding,
71
+ file,
72
+ range: value.location,
73
+ coverage,
74
+ relation: "exposes",
75
+ operation: "associate-native-addon-requested-exports",
76
+ properties: {
77
+ resolved_path: resolved,
78
+ requested_members: value.members,
79
+ verified: false,
80
+ },
81
+ confidence: "medium",
82
+ limitations: [
83
+ "The .node artifact path is resolved, but requested JavaScript members have not been verified against native symbols.",
84
+ ],
85
+ });
86
+ };
@@ -0,0 +1,199 @@
1
+ import { artifactLocalIdentity, createElectronRoleNode, javascriptAnalysisCoverage, linkElectronRoleToAsset, resolveArtifactPath, } from "./JavaScriptArtifactGraphContext.js";
2
+ import { astObservationEvidence } from "./JavaScriptArtifactGraphEvidence.js";
3
+ import { addElectronAstEdge, addElectronInferenceEdge, electronFindingSourceNode, electronRangeKey, } from "./ElectronBoundaryGraphContext.js";
4
+ /** Project BrowserWindow, contextBridge, preload, and utility-process facts. */
5
+ export const addElectronWindowBoundaries = (context) => {
6
+ for (const analyzed of context.analysis.files) {
7
+ const { file, javascript } = analyzed;
8
+ if (javascript === null)
9
+ continue;
10
+ const coverage = javascriptAnalysisCoverage(javascript, context.input);
11
+ for (const value of javascript.electron.browser_windows) {
12
+ const source = electronFindingSourceNode(context, file, value.module_key);
13
+ if (source !== undefined)
14
+ addBrowserWindow({ context, file, source, value, coverage });
15
+ }
16
+ for (const value of javascript.electron.context_bridge_apis) {
17
+ const source = electronFindingSourceNode(context, file, value.module_key);
18
+ if (source !== undefined)
19
+ addContextBridge({ context, file, source, value, coverage });
20
+ }
21
+ for (const value of javascript.electron.utility_processes) {
22
+ const source = electronFindingSourceNode(context, file, value.module_key);
23
+ if (source !== undefined)
24
+ addUtilityProcess({ context, file, source, value, coverage });
25
+ }
26
+ }
27
+ };
28
+ const addBrowserWindow = (input) => {
29
+ const { context, file, value, coverage } = input;
30
+ const window = context.accumulator.addNode({
31
+ kind: "browser-window",
32
+ identity: artifactLocalIdentity(file.sha256, "electron-browser-window", electronRangeKey(value.location)),
33
+ observations: [
34
+ {
35
+ label: `BrowserWindow@${electronRangeKey(value.location)}`,
36
+ properties: {
37
+ source_path: file.path,
38
+ options_status: value.options_status,
39
+ web_preferences_status: value.web_preferences_status,
40
+ web_preferences: value.web_preferences,
41
+ omitted_web_preferences: value.omitted_web_preferences,
42
+ preload_path: value.preload_path,
43
+ absence_means_default: false,
44
+ },
45
+ evidence: astObservationEvidence({
46
+ sha256: file.sha256,
47
+ path: file.path,
48
+ range: value.location,
49
+ operation: "observe-browser-window-construction",
50
+ coverage,
51
+ limitations: [
52
+ "Only explicitly present webPreferences are reported; omitted properties are not interpreted as Electron defaults.",
53
+ ],
54
+ }),
55
+ },
56
+ ],
57
+ });
58
+ addElectronAstEdge(context, {
59
+ source: input.source,
60
+ target: window,
61
+ file,
62
+ range: value.location,
63
+ coverage,
64
+ relation: "contains",
65
+ operation: "locate-browser-window-construction",
66
+ properties: { source_path: file.path },
67
+ });
68
+ addWindowPreload(input, window);
69
+ };
70
+ const addWindowPreload = (input, window) => {
71
+ const declared = input.value.preload_path;
72
+ if (declared === null)
73
+ return;
74
+ const resolved = resolveArtifactPath(declared, input.file.path, input.context.filesByPath);
75
+ const role = createElectronRoleNode(input.context, {
76
+ kind: "electron-preload",
77
+ anchor: input.file,
78
+ declaredPath: declared,
79
+ resolvedPath: resolved,
80
+ mechanism: "BrowserWindow:webPreferences.preload",
81
+ range: input.value.location,
82
+ coverage: input.coverage,
83
+ });
84
+ addElectronInferenceEdge(input.context, {
85
+ source: window,
86
+ target: role,
87
+ file: input.file,
88
+ range: input.value.location,
89
+ coverage: input.coverage,
90
+ relation: "loads",
91
+ operation: "associate-browser-window-preload",
92
+ properties: { declared_path: declared, resolved_path: resolved },
93
+ });
94
+ linkElectronRoleToAsset(input.context, {
95
+ role,
96
+ anchor: input.file,
97
+ path: resolved,
98
+ range: input.value.location,
99
+ coverage: input.coverage,
100
+ });
101
+ };
102
+ const addContextBridge = (input) => {
103
+ const { context, file, value, coverage } = input;
104
+ const bridge = context.accumulator.addNode({
105
+ kind: "context-bridge-api",
106
+ identity: artifactLocalIdentity(file.sha256, "electron-context-bridge", `${value.world}:${value.api_key ?? value.api_key_expression ?? "[missing]"}:${electronRangeKey(value.location)}`),
107
+ observations: [
108
+ {
109
+ label: value.api_key,
110
+ properties: {
111
+ world: value.world,
112
+ world_id: value.world_id,
113
+ api_key: value.api_key,
114
+ api_key_expression: value.api_key_expression,
115
+ api_status: value.api_status,
116
+ members: value.members,
117
+ unknown_members: value.unknown_members,
118
+ omitted_members: value.omitted_members,
119
+ },
120
+ evidence: astObservationEvidence({
121
+ sha256: file.sha256,
122
+ path: file.path,
123
+ range: value.location,
124
+ operation: "observe-context-bridge-api",
125
+ coverage,
126
+ }),
127
+ },
128
+ ],
129
+ });
130
+ addElectronInferenceEdge(context, {
131
+ source: input.source,
132
+ target: bridge,
133
+ file,
134
+ range: value.location,
135
+ coverage,
136
+ relation: "exposes",
137
+ operation: "map-context-bridge-exposure",
138
+ properties: {
139
+ world: value.world,
140
+ api_key: value.api_key,
141
+ resolution: value.api_key === null ? "dynamic" : "literal",
142
+ },
143
+ });
144
+ };
145
+ const addUtilityProcess = (input) => {
146
+ const { context, file, value, coverage } = input;
147
+ const utility = context.accumulator.addNode({
148
+ kind: "electron-utility",
149
+ identity: artifactLocalIdentity(file.sha256, "electron-utility-process", `${value.module_path ?? value.module_expression ?? "[missing]"}:${electronRangeKey(value.location)}`),
150
+ observations: [
151
+ {
152
+ label: value.service_name ?? value.module_path,
153
+ properties: {
154
+ module_path: value.module_path,
155
+ module_expression: value.module_expression,
156
+ service_name: value.service_name,
157
+ },
158
+ evidence: astObservationEvidence({
159
+ sha256: file.sha256,
160
+ path: file.path,
161
+ range: value.location,
162
+ operation: "observe-utility-process-fork",
163
+ coverage,
164
+ }),
165
+ },
166
+ ],
167
+ });
168
+ addElectronInferenceEdge(context, {
169
+ source: input.source,
170
+ target: utility,
171
+ file,
172
+ range: value.location,
173
+ coverage,
174
+ relation: "loads",
175
+ operation: "map-utility-process-launch",
176
+ properties: {
177
+ declared_path: value.module_path,
178
+ expression: value.module_expression,
179
+ },
180
+ });
181
+ if (value.module_path === null)
182
+ return;
183
+ const resolved = resolveArtifactPath(value.module_path, file.path, context.filesByPath);
184
+ const target = resolved === null
185
+ ? undefined
186
+ : (context.assetNodes.get(resolved) ?? context.fileNodes.get(resolved));
187
+ if (target === undefined)
188
+ return;
189
+ addElectronInferenceEdge(context, {
190
+ source: utility,
191
+ target,
192
+ file,
193
+ range: value.location,
194
+ coverage,
195
+ relation: "maps_to",
196
+ operation: "resolve-utility-process-entrypoint",
197
+ properties: { resolved_path: resolved },
198
+ });
199
+ };
@@ -5,71 +5,96 @@ import { err, ok } from "../domain/result.js";
5
5
  import { categorizeSwiftTypes, discoverObjcClasses, discoverObjcProtocols, discoverSwiftClasses, } from "../domain/symbolAnalysis.js";
6
6
  /**
7
7
  * Composes direct provider operations into bounded reverse-engineering tools.
8
- * Inputs are parsed again at this application boundary even though MCP normally
9
- * validates them, allowing direct callers to use the same service safely.
8
+ * Direct callers use `execute`; adapters that own validation use
9
+ * `executeValidated` so an MCP input is parsed exactly once.
10
10
  */
11
11
  export class EnhancedTools {
12
12
  analysis;
13
13
  constructor(analysis) {
14
14
  this.analysis = analysis;
15
15
  }
16
- /** Parse inputs again at the direct-call boundary, then dispatch exhaustively. */
16
+ /** Parse an untrusted direct-call input once, then dispatch exhaustively. */
17
17
  execute(name, input, signal) {
18
18
  switch (name) {
19
19
  case "swift_classes": {
20
20
  const parsed = enhancedInputSchemas.swift_classes.safeParse(input);
21
21
  return parsed.success
22
- ? this.#swiftClasses(parsed.data.pattern, signal)
22
+ ? this.executeValidated({ name, input: parsed.data }, signal)
23
23
  : invalidInput(name, parsed.error);
24
24
  }
25
25
  case "get_objc_classes": {
26
26
  const parsed = enhancedInputSchemas.get_objc_classes.safeParse(input);
27
27
  return parsed.success
28
- ? this.#objcClasses(parsed.data.pattern, signal)
28
+ ? this.executeValidated({ name, input: parsed.data }, signal)
29
29
  : invalidInput(name, parsed.error);
30
30
  }
31
31
  case "get_objc_protocols":
32
- return this.#objcProtocols(signal);
32
+ return this.executeValidated({ name, input: {} }, signal);
33
33
  case "batch_decompile": {
34
34
  const parsed = enhancedInputSchemas.batch_decompile.safeParse(input);
35
35
  return parsed.success
36
- ? this.#batchDecompile(parsed.data.addresses, signal)
36
+ ? this.executeValidated({ name, input: parsed.data }, signal)
37
37
  : invalidInput(name, parsed.error);
38
38
  }
39
39
  case "get_call_graph": {
40
40
  const parsed = enhancedInputSchemas.get_call_graph.safeParse(input);
41
41
  return parsed.success
42
- ? this.#callGraph(parsed.data, signal)
42
+ ? this.executeValidated({ name, input: parsed.data }, signal)
43
43
  : invalidInput(name, parsed.error);
44
44
  }
45
45
  case "analyze_swift_types":
46
- return this.#analyzeSwiftTypes(signal);
46
+ return this.executeValidated({ name, input: {} }, signal);
47
47
  case "find_xrefs_to_name": {
48
48
  const parsed = enhancedInputSchemas.find_xrefs_to_name.safeParse(input);
49
49
  return parsed.success
50
- ? this.#findXrefs(parsed.data.name, signal)
50
+ ? this.executeValidated({ name, input: parsed.data }, signal)
51
51
  : invalidInput(name, parsed.error);
52
52
  }
53
53
  case "binary_overview": {
54
54
  const parsed = enhancedInputSchemas.binary_overview.safeParse(input);
55
55
  return parsed.success
56
- ? this.#binaryOverview(parsed.data, signal)
56
+ ? this.executeValidated({ name, input: parsed.data }, signal)
57
57
  : invalidInput(name, parsed.error);
58
58
  }
59
59
  case "analyze_function": {
60
60
  const parsed = enhancedInputSchemas.analyze_function.safeParse(input);
61
61
  return parsed.success
62
- ? this.#analyzeFunction(parsed.data, signal)
62
+ ? this.executeValidated({ name, input: parsed.data }, signal)
63
63
  : invalidInput(name, parsed.error);
64
64
  }
65
65
  case "trace_feature": {
66
66
  const parsed = enhancedInputSchemas.trace_feature.safeParse(input);
67
67
  return parsed.success
68
- ? this.#traceFeature(parsed.data, signal)
68
+ ? this.executeValidated({ name, input: parsed.data }, signal)
69
69
  : invalidInput(name, parsed.error);
70
70
  }
71
71
  }
72
72
  }
73
+ /** Dispatch input already parsed by a trusted adapter boundary. */
74
+ executeValidated(call, signal) {
75
+ switch (call.name) {
76
+ case "swift_classes":
77
+ return this.#swiftClasses(call.input.pattern, signal);
78
+ case "get_objc_classes":
79
+ return this.#objcClasses(call.input.pattern, signal);
80
+ case "get_objc_protocols":
81
+ return this.#objcProtocols(signal);
82
+ case "batch_decompile":
83
+ return this.#batchDecompile(call.input.addresses, signal);
84
+ case "get_call_graph":
85
+ return this.#callGraph(call.input, signal);
86
+ case "analyze_swift_types":
87
+ return this.#analyzeSwiftTypes(signal);
88
+ case "find_xrefs_to_name":
89
+ return this.#findXrefs(call.input.name, signal);
90
+ case "binary_overview":
91
+ return this.#binaryOverview(call.input, signal);
92
+ case "analyze_function":
93
+ return this.#analyzeFunction(call.input, signal);
94
+ case "trace_feature":
95
+ return this.#traceFeature(call.input, signal);
96
+ }
97
+ }
73
98
  async #analyzeFunction(input, signal) {
74
99
  const result = await this.#call("analyze_function", input, signal);
75
100
  return result.ok ? parseFunctionDossier(result.value) : result;
@@ -15,6 +15,36 @@ export const ARTIFACT_GRAPH_PROVIDER = {
15
15
  name: "REA safe artifact graph provider",
16
16
  version: "1",
17
17
  };
18
+ /** Provider identity for execution-free managed PE/CLI analysis. */
19
+ export const MANAGED_STATIC_PROVIDER = {
20
+ id: "rea-dotnet-static",
21
+ name: "REA managed static analysis provider",
22
+ version: "1",
23
+ };
24
+ /** Provider identity for deterministic managed-code comparison workflows. */
25
+ export const MANAGED_WORKFLOW_PROVIDER = {
26
+ id: "rea-dotnet-workflows",
27
+ name: "REA managed-code workflows",
28
+ version: "1",
29
+ };
30
+ /** Provider identity for deterministic static JavaScript application analysis. */
31
+ export const JAVASCRIPT_APPLICATION_PROVIDER = {
32
+ id: "rea-javascript-application",
33
+ name: "REA JavaScript application analyzer",
34
+ version: "1",
35
+ };
36
+ /** Provider identity for static/passive-runtime JavaScript reconciliation. */
37
+ export const JAVASCRIPT_RUNTIME_RECONCILIATION_PROVIDER = {
38
+ id: "rea-javascript-runtime-reconciliation",
39
+ name: "REA JavaScript runtime reconciliation",
40
+ version: "1",
41
+ };
42
+ /** Provider identity for graph-native feature tracing and version comparison. */
43
+ export const JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER = {
44
+ id: "rea-javascript-application-workflows",
45
+ name: "REA JavaScript application workflows",
46
+ version: "1",
47
+ };
18
48
  /** Provider identity for deterministic cross-artifact comparisons. */
19
49
  export const ARTIFACT_COMPARISON_PROVIDER = {
20
50
  id: "rea-artifact-comparison",