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,125 @@
|
|
|
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
|
+
/** Resolve a direct or namespace-imported built-in method. */
|
|
6
|
+
export const builtinDataEffectMethod = (callee, state, specifiers) => {
|
|
7
|
+
if (t.isIdentifier(callee)) {
|
|
8
|
+
const binding = resolveSemanticBindingState(state, callee, callee.name);
|
|
9
|
+
return moduleMethod(binding, specifiers);
|
|
10
|
+
}
|
|
11
|
+
if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee))
|
|
12
|
+
return null;
|
|
13
|
+
if (!t.isIdentifier(callee.object))
|
|
14
|
+
return null;
|
|
15
|
+
const binding = resolveSemanticBindingState(state, callee.object, callee.object.name);
|
|
16
|
+
const namespace = binding?.directOrigins.some(({ specifier, importedPath }) => specifiers.includes(specifier) && importedPath.length === 0);
|
|
17
|
+
return namespace
|
|
18
|
+
? semanticStaticPropertyName(callee.property, callee.computed)
|
|
19
|
+
: null;
|
|
20
|
+
};
|
|
21
|
+
const moduleMethod = (binding, specifiers) => {
|
|
22
|
+
for (const { specifier, importedPath } of binding?.directOrigins ?? []) {
|
|
23
|
+
if (!specifiers.includes(specifier))
|
|
24
|
+
continue;
|
|
25
|
+
return importedPath.at(-1) ?? null;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
/** Recognize global or node:process aliases without accepting shadowing. */
|
|
30
|
+
export const isSemanticProcessObject = (node, state) => {
|
|
31
|
+
if (!t.isIdentifier(node))
|
|
32
|
+
return false;
|
|
33
|
+
const binding = resolveSemanticBindingState(state, node, node.name);
|
|
34
|
+
if (binding === undefined)
|
|
35
|
+
return (node.name === "process" &&
|
|
36
|
+
!semanticResolutionBlocked(state, node, node.name));
|
|
37
|
+
return binding.directOrigins.some(({ specifier }) => specifier === "process" || specifier === "node:process");
|
|
38
|
+
};
|
|
39
|
+
/** Resolve the nearest direct variable/assignment owner before a callable. */
|
|
40
|
+
export const outerDataEffectBinding = (node, context) => {
|
|
41
|
+
for (let index = context.ancestors.length - 1; index >= 0; index -= 1) {
|
|
42
|
+
const ancestor = context.ancestors[index];
|
|
43
|
+
if (ancestor === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
if (t.isVariableDeclarator(ancestor) &&
|
|
46
|
+
t.isNode(ancestor.init) &&
|
|
47
|
+
containsSemanticNode(ancestor.init, node) &&
|
|
48
|
+
t.isIdentifier(ancestor.id))
|
|
49
|
+
return (resolveSemanticBindingState(context.state, ancestor.id, ancestor.id.name)?.bindingId ?? null);
|
|
50
|
+
if (t.isAssignmentExpression(ancestor) &&
|
|
51
|
+
containsSemanticNode(ancestor.right, node) &&
|
|
52
|
+
t.isIdentifier(ancestor.left))
|
|
53
|
+
return (resolveSemanticBindingState(context.state, ancestor.left, ancestor.left.name)?.bindingId ?? null);
|
|
54
|
+
if (semanticCallableIdForNode(ancestor) !== null)
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
/** Traverse with exact callable and ancestor stacks. */
|
|
60
|
+
export const traverseDataEffects = (program, context, visit) => {
|
|
61
|
+
context.callableStack.length = 0;
|
|
62
|
+
context.ancestors.length = 0;
|
|
63
|
+
traverseJavaScriptAst(program, {
|
|
64
|
+
enter: (node, parent) => {
|
|
65
|
+
const callableId = semanticCallableIdForNode(node);
|
|
66
|
+
if (callableId !== null && context.callableIds.has(callableId))
|
|
67
|
+
context.callableStack.push(callableId);
|
|
68
|
+
visit(node, parent);
|
|
69
|
+
context.ancestors.push(node);
|
|
70
|
+
},
|
|
71
|
+
exit: (node) => {
|
|
72
|
+
context.ancestors.pop();
|
|
73
|
+
const callableId = semanticCallableIdForNode(node);
|
|
74
|
+
if (callableId !== null && context.callableStack.at(-1) === callableId)
|
|
75
|
+
context.callableStack.pop();
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
/** Resolve one direct identifier argument to a lexical binding. */
|
|
80
|
+
export const dataEffectArgumentBindingId = (node, state) => t.isIdentifier(node)
|
|
81
|
+
? (resolveSemanticBindingState(state, node, node.name)?.bindingId ?? null)
|
|
82
|
+
: null;
|
|
83
|
+
/** Read one JSON primitive default without evaluating code. */
|
|
84
|
+
export const dataEffectPrimitive = (node) => {
|
|
85
|
+
if (t.isStringLiteral(node) || t.isNumericLiteral(node))
|
|
86
|
+
return node.value;
|
|
87
|
+
if (t.isBooleanLiteral(node))
|
|
88
|
+
return node.value;
|
|
89
|
+
return null;
|
|
90
|
+
};
|
|
91
|
+
/** Read one static string or expression-free template. */
|
|
92
|
+
export const dataEffectLiteralString = (node) => {
|
|
93
|
+
if (t.isStringLiteral(node))
|
|
94
|
+
return node.value;
|
|
95
|
+
if (t.isTemplateLiteral(node) && node.expressions.length === 0)
|
|
96
|
+
return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? null;
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
/** Read a member callee from call or construction syntax. */
|
|
100
|
+
export const dataEffectMemberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
|
|
101
|
+
? node.callee
|
|
102
|
+
: null;
|
|
103
|
+
/** Read a parent member's object when present. */
|
|
104
|
+
export const dataEffectMemberObject = (node) => node !== null &&
|
|
105
|
+
(t.isMemberExpression(node) || t.isOptionalMemberExpression(node))
|
|
106
|
+
? node.object
|
|
107
|
+
: null;
|
|
108
|
+
/** Compare Babel source offsets for exact containment. */
|
|
109
|
+
export const containsSemanticNode = (outer, inner) => outer.start !== null &&
|
|
110
|
+
outer.start !== undefined &&
|
|
111
|
+
outer.end !== null &&
|
|
112
|
+
outer.end !== undefined &&
|
|
113
|
+
inner.start !== null &&
|
|
114
|
+
inner.start !== undefined &&
|
|
115
|
+
inner.end !== null &&
|
|
116
|
+
inner.end !== undefined &&
|
|
117
|
+
outer.start <= inner.start &&
|
|
118
|
+
outer.end >= inner.end;
|
|
119
|
+
/** Record one bounded data-effect frontier. */
|
|
120
|
+
export const dataEffectLimitReached = (retained, limit, name, state) => {
|
|
121
|
+
if (retained < limit)
|
|
122
|
+
return false;
|
|
123
|
+
reachSemanticLimit(state, name);
|
|
124
|
+
return true;
|
|
125
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { semanticStaticPropertyName } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, semanticResolutionBlocked, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { builtinDataEffectMethod as builtinMethod, containsSemanticNode as containsNode, dataEffectArgumentBindingId as argumentBindingId, dataEffectLimitReached as limitReached, dataEffectLiteralString as literalString, dataEffectMemberCallee as memberCallee, dataEffectMemberObject as memberObject, dataEffectPrimitive as primitiveValue, isSemanticProcessObject as isProcessObject, outerDataEffectBinding as outerAssignedBinding, traverseDataEffects as traverseWithContext, } from "./javascriptSemanticDataEffectHelpers.js";
|
|
5
|
+
import { compareCodePoints, propertyName, range, } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
/** Recover configuration, request, and boundary candidates from inert syntax. */
|
|
7
|
+
export const collectJavaScriptSemanticDataEffects = (program, state, callables) => {
|
|
8
|
+
const context = {
|
|
9
|
+
state,
|
|
10
|
+
callableIds: new Set(callables.map(({ callableId }) => callableId)),
|
|
11
|
+
callableStack: [],
|
|
12
|
+
ancestors: [],
|
|
13
|
+
};
|
|
14
|
+
const configuration = collectConfigurations(program, context);
|
|
15
|
+
const requests = collectRequests(program, context);
|
|
16
|
+
return {
|
|
17
|
+
configurationOperations: [
|
|
18
|
+
...configuration.map(({ operation }) => operation),
|
|
19
|
+
...collectDefaults(program, context, configuration),
|
|
20
|
+
],
|
|
21
|
+
requestOperations: [
|
|
22
|
+
...requests.map(({ operation }) => operation),
|
|
23
|
+
...collectResponseConsumers(program, context, requests),
|
|
24
|
+
],
|
|
25
|
+
boundaryOperations: collectBoundaries(program, context),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const collectConfigurations = (program, context) => {
|
|
29
|
+
const output = [];
|
|
30
|
+
traverseWithContext(program, context, (node, parent) => {
|
|
31
|
+
const details = configurationDetails(node, parent, context);
|
|
32
|
+
if (details === null)
|
|
33
|
+
return;
|
|
34
|
+
if (limitReached(output.length, context.state.limits.maxConfigurationOperations, "maxConfigurationOperations", context.state))
|
|
35
|
+
return;
|
|
36
|
+
const operation = {
|
|
37
|
+
configId: `config:${details.kind}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
38
|
+
kind: details.kind,
|
|
39
|
+
location: range(node),
|
|
40
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
41
|
+
resultBindingId: outerAssignedBinding(node, context),
|
|
42
|
+
key: details.key,
|
|
43
|
+
sourceConfigId: null,
|
|
44
|
+
value: null,
|
|
45
|
+
resolution: details.resolution,
|
|
46
|
+
};
|
|
47
|
+
output.push({ node, operation });
|
|
48
|
+
});
|
|
49
|
+
return output;
|
|
50
|
+
};
|
|
51
|
+
const configurationDetails = (node, parent, context) => {
|
|
52
|
+
if ((t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) &&
|
|
53
|
+
!(memberObject(parent) === node)) {
|
|
54
|
+
const processMember = processConfigurationMember(node, context.state);
|
|
55
|
+
if (processMember !== null)
|
|
56
|
+
return processMember;
|
|
57
|
+
}
|
|
58
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
59
|
+
return null;
|
|
60
|
+
const fileMethod = builtinMethod(node.callee, context.state, [
|
|
61
|
+
"fs",
|
|
62
|
+
"node:fs",
|
|
63
|
+
]);
|
|
64
|
+
return fileMethod === "readFile" || fileMethod === "readFileSync"
|
|
65
|
+
? {
|
|
66
|
+
kind: "file",
|
|
67
|
+
key: literalString(node.arguments[0]),
|
|
68
|
+
resolution: literalString(node.arguments[0]) === null ? "partial" : "complete",
|
|
69
|
+
}
|
|
70
|
+
: null;
|
|
71
|
+
};
|
|
72
|
+
const processConfigurationMember = (node, state) => {
|
|
73
|
+
const object = node.object;
|
|
74
|
+
if (!t.isMemberExpression(object) && !t.isOptionalMemberExpression(object))
|
|
75
|
+
return null;
|
|
76
|
+
if (!isProcessObject(object.object, state))
|
|
77
|
+
return null;
|
|
78
|
+
const family = semanticStaticPropertyName(object.property, object.computed);
|
|
79
|
+
if (family !== "env" && family !== "argv")
|
|
80
|
+
return null;
|
|
81
|
+
const key = semanticStaticPropertyName(node.property, node.computed) || null;
|
|
82
|
+
return {
|
|
83
|
+
kind: family === "env" ? "environment" : "argv",
|
|
84
|
+
key,
|
|
85
|
+
resolution: key === null ? "partial" : "complete",
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const collectDefaults = (program, context, configurations) => {
|
|
89
|
+
const output = [];
|
|
90
|
+
traverseWithContext(program, context, (node) => {
|
|
91
|
+
if (!t.isLogicalExpression(node) ||
|
|
92
|
+
(node.operator !== "??" && node.operator !== "||"))
|
|
93
|
+
return;
|
|
94
|
+
const sources = configurations.filter(({ node: source }) => containsNode(node.left, source));
|
|
95
|
+
if (sources.length !== 1 || sources[0] === undefined)
|
|
96
|
+
return;
|
|
97
|
+
if (limitReached(configurations.length + output.length, context.state.limits.maxConfigurationOperations, "maxConfigurationOperations", context.state))
|
|
98
|
+
return;
|
|
99
|
+
output.push({
|
|
100
|
+
configId: `config:default:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
101
|
+
kind: "default",
|
|
102
|
+
location: range(node.right),
|
|
103
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
104
|
+
resultBindingId: outerAssignedBinding(node, context),
|
|
105
|
+
key: sources[0].operation.key,
|
|
106
|
+
sourceConfigId: sources[0].operation.configId,
|
|
107
|
+
value: primitiveValue(node.right),
|
|
108
|
+
resolution: primitiveValue(node.right) === null ? "partial" : "complete",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
return output;
|
|
112
|
+
};
|
|
113
|
+
const collectRequests = (program, context) => {
|
|
114
|
+
const output = [];
|
|
115
|
+
traverseWithContext(program, context, (node) => {
|
|
116
|
+
if (!t.isCallExpression(node) &&
|
|
117
|
+
!t.isOptionalCallExpression(node) &&
|
|
118
|
+
!t.isNewExpression(node))
|
|
119
|
+
return;
|
|
120
|
+
const method = requestMethod(node, context.state);
|
|
121
|
+
if (method === null)
|
|
122
|
+
return;
|
|
123
|
+
if (limitReached(output.length, context.state.limits.maxRequestOperations, "maxRequestOperations", context.state))
|
|
124
|
+
return;
|
|
125
|
+
const endpoint = requestEndpoint(node);
|
|
126
|
+
output.push({
|
|
127
|
+
node,
|
|
128
|
+
operation: {
|
|
129
|
+
requestId: `request:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
130
|
+
kind: "request",
|
|
131
|
+
method,
|
|
132
|
+
location: range(node),
|
|
133
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
134
|
+
resultBindingId: outerAssignedBinding(node, context),
|
|
135
|
+
linkedRequestIds: [],
|
|
136
|
+
endpoint,
|
|
137
|
+
fields: requestFields(node, method, context.state),
|
|
138
|
+
resolution: endpoint === null ? "partial" : "complete",
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
return output;
|
|
143
|
+
};
|
|
144
|
+
const collectResponseConsumers = (program, context, requests) => {
|
|
145
|
+
const requestsByBinding = new Map();
|
|
146
|
+
for (const request of requests) {
|
|
147
|
+
const bindingId = request.operation.resultBindingId;
|
|
148
|
+
if (bindingId === null)
|
|
149
|
+
continue;
|
|
150
|
+
const existing = requestsByBinding.get(bindingId) ?? [];
|
|
151
|
+
existing.push(request);
|
|
152
|
+
requestsByBinding.set(bindingId, existing);
|
|
153
|
+
}
|
|
154
|
+
const output = [];
|
|
155
|
+
traverseWithContext(program, context, (node) => {
|
|
156
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
157
|
+
return;
|
|
158
|
+
const member = memberCallee(node);
|
|
159
|
+
if (member === null || !t.isIdentifier(member.object))
|
|
160
|
+
return;
|
|
161
|
+
const method = semanticStaticPropertyName(member.property, member.computed);
|
|
162
|
+
if (method !== "json" && method !== "text" && method !== "arrayBuffer")
|
|
163
|
+
return;
|
|
164
|
+
const binding = resolveSemanticBindingState(context.state, member.object, member.object.name);
|
|
165
|
+
const linked = binding === undefined
|
|
166
|
+
? []
|
|
167
|
+
: (requestsByBinding.get(binding.bindingId) ?? []);
|
|
168
|
+
if (linked.length === 0)
|
|
169
|
+
return;
|
|
170
|
+
if (limitReached(requests.length + output.length, context.state.limits.maxRequestOperations, "maxRequestOperations", context.state))
|
|
171
|
+
return;
|
|
172
|
+
output.push({
|
|
173
|
+
requestId: `response:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
174
|
+
kind: "response-consumer",
|
|
175
|
+
method,
|
|
176
|
+
location: range(node),
|
|
177
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
178
|
+
resultBindingId: outerAssignedBinding(node, context),
|
|
179
|
+
linkedRequestIds: linked
|
|
180
|
+
.map(({ operation }) => operation.requestId)
|
|
181
|
+
.sort(compareCodePoints),
|
|
182
|
+
endpoint: null,
|
|
183
|
+
fields: [],
|
|
184
|
+
resolution: linked.length === 1 ? "complete" : "partial",
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
return output;
|
|
188
|
+
};
|
|
189
|
+
const collectBoundaries = (program, context) => {
|
|
190
|
+
const output = [];
|
|
191
|
+
traverseWithContext(program, context, (node) => {
|
|
192
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
193
|
+
return;
|
|
194
|
+
const details = boundaryDetails(node, context.state);
|
|
195
|
+
if (details === null)
|
|
196
|
+
return;
|
|
197
|
+
if (limitReached(output.length, context.state.limits.maxBoundaryOperations, "maxBoundaryOperations", context.state))
|
|
198
|
+
return;
|
|
199
|
+
output.push({
|
|
200
|
+
boundaryId: `boundary:${details.kind}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
201
|
+
kind: details.kind,
|
|
202
|
+
method: details.method,
|
|
203
|
+
location: range(node),
|
|
204
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
205
|
+
sourceBindingId: argumentBindingId(node.arguments[0], context.state),
|
|
206
|
+
resultBindingId: outerAssignedBinding(node, context),
|
|
207
|
+
resolution: details.resolution,
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
return output;
|
|
211
|
+
};
|
|
212
|
+
const boundaryDetails = (node, state) => {
|
|
213
|
+
if (t.isIdentifier(node.callee)) {
|
|
214
|
+
const name = node.callee.name;
|
|
215
|
+
const unbound = resolveSemanticBindingState(state, node.callee, name) === undefined &&
|
|
216
|
+
!semanticResolutionBlocked(state, node.callee, name);
|
|
217
|
+
if (unbound &&
|
|
218
|
+
["String", "Number", "Boolean", "parseInt", "parseFloat"].includes(name))
|
|
219
|
+
return { kind: "coerce", method: name, resolution: "complete" };
|
|
220
|
+
}
|
|
221
|
+
const member = memberCallee(node);
|
|
222
|
+
if (member === null)
|
|
223
|
+
return null;
|
|
224
|
+
const method = semanticStaticPropertyName(member.property, member.computed);
|
|
225
|
+
if (method === "parse" &&
|
|
226
|
+
t.isIdentifier(member.object, { name: "JSON" }) &&
|
|
227
|
+
resolveSemanticBindingState(state, member.object, "JSON") === undefined &&
|
|
228
|
+
!semanticResolutionBlocked(state, member.object, "JSON"))
|
|
229
|
+
return { kind: "parse", method: "JSON.parse", resolution: "complete" };
|
|
230
|
+
if (method === "parse" || method === "safeParse")
|
|
231
|
+
return { kind: "parse", method, resolution: "partial" };
|
|
232
|
+
if (method === "validate" || method === "assert" || method === "is")
|
|
233
|
+
return { kind: "validate", method, resolution: "partial" };
|
|
234
|
+
return null;
|
|
235
|
+
};
|
|
236
|
+
const requestMethod = (node, state) => {
|
|
237
|
+
if (t.isIdentifier(node.callee)) {
|
|
238
|
+
const name = node.callee.name;
|
|
239
|
+
const unbound = resolveSemanticBindingState(state, node.callee, name) === undefined &&
|
|
240
|
+
!semanticResolutionBlocked(state, node.callee, name);
|
|
241
|
+
if (!t.isNewExpression(node) && unbound && name === "fetch")
|
|
242
|
+
return "fetch";
|
|
243
|
+
if (t.isNewExpression(node) && unbound && name === "WebSocket")
|
|
244
|
+
return "WebSocket";
|
|
245
|
+
}
|
|
246
|
+
const member = memberCallee(node);
|
|
247
|
+
if (member === null || !t.isIdentifier(member.object))
|
|
248
|
+
return null;
|
|
249
|
+
const method = semanticStaticPropertyName(member.property, member.computed);
|
|
250
|
+
if (method !== "request" && method !== "get")
|
|
251
|
+
return null;
|
|
252
|
+
const binding = resolveSemanticBindingState(state, member.object, member.object.name);
|
|
253
|
+
return binding?.directOrigins.some(({ specifier, importedPath }) => ["http", "https", "node:http", "node:https"].includes(specifier) &&
|
|
254
|
+
importedPath.length === 0)
|
|
255
|
+
? method
|
|
256
|
+
: null;
|
|
257
|
+
};
|
|
258
|
+
const requestFields = (node, method, state) => {
|
|
259
|
+
const candidate = method === "fetch" || method === "WebSocket"
|
|
260
|
+
? node.arguments[1]
|
|
261
|
+
: t.isObjectExpression(node.arguments[0])
|
|
262
|
+
? node.arguments[0]
|
|
263
|
+
: node.arguments[1];
|
|
264
|
+
if (!t.isObjectExpression(candidate))
|
|
265
|
+
return [];
|
|
266
|
+
return candidate.properties.flatMap((property) => {
|
|
267
|
+
if (!t.isObjectProperty(property))
|
|
268
|
+
return [];
|
|
269
|
+
const name = propertyName(property.key);
|
|
270
|
+
if (name.length === 0)
|
|
271
|
+
return [];
|
|
272
|
+
return [
|
|
273
|
+
{
|
|
274
|
+
name,
|
|
275
|
+
sourceBindingId: t.isIdentifier(property.value)
|
|
276
|
+
? (resolveSemanticBindingState(state, property.value, property.value.name)?.bindingId ?? null)
|
|
277
|
+
: null,
|
|
278
|
+
},
|
|
279
|
+
];
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
const requestEndpoint = (node) => t.isObjectExpression(node.arguments[0])
|
|
283
|
+
? null
|
|
284
|
+
: literalString(node.arguments[0]);
|
|
@@ -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:
|
|
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: [
|