rea-agents 2.3.0 → 2.5.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 (209) hide show
  1. package/README.md +80 -18
  2. package/bridge/ghidra/ReaGhidraBridge.java +48 -1
  3. package/bridge/hopper_bridge.py +259 -24
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  6. package/dist/application/ArtifactExtraction.js +5 -1
  7. package/dist/application/ArtifactGraphConstruction.js +3 -15
  8. package/dist/application/ArtifactInventory/classify.js +5 -1
  9. package/dist/application/ArtifactInventory/reader.js +2 -0
  10. package/dist/application/BinarySession.js +15 -4
  11. package/dist/application/BinarySessionStatus.js +32 -1
  12. package/dist/application/BrowserEvidence.js +14 -0
  13. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  14. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  15. package/dist/application/BrowserScenarioEvidence.js +40 -0
  16. package/dist/application/CallPathTracing.js +160 -0
  17. package/dist/application/CapabilityInventory.js +79 -15
  18. package/dist/application/CompositeProvider.js +10 -1
  19. package/dist/application/DirectAnalysis.js +7 -2
  20. package/dist/application/EnhancedLiteralTracing.js +118 -0
  21. package/dist/application/EnhancedTools.js +35 -121
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  24. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  25. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  26. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  27. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  29. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  30. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  31. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  32. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  33. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  34. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  35. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  36. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  37. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  38. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  39. package/dist/application/ProcessCaptureLifecycle.js +1 -0
  40. package/dist/application/ProcessCheckpoints.js +5 -0
  41. package/dist/application/ProcessHarness.js +45 -34
  42. package/dist/application/ProcessNormalization.js +6 -0
  43. package/dist/application/ProcessReactiveCoordinator.js +10 -3
  44. package/dist/application/ProcessReactiveEffects.js +15 -1
  45. package/dist/application/ProcessReactiveHarness.js +100 -0
  46. package/dist/application/ProcessReactiveObservations.js +101 -16
  47. package/dist/application/ProjectPermissionStore.js +1 -0
  48. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  49. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  50. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  51. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  52. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  53. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  54. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  55. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  56. package/dist/application/ReconstructionReadinessService.js +48 -0
  57. package/dist/artifacts/ArtifactProvider.js +87 -22
  58. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  59. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  60. package/dist/browser/CdpBrowserProvider.js +2 -2
  61. package/dist/browser/CdpCaptureDocuments.js +57 -0
  62. package/dist/browser/CdpCaptureStorage.js +71 -29
  63. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  64. package/dist/browser/CdpEndpoint.js +8 -6
  65. package/dist/browser/CdpPageCapture.js +2 -0
  66. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  67. package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
  68. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  69. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  70. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  71. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  72. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  73. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  74. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  75. package/dist/browser/V8InspectorEndpoint.js +72 -0
  76. package/dist/browser/V8InspectorProvider.js +267 -0
  77. package/dist/catalogIdentity.js +8 -0
  78. package/dist/cli/artifactCommands.js +39 -0
  79. package/dist/cli/coreAnalysisCommands.js +37 -0
  80. package/dist/cli.js +4 -0
  81. package/dist/cliApplicationCommands.js +30 -2
  82. package/dist/cliBrowserAdvancedCommands.js +17 -5
  83. package/dist/cliBrowserCommands.js +1 -0
  84. package/dist/cliBrowserScenarioCommands.js +43 -0
  85. package/dist/cliCommandNames.js +8 -0
  86. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  87. package/dist/cliObservationOptions.js +4 -0
  88. package/dist/cliPolicyCommands.js +1 -0
  89. package/dist/config/environment.js +12 -0
  90. package/dist/config/parseConfig.js +88 -17
  91. package/dist/contracts/analysisSearchContract.js +14 -0
  92. package/dist/contracts/applicationToolContracts.js +86 -2
  93. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  94. package/dist/contracts/artifactToolContracts.js +22 -0
  95. package/dist/contracts/browserProviderToolContracts.js +7 -0
  96. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  97. package/dist/contracts/browserToolContracts.js +88 -98
  98. package/dist/contracts/enhancedInputs.js +25 -6
  99. package/dist/contracts/functionInstructionContract.js +8 -0
  100. package/dist/contracts/hopperMemoryContracts.js +22 -0
  101. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  102. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  103. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  104. package/dist/contracts/toolContractExamples.js +5 -0
  105. package/dist/contracts/toolContractTypes.js +1 -0
  106. package/dist/contracts/toolContracts.js +13 -21
  107. package/dist/contracts/toolEffects.js +21 -0
  108. package/dist/contracts/toolOutputSchemaGroups.js +70 -20
  109. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  110. package/dist/domain/analysisErrorProjection.js +8 -0
  111. package/dist/domain/analysisSnapshot.js +2 -0
  112. package/dist/domain/artifactGraph.js +2 -0
  113. package/dist/domain/artifactInspection.js +347 -0
  114. package/dist/domain/binaryTarget.js +3 -6
  115. package/dist/domain/browserCaptureComparison.js +82 -0
  116. package/dist/domain/browserObservation.js +8 -1
  117. package/dist/domain/browserObservationErrors.js +1 -0
  118. package/dist/domain/browserObservationSchemas.js +26 -0
  119. package/dist/domain/browserScenario.js +165 -0
  120. package/dist/domain/browserScenarioCapture.js +57 -0
  121. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  122. package/dist/domain/browserScenarioDiff.js +324 -0
  123. package/dist/domain/browserScenarioDiffValues.js +157 -0
  124. package/dist/domain/browserScenarioNormalization.js +45 -0
  125. package/dist/domain/browserScenarioValues.js +372 -0
  126. package/dist/domain/evidence.js +2 -0
  127. package/dist/domain/hopperValues.js +10 -0
  128. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  129. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  130. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  131. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  132. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  133. package/dist/domain/javascriptSemanticAnalysis.js +5 -4
  134. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  135. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  136. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  137. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  138. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  139. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  140. package/dist/domain/javascriptSemanticIr.js +21 -1
  141. package/dist/domain/javascriptSemanticObjects.js +115 -0
  142. package/dist/domain/javascriptSemanticPromises.js +290 -0
  143. package/dist/domain/javascriptSemanticResources.js +136 -0
  144. package/dist/domain/javascriptStaticAnalysis.js +4 -3
  145. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  146. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  147. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  148. package/dist/domain/processCapture.js +71 -1
  149. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  150. package/dist/domain/processCaptureValidation.js +2 -0
  151. package/dist/domain/processObservation.js +15 -6
  152. package/dist/domain/processReactiveRuntime.js +21 -10
  153. package/dist/domain/processReactiveScenario.js +1 -0
  154. package/dist/domain/processReactiveTransition.js +17 -10
  155. package/dist/domain/processScenario.js +21 -0
  156. package/dist/domain/providerCleanupError.js +11 -0
  157. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  158. package/dist/domain/reconstructionReadiness.js +121 -0
  159. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  160. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  161. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  162. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  163. package/dist/domain/sourceToBundleComparison.js +335 -0
  164. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  165. package/dist/domain/sourceToBundleSignals.js +202 -0
  166. package/dist/domain/webCaptureDiff.js +141 -27
  167. package/dist/domain/webScreenshot.js +1 -1
  168. package/dist/domain/zipPackageFormat.js +20 -0
  169. package/dist/generatedPackageMetadata.js +1 -1
  170. package/dist/ghidra/GhidraDefaults.js +1 -1
  171. package/dist/ghidra/GhidraFunctionValues.js +13 -1
  172. package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
  173. package/dist/hopper/HopperCleanup.js +158 -0
  174. package/dist/hopper/HopperClient.js +69 -99
  175. package/dist/hopper/HopperProvider.js +20 -1
  176. package/dist/hopper/HopperRequestQueue.js +187 -0
  177. package/dist/hopper/HopperResponseStream.js +46 -0
  178. package/dist/hopper/protocol.js +1 -0
  179. package/dist/main/transport.js +24 -24
  180. package/dist/main.js +6 -0
  181. package/dist/mcpDoctor.js +25 -2
  182. package/dist/native/NativeMacOSProvider.js +1 -0
  183. package/dist/process/ProcessCommandIdentity.js +19 -0
  184. package/dist/process/ProcessOwnership.js +11 -8
  185. package/dist/server/DynamicToolAvailability.js +70 -0
  186. package/dist/server/createServer.js +46 -1
  187. package/dist/server/lifecycleProgress.js +10 -0
  188. package/dist/server/permissionFailure.js +12 -0
  189. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  190. package/dist/server/registerApplicationTools/helpers.js +11 -7
  191. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  192. package/dist/server/registerApplicationTools/readiness.js +55 -0
  193. package/dist/server/registerApplicationTools.js +6 -0
  194. package/dist/server/registerArtifactTools.js +9 -1
  195. package/dist/server/registerBrowserScenarioTool.js +25 -0
  196. package/dist/server/registerBrowserTools.js +2 -2
  197. package/dist/server/registerCloseLifecycleTool.js +57 -0
  198. package/dist/server/registerEnhancedTools.js +16 -5
  199. package/dist/server/registerEvidenceResources.js +4 -0
  200. package/dist/server/registerEvidenceTools.js +9 -2
  201. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  202. package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
  203. package/dist/server/registerReconstructionReadinessResource.js +39 -0
  204. package/dist/server/registerSessionStatusTool.js +11 -8
  205. package/dist/server/registerSessionTools.js +11 -57
  206. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  207. package/package.json +12 -6
  208. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  209. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
@@ -0,0 +1,41 @@
1
+ import { collectJavaScriptSemanticAsyncEffects } from "./javascriptSemanticAsyncEffects.js";
2
+ import { collectJavaScriptSemanticCalls } from "./javascriptSemanticCalls.js";
3
+ import { collectJavaScriptSemanticChildProcesses } from "./javascriptSemanticChildProcesses.js";
4
+ import { collectJavaScriptSemanticFingerprints } from "./javascriptSemanticFingerprints.js";
5
+ import { collectJavaScriptSemanticDataEffects } from "./javascriptSemanticDataEffects.js";
6
+ import { collectJavaScriptSemanticPromises } from "./javascriptSemanticPromises.js";
7
+ import { collectJavaScriptSemanticObjects } from "./javascriptSemanticObjects.js";
8
+ import { collectJavaScriptSemanticResources } from "./javascriptSemanticResources.js";
9
+ /** Compose bounded semantic passes after lexical definitions are available. */
10
+ export const collectJavaScriptDerivedSemantics = (program, state, callables, parserPartial) => {
11
+ const calls = collectJavaScriptSemanticCalls(program, state, callables);
12
+ const promiseOperations = collectJavaScriptSemanticPromises(program, state, callables);
13
+ const asyncEffects = collectJavaScriptSemanticAsyncEffects(program, state, callables);
14
+ const childProcesses = collectJavaScriptSemanticChildProcesses(program, state, callables);
15
+ const dataEffects = collectJavaScriptSemanticDataEffects(program, state, callables);
16
+ const resourceOperations = collectJavaScriptSemanticResources(program, state, callables);
17
+ const objectOperations = collectJavaScriptSemanticObjects(program, state, callables);
18
+ const functionFingerprints = collectJavaScriptSemanticFingerprints({
19
+ state,
20
+ callables,
21
+ calls,
22
+ promises: promiseOperations,
23
+ events: asyncEffects.eventOperations,
24
+ timers: asyncEffects.timerOperations,
25
+ childProcessSpawns: childProcesses.childProcessSpawns,
26
+ childProcessInteractions: childProcesses.childProcessInteractions,
27
+ requestOperations: dataEffects.requestOperations,
28
+ resourceOperations,
29
+ parserPartial,
30
+ });
31
+ return {
32
+ ...calls,
33
+ promiseOperations,
34
+ ...asyncEffects,
35
+ ...childProcesses,
36
+ ...dataEffects,
37
+ resourceOperations,
38
+ objectOperations,
39
+ functionFingerprints,
40
+ };
41
+ };
@@ -0,0 +1,174 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as t from "@babel/types";
3
+ import { semanticCallableIdForNode } from "./javascriptSemanticProjection.js";
4
+ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
5
+ import { compareCodePoints } from "./javascriptStaticAnalysisHelpers.js";
6
+ /** Build bounded rename- and formatting-resistant components per callable. */
7
+ export const collectJavaScriptSemanticFingerprints = (input) => {
8
+ const { state, callables, calls, promises, events, timers, childProcessSpawns, childProcessInteractions, requestOperations, resourceOperations, parserPartial, } = input;
9
+ return callables.flatMap((callable) => {
10
+ if (callable.kind === "class")
11
+ return [];
12
+ const node = state.callableNodesById.get(callable.callableId);
13
+ if (node === undefined)
14
+ return [];
15
+ const syntax = fingerprintSyntax(node, callable.callableId);
16
+ const promiseEffects = promises.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
17
+ const eventEffects = events.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
18
+ const timerEffects = timers.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
19
+ const childProcessEffects = [
20
+ ...childProcessSpawns,
21
+ ...childProcessInteractions,
22
+ ].filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
23
+ const networkEffects = requestOperations.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
24
+ const resourceEffects = resourceOperations.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
25
+ return [
26
+ {
27
+ callableId: callable.callableId,
28
+ status: parserPartial ||
29
+ state.limitsReached.size > 0 ||
30
+ callable.returnCoverage.status !== "complete"
31
+ ? "partial"
32
+ : "complete",
33
+ components: {
34
+ parameterArity: callableParameterArity(node),
35
+ normalizedAstSha256: digest(syntax.normalizedTokens),
36
+ controlFlowSha256: digest(syntax.controlFlowTokens),
37
+ relationShapeSha256: digest(relationShape(callable, calls, promiseEffects)),
38
+ literalSetSha256: digest([...new Set(syntax.literals)].sort(compareCodePoints)),
39
+ effects: [
40
+ ...(isAsyncCallable(node) ? ["async"] : []),
41
+ ...(childProcessEffects.length > 0
42
+ ? ["child-process"]
43
+ : []),
44
+ ...(eventEffects.length > 0 ? ["event"] : []),
45
+ ...(networkEffects.length > 0 ? ["network"] : []),
46
+ ...(promiseEffects.length > 0 ? ["promise"] : []),
47
+ ...(resourceEffects.length > 0 ? ["resource"] : []),
48
+ ...(timerEffects.length > 0 ? ["timer"] : []),
49
+ ],
50
+ },
51
+ limitations: [
52
+ "The fingerprint is a bounded static candidate and does not prove behavioral equivalence.",
53
+ ...(parserPartial || state.limitsReached.size > 0
54
+ ? ["Incomplete semantic recovery makes this fingerprint partial."]
55
+ : []),
56
+ ],
57
+ },
58
+ ];
59
+ });
60
+ };
61
+ const fingerprintSyntax = (root, targetCallableId) => {
62
+ const normalizedTokens = [];
63
+ const controlFlowTokens = [];
64
+ const literals = [];
65
+ const callableStack = [];
66
+ traverseJavaScriptAst(root, {
67
+ enter: (node, parent) => {
68
+ const callableId = semanticCallableIdForNode(node);
69
+ if (callableId !== null)
70
+ callableStack.push(callableId);
71
+ if (callableStack.at(-1) !== targetCallableId)
72
+ return;
73
+ normalizedTokens.push(normalizedNodeToken(node, parent));
74
+ const control = controlFlowToken(node);
75
+ if (control !== null)
76
+ controlFlowTokens.push(control);
77
+ const literal = semanticLiteral(node);
78
+ if (literal !== null)
79
+ literals.push(literal);
80
+ },
81
+ exit: (node) => {
82
+ const callableId = semanticCallableIdForNode(node);
83
+ if (callableId !== null && callableStack.at(-1) === callableId)
84
+ callableStack.pop();
85
+ },
86
+ });
87
+ return { normalizedTokens, controlFlowTokens, literals };
88
+ };
89
+ const normalizedNodeToken = (node, parent) => {
90
+ if (t.isIdentifier(node))
91
+ return propertyIdentity(node, parent)
92
+ ? `property:${node.name}`
93
+ : "identifier";
94
+ if (t.isStringLiteral(node))
95
+ return propertyIdentity(node, parent)
96
+ ? `property:${node.value}`
97
+ : "string-literal";
98
+ const attributes = [
99
+ primitiveAttribute(node, "operator"),
100
+ primitiveAttribute(node, "kind"),
101
+ primitiveAttribute(node, "computed"),
102
+ primitiveAttribute(node, "optional"),
103
+ primitiveAttribute(node, "async"),
104
+ primitiveAttribute(node, "generator"),
105
+ ].filter((value) => value !== null);
106
+ return [node.type, ...attributes].join(":");
107
+ };
108
+ const primitiveAttribute = (node, key) => {
109
+ const value = Reflect.get(node, key);
110
+ return typeof value === "string" ||
111
+ typeof value === "number" ||
112
+ typeof value === "boolean"
113
+ ? `${key}=${String(value)}`
114
+ : null;
115
+ };
116
+ const propertyIdentity = (node, parent) => {
117
+ if (parent === null)
118
+ return false;
119
+ if ((t.isMemberExpression(parent) || t.isOptionalMemberExpression(parent)) &&
120
+ parent.property === node)
121
+ return !parent.computed || t.isStringLiteral(node);
122
+ if ((t.isObjectProperty(parent) ||
123
+ t.isObjectMethod(parent) ||
124
+ t.isClassMethod(parent) ||
125
+ t.isClassProperty(parent)) &&
126
+ parent.key === node)
127
+ return !parent.computed || t.isStringLiteral(node);
128
+ return false;
129
+ };
130
+ const controlFlowToken = (node) => {
131
+ if (!t.isIfStatement(node) &&
132
+ !t.isConditionalExpression(node) &&
133
+ !t.isSwitchStatement(node) &&
134
+ !t.isSwitchCase(node) &&
135
+ !t.isLoop(node) &&
136
+ !t.isTryStatement(node) &&
137
+ !t.isCatchClause(node) &&
138
+ !t.isReturnStatement(node) &&
139
+ !t.isThrowStatement(node) &&
140
+ !t.isAwaitExpression(node) &&
141
+ !t.isYieldExpression(node) &&
142
+ !t.isLogicalExpression(node))
143
+ return null;
144
+ const operator = primitiveAttribute(node, "operator");
145
+ return operator === null ? node.type : `${node.type}:${operator}`;
146
+ };
147
+ const semanticLiteral = (node) => {
148
+ if (t.isStringLiteral(node))
149
+ return `string:${node.value}`;
150
+ if (t.isNumericLiteral(node))
151
+ return `number:${String(node.value)}`;
152
+ if (t.isBooleanLiteral(node))
153
+ return `boolean:${String(node.value)}`;
154
+ if (t.isNullLiteral(node))
155
+ return "null";
156
+ if (t.isBigIntLiteral(node))
157
+ return `bigint:${node.value}`;
158
+ if (t.isRegExpLiteral(node))
159
+ return `regexp:${node.pattern}/${node.flags}`;
160
+ if (t.isTemplateElement(node))
161
+ return `template:${node.value.cooked ?? node.value.raw}`;
162
+ return null;
163
+ };
164
+ const relationShape = (callable, calls, promises) => [
165
+ `returns:${String(callable.returnSites.length)}:${callable.returnCoverage.status}`,
166
+ ...calls.callSites
167
+ .filter(({ callerCallableId }) => callerCallableId === callable.callableId)
168
+ .map(({ kind, resolution, arguments: args, calleeCallableIds }) => `call:${kind}:${resolution}:${String(args.length)}:${String(calleeCallableIds.length)}`),
169
+ `captures:${String(calls.closureCaptures.filter(({ callableId }) => callableId === callable.callableId).length)}`,
170
+ ...promises.map(({ kind, method, ownership, sourceResolution }) => `promise:${kind}:${method}:${ownership}:${sourceResolution}`),
171
+ ];
172
+ const callableParameterArity = (node) => t.isFunction(node) ? node.params.length : 0;
173
+ const isAsyncCallable = (node) => t.isFunction(node) && node.async;
174
+ const digest = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
@@ -11,6 +11,15 @@ export const DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS = {
11
11
  maxArgumentFlows: 100_000,
12
12
  maxCallReturnFlows: 100_000,
13
13
  maxClosureCaptures: 100_000,
14
+ maxPromiseOperations: 100_000,
15
+ maxEventOperations: 100_000,
16
+ maxTimerOperations: 100_000,
17
+ maxChildProcessOperations: 100_000,
18
+ maxConfigurationOperations: 100_000,
19
+ maxRequestOperations: 100_000,
20
+ maxBoundaryOperations: 100_000,
21
+ maxResourceOperations: 100_000,
22
+ maxObjectOperations: 100_000,
14
23
  maxFrontiers: 20_000,
15
24
  maxValueDepth: 16,
16
25
  maxUnionValues: 32,
@@ -23,7 +32,7 @@ export const semanticBinding = (ir, bindingId) => ir.bindings.find(({ bindingId:
23
32
  /** Fail-closed result when Babel cannot produce an inert syntax tree. */
24
33
  export const failedJavaScriptSemanticIr = () => ({
25
34
  schema: "JavaScriptSemanticIR",
26
- schemaVersion: 3,
35
+ schemaVersion: 4,
27
36
  scopes: [],
28
37
  bindings: [],
29
38
  callables: [],
@@ -34,6 +43,17 @@ export const failedJavaScriptSemanticIr = () => ({
34
43
  callReturnFlows: [],
35
44
  callResultFlows: [],
36
45
  closureCaptures: [],
46
+ promiseOperations: [],
47
+ eventOperations: [],
48
+ timerOperations: [],
49
+ childProcessSpawns: [],
50
+ childProcessInteractions: [],
51
+ configurationOperations: [],
52
+ requestOperations: [],
53
+ boundaryOperations: [],
54
+ resourceOperations: [],
55
+ objectOperations: [],
56
+ functionFingerprints: [],
37
57
  frontiers: [],
38
58
  coverage: { status: "failed", omittedCount: null, limitsReached: [] },
39
59
  limitations: [
@@ -0,0 +1,115 @@
1
+ import * as t from "@babel/types";
2
+ import { reachSemanticLimit, semanticCallableIdForNode, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
3
+ import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
4
+ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
5
+ import { propertyName, range } from "./javascriptStaticAnalysisHelpers.js";
6
+ /** Recover bounded static property reads, writes, spreads, and destructuring. */
7
+ export const collectJavaScriptSemanticObjects = (program, state, callables) => {
8
+ const output = [];
9
+ const callableIds = new Set(callables.map(({ callableId }) => callableId));
10
+ const callableStack = [];
11
+ const context = { state, output };
12
+ traverseJavaScriptAst(program, {
13
+ enter: (node, parent) => {
14
+ const callableId = semanticCallableIdForNode(node);
15
+ if (callableId !== null && callableIds.has(callableId))
16
+ callableStack.push(callableId);
17
+ const ownerCallableId = callableStack.at(-1) ?? null;
18
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node))
19
+ collectMember(node, parent, ownerCallableId, context);
20
+ else if (t.isSpreadElement(node) &&
21
+ parent !== null &&
22
+ t.isObjectExpression(parent))
23
+ addObjectOperation({
24
+ node,
25
+ kind: "spread",
26
+ ownerCallableId,
27
+ objectBindingId: expressionBindingId(node.argument, state),
28
+ targetBindingId: null,
29
+ propertyName: null,
30
+ }, state, output);
31
+ else if (t.isVariableDeclarator(node) && t.isObjectPattern(node.id))
32
+ collectDestructuring(node, ownerCallableId, state, output);
33
+ },
34
+ exit: (node) => {
35
+ const callableId = semanticCallableIdForNode(node);
36
+ if (callableId !== null && callableStack.at(-1) === callableId)
37
+ callableStack.pop();
38
+ },
39
+ });
40
+ return output;
41
+ };
42
+ const collectMember = (node, parent, ownerCallableId, context) => {
43
+ const { state, output } = context;
44
+ const name = semanticStaticPropertyName(node.property, node.computed);
45
+ if (name.length === 0)
46
+ return;
47
+ const write = (t.isAssignmentExpression(parent) && parent.left === node) ||
48
+ (t.isUpdateExpression(parent) && parent.argument === node);
49
+ addObjectOperation({
50
+ node,
51
+ kind: write ? "write" : "read",
52
+ ownerCallableId,
53
+ objectBindingId: expressionBindingId(node.object, state),
54
+ targetBindingId: null,
55
+ propertyName: name,
56
+ }, state, output);
57
+ };
58
+ const collectDestructuring = (node, ownerCallableId, state, output) => {
59
+ if (!t.isObjectPattern(node.id))
60
+ return;
61
+ const objectBindingId = expressionBindingId(node.init, state);
62
+ for (const property of node.id.properties) {
63
+ if (!t.isObjectProperty(property))
64
+ continue;
65
+ const name = propertyName(property.key);
66
+ if (name.length === 0)
67
+ continue;
68
+ const target = bindingIdentifier(property.value);
69
+ addObjectOperation({
70
+ node: property,
71
+ kind: "destructure",
72
+ ownerCallableId,
73
+ objectBindingId,
74
+ targetBindingId: target === null
75
+ ? null
76
+ : (resolveSemanticBindingState(state, target, target.name)
77
+ ?.bindingId ?? null),
78
+ propertyName: name,
79
+ }, state, output);
80
+ }
81
+ };
82
+ const addObjectOperation = (input, state, output) => {
83
+ if (output.length >= state.limits.maxObjectOperations) {
84
+ reachSemanticLimit(state, "maxObjectOperations");
85
+ return;
86
+ }
87
+ output.push({
88
+ objectOperationId: `object:${input.kind}:${String(input.node.start ?? -1)}:${String(input.node.end ?? -1)}`,
89
+ kind: input.kind,
90
+ location: range(input.node),
91
+ ownerCallableId: input.ownerCallableId,
92
+ objectBindingId: input.objectBindingId,
93
+ targetBindingId: input.targetBindingId,
94
+ propertyName: input.propertyName,
95
+ resolution: input.kind === "spread"
96
+ ? input.objectBindingId === null
97
+ ? "partial"
98
+ : "complete"
99
+ : input.objectBindingId === null ||
100
+ input.propertyName === null ||
101
+ (input.kind === "destructure" && input.targetBindingId === null)
102
+ ? "partial"
103
+ : "complete",
104
+ });
105
+ };
106
+ const expressionBindingId = (node, state) => t.isIdentifier(node)
107
+ ? (resolveSemanticBindingState(state, node, node.name)?.bindingId ?? null)
108
+ : null;
109
+ const bindingIdentifier = (node) => {
110
+ if (t.isIdentifier(node))
111
+ return node;
112
+ if (t.isAssignmentPattern(node) && t.isIdentifier(node.left))
113
+ return node.left;
114
+ return null;
115
+ };
@@ -0,0 +1,290 @@
1
+ import * as t from "@babel/types";
2
+ import { reachSemanticLimit, semanticCallableIdForNode, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
3
+ import { resolveSemanticBindingState, semanticResolutionBlocked, } from "./javascriptSemanticState.js";
4
+ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
5
+ import { compareCodePoints, range, sourceRangesEqual, } from "./javascriptStaticAnalysisHelpers.js";
6
+ /** Recover explicit Promise construction, chaining, aggregation, and ownership. */
7
+ export const collectJavaScriptSemanticPromises = (program, state, callables) => {
8
+ const candidates = collectCandidates(program, state, callables);
9
+ const candidateByNode = new WeakMap();
10
+ for (const candidate of candidates)
11
+ candidateByNode.set(candidate.node, candidate);
12
+ return candidates.map((candidate) => {
13
+ const ownership = promiseOwnership(candidate, candidateByNode, state, callables);
14
+ const sources = promiseSources(candidate, candidateByNode, state);
15
+ return {
16
+ promiseId: candidate.promiseId,
17
+ kind: candidate.kind,
18
+ method: candidate.method,
19
+ location: range(candidate.node),
20
+ ownerCallableId: candidate.ownerCallableId,
21
+ ...ownership,
22
+ sourcePromiseIds: [...new Set(sources.promiseIds)].sort(compareCodePoints),
23
+ sourceResolution: sources.status,
24
+ };
25
+ });
26
+ };
27
+ const collectCandidates = (program, state, callables) => {
28
+ const output = [];
29
+ const admittedCallables = new Set(callables.map(({ callableId }) => callableId));
30
+ const callableStack = [];
31
+ const ancestors = [];
32
+ traverseJavaScriptAst(program, {
33
+ enter: (node) => {
34
+ const callableId = semanticCallableIdForNode(node);
35
+ if (callableId !== null && admittedCallables.has(callableId))
36
+ callableStack.push(callableId);
37
+ const details = promiseCandidateDetails(node, state);
38
+ if (details !== null) {
39
+ if (output.length >= state.limits.maxPromiseOperations)
40
+ reachSemanticLimit(state, "maxPromiseOperations");
41
+ else
42
+ output.push({
43
+ node,
44
+ ...details,
45
+ promiseId: semanticPromiseId(details.kind, node),
46
+ ownerCallableId: callableStack.at(-1) ?? null,
47
+ ancestors: [...ancestors],
48
+ });
49
+ }
50
+ ancestors.push(node);
51
+ },
52
+ exit: (node) => {
53
+ ancestors.pop();
54
+ const callableId = semanticCallableIdForNode(node);
55
+ if (callableId !== null && callableStack.at(-1) === callableId)
56
+ callableStack.pop();
57
+ },
58
+ });
59
+ return output;
60
+ };
61
+ const promiseCandidateDetails = (node, state) => {
62
+ if (t.isNewExpression(node) &&
63
+ t.isIdentifier(node.callee, { name: "Promise" }) &&
64
+ isUnshadowedGlobalPromise(node.callee, state))
65
+ return { kind: "constructor", method: "new" };
66
+ if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) {
67
+ const member = memberCallee(node);
68
+ if (member === null)
69
+ return null;
70
+ const method = semanticStaticPropertyName(member.property, member.computed);
71
+ if (t.isIdentifier(member.object, { name: "Promise" }) &&
72
+ isUnshadowedGlobalPromise(member.object, state)) {
73
+ if (method === "all" || method === "allSettled")
74
+ return { kind: "aggregate", method };
75
+ if (method === "resolve" || method === "reject")
76
+ return { kind: "static", method };
77
+ }
78
+ if (method === "then" || method === "catch" || method === "finally")
79
+ return { kind: "chain", method };
80
+ }
81
+ if (t.isAwaitExpression(node) && !isPromiseProducer(node.argument, state))
82
+ return { kind: "awaited-expression", method: "await" };
83
+ return null;
84
+ };
85
+ const isUnshadowedGlobalPromise = (node, state) => resolveSemanticBindingState(state, node, "Promise") === undefined &&
86
+ !semanticResolutionBlocked(state, node, "Promise");
87
+ const isPromiseProducer = (node, state) => promiseCandidateDetails(unwrapExpression(node), state) !== null;
88
+ const semanticPromiseId = (kind, node) => `promise:${kind}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`;
89
+ const memberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
90
+ ? node.callee
91
+ : null;
92
+ const promiseOwnership = (candidate, candidateByNode, state, callables) => {
93
+ if (candidate.kind === "awaited-expression")
94
+ return emptyOwnership("awaited");
95
+ const context = {
96
+ candidate,
97
+ candidateByNode,
98
+ state,
99
+ callables,
100
+ };
101
+ for (let index = candidate.ancestors.length - 1; index >= 0; index -= 1) {
102
+ const ancestor = candidate.ancestors[index];
103
+ if (ancestor === undefined)
104
+ continue;
105
+ const ownership = ownershipAtAncestor(ancestor, context);
106
+ if (ownership === "boundary")
107
+ break;
108
+ if (ownership !== null)
109
+ return ownership;
110
+ }
111
+ return emptyOwnership("unknown");
112
+ };
113
+ const ownershipAtAncestor = (ancestor, context) => {
114
+ const { candidate, candidateByNode, state, callables } = context;
115
+ if (t.isAwaitExpression(ancestor))
116
+ return emptyOwnership("awaited");
117
+ const outer = candidateByNode.get(ancestor);
118
+ if (outer !== undefined &&
119
+ outer.ownerCallableId === candidate.ownerCallableId &&
120
+ outerConsumesCandidate(outer, candidate))
121
+ return outer.kind === "chain"
122
+ ? emptyOwnership("chained")
123
+ : emptyOwnership("aggregated");
124
+ const assigned = assignedPromiseBinding(ancestor, state);
125
+ if (assigned !== undefined)
126
+ return {
127
+ ownership: "assigned",
128
+ ownerBindingId: assigned,
129
+ returnSiteId: null,
130
+ };
131
+ if (t.isReturnStatement(ancestor))
132
+ return returnedPromiseOwnership(candidate.ownerCallableId, range(ancestor), callables);
133
+ if (t.isExpressionStatement(ancestor))
134
+ return emptyOwnership("detached");
135
+ const callableId = semanticCallableIdForNode(ancestor);
136
+ if (callableId === null || callableId !== candidate.ownerCallableId)
137
+ return null;
138
+ return t.isArrowFunctionExpression(ancestor) &&
139
+ ancestor.body === candidate.node
140
+ ? returnedPromiseOwnership(candidate.ownerCallableId, range(candidate.node), callables)
141
+ : "boundary";
142
+ };
143
+ const assignedPromiseBinding = (ancestor, state) => {
144
+ if (t.isVariableDeclarator(ancestor) &&
145
+ ancestor.init !== null &&
146
+ t.isIdentifier(ancestor.id))
147
+ return (resolveSemanticBindingState(state, ancestor.id, ancestor.id.name)
148
+ ?.bindingId ?? null);
149
+ if (t.isAssignmentExpression(ancestor) && t.isIdentifier(ancestor.left))
150
+ return (resolveSemanticBindingState(state, ancestor.left, ancestor.left.name)
151
+ ?.bindingId ?? null);
152
+ return undefined;
153
+ };
154
+ const returnedPromiseOwnership = (callableId, location, callables) => ({
155
+ ownership: "returned",
156
+ ownerBindingId: null,
157
+ returnSiteId: matchingReturnSite(callableId, location, callables),
158
+ });
159
+ const outerConsumesCandidate = (outer, candidate) => {
160
+ if (outer.kind === "chain" &&
161
+ (t.isCallExpression(outer.node) || t.isOptionalCallExpression(outer.node))) {
162
+ const member = memberCallee(outer.node);
163
+ return member !== null && containsNode(member.object, candidate.node);
164
+ }
165
+ if (outer.kind === "aggregate" &&
166
+ (t.isCallExpression(outer.node) || t.isOptionalCallExpression(outer.node))) {
167
+ const input = outer.node.arguments[0];
168
+ return t.isNode(input) && containsNode(input, candidate.node);
169
+ }
170
+ return false;
171
+ };
172
+ const containsNode = (outer, inner) => outer.start !== null &&
173
+ outer.start !== undefined &&
174
+ outer.end !== null &&
175
+ outer.end !== undefined &&
176
+ inner.start !== null &&
177
+ inner.start !== undefined &&
178
+ inner.end !== null &&
179
+ inner.end !== undefined &&
180
+ outer.start <= inner.start &&
181
+ outer.end >= inner.end;
182
+ const emptyOwnership = (ownership) => ({
183
+ ownership,
184
+ ownerBindingId: null,
185
+ returnSiteId: null,
186
+ });
187
+ const matchingReturnSite = (callableId, location, callables) => callables
188
+ .find(({ callableId: candidate }) => candidate === callableId)
189
+ ?.returnSites.find(({ location: candidate }) => sourceRangesEqual(candidate, location))?.returnSiteId ?? null;
190
+ const promiseSources = (candidate, candidateByNode, state) => {
191
+ if (candidate.kind === "chain") {
192
+ const member = t.isCallExpression(candidate.node) ||
193
+ t.isOptionalCallExpression(candidate.node)
194
+ ? memberCallee(candidate.node)
195
+ : null;
196
+ return member === null
197
+ ? unresolvedPromise()
198
+ : resolvePromiseExpression(member.object, promiseResolutionContext(candidate, candidateByNode, state));
199
+ }
200
+ if (candidate.kind === "aggregate")
201
+ return aggregateSources(candidate, candidateByNode, state);
202
+ if (candidate.kind === "awaited-expression")
203
+ return t.isAwaitExpression(candidate.node)
204
+ ? resolvePromiseExpression(candidate.node.argument, promiseResolutionContext(candidate, candidateByNode, state))
205
+ : unresolvedPromise();
206
+ return { promiseIds: [], status: "complete" };
207
+ };
208
+ const aggregateSources = (candidate, candidateByNode, state) => {
209
+ if (!t.isCallExpression(candidate.node) &&
210
+ !t.isOptionalCallExpression(candidate.node))
211
+ return unresolvedPromise();
212
+ const input = candidate.node.arguments[0];
213
+ if (!t.isArrayExpression(input))
214
+ return unresolvedPromise();
215
+ const resolutions = input.elements.map((element) => {
216
+ if (element === null)
217
+ return completePromise();
218
+ if (t.isSpreadElement(element))
219
+ return unresolvedPromise();
220
+ if (isObviouslyNonPromise(element))
221
+ return completePromise();
222
+ return resolvePromiseExpression(element, promiseResolutionContext(candidate, candidateByNode, state));
223
+ });
224
+ return combinePromiseResolutions(resolutions);
225
+ };
226
+ const resolvePromiseExpression = (rawNode, context) => {
227
+ const { current, candidateByNode, state, seenBindings, depth } = context;
228
+ if (depth >= state.limits.maxValueDepth)
229
+ return unresolvedPromise();
230
+ const node = unwrapExpression(rawNode);
231
+ const direct = candidateByNode.get(node);
232
+ if (direct !== undefined && direct.promiseId !== current.promiseId)
233
+ return { promiseIds: [direct.promiseId], status: "complete" };
234
+ if (!t.isIdentifier(node))
235
+ return unresolvedPromise();
236
+ const binding = resolveSemanticBindingState(state, node, node.name);
237
+ if (binding === undefined ||
238
+ binding.mutable ||
239
+ binding.initializers.length !== 1 ||
240
+ seenBindings.has(binding.bindingId))
241
+ return unresolvedPromise();
242
+ const initializer = binding.initializers[0];
243
+ if (initializer === undefined || initializer.projection.length > 0)
244
+ return unresolvedPromise();
245
+ const nestedSeen = new Set(seenBindings);
246
+ nestedSeen.add(binding.bindingId);
247
+ return resolvePromiseExpression(initializer.node, {
248
+ ...context,
249
+ seenBindings: nestedSeen,
250
+ depth: depth + 1,
251
+ });
252
+ };
253
+ const promiseResolutionContext = (current, candidateByNode, state) => ({
254
+ current,
255
+ candidateByNode,
256
+ state,
257
+ seenBindings: new Set(),
258
+ depth: 0,
259
+ });
260
+ const unwrapExpression = (node) => {
261
+ if (t.isParenthesizedExpression(node) ||
262
+ t.isTSAsExpression(node) ||
263
+ t.isTSSatisfiesExpression(node) ||
264
+ t.isTSNonNullExpression(node) ||
265
+ t.isTypeCastExpression(node))
266
+ return unwrapExpression(node.expression);
267
+ return node;
268
+ };
269
+ const combinePromiseResolutions = (resolutions) => ({
270
+ promiseIds: resolutions.flatMap(({ promiseIds }) => promiseIds),
271
+ status: resolutions.some(({ status }) => status === "unresolved")
272
+ ? resolutions.some(({ status }) => status === "complete")
273
+ ? "partial"
274
+ : "unresolved"
275
+ : resolutions.some(({ status }) => status === "partial")
276
+ ? "partial"
277
+ : "complete",
278
+ });
279
+ const completePromise = () => ({
280
+ promiseIds: [],
281
+ status: "complete",
282
+ });
283
+ const unresolvedPromise = () => ({
284
+ promiseIds: [],
285
+ status: "unresolved",
286
+ });
287
+ const isObviouslyNonPromise = (node) => t.isLiteral(node) ||
288
+ t.isObjectExpression(node) ||
289
+ t.isFunction(node) ||
290
+ t.isClassExpression(node);