rea-agents 2.4.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.
- package/README.md +80 -18
- package/bridge/ghidra/ReaGhidraBridge.java +48 -1
- package/bridge/hopper_bridge.py +259 -24
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/BinarySession.js +15 -4
- 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 +68 -12
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +7 -2
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +35 -121
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- 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/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/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 +253 -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 +8 -0
- package/dist/cli/artifactCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +37 -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 +8 -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 +22 -0
- 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 +25 -6
- package/dist/contracts/functionInstructionContract.js +8 -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/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +13 -21
- package/dist/contracts/toolEffects.js +21 -0
- package/dist/contracts/toolOutputSchemaGroups.js +70 -20
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorProjection.js +8 -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/hopperValues.js +10 -0
- 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 +5 -4
- 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/javascriptStaticAnalysis.js +4 -3
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -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/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/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +13 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +69 -99
- package/dist/hopper/HopperProvider.js +20 -1
- package/dist/hopper/HopperRequestQueue.js +187 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +1 -0
- package/dist/main/transport.js +24 -24
- package/dist/main.js +6 -0
- package/dist/mcpDoctor.js +25 -2
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/createServer.js +46 -1
- 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 +16 -5
- package/dist/server/registerEvidenceResources.js +4 -0
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
- package/dist/server/registerReconstructionReadinessResource.js +39 -0
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +11 -57
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/package.json +12 -6
- package/skills/reverse-engineer-anything/SKILL.md +2 -2
|
@@ -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);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { semanticStaticPropertyName } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { dataEffectLimitReached, outerDataEffectBinding, traverseDataEffects, } from "./javascriptSemanticDataEffectHelpers.js";
|
|
5
|
+
import { range } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
const ACQUIRE_METHODS = [
|
|
7
|
+
"open",
|
|
8
|
+
"openSync",
|
|
9
|
+
"createReadStream",
|
|
10
|
+
"createWriteStream",
|
|
11
|
+
"connect",
|
|
12
|
+
"createConnection",
|
|
13
|
+
];
|
|
14
|
+
const RELEASE_METHODS = ["close", "destroy", "end"];
|
|
15
|
+
/** Recover built-in filesystem/network acquisition and local release handles. */
|
|
16
|
+
export const collectJavaScriptSemanticResources = (program, state, callables) => {
|
|
17
|
+
const context = {
|
|
18
|
+
state,
|
|
19
|
+
callableIds: new Set(callables.map(({ callableId }) => callableId)),
|
|
20
|
+
callableStack: [],
|
|
21
|
+
ancestors: [],
|
|
22
|
+
};
|
|
23
|
+
const acquisitions = collectAcquisitions(program, context);
|
|
24
|
+
return [
|
|
25
|
+
...acquisitions.map(immutableAcquisition),
|
|
26
|
+
...collectReleases(program, context, acquisitions),
|
|
27
|
+
];
|
|
28
|
+
};
|
|
29
|
+
const collectAcquisitions = (program, context) => {
|
|
30
|
+
const output = [];
|
|
31
|
+
traverseDataEffects(program, context, (node) => {
|
|
32
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
33
|
+
return;
|
|
34
|
+
const method = acquisitionMethod(node.callee, context.state);
|
|
35
|
+
if (method === null)
|
|
36
|
+
return;
|
|
37
|
+
if (resourceLimitReached(output.length, context.state))
|
|
38
|
+
return;
|
|
39
|
+
output.push({
|
|
40
|
+
node,
|
|
41
|
+
method,
|
|
42
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
43
|
+
resultBindingId: outerDataEffectBinding(node, context),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return output;
|
|
47
|
+
};
|
|
48
|
+
const collectReleases = (program, context, acquisitions) => {
|
|
49
|
+
const byBinding = new Map();
|
|
50
|
+
for (const acquisition of acquisitions) {
|
|
51
|
+
if (acquisition.resultBindingId === null)
|
|
52
|
+
continue;
|
|
53
|
+
const existing = byBinding.get(acquisition.resultBindingId) ?? [];
|
|
54
|
+
existing.push(acquisition);
|
|
55
|
+
byBinding.set(acquisition.resultBindingId, existing);
|
|
56
|
+
}
|
|
57
|
+
const output = [];
|
|
58
|
+
traverseDataEffects(program, context, (node) => {
|
|
59
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
60
|
+
return;
|
|
61
|
+
const member = memberCallee(node);
|
|
62
|
+
if (member === null || !t.isIdentifier(member.object))
|
|
63
|
+
return;
|
|
64
|
+
const method = RELEASE_METHODS.find((candidate) => candidate ===
|
|
65
|
+
semanticStaticPropertyName(member.property, member.computed));
|
|
66
|
+
if (method === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const binding = resolveSemanticBindingState(context.state, member.object, member.object.name);
|
|
69
|
+
const linked = binding === undefined ? [] : (byBinding.get(binding.bindingId) ?? []);
|
|
70
|
+
if (linked.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
if (resourceLimitReached(acquisitions.length + output.length, context.state))
|
|
73
|
+
return;
|
|
74
|
+
output.push({
|
|
75
|
+
resourceId: `resource:release:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
76
|
+
kind: "release",
|
|
77
|
+
method,
|
|
78
|
+
location: range(node),
|
|
79
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
80
|
+
resultBindingId: binding?.bindingId ?? null,
|
|
81
|
+
linkedResourceIds: linked.map(resourceCandidateId).sort(),
|
|
82
|
+
resolution: linked.length === 1 ? "complete" : "partial",
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return output;
|
|
86
|
+
};
|
|
87
|
+
const immutableAcquisition = (candidate) => ({
|
|
88
|
+
resourceId: resourceCandidateId(candidate),
|
|
89
|
+
kind: "acquire",
|
|
90
|
+
method: candidate.method,
|
|
91
|
+
location: range(candidate.node),
|
|
92
|
+
ownerCallableId: candidate.ownerCallableId,
|
|
93
|
+
resultBindingId: candidate.resultBindingId,
|
|
94
|
+
linkedResourceIds: [],
|
|
95
|
+
resolution: "complete",
|
|
96
|
+
});
|
|
97
|
+
const acquisitionMethod = (callee, state) => {
|
|
98
|
+
if (t.isIdentifier(callee)) {
|
|
99
|
+
const binding = resolveSemanticBindingState(state, callee, callee.name);
|
|
100
|
+
return methodFromBinding(binding);
|
|
101
|
+
}
|
|
102
|
+
if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee))
|
|
103
|
+
return null;
|
|
104
|
+
const method = ACQUIRE_METHODS.find((candidate) => candidate ===
|
|
105
|
+
semanticStaticPropertyName(callee.property, callee.computed));
|
|
106
|
+
if (method === undefined || !t.isIdentifier(callee.object))
|
|
107
|
+
return null;
|
|
108
|
+
const binding = resolveSemanticBindingState(state, callee.object, callee.object.name);
|
|
109
|
+
return resourceNamespace(binding) ? method : null;
|
|
110
|
+
};
|
|
111
|
+
const methodFromBinding = (binding) => {
|
|
112
|
+
for (const { specifier, importedPath } of binding?.directOrigins ?? []) {
|
|
113
|
+
if (!resourceModule(specifier))
|
|
114
|
+
continue;
|
|
115
|
+
const method = ACQUIRE_METHODS.find((candidate) => candidate === importedPath.at(-1));
|
|
116
|
+
if (method !== undefined)
|
|
117
|
+
return method;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
const resourceNamespace = (binding) => binding?.directOrigins.some(({ specifier, importedPath }) => resourceModule(specifier) && importedPath.length === 0) ?? false;
|
|
122
|
+
const resourceModule = (specifier) => [
|
|
123
|
+
"fs",
|
|
124
|
+
"fs/promises",
|
|
125
|
+
"net",
|
|
126
|
+
"node:fs",
|
|
127
|
+
"node:fs/promises",
|
|
128
|
+
"node:net",
|
|
129
|
+
"node:tls",
|
|
130
|
+
"tls",
|
|
131
|
+
].includes(specifier);
|
|
132
|
+
const resourceLimitReached = (retained, state) => dataEffectLimitReached(retained, state.limits.maxResourceOperations, "maxResourceOperations", state);
|
|
133
|
+
const resourceCandidateId = (candidate) => `resource:acquire:${String(candidate.node.start ?? -1)}:${String(candidate.node.end ?? -1)}`;
|
|
134
|
+
const memberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
|
|
135
|
+
? node.callee
|
|
136
|
+
: null;
|
|
@@ -3,7 +3,7 @@ import * as t from "@babel/types";
|
|
|
3
3
|
import { inspectElectronStaticNode } from "./electronStaticAnalysis.js";
|
|
4
4
|
import { detectVendors, failedJavaScriptStaticAnalysis, registrationKey, sortedUnique, stringValue, } from "./javascriptStaticAnalysisHelpers.js";
|
|
5
5
|
import { addReference, addSourceMapDirectives, inspectCall, inspectRouteProperty, inspectRoleProperty, } from "./javascriptStaticAnalysisCalls.js";
|
|
6
|
-
import { inspectBundlerRegistration } from "./javascriptStaticAnalysisBundler.js";
|
|
6
|
+
import { inspectBundlerRegistration, inspectEsbuildWrapper, } from "./javascriptStaticAnalysisBundler.js";
|
|
7
7
|
import { finalizeLocatedFindings } from "./javascriptStaticAnalysisFindings.js";
|
|
8
8
|
import { createJavaScriptAnalysisAccumulator, } from "./javascriptStaticAnalysisState.js";
|
|
9
9
|
/** Parse one bounded JavaScript artifact and recover static structure only. */
|
|
@@ -100,10 +100,11 @@ const inspectNode = (source, node, accumulator, limits) => {
|
|
|
100
100
|
inspectElectronStaticNode(node, findings);
|
|
101
101
|
if (t.isCallExpression(node)) {
|
|
102
102
|
inspectBundlerRegistration(source, node, accumulator, limits);
|
|
103
|
-
|
|
103
|
+
inspectEsbuildWrapper(source, node, accumulator, limits);
|
|
104
|
+
inspectCall(source, node, findings);
|
|
104
105
|
}
|
|
105
106
|
else if (t.isNewExpression(node))
|
|
106
|
-
inspectCall(node, findings);
|
|
107
|
+
inspectCall(source, node, findings);
|
|
107
108
|
if ((t.isImportDeclaration(node) || t.isExportAllDeclaration(node)) &&
|
|
108
109
|
node.source !== undefined)
|
|
109
110
|
addReference(findings, {
|
|
@@ -61,6 +61,44 @@ export const inspectBundlerRegistration = (source, call, accumulator, limits) =>
|
|
|
61
61
|
};
|
|
62
62
|
addBoundedFinding(accumulator, `registration\0${registrationKey(registration)}`, limits.maxFindings, () => accumulator.registrations.push(registration));
|
|
63
63
|
};
|
|
64
|
+
/** Inspect esbuild helper wrappers and recover their literal module tables. */
|
|
65
|
+
export const inspectEsbuildWrapper = (source, call, accumulator, limits) => {
|
|
66
|
+
const name = calleeName(call.callee);
|
|
67
|
+
const wrapperKind = name === "__commonJS" || name.endsWith(".__commonJS")
|
|
68
|
+
? "commonjs"
|
|
69
|
+
: name === "__esm" || name.endsWith(".__esm")
|
|
70
|
+
? "esm"
|
|
71
|
+
: null;
|
|
72
|
+
const table = call.arguments[0];
|
|
73
|
+
if (wrapperKind === null || !t.isObjectExpression(table))
|
|
74
|
+
return;
|
|
75
|
+
const recovered = recoverBundlerModules(source, table, accumulator, limits);
|
|
76
|
+
if (recovered.modules.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
if (recovered.omittedAsyncChunkKeys > 0) {
|
|
79
|
+
accumulator.droppedFindings += recovered.omittedAsyncChunkKeys;
|
|
80
|
+
accumulator.structuralTruncation = true;
|
|
81
|
+
}
|
|
82
|
+
accumulator.unknownFindings += recovered.unknownAsyncChunkKeys;
|
|
83
|
+
const registration = {
|
|
84
|
+
bundler: "esbuild",
|
|
85
|
+
runtime: `esbuild-${wrapperKind}`,
|
|
86
|
+
chunk_keys: [`${wrapperKind}@${String(call.start ?? 0)}`],
|
|
87
|
+
omitted_chunk_keys: 0,
|
|
88
|
+
unknown_chunk_keys: 0,
|
|
89
|
+
runtime_require_name: null,
|
|
90
|
+
runtime_module_cache_status: "not-observed",
|
|
91
|
+
entry_module_keys: [],
|
|
92
|
+
omitted_entry_module_keys: 0,
|
|
93
|
+
unknown_entry_module_keys: 0,
|
|
94
|
+
async_chunk_keys: recovered.asyncChunkKeys,
|
|
95
|
+
omitted_async_chunk_keys: recovered.omittedAsyncChunkKeys,
|
|
96
|
+
unknown_async_chunk_keys: recovered.unknownAsyncChunkKeys,
|
|
97
|
+
modules: recovered.modules.sort((left, right) => compareCodePoints(left.module_key, right.module_key)),
|
|
98
|
+
location: range(call),
|
|
99
|
+
};
|
|
100
|
+
addBoundedFinding(accumulator, `registration\0${registrationKey(registration)}`, limits.maxFindings, () => accumulator.registrations.push(registration));
|
|
101
|
+
};
|
|
64
102
|
const recoverBundlerModules = (source, table, accumulator, limits) => {
|
|
65
103
|
const modules = [];
|
|
66
104
|
const asyncChunkKeys = [];
|
|
@@ -2,7 +2,7 @@ import * as t from "@babel/types";
|
|
|
2
2
|
import { addBoundedFinding, addLocatedFinding, moduleAtOffset, } from "./javascriptStaticAnalysisFindings.js";
|
|
3
3
|
import { STATIC_ROUTE_CALL_NAMES, argumentValue, calleeName, endpointArgument as endpointArgumentHelper, prefixedArgument, propertyName, range, rangeForOffsets, sanitizeCandidate, sourceSlice, staticPath, staticPathResolutionContext, storageKind, stringValue, } from "./javascriptStaticAnalysisHelpers.js";
|
|
4
4
|
/** Inspect one call or new expression for references, endpoints, storage, and roles. */
|
|
5
|
-
export const inspectCall = (node, context) => {
|
|
5
|
+
export const inspectCall = (source, node, context) => {
|
|
6
6
|
const { accumulator } = context;
|
|
7
7
|
const name = calleeName(node.callee);
|
|
8
8
|
const first = stringValue(node.arguments[0]);
|
|
@@ -22,6 +22,8 @@ export const inspectCall = (node, context) => {
|
|
|
22
22
|
kind: "dynamic-import",
|
|
23
23
|
specifier: chunkLoad.specifier,
|
|
24
24
|
});
|
|
25
|
+
for (const specifier of vitePreloadDependencySpecifiers(source, node, name))
|
|
26
|
+
addReference(context, { node, kind: "dynamic-import", specifier });
|
|
25
27
|
if (name === "Worker" || name.endsWith(".Worker"))
|
|
26
28
|
addReference(context, { node, kind: "worker", specifier: first });
|
|
27
29
|
if (name.endsWith("serviceWorker.register"))
|
|
@@ -73,6 +75,26 @@ const chunkLoadSpecifier = (node, name, moduleRequireName) => {
|
|
|
73
75
|
return null;
|
|
74
76
|
return { specifier: prefixedArgument("chunk:", node.arguments[0]) };
|
|
75
77
|
};
|
|
78
|
+
const vitePreloadDependencySpecifiers = (source, node, name) => {
|
|
79
|
+
if (name !== "__vite__mapDeps" || !t.isArrayExpression(node.arguments[0]))
|
|
80
|
+
return [];
|
|
81
|
+
const indexes = node.arguments[0].elements.flatMap((element) => t.isNumericLiteral(element) ? [element.value] : []);
|
|
82
|
+
if (indexes.length !== node.arguments[0].elements.length)
|
|
83
|
+
return [];
|
|
84
|
+
const table = vitePreloadDependencyTable(source);
|
|
85
|
+
return indexes.flatMap((index) => table[index] ?? []);
|
|
86
|
+
};
|
|
87
|
+
const vitePreloadDependencyTable = (source) => {
|
|
88
|
+
const match = /\.\s*f\s*\|\|\s*\(\s*[^)]*?\.f\s*=\s*\[([\s\S]*?)\]\s*\)/u.exec(source);
|
|
89
|
+
const body = match?.[1];
|
|
90
|
+
if (body === undefined)
|
|
91
|
+
return [];
|
|
92
|
+
return [
|
|
93
|
+
...body.matchAll(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gu),
|
|
94
|
+
]
|
|
95
|
+
.map((literal) => (literal[1] ?? literal[2] ?? "").slice(0, 4_096))
|
|
96
|
+
.slice(0, 256);
|
|
97
|
+
};
|
|
76
98
|
const inspectEndpointCall = (node, name, first, context) => {
|
|
77
99
|
const route = STATIC_ROUTE_CALL_NAMES.some((candidate) => name === candidate || name.endsWith(`.${candidate}`));
|
|
78
100
|
if (route && first !== undefined)
|
|
@@ -294,6 +294,12 @@ export const rangeForOffsets = (source, start, end) => ({
|
|
|
294
294
|
start: pointForOffset(source, start),
|
|
295
295
|
end: pointForOffset(source, end),
|
|
296
296
|
});
|
|
297
|
+
/** Compare two exact source ranges. */
|
|
298
|
+
export const sourceRangesEqual = (left, right) => left !== null &&
|
|
299
|
+
left.start.line === right.start.line &&
|
|
300
|
+
left.start.column === right.start.column &&
|
|
301
|
+
left.end.line === right.end.line &&
|
|
302
|
+
left.end.column === right.end.column;
|
|
297
303
|
const pointForOffset = (source, offset) => {
|
|
298
304
|
const before = source.slice(0, offset);
|
|
299
305
|
const lines = before.split("\n");
|
|
@@ -335,6 +341,8 @@ const vendorPatterns = [
|
|
|
335
341
|
{ name: "webpack", patterns: ["__webpack_require__", "webpackChunk"] },
|
|
336
342
|
{ name: "Rspack", patterns: ["__webpack_require__.f", "rspackChunk"] },
|
|
337
343
|
{ name: "Vite", patterns: ["__vite__", "import.meta.hot"] },
|
|
344
|
+
{ name: "Rollup", patterns: ["ROLLUP_FILE_URL", "import.meta.ROLLUP"] },
|
|
345
|
+
{ name: "esbuild", patterns: ["__commonJS", "__esm"] },
|
|
338
346
|
{ name: "React", patterns: ["React.createElement", "react/jsx-runtime"] },
|
|
339
347
|
{ name: "Vue", patterns: ["__VUE__", "createApp("] },
|
|
340
348
|
{ name: "Next.js", patterns: ["__NEXT_DATA__", "/_next/"] },
|