rea-agents 2.4.0 → 3.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.
- package/README.md +91 -19
- package/bridge/ghidra/ReaGhidraBridge.java +524 -4
- package/bridge/hopper_bridge.py +313 -28
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +20 -8
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +84 -14
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +13 -4
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +71 -127
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +11 -3
- package/dist/cli/artifactCommands.js +44 -3
- package/dist/cli/coreAnalysisCommands.js +78 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +9 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +29 -2
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +35 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +8 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +34 -25
- package/dist/contracts/toolEffects.js +25 -0
- package/dist/contracts/toolOutputSchemaGroups.js +120 -30
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +9 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +14 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +13 -16
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +12 -15
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +40 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +93 -99
- package/dist/hopper/HopperProvider.js +61 -30
- package/dist/hopper/HopperRequestQueue.js +215 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +63 -20
- package/dist/main/transport.js +24 -27
- package/dist/main.js +14 -6
- package/dist/mcpDoctor.js +27 -4
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +92 -154
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +28 -7
- package/dist/server/registerEvidenceResources.js +54 -101
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
- package/dist/server/registerReconstructionReadinessResource.js +32 -0
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +32 -58
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +28 -11
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { isAbsolute, relative } from "node:path";
|
|
2
3
|
import canonicalize from "canonicalize";
|
|
3
4
|
import { z } from "zod";
|
|
4
|
-
import { browserAllowedOriginsSchema, browserEndpointSchema, webPageInspectionSchema, } from "./browserObservation.js";
|
|
5
|
+
import { browserAllowedOriginsSchema, browserEndpointSchema, browserOriginSchema, webPageInspectionSchema, } from "./browserObservation.js";
|
|
6
|
+
import { classifyBrowserCompleteness, } from "./browserCompleteness.js";
|
|
5
7
|
import { electronFileRootsSchema, electronPageInspectionSchema, } from "./electronObservation.js";
|
|
8
|
+
import { javascriptRuntimeObservationSchema, javascriptRuntimeKindSchema, } from "./javascriptRuntimeObservation.js";
|
|
6
9
|
import { parseEvidence } from "./evidence.js";
|
|
7
10
|
import { javascriptApplicationAnalysisResultSchema, } from "./javascriptApplicationAnalysis.js";
|
|
8
11
|
import { compareCodePoints, } from "./javascriptApplicationGraph.js";
|
|
@@ -109,8 +112,123 @@ const parseRuntimeCapture = (input) => {
|
|
|
109
112
|
scriptsCompleteWithinScope: scriptsComplete(inspection.completeness),
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
|
-
|
|
115
|
+
if (evidence.operation === "observe_javascript_runtime") {
|
|
116
|
+
assertEvidenceIdentity(evidence, {
|
|
117
|
+
operation: "observe_javascript_runtime",
|
|
118
|
+
predicate: "rea.javascript-runtime-observation/v1",
|
|
119
|
+
providerId: "rea-v8-inspector",
|
|
120
|
+
providerName: "REA passive Node/Electron V8 Inspector provider",
|
|
121
|
+
providerVersion: "1",
|
|
122
|
+
authority: "external-service",
|
|
123
|
+
confidence: "observed",
|
|
124
|
+
});
|
|
125
|
+
const result = javascriptRuntimeObservationSchema.parse(evidence.normalized_result);
|
|
126
|
+
assertV8RuntimeParameters(evidence, result);
|
|
127
|
+
const inspection = normalizeV8Inspection(result);
|
|
128
|
+
return {
|
|
129
|
+
kind: "v8-inspector",
|
|
130
|
+
evidence,
|
|
131
|
+
inspection,
|
|
132
|
+
captureSha256: digestCanonical(result),
|
|
133
|
+
scriptsCompleteWithinScope: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
throw new TypeError("Runtime reconciliation requires inspect_web_page, inspect_electron_page, or observe_javascript_runtime Evidence");
|
|
137
|
+
};
|
|
138
|
+
const assertV8RuntimeParameters = (evidence, result) => {
|
|
139
|
+
const parameters = z
|
|
140
|
+
.object({
|
|
141
|
+
inspector_endpoint: browserEndpointSchema,
|
|
142
|
+
allowed_file_roots: z
|
|
143
|
+
.array(z.string().min(1).max(16_384).refine(isAbsolute))
|
|
144
|
+
.max(32),
|
|
145
|
+
allowed_origins: z.array(browserOriginSchema).max(32),
|
|
146
|
+
target_id: z.string().trim().min(1).max(256),
|
|
147
|
+
runtime_kind: javascriptRuntimeKindSchema,
|
|
148
|
+
})
|
|
149
|
+
.passthrough()
|
|
150
|
+
.parse(evidence.parameters);
|
|
151
|
+
if (parameters.target_id !== result.target.target_id ||
|
|
152
|
+
parameters.runtime_kind !== result.target.runtime_kind)
|
|
153
|
+
throw new TypeError("Runtime Evidence target or declared role disagrees with its result");
|
|
154
|
+
for (const location of [
|
|
155
|
+
result.target.location,
|
|
156
|
+
...result.scripts.items.map(({ location }) => location),
|
|
157
|
+
])
|
|
158
|
+
if ((location.kind === "file" &&
|
|
159
|
+
!parameters.allowed_file_roots.some((root) => isWithinRoot(root, location.file_path))) ||
|
|
160
|
+
(location.kind === "url" &&
|
|
161
|
+
!parameters.allowed_origins.includes(location.origin)) ||
|
|
162
|
+
(location.kind === "builtin" && !location.specifier.startsWith("node:")))
|
|
163
|
+
throw new TypeError("Runtime Evidence contains a location outside its recorded scope");
|
|
164
|
+
};
|
|
165
|
+
const isWithinRoot = (root, path) => {
|
|
166
|
+
const remainder = relative(root, path);
|
|
167
|
+
return (remainder === "" || (!remainder.startsWith("..") && !isAbsolute(remainder)));
|
|
168
|
+
};
|
|
169
|
+
const normalizeV8Inspection = (result) => ({
|
|
170
|
+
target: {
|
|
171
|
+
target_id: result.target.target_id,
|
|
172
|
+
type: result.target.protocol_type,
|
|
173
|
+
title: result.target.runtime_kind,
|
|
174
|
+
attached: result.target.attached,
|
|
175
|
+
...runtimeLocation(result.target.location),
|
|
176
|
+
},
|
|
177
|
+
frames: [],
|
|
178
|
+
scripts: {
|
|
179
|
+
items: result.scripts.items.map((script) => ({
|
|
180
|
+
script_key: script.script_key,
|
|
181
|
+
frame_id: script.execution_context_key,
|
|
182
|
+
...runtimeLocation(script.location),
|
|
183
|
+
cdp_hash: script.cdp_hash ?? "",
|
|
184
|
+
length: script.length,
|
|
185
|
+
is_module: script.is_module,
|
|
186
|
+
language: null,
|
|
187
|
+
source: {
|
|
188
|
+
included: false,
|
|
189
|
+
reason: "V8 Inspector source capture is outside passive authority.",
|
|
190
|
+
},
|
|
191
|
+
})),
|
|
192
|
+
},
|
|
193
|
+
workers: [],
|
|
194
|
+
completeness: classifyBrowserCompleteness({
|
|
195
|
+
policyFilteredSections: new Set(excludedV8Scripts(result) > 0 ? ["scripts"] : []),
|
|
196
|
+
attachLimitedSections: new Set([
|
|
197
|
+
"frames",
|
|
198
|
+
"scripts",
|
|
199
|
+
"script_sources",
|
|
200
|
+
"workers",
|
|
201
|
+
]),
|
|
202
|
+
truncatedSections: new Set(result.capture.truncated ? ["scripts"] : []),
|
|
203
|
+
unavailableSections: new Set(["frames", "script_sources", "workers"]),
|
|
204
|
+
excluded: excludedV8Scripts(result) === 0
|
|
205
|
+
? []
|
|
206
|
+
: [
|
|
207
|
+
{
|
|
208
|
+
section: "scripts",
|
|
209
|
+
reason: "out_of_target_scope",
|
|
210
|
+
count: excludedV8Scripts(result),
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
droppedEvents: {
|
|
214
|
+
scripts: result.capture.events_dropped,
|
|
215
|
+
network_requests: 0,
|
|
216
|
+
console_events: 0,
|
|
217
|
+
websocket_connections: 0,
|
|
218
|
+
websocket_frames: 0,
|
|
219
|
+
webmcp_tools: 0,
|
|
220
|
+
timeline_events: 0,
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
223
|
+
});
|
|
224
|
+
const runtimeLocation = (location) => {
|
|
225
|
+
if (location.kind === "file")
|
|
226
|
+
return { file_path: location.file_path };
|
|
227
|
+
if (location.kind === "url")
|
|
228
|
+
return { url: location.sanitized_url };
|
|
229
|
+
return { url: location.specifier };
|
|
113
230
|
};
|
|
231
|
+
const excludedV8Scripts = (result) => Object.values(result.scripts.excluded).reduce((total, count) => total + count, 0);
|
|
114
232
|
const assertRuntimeParameters = (evidence, expected) => {
|
|
115
233
|
const common = {
|
|
116
234
|
target_id: z.string().trim().min(1).max(256),
|
|
@@ -45,9 +45,9 @@ const captureSummaries = (captures, runtime) => captures.map((capture) => {
|
|
|
45
45
|
kind: capture.kind,
|
|
46
46
|
target_node_id: targetNode.node_id,
|
|
47
47
|
target_key: capture.inspection.target.target_id,
|
|
48
|
-
target_location: capture.
|
|
49
|
-
? capture.inspection.target.url
|
|
50
|
-
: capture.inspection.target.file_path.slice(0, 4_096),
|
|
48
|
+
target_location: ("url" in capture.inspection.target
|
|
49
|
+
? capture.inspection.target.url
|
|
50
|
+
: capture.inspection.target.file_path).slice(0, 4_096),
|
|
51
51
|
frames: capture.inspection.frames.length,
|
|
52
52
|
scripts: capture.inspection.scripts.items.length,
|
|
53
53
|
workers: capture.inspection.workers.length,
|
|
@@ -197,7 +197,7 @@ const runtimeEvidence = (capture, input) => ({
|
|
|
197
197
|
name: capture.evidence.provider.id,
|
|
198
198
|
version: capture.evidence.provider.version ??
|
|
199
199
|
(capture.kind === "browser" ? "2" : "1"),
|
|
200
|
-
operation: capture.
|
|
200
|
+
operation: capture.evidence.operation,
|
|
201
201
|
executable_sha256: null,
|
|
202
202
|
},
|
|
203
203
|
coverage: runtimeSectionCoverage(capture, input.section),
|
|
@@ -260,7 +260,7 @@ const runtimeRelationshipEvidence = (capture, entity) => ({
|
|
|
260
260
|
name: capture.evidence.provider.id,
|
|
261
261
|
version: capture.evidence.provider.version ??
|
|
262
262
|
(capture.kind === "browser" ? "2" : "1"),
|
|
263
|
-
operation: capture.
|
|
263
|
+
operation: capture.evidence.operation,
|
|
264
264
|
executable_sha256: null,
|
|
265
265
|
},
|
|
266
266
|
coverage: runtimeSectionCoverage(capture, entity.section),
|
|
@@ -133,7 +133,7 @@ const layerSummarySchema = z.strictObject({
|
|
|
133
133
|
const captureSummarySchema = z.strictObject({
|
|
134
134
|
evidence_id: evidenceIdSchema,
|
|
135
135
|
capture_sha256: digestSchema,
|
|
136
|
-
kind: z.enum(["browser", "electron"]),
|
|
136
|
+
kind: z.enum(["browser", "electron", "v8-inspector"]),
|
|
137
137
|
target_node_id: nodeIdSchema,
|
|
138
138
|
target_key: boundedTextSchema,
|
|
139
139
|
target_location: boundedTextSchema,
|
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
import { parse } from "@babel/parser";
|
|
2
1
|
import * as t from "@babel/types";
|
|
3
2
|
import { DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS, failedJavaScriptSemanticIr, } from "./javascriptSemanticIr.js";
|
|
4
3
|
import { collectSemanticModuleLink, collectSemanticCallable, collectSemanticReferences, immutableSemanticBindings, immutableSemanticScopes, reachSemanticLimit, semanticModuleOrigin, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
|
|
5
4
|
import { currentSemanticScope, resolveSemanticBindingFromScope, semanticScopeId, semanticVariableScope, } from "./javascriptSemanticState.js";
|
|
6
5
|
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
|
7
6
|
import { collectSemanticReturns, resolveSemanticModuleCallables, } from "./javascriptSemanticReturns.js";
|
|
8
|
-
import {
|
|
7
|
+
import { collectJavaScriptDerivedSemantics } from "./javascriptSemanticDerivedAnalysis.js";
|
|
9
8
|
import { compareCodePoints, propertyName, range, } from "./javascriptStaticAnalysisHelpers.js";
|
|
9
|
+
import { parseJavaScriptSource, } from "./javascriptSourceParser.js";
|
|
10
10
|
/** Recover bounded lexical bindings, aliases, constants, and module links. */
|
|
11
11
|
export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
12
|
+
const file = parseJavaScriptSource(source);
|
|
13
|
+
return file === null
|
|
14
|
+
? failedJavaScriptSemanticIr()
|
|
15
|
+
: analyzeParsedJavaScriptSemantics(file, inputLimits);
|
|
16
|
+
};
|
|
17
|
+
/** Recover semantics from an already parsed JavaScript artifact. */
|
|
18
|
+
export const analyzeParsedJavaScriptSemantics = (file, inputLimits = {}) => {
|
|
12
19
|
const limits = { ...DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS, ...inputLimits };
|
|
13
|
-
let file;
|
|
14
|
-
try {
|
|
15
|
-
file = parse(source, {
|
|
16
|
-
sourceType: "unambiguous",
|
|
17
|
-
errorRecovery: true,
|
|
18
|
-
plugins: ["jsx", "typescript"],
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return failedJavaScriptSemanticIr();
|
|
23
|
-
}
|
|
24
20
|
const state = createState(file.program, limits);
|
|
25
21
|
collectDefinitions(file.program, state);
|
|
26
22
|
const references = collectSemanticReferences(file.program, state);
|
|
@@ -28,16 +24,16 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
|
28
24
|
const bindings = immutableSemanticBindings(state);
|
|
29
25
|
const callables = collectSemanticReturns(file.program, state, parserPartial);
|
|
30
26
|
const moduleLinks = resolveSemanticModuleCallables(state, callables);
|
|
31
|
-
const
|
|
27
|
+
const derived = collectJavaScriptDerivedSemantics(file.program, state, callables, parserPartial);
|
|
32
28
|
return {
|
|
33
29
|
schema: "JavaScriptSemanticIR",
|
|
34
|
-
schemaVersion:
|
|
30
|
+
schemaVersion: 4,
|
|
35
31
|
scopes: immutableSemanticScopes(state),
|
|
36
32
|
bindings,
|
|
37
33
|
callables,
|
|
38
34
|
references,
|
|
39
35
|
moduleLinks,
|
|
40
|
-
...
|
|
36
|
+
...derived,
|
|
41
37
|
coverage: {
|
|
42
38
|
status: state.limitsReached.size > 0
|
|
43
39
|
? "truncated"
|
|
@@ -59,6 +55,7 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
|
59
55
|
"Values and aliases were recovered from inert syntax only; no JavaScript was executed.",
|
|
60
56
|
"Return sites include only direct callable returns; nested callable returns remain separate.",
|
|
61
57
|
"Local call, argument, return, and closure relations are static candidates and do not prove runtime invocation.",
|
|
58
|
+
"Promise ownership covers explicit unshadowed Promise construction, static factories, aggregation, chaining, and await syntax only.",
|
|
62
59
|
"Cross-function mutation and dynamic property resolution remain unknown.",
|
|
63
60
|
],
|
|
64
61
|
};
|
|
@@ -0,0 +1,253 @@
|
|
|
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 { range } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
const EVENT_REGISTER_METHODS = [
|
|
7
|
+
"on",
|
|
8
|
+
"once",
|
|
9
|
+
"addListener",
|
|
10
|
+
"prependListener",
|
|
11
|
+
"prependOnceListener",
|
|
12
|
+
];
|
|
13
|
+
const EVENT_REMOVE_METHODS = [
|
|
14
|
+
"off",
|
|
15
|
+
"removeListener",
|
|
16
|
+
"removeAllListeners",
|
|
17
|
+
];
|
|
18
|
+
const EVENT_METHODS = [
|
|
19
|
+
...EVENT_REGISTER_METHODS,
|
|
20
|
+
...EVENT_REMOVE_METHODS,
|
|
21
|
+
"emit",
|
|
22
|
+
];
|
|
23
|
+
const TIMER_SCHEDULE_METHODS = [
|
|
24
|
+
"setTimeout",
|
|
25
|
+
"setInterval",
|
|
26
|
+
"setImmediate",
|
|
27
|
+
];
|
|
28
|
+
const TIMER_CANCEL_METHODS = [
|
|
29
|
+
"clearTimeout",
|
|
30
|
+
"clearInterval",
|
|
31
|
+
"clearImmediate",
|
|
32
|
+
];
|
|
33
|
+
/** Recover explicit EventEmitter and Node timer candidates from inert syntax. */
|
|
34
|
+
export const collectJavaScriptSemanticAsyncEffects = (program, state, callables) => {
|
|
35
|
+
const events = [];
|
|
36
|
+
const timers = [];
|
|
37
|
+
const callableIds = new Set(callables.map(({ callableId }) => callableId));
|
|
38
|
+
const callableStack = [];
|
|
39
|
+
traverseJavaScriptAst(program, {
|
|
40
|
+
enter: (node, parent) => {
|
|
41
|
+
const callableId = semanticCallableIdForNode(node);
|
|
42
|
+
if (callableId !== null && callableIds.has(callableId))
|
|
43
|
+
callableStack.push(callableId);
|
|
44
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
45
|
+
return;
|
|
46
|
+
const ownerCallableId = callableStack.at(-1) ?? null;
|
|
47
|
+
collectEvent(node, ownerCallableId, state, events);
|
|
48
|
+
collectTimer({ node, parent, ownerCallableId, state, output: timers });
|
|
49
|
+
},
|
|
50
|
+
exit: (node) => {
|
|
51
|
+
const callableId = semanticCallableIdForNode(node);
|
|
52
|
+
if (callableId !== null && callableStack.at(-1) === callableId)
|
|
53
|
+
callableStack.pop();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
eventOperations: events,
|
|
58
|
+
timerOperations: resolveTimers(timers),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const collectEvent = (node, ownerCallableId, state, output) => {
|
|
62
|
+
const member = memberCallee(node);
|
|
63
|
+
if (member === null)
|
|
64
|
+
return;
|
|
65
|
+
const method = EVENT_METHODS.find((candidate) => candidate ===
|
|
66
|
+
semanticStaticPropertyName(member.property, member.computed));
|
|
67
|
+
if (method === undefined)
|
|
68
|
+
return;
|
|
69
|
+
const kind = eventKind(method);
|
|
70
|
+
if (output.length >= state.limits.maxEventOperations) {
|
|
71
|
+
reachSemanticLimit(state, "maxEventOperations");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const eventArgument = node.arguments[0];
|
|
75
|
+
const listenerArgument = node.arguments[1];
|
|
76
|
+
const emitter = emitterIdentity(member.object, state);
|
|
77
|
+
const eventName = literalString(eventArgument);
|
|
78
|
+
output.push({
|
|
79
|
+
eventId: `event:${kind}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
80
|
+
kind,
|
|
81
|
+
method,
|
|
82
|
+
location: range(node),
|
|
83
|
+
ownerCallableId,
|
|
84
|
+
emitterKey: emitter.key,
|
|
85
|
+
emitterBindingId: emitter.bindingId,
|
|
86
|
+
eventName,
|
|
87
|
+
listenerBindingId: bindingId(listenerArgument, state),
|
|
88
|
+
listenerLocation: t.isNode(listenerArgument)
|
|
89
|
+
? range(listenerArgument)
|
|
90
|
+
: null,
|
|
91
|
+
resolution: eventName === null
|
|
92
|
+
? "unresolved"
|
|
93
|
+
: emitter.complete
|
|
94
|
+
? "complete"
|
|
95
|
+
: "partial",
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const eventKind = (method) => {
|
|
99
|
+
if (EVENT_REGISTER_METHODS.some((candidate) => candidate === method))
|
|
100
|
+
return "register";
|
|
101
|
+
if (EVENT_REMOVE_METHODS.some((candidate) => candidate === method))
|
|
102
|
+
return "remove";
|
|
103
|
+
return "dispatch";
|
|
104
|
+
};
|
|
105
|
+
const emitterIdentity = (node, state) => {
|
|
106
|
+
if (t.isIdentifier(node)) {
|
|
107
|
+
const binding = resolveSemanticBindingState(state, node, node.name);
|
|
108
|
+
if (binding !== undefined)
|
|
109
|
+
return {
|
|
110
|
+
key: `binding:${binding.bindingId}`,
|
|
111
|
+
bindingId: binding.bindingId,
|
|
112
|
+
complete: true,
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
key: `global:${node.name}`,
|
|
116
|
+
bindingId: null,
|
|
117
|
+
complete: !semanticResolutionBlocked(state, node, node.name),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (t.isThisExpression(node))
|
|
121
|
+
return { key: "this", bindingId: null, complete: true };
|
|
122
|
+
return {
|
|
123
|
+
key: `expression:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
124
|
+
bindingId: null,
|
|
125
|
+
complete: false,
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
const collectTimer = (input) => {
|
|
129
|
+
const { node, parent, ownerCallableId, state, output } = input;
|
|
130
|
+
const method = timerMethod(node.callee, state);
|
|
131
|
+
if (method === null)
|
|
132
|
+
return;
|
|
133
|
+
if (output.length >= state.limits.maxTimerOperations) {
|
|
134
|
+
reachSemanticLimit(state, "maxTimerOperations");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const kind = TIMER_SCHEDULE_METHODS.some((candidate) => candidate === method)
|
|
138
|
+
? "schedule"
|
|
139
|
+
: "cancel";
|
|
140
|
+
output.push({
|
|
141
|
+
node,
|
|
142
|
+
method,
|
|
143
|
+
kind,
|
|
144
|
+
ownerCallableId,
|
|
145
|
+
handleBindingId: kind === "schedule"
|
|
146
|
+
? assignedBindingId(parent, node, state)
|
|
147
|
+
: bindingId(node.arguments[0], state),
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
const resolveTimers = (candidates) => {
|
|
151
|
+
const schedulesByBinding = new Map();
|
|
152
|
+
for (const candidate of candidates) {
|
|
153
|
+
if (candidate.kind !== "schedule" || candidate.handleBindingId === null)
|
|
154
|
+
continue;
|
|
155
|
+
const existing = schedulesByBinding.get(candidate.handleBindingId) ?? [];
|
|
156
|
+
existing.push(candidate);
|
|
157
|
+
schedulesByBinding.set(candidate.handleBindingId, existing);
|
|
158
|
+
}
|
|
159
|
+
return candidates.map((candidate) => {
|
|
160
|
+
const linked = candidate.kind === "cancel" && candidate.handleBindingId !== null
|
|
161
|
+
? (schedulesByBinding.get(candidate.handleBindingId) ?? [])
|
|
162
|
+
: [];
|
|
163
|
+
return {
|
|
164
|
+
timerId: timerId(candidate),
|
|
165
|
+
kind: candidate.kind,
|
|
166
|
+
method: candidate.method,
|
|
167
|
+
location: range(candidate.node),
|
|
168
|
+
ownerCallableId: candidate.ownerCallableId,
|
|
169
|
+
handleBindingId: candidate.handleBindingId,
|
|
170
|
+
linkedTimerId: linked.length === 1 && linked[0] !== undefined
|
|
171
|
+
? timerId(linked[0])
|
|
172
|
+
: null,
|
|
173
|
+
delayMilliseconds: timerDelay(candidate),
|
|
174
|
+
resolution: candidate.kind === "schedule"
|
|
175
|
+
? "complete"
|
|
176
|
+
: linked.length === 1
|
|
177
|
+
? "complete"
|
|
178
|
+
: linked.length > 1
|
|
179
|
+
? "partial"
|
|
180
|
+
: "unresolved",
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
const timerMethod = (callee, state) => {
|
|
185
|
+
if (t.isIdentifier(callee)) {
|
|
186
|
+
const binding = resolveSemanticBindingState(state, callee, callee.name);
|
|
187
|
+
if (binding === undefined) {
|
|
188
|
+
const method = admittedTimerMethod(callee.name);
|
|
189
|
+
if (method === null)
|
|
190
|
+
return null;
|
|
191
|
+
return semanticResolutionBlocked(state, callee, callee.name)
|
|
192
|
+
? null
|
|
193
|
+
: method;
|
|
194
|
+
}
|
|
195
|
+
return timerMethodFromBinding(binding);
|
|
196
|
+
}
|
|
197
|
+
if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee))
|
|
198
|
+
return null;
|
|
199
|
+
const method = admittedTimerMethod(semanticStaticPropertyName(callee.property, callee.computed));
|
|
200
|
+
return method !== null &&
|
|
201
|
+
t.isIdentifier(callee.object) &&
|
|
202
|
+
timerNamespaceBinding(resolveSemanticBindingState(state, callee.object, callee.object.name))
|
|
203
|
+
? method
|
|
204
|
+
: null;
|
|
205
|
+
};
|
|
206
|
+
const admittedTimerMethod = (value) => [...TIMER_SCHEDULE_METHODS, ...TIMER_CANCEL_METHODS].find((candidate) => candidate === value) ?? null;
|
|
207
|
+
const timerMethodFromBinding = (binding) => {
|
|
208
|
+
for (const { specifier, importedPath } of binding.directOrigins) {
|
|
209
|
+
if (!timerModule(specifier))
|
|
210
|
+
continue;
|
|
211
|
+
const method = admittedTimerMethod(importedPath.at(-1) ?? "");
|
|
212
|
+
if (method !== null)
|
|
213
|
+
return method;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
};
|
|
217
|
+
const timerNamespaceBinding = (binding) => binding?.directOrigins.some(({ specifier, importedPath }) => timerModule(specifier) && importedPath.length === 0) ?? false;
|
|
218
|
+
const timerModule = (specifier) => specifier === "timers" || specifier === "node:timers";
|
|
219
|
+
const assignedBindingId = (parent, node, state) => {
|
|
220
|
+
const identifier = t.isVariableDeclarator(parent) &&
|
|
221
|
+
parent.init === node &&
|
|
222
|
+
t.isIdentifier(parent.id)
|
|
223
|
+
? parent.id
|
|
224
|
+
: t.isAssignmentExpression(parent) &&
|
|
225
|
+
parent.right === node &&
|
|
226
|
+
t.isIdentifier(parent.left)
|
|
227
|
+
? parent.left
|
|
228
|
+
: null;
|
|
229
|
+
return identifier === null
|
|
230
|
+
? null
|
|
231
|
+
: (resolveSemanticBindingState(state, identifier, identifier.name)
|
|
232
|
+
?.bindingId ?? null);
|
|
233
|
+
};
|
|
234
|
+
const bindingId = (node, state) => t.isIdentifier(node)
|
|
235
|
+
? (resolveSemanticBindingState(state, node, node.name)?.bindingId ?? null)
|
|
236
|
+
: null;
|
|
237
|
+
const literalString = (node) => {
|
|
238
|
+
if (t.isStringLiteral(node))
|
|
239
|
+
return node.value;
|
|
240
|
+
if (t.isTemplateLiteral(node) && node.expressions.length === 0)
|
|
241
|
+
return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? null;
|
|
242
|
+
return null;
|
|
243
|
+
};
|
|
244
|
+
const timerDelay = (candidate) => {
|
|
245
|
+
if (candidate.kind !== "schedule" || candidate.method === "setImmediate")
|
|
246
|
+
return null;
|
|
247
|
+
const delay = candidate.node.arguments[1];
|
|
248
|
+
return t.isNumericLiteral(delay) ? delay.value : null;
|
|
249
|
+
};
|
|
250
|
+
const timerId = (candidate) => `timer:${candidate.kind}:${String(candidate.node.start ?? -1)}:${String(candidate.node.end ?? -1)}`;
|
|
251
|
+
const memberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
|
|
252
|
+
? node.callee
|
|
253
|
+
: null;
|