rea-agents 2.2.0 → 2.3.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 +56 -9
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
- package/dist/application/BinarySession.js +9 -2
- package/dist/application/BinarySessionStatus.js +39 -1
- package/dist/application/CapabilityInventory.js +15 -0
- package/dist/application/CommandShimReplay.js +6 -3
- package/dist/application/CompositeProvider.js +5 -2
- package/dist/application/DirectAnalysis.js +1 -20
- package/dist/application/DirectAnalysisStatus.js +83 -0
- package/dist/application/Doctor.js +34 -25
- package/dist/application/DoctorDiagnostics.js +1 -4
- package/dist/application/DoctorProjection.js +43 -0
- package/dist/application/DoctorScope.js +102 -0
- package/dist/application/EvidenceReferenceResolver.js +26 -0
- package/dist/application/JavaScriptApplicationEvidence.js +1 -1
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
- package/dist/application/JavaScriptApplicationService.js +3 -2
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
- package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
- package/dist/application/JavaScriptModuleRelationships.js +25 -1
- package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
- package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
- package/dist/application/JavaScriptSemanticTraceService.js +45 -0
- package/dist/application/LoopbackReplay.js +277 -133
- package/dist/application/LoopbackReplayRecorder.js +101 -0
- package/dist/application/PermissionAuthority.js +7 -3
- package/dist/application/ProcessCaptureJournal.js +95 -0
- package/dist/application/ProcessCaptureLifecycle.js +24 -13
- package/dist/application/ProcessCheckpoints.js +30 -4
- package/dist/application/ProcessCli.js +26 -4
- package/dist/application/ProcessHarness.js +20 -50
- package/dist/application/ProcessNormalization.js +3 -2
- package/dist/application/ProcessReactiveCoordinator.js +218 -0
- package/dist/application/ProcessReactiveEffects.js +133 -0
- package/dist/application/ProcessReactiveObservations.js +20 -0
- package/dist/application/ProcessSampling.js +2 -0
- package/dist/application/SessionProviderRouter.js +7 -6
- package/dist/application/Setup.js +11 -10
- package/dist/application/SetupPlan.js +1 -1
- package/dist/application/SetupRegistrationEnvironment.js +12 -0
- package/dist/application/TerminalRenderer.js +3 -1
- package/dist/browser/CdpEndpoint.js +37 -4
- package/dist/catalogIdentity.js +16 -7
- package/dist/cli/coreAnalysisCommands.js +34 -2
- package/dist/cli/javascriptApplicationAnalysis.js +19 -0
- package/dist/cli/setupCommands.js +81 -32
- package/dist/cli/utilityCommands.js +10 -2
- package/dist/cliApplicationCommands.js +79 -16
- package/dist/cliCommandNames.js +3 -0
- package/dist/cliElectronCommands.js +23 -33
- package/dist/cliObservationOptions.js +1 -1
- package/dist/cliOutput.js +56 -0
- package/dist/cliProcessCommands.js +32 -1
- package/dist/cliSetup.js +30 -21
- package/dist/contracts/applicationToolContracts.js +54 -7
- package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
- package/dist/contracts/electronToolContracts.js +53 -32
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
- package/dist/contracts/processCaptureExample.js +1 -0
- package/dist/contracts/replayMachineExample.js +37 -0
- package/dist/contracts/toolContractExamples.js +2 -0
- package/dist/contracts/toolContracts.js +5 -1
- package/dist/contracts/toolEffects.js +3 -0
- package/dist/contracts/toolOutputSchemaGroups.js +2 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
- package/dist/doctorRuntime.js +41 -0
- package/dist/domain/analysisErrorPresentation.js +5 -3
- package/dist/domain/analysisErrorProjection.js +7 -1
- package/dist/domain/changedBehavior.js +5 -4
- package/dist/domain/completionLedgerGeneration.js +190 -0
- package/dist/domain/errors.js +4 -2
- package/dist/domain/evidenceCompletionLedger.js +113 -0
- package/dist/domain/hopperStartupFailure.js +9 -0
- package/dist/domain/javascriptApplicationAnalysis.js +44 -2
- package/dist/domain/javascriptExportShapeComparison.js +145 -0
- package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
- package/dist/domain/javascriptExportShapeSelection.js +132 -0
- package/dist/domain/javascriptExportShapeVariants.js +272 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
- package/dist/domain/javascriptSemanticAnalysis.js +17 -36
- package/dist/domain/javascriptSemanticCallResolution.js +129 -0
- package/dist/domain/javascriptSemanticCalls.js +269 -0
- package/dist/domain/javascriptSemanticGraph.js +356 -0
- package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
- package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
- package/dist/domain/javascriptSemanticIr.js +14 -1
- package/dist/domain/javascriptSemanticPrimitives.js +36 -0
- package/dist/domain/javascriptSemanticProjection.js +36 -4
- package/dist/domain/javascriptSemanticProvenance.js +29 -0
- package/dist/domain/javascriptSemanticQuery.js +294 -0
- package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
- package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
- package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
- package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
- package/dist/domain/javascriptSemanticReturns.js +187 -0
- package/dist/domain/javascriptSemanticState.js +32 -0
- package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
- package/dist/domain/javascriptSemanticValues.js +54 -70
- package/dist/domain/processCapture.js +36 -0
- package/dist/domain/processCaptureValidation.js +101 -12
- package/dist/domain/processComparison.js +78 -25
- package/dist/domain/processObservation.js +153 -0
- package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
- package/dist/domain/processReactiveMatching.js +210 -0
- package/dist/domain/processReactiveRuntime.js +109 -0
- package/dist/domain/processReactiveScenario.js +361 -0
- package/dist/domain/processReactiveScenarioPreflight.js +120 -0
- package/dist/domain/processReactiveTransition.js +159 -0
- package/dist/domain/processScenario.js +12 -0
- package/dist/domain/processTraceComparison.js +43 -0
- package/dist/domain/processTraceDimensionProjection.js +50 -0
- package/dist/domain/processTraceEvaluation.js +312 -0
- package/dist/domain/processTraceSpecification.js +373 -0
- package/dist/domain/reconstructionVerification.js +5 -4
- package/dist/domain/replayMachine.js +18 -2
- package/dist/domain/replayMachineRun.js +216 -0
- package/dist/domain/replayMachineRuntime.js +88 -9
- package/dist/domain/staticRuntimeCorrelation.js +4 -2
- package/dist/evaluation/CodexAgentEval.js +24 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraClient.js +13 -2
- package/dist/ghidra/GhidraDiagnostics.js +1 -0
- package/dist/ghidra/GhidraLauncher.js +7 -3
- package/dist/ghidra/GhidraProvider.js +8 -1
- package/dist/hopper/BridgeLauncher.js +56 -14
- package/dist/hopper/HopperClient.js +23 -10
- package/dist/hopper/HopperDiagnostics.js +14 -0
- package/dist/hopper/HopperProcessDiagnostic.js +14 -0
- package/dist/hopper/HopperProvider.js +8 -1
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
- package/dist/main/transport.js +2 -0
- package/dist/mcpDoctor.js +310 -0
- package/dist/process/ProcessOwnership.js +200 -53
- package/dist/process/ProviderRunLineage.js +21 -0
- package/dist/server/createServer.js +4 -1
- package/dist/server/javascriptApplicationResult.js +22 -1
- package/dist/server/registerApplicationTools/characterization.js +3 -3
- package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
- package/dist/server/registerApplicationTools/compareVersions.js +12 -6
- package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -3
- package/dist/server/registerApplicationTools/helpers.js +37 -25
- package/dist/server/registerApplicationTools/traceFeature.js +9 -5
- package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
- package/dist/server/registerApplicationTools.js +4 -0
- package/dist/server/registerEvidenceResources.js +5 -1
- package/dist/server/registerProcessComparisonTool.js +17 -7
- package/dist/server/registerReplayMachineTool.js +20 -0
- package/dist/server/registerSessionTools.js +12 -7
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/serverIdentity.js +19 -0
- package/package.json +62 -30
- package/scripts/hopper-demo-x11.py +459 -61
- package/scripts/prepack.mjs +55 -0
- package/scripts/prepare.mjs +28 -0
- package/scripts/rea.mjs +26 -5
- package/skills/reverse-engineer-anything/SKILL.md +2 -2
- package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { compareCodePoints } from "./javascriptApplicationGraph.js";
|
|
2
|
+
import { canonicalExportShapeValue, digestExportShapeValue, } from "./javascriptExportShapeComparisonIdentity.js";
|
|
3
|
+
/** Pair variants only through reciprocal unique exact literal discriminants. */
|
|
4
|
+
export const pairJavaScriptExportShapeVariants = (left, right) => {
|
|
5
|
+
const leftKeys = discriminantOccurrences(left);
|
|
6
|
+
const rightKeys = discriminantOccurrences(right);
|
|
7
|
+
const edges = new Map();
|
|
8
|
+
const leftTargets = new Map();
|
|
9
|
+
const rightTargets = new Map();
|
|
10
|
+
for (const [key, leftOccurrence] of leftKeys) {
|
|
11
|
+
const rightOccurrence = rightKeys.get(key);
|
|
12
|
+
if (leftOccurrence.indexes.length !== 1 ||
|
|
13
|
+
rightOccurrence?.indexes.length !== 1)
|
|
14
|
+
continue;
|
|
15
|
+
const leftIndex = leftOccurrence.indexes[0];
|
|
16
|
+
const rightIndex = rightOccurrence.indexes[0];
|
|
17
|
+
if (leftIndex === undefined || rightIndex === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
const edgeKey = `${String(leftIndex)}:${String(rightIndex)}`;
|
|
20
|
+
edges.set(edgeKey, [
|
|
21
|
+
...(edges.get(edgeKey) ?? []),
|
|
22
|
+
leftOccurrence.discriminant,
|
|
23
|
+
]);
|
|
24
|
+
addTarget(leftTargets, leftIndex, rightIndex);
|
|
25
|
+
addTarget(rightTargets, rightIndex, leftIndex);
|
|
26
|
+
}
|
|
27
|
+
const pairs = reciprocalPairs(leftTargets, rightTargets, edges);
|
|
28
|
+
const pairedLeft = new Set(pairs.map(({ leftIndex }) => leftIndex));
|
|
29
|
+
const pairedRight = new Set(pairs.map(({ rightIndex }) => rightIndex));
|
|
30
|
+
return {
|
|
31
|
+
pairs,
|
|
32
|
+
unpairedLeft: indexesNotIn(left, pairedLeft),
|
|
33
|
+
unpairedRight: indexesNotIn(right, pairedRight),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
/** Build bounded-ready field and unpaired-variant changes before caller slicing. */
|
|
37
|
+
export const buildJavaScriptExportShapeChanges = (input) => {
|
|
38
|
+
if (input.leftSelection.selection.status !== "selected" ||
|
|
39
|
+
input.rightSelection.selection.status !== "selected")
|
|
40
|
+
return [selectionUnknownChange(input)];
|
|
41
|
+
if (input.leftShapes.length === 0 || input.rightShapes.length === 0)
|
|
42
|
+
return [emptyShapeUnknownChange(input)];
|
|
43
|
+
const paired = input.pairing.pairs.flatMap((pair) => {
|
|
44
|
+
const left = input.leftShapes[pair.leftIndex];
|
|
45
|
+
const right = input.rightShapes[pair.rightIndex];
|
|
46
|
+
return left === undefined || right === undefined
|
|
47
|
+
? []
|
|
48
|
+
: diffPair(left, right, pair.discriminant, input.evidenceLinks);
|
|
49
|
+
});
|
|
50
|
+
const unpairedLeft = input.pairing.unpairedLeft.flatMap((index) => {
|
|
51
|
+
const shape = input.leftShapes[index];
|
|
52
|
+
return shape === undefined
|
|
53
|
+
? []
|
|
54
|
+
: [unpairedChange("left", shape, input.evidenceLinks)];
|
|
55
|
+
});
|
|
56
|
+
const unpairedRight = input.pairing.unpairedRight.flatMap((index) => {
|
|
57
|
+
const shape = input.rightShapes[index];
|
|
58
|
+
return shape === undefined
|
|
59
|
+
? []
|
|
60
|
+
: [unpairedChange("right", shape, input.evidenceLinks)];
|
|
61
|
+
});
|
|
62
|
+
return [...paired, ...unpairedLeft, ...unpairedRight];
|
|
63
|
+
};
|
|
64
|
+
/** Sort changes by semantic content rather than traversal order. */
|
|
65
|
+
export const compareJavaScriptExportShapeChanges = (left, right) => compareCodePoints(canonicalExportShapeValue(changeSortKey(left)), canonicalExportShapeValue(changeSortKey(right)));
|
|
66
|
+
/** Report whether any retained shape lacks complete property coverage. */
|
|
67
|
+
export const hasPartialJavaScriptExportPropertyCoverage = (shapes) => shapes.some((shape) => shape.property_coverage.some(({ status }) => status === "partial"));
|
|
68
|
+
const discriminantOccurrences = (shapes) => {
|
|
69
|
+
const output = new Map();
|
|
70
|
+
shapes.forEach((shape, index) => {
|
|
71
|
+
for (const field of shape.fields) {
|
|
72
|
+
const value = literalValue(field);
|
|
73
|
+
if (!value.found)
|
|
74
|
+
continue;
|
|
75
|
+
const discriminant = { path: field.path, value: value.value };
|
|
76
|
+
const key = canonicalExportShapeValue(discriminant);
|
|
77
|
+
const current = output.get(key);
|
|
78
|
+
output.set(key, {
|
|
79
|
+
discriminant,
|
|
80
|
+
indexes: [...(current?.indexes ?? []), index],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return output;
|
|
85
|
+
};
|
|
86
|
+
const reciprocalPairs = (leftTargets, rightTargets, edges) => {
|
|
87
|
+
const pairs = [];
|
|
88
|
+
for (const [leftIndex, targets] of leftTargets) {
|
|
89
|
+
if (targets.size !== 1)
|
|
90
|
+
continue;
|
|
91
|
+
const rightIndex = [...targets][0];
|
|
92
|
+
if (rightIndex === undefined ||
|
|
93
|
+
rightTargets.get(rightIndex)?.size !== 1 ||
|
|
94
|
+
!rightTargets.get(rightIndex)?.has(leftIndex))
|
|
95
|
+
continue;
|
|
96
|
+
const discriminants = edges
|
|
97
|
+
.get(`${String(leftIndex)}:${String(rightIndex)}`)
|
|
98
|
+
?.toSorted(compareDiscriminants);
|
|
99
|
+
const discriminant = discriminants?.[0];
|
|
100
|
+
if (discriminant !== undefined)
|
|
101
|
+
pairs.push({ leftIndex, rightIndex, discriminant });
|
|
102
|
+
}
|
|
103
|
+
return pairs.sort((first, second) => compareDiscriminants(first.discriminant, second.discriminant));
|
|
104
|
+
};
|
|
105
|
+
const addTarget = (map, source, target) => {
|
|
106
|
+
const values = map.get(source) ?? new Set();
|
|
107
|
+
values.add(target);
|
|
108
|
+
map.set(source, values);
|
|
109
|
+
};
|
|
110
|
+
const indexesNotIn = (shapes, paired) => shapes.map((_, index) => index).filter((index) => !paired.has(index));
|
|
111
|
+
const compareDiscriminants = (left, right) => compareCodePoints(canonicalExportShapeValue(left), canonicalExportShapeValue(right));
|
|
112
|
+
const selectionUnknownChange = (input) => changeWithId({
|
|
113
|
+
status: "unknown",
|
|
114
|
+
path: "",
|
|
115
|
+
discriminant: null,
|
|
116
|
+
left: selectionAvailability(input.leftSelection.selection, "left"),
|
|
117
|
+
right: selectionAvailability(input.rightSelection.selection, "right"),
|
|
118
|
+
left_source_range: null,
|
|
119
|
+
right_source_range: null,
|
|
120
|
+
evidence_links: input.evidenceLinks,
|
|
121
|
+
limitations: [
|
|
122
|
+
"Both selectors must resolve one exact export with trusted static return shapes before comparison.",
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
const emptyShapeUnknownChange = (input) => changeWithId({
|
|
126
|
+
status: "unknown",
|
|
127
|
+
path: "",
|
|
128
|
+
discriminant: null,
|
|
129
|
+
left: emptyShapeAvailability(input.leftShapes, "left"),
|
|
130
|
+
right: emptyShapeAvailability(input.rightShapes, "right"),
|
|
131
|
+
left_source_range: null,
|
|
132
|
+
right_source_range: null,
|
|
133
|
+
evidence_links: input.evidenceLinks,
|
|
134
|
+
limitations: [
|
|
135
|
+
"At least one selected export has no retained direct return shape.",
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
const diffPair = (left, right, discriminant, evidenceLinks) => {
|
|
139
|
+
const leftFields = new Map(left.fields.map((field) => [field.path, field]));
|
|
140
|
+
const rightFields = new Map(right.fields.map((field) => [field.path, field]));
|
|
141
|
+
const paths = uniqueSorted([...leftFields.keys(), ...rightFields.keys()]);
|
|
142
|
+
return paths.flatMap((path) => {
|
|
143
|
+
const leftField = leftFields.get(path);
|
|
144
|
+
const rightField = rightFields.get(path);
|
|
145
|
+
const status = fieldChangeStatus({
|
|
146
|
+
leftShape: left,
|
|
147
|
+
rightShape: right,
|
|
148
|
+
path,
|
|
149
|
+
leftField,
|
|
150
|
+
rightField,
|
|
151
|
+
});
|
|
152
|
+
if (status === null)
|
|
153
|
+
return [];
|
|
154
|
+
return [
|
|
155
|
+
changeWithId({
|
|
156
|
+
status,
|
|
157
|
+
path,
|
|
158
|
+
discriminant,
|
|
159
|
+
left: fieldAvailability(leftField),
|
|
160
|
+
right: fieldAvailability(rightField),
|
|
161
|
+
left_source_range: left.source_range,
|
|
162
|
+
right_source_range: right.source_range,
|
|
163
|
+
evidence_links: evidenceLinks,
|
|
164
|
+
limitations: status === "unknown"
|
|
165
|
+
? [
|
|
166
|
+
"The static value or relevant parent-property coverage is incomplete.",
|
|
167
|
+
]
|
|
168
|
+
: [],
|
|
169
|
+
}),
|
|
170
|
+
];
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const fieldChangeStatus = ({ leftShape, rightShape, path, leftField, rightField, }) => {
|
|
174
|
+
if (leftField !== undefined && rightField !== undefined) {
|
|
175
|
+
if (canonicalExportShapeValue(leftField) ===
|
|
176
|
+
canonicalExportShapeValue(rightField))
|
|
177
|
+
return null;
|
|
178
|
+
return leftField.state === "unknown" || rightField.state === "unknown"
|
|
179
|
+
? "unknown"
|
|
180
|
+
: "changed";
|
|
181
|
+
}
|
|
182
|
+
const present = leftField ?? rightField;
|
|
183
|
+
if (present === undefined)
|
|
184
|
+
return null;
|
|
185
|
+
const parent = parentPointer(path);
|
|
186
|
+
const parentsComplete = propertyCoverageComplete(leftShape, parent) &&
|
|
187
|
+
propertyCoverageComplete(rightShape, parent);
|
|
188
|
+
if (present.state === "unknown" || !parentsComplete)
|
|
189
|
+
return "unknown";
|
|
190
|
+
return leftField === undefined ? "added" : "removed";
|
|
191
|
+
};
|
|
192
|
+
const propertyCoverageComplete = (shape, path) => path !== null &&
|
|
193
|
+
shape.property_coverage.some((coverage) => coverage.path === path && coverage.status === "complete");
|
|
194
|
+
const parentPointer = (path) => {
|
|
195
|
+
if (path === "")
|
|
196
|
+
return null;
|
|
197
|
+
const separator = path.lastIndexOf("/");
|
|
198
|
+
return separator < 0 ? null : path.slice(0, separator);
|
|
199
|
+
};
|
|
200
|
+
const fieldAvailability = (field) => {
|
|
201
|
+
if (field === undefined)
|
|
202
|
+
return { availability: "absent" };
|
|
203
|
+
if (field.state === "unknown")
|
|
204
|
+
return {
|
|
205
|
+
availability: "unknown",
|
|
206
|
+
reason: field.reason ?? "Static field value is unknown.",
|
|
207
|
+
};
|
|
208
|
+
if (field.state === "union")
|
|
209
|
+
return { availability: "union", values: primitiveArray(field.value) };
|
|
210
|
+
const literal = literalValue(field);
|
|
211
|
+
return literal.found
|
|
212
|
+
? { availability: "literal", value: literal.value }
|
|
213
|
+
: {
|
|
214
|
+
availability: "unknown",
|
|
215
|
+
reason: "Static literal field failed its projection boundary.",
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
const selectionAvailability = (selection, side) => ({
|
|
219
|
+
availability: "unknown",
|
|
220
|
+
reason: `${capitalize(side)} export selection is ${selection.status}.`,
|
|
221
|
+
});
|
|
222
|
+
const emptyShapeAvailability = (shapes, side) => ({
|
|
223
|
+
availability: "unknown",
|
|
224
|
+
reason: shapes.length === 0
|
|
225
|
+
? `${capitalize(side)} export has no retained direct return shape.`
|
|
226
|
+
: `${capitalize(side)} export return shape cannot be paired without an opposite variant.`,
|
|
227
|
+
});
|
|
228
|
+
const unpairedChange = (side, shape, evidenceLinks) => changeWithId({
|
|
229
|
+
status: "unknown",
|
|
230
|
+
path: "",
|
|
231
|
+
discriminant: null,
|
|
232
|
+
left: {
|
|
233
|
+
availability: "unknown",
|
|
234
|
+
reason: side === "left"
|
|
235
|
+
? "Left return variant has no unique exact cross-version pairing."
|
|
236
|
+
: "No left return variant was uniquely paired with this right variant.",
|
|
237
|
+
},
|
|
238
|
+
right: {
|
|
239
|
+
availability: "unknown",
|
|
240
|
+
reason: side === "right"
|
|
241
|
+
? "Right return variant has no unique exact cross-version pairing."
|
|
242
|
+
: "No right return variant was uniquely paired with this left variant.",
|
|
243
|
+
},
|
|
244
|
+
left_source_range: side === "left" ? shape.source_range : null,
|
|
245
|
+
right_source_range: side === "right" ? shape.source_range : null,
|
|
246
|
+
evidence_links: evidenceLinks,
|
|
247
|
+
limitations: [
|
|
248
|
+
"The return variant was not paired because no reciprocal unique literal discriminant exists.",
|
|
249
|
+
],
|
|
250
|
+
});
|
|
251
|
+
const changeWithId = (semantic) => ({
|
|
252
|
+
...semantic,
|
|
253
|
+
change_id: `jesc_change_${digestExportShapeValue(semantic)}`,
|
|
254
|
+
});
|
|
255
|
+
const changeSortKey = (change) => ({
|
|
256
|
+
discriminant: change.discriminant,
|
|
257
|
+
path: change.path,
|
|
258
|
+
status: change.status,
|
|
259
|
+
left_source_range: change.left_source_range,
|
|
260
|
+
right_source_range: change.right_source_range,
|
|
261
|
+
change_id: change.change_id,
|
|
262
|
+
});
|
|
263
|
+
const literalValue = (field) => field.state === "literal" && isPrimitive(field.value)
|
|
264
|
+
? { found: true, value: field.value }
|
|
265
|
+
: { found: false };
|
|
266
|
+
const primitiveArray = (value) => Array.isArray(value) ? value.filter(isPrimitive) : [];
|
|
267
|
+
const isPrimitive = (value) => value === null ||
|
|
268
|
+
typeof value === "string" ||
|
|
269
|
+
typeof value === "number" ||
|
|
270
|
+
typeof value === "boolean";
|
|
271
|
+
const capitalize = (value) => `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`;
|
|
272
|
+
const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
|
|
@@ -22,16 +22,16 @@ export const digestCanonical = (value) => {
|
|
|
22
22
|
};
|
|
23
23
|
const parseStaticLayer = (layer) => {
|
|
24
24
|
const evidence = parseEvidence(layer.analysis);
|
|
25
|
+
const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
|
|
25
26
|
assertEvidenceIdentity(evidence, {
|
|
26
27
|
operation: "analyze_javascript_application",
|
|
27
|
-
predicate:
|
|
28
|
+
predicate: `rea.javascript-application-analysis/v${String(result.schema_version)}`,
|
|
28
29
|
providerId: "rea-javascript-application",
|
|
29
30
|
providerName: "REA JavaScript application analyzer",
|
|
30
31
|
providerVersion: "1",
|
|
31
32
|
authority: "shipped-artifact",
|
|
32
33
|
confidence: "derived",
|
|
33
34
|
});
|
|
34
|
-
const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
|
|
35
35
|
if (evidence.subject === null ||
|
|
36
36
|
evidence.subject.digest.sha256 !== result.root_artifact_sha256 ||
|
|
37
37
|
evidence.subject.format !== result.format ||
|
|
@@ -2,8 +2,10 @@ import { parse } from "@babel/parser";
|
|
|
2
2
|
import * as t from "@babel/types";
|
|
3
3
|
import { DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS, failedJavaScriptSemanticIr, } from "./javascriptSemanticIr.js";
|
|
4
4
|
import { collectSemanticModuleLink, collectSemanticCallable, collectSemanticReferences, immutableSemanticBindings, immutableSemanticScopes, reachSemanticLimit, semanticModuleOrigin, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
|
|
5
|
-
import { currentSemanticScope, semanticScopeId, } from "./javascriptSemanticState.js";
|
|
5
|
+
import { currentSemanticScope, resolveSemanticBindingFromScope, semanticScopeId, semanticVariableScope, } from "./javascriptSemanticState.js";
|
|
6
6
|
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
|
7
|
+
import { collectSemanticReturns, resolveSemanticModuleCallables, } from "./javascriptSemanticReturns.js";
|
|
8
|
+
import { collectJavaScriptSemanticCalls } from "./javascriptSemanticCalls.js";
|
|
7
9
|
import { compareCodePoints, propertyName, range, } from "./javascriptStaticAnalysisHelpers.js";
|
|
8
10
|
/** Recover bounded lexical bindings, aliases, constants, and module links. */
|
|
9
11
|
export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
@@ -23,14 +25,19 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
|
23
25
|
collectDefinitions(file.program, state);
|
|
24
26
|
const references = collectSemanticReferences(file.program, state);
|
|
25
27
|
const parserPartial = file.errors.length > 0;
|
|
28
|
+
const bindings = immutableSemanticBindings(state);
|
|
29
|
+
const callables = collectSemanticReturns(file.program, state, parserPartial);
|
|
30
|
+
const moduleLinks = resolveSemanticModuleCallables(state, callables);
|
|
31
|
+
const calls = collectJavaScriptSemanticCalls(file.program, state, callables);
|
|
26
32
|
return {
|
|
27
33
|
schema: "JavaScriptSemanticIR",
|
|
28
|
-
schemaVersion:
|
|
34
|
+
schemaVersion: 3,
|
|
29
35
|
scopes: immutableSemanticScopes(state),
|
|
30
|
-
bindings
|
|
31
|
-
callables
|
|
36
|
+
bindings,
|
|
37
|
+
callables,
|
|
32
38
|
references,
|
|
33
|
-
moduleLinks
|
|
39
|
+
moduleLinks,
|
|
40
|
+
...calls,
|
|
34
41
|
coverage: {
|
|
35
42
|
status: state.limitsReached.size > 0
|
|
36
43
|
? "truncated"
|
|
@@ -50,6 +57,8 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
|
|
|
50
57
|
? []
|
|
51
58
|
: ["Semantic recovery stopped retaining facts at explicit limits."]),
|
|
52
59
|
"Values and aliases were recovered from inert syntax only; no JavaScript was executed.",
|
|
60
|
+
"Return sites include only direct callable returns; nested callable returns remain separate.",
|
|
61
|
+
"Local call, argument, return, and closure relations are static candidates and do not prove runtime invocation.",
|
|
53
62
|
"Cross-function mutation and dynamic property resolution remain unknown.",
|
|
54
63
|
],
|
|
55
64
|
};
|
|
@@ -70,6 +79,7 @@ const createState = (program, limits) => {
|
|
|
70
79
|
scopeByNode: new WeakMap([[program, root]]),
|
|
71
80
|
bindingsById: new Map(),
|
|
72
81
|
callables: [],
|
|
82
|
+
callableNodesById: new Map(),
|
|
73
83
|
moduleLinks: [],
|
|
74
84
|
limitsReached: new Set(),
|
|
75
85
|
omittedCount: 0,
|
|
@@ -136,7 +146,7 @@ const bindInnerDeclaration = (node, parent, scope, state) => {
|
|
|
136
146
|
bindPattern({
|
|
137
147
|
pattern: node.id,
|
|
138
148
|
initializer: node.init ?? null,
|
|
139
|
-
scope:
|
|
149
|
+
scope: semanticVariableScope(scope, parent, state),
|
|
140
150
|
state,
|
|
141
151
|
mutable: parent !== null && t.isVariableDeclaration(parent)
|
|
142
152
|
? parent.kind !== "const"
|
|
@@ -348,39 +358,10 @@ const createBinding = (scope, name, kind, mutable) => ({
|
|
|
348
358
|
directOrigins: [],
|
|
349
359
|
});
|
|
350
360
|
const addAssignment = (identifier, initializer, scope, state) => {
|
|
351
|
-
const binding =
|
|
361
|
+
const binding = resolveSemanticBindingFromScope(scope, identifier.name, state);
|
|
352
362
|
if (binding === undefined)
|
|
353
363
|
return;
|
|
354
364
|
binding.mutable = true;
|
|
355
365
|
binding.definitions.push({ kind: "assignment", location: range(identifier) });
|
|
356
366
|
binding.initializers.push({ node: initializer, projection: [] });
|
|
357
367
|
};
|
|
358
|
-
const resolveFromScope = (scope, name, state) => {
|
|
359
|
-
let candidate = scope;
|
|
360
|
-
while (candidate !== undefined) {
|
|
361
|
-
const binding = candidate.bindings.get(name);
|
|
362
|
-
if (binding !== undefined)
|
|
363
|
-
return binding;
|
|
364
|
-
if (!candidate.bindingsComplete)
|
|
365
|
-
return undefined;
|
|
366
|
-
candidate =
|
|
367
|
-
candidate.parentScopeId === null
|
|
368
|
-
? undefined
|
|
369
|
-
: state.scopesById.get(candidate.parentScopeId);
|
|
370
|
-
}
|
|
371
|
-
return undefined;
|
|
372
|
-
};
|
|
373
|
-
const variableScope = (scope, parent, state) => {
|
|
374
|
-
if (!(parent !== null && t.isVariableDeclaration(parent, { kind: "var" })))
|
|
375
|
-
return scope;
|
|
376
|
-
let candidate = scope;
|
|
377
|
-
while (candidate.kind === "block" || candidate.kind === "catch") {
|
|
378
|
-
const outer = candidate.parentScopeId === null
|
|
379
|
-
? undefined
|
|
380
|
-
: state.scopesById.get(candidate.parentScopeId);
|
|
381
|
-
if (outer === undefined)
|
|
382
|
-
break;
|
|
383
|
-
candidate = outer;
|
|
384
|
-
}
|
|
385
|
-
return candidate;
|
|
386
|
-
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { reachSemanticLimit, semanticCallableIdForNode, } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { compareCodePoints, range } from "./javascriptStaticAnalysisHelpers.js";
|
|
5
|
+
/** Resolve bounded local callable candidates without following dynamic properties. */
|
|
6
|
+
export const resolveLocalCallables = (input) => resolveCallables(input.node, {
|
|
7
|
+
state: input.state,
|
|
8
|
+
callableById: input.callableById,
|
|
9
|
+
seenBindings: input.seenBindings,
|
|
10
|
+
depth: input.depth,
|
|
11
|
+
bindingCache: input.bindingCache,
|
|
12
|
+
});
|
|
13
|
+
const resolveCallables = (node, context) => {
|
|
14
|
+
if (context.depth >= context.state.limits.maxValueDepth) {
|
|
15
|
+
reachSemanticLimit(context.state, "maxValueDepth");
|
|
16
|
+
return {
|
|
17
|
+
callableIds: [],
|
|
18
|
+
complete: false,
|
|
19
|
+
reason: "Call target depth limit reached.",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const direct = semanticCallableIdForNode(node);
|
|
23
|
+
if (direct !== null && context.callableById.has(direct))
|
|
24
|
+
return {
|
|
25
|
+
callableIds: [direct],
|
|
26
|
+
complete: true,
|
|
27
|
+
reason: "Direct local callable.",
|
|
28
|
+
};
|
|
29
|
+
if (t.isIdentifier(node))
|
|
30
|
+
return resolveIdentifier(node, context);
|
|
31
|
+
if (t.isTSAsExpression(node) ||
|
|
32
|
+
t.isTSTypeAssertion(node) ||
|
|
33
|
+
t.isTSNonNullExpression(node))
|
|
34
|
+
return resolveCallables(node.expression, nestedContext(context));
|
|
35
|
+
if (t.isConditionalExpression(node) || t.isLogicalExpression(node))
|
|
36
|
+
return resolveAlternatives(t.isConditionalExpression(node) ? node.consequent : node.left, t.isConditionalExpression(node) ? node.alternate : node.right, context);
|
|
37
|
+
return {
|
|
38
|
+
callableIds: [],
|
|
39
|
+
complete: false,
|
|
40
|
+
reason: `Unsupported ${node.type} call target.`,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const resolveIdentifier = (node, context) => {
|
|
44
|
+
const binding = resolveSemanticBindingState(context.state, node, node.name);
|
|
45
|
+
return binding === undefined
|
|
46
|
+
? {
|
|
47
|
+
callableIds: [],
|
|
48
|
+
complete: false,
|
|
49
|
+
reason: `Unresolved call target ${node.name}.`,
|
|
50
|
+
}
|
|
51
|
+
: resolveBindingCallables(binding, nestedContext(context));
|
|
52
|
+
};
|
|
53
|
+
const resolveAlternatives = (left, right, context) => {
|
|
54
|
+
const candidates = [left, right].map((candidate) => resolveCallables(candidate, nestedContext(context)));
|
|
55
|
+
return {
|
|
56
|
+
callableIds: uniqueCallableIds(candidates.flatMap(({ callableIds }) => callableIds)),
|
|
57
|
+
complete: candidates.every(({ complete }) => complete),
|
|
58
|
+
reason: "Conditional or logical call target has multiple candidates.",
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const resolveBindingCallables = (binding, context) => {
|
|
62
|
+
const cached = context.bindingCache.get(binding.bindingId);
|
|
63
|
+
if (cached !== undefined)
|
|
64
|
+
return cached;
|
|
65
|
+
if (context.seenBindings.has(binding.bindingId))
|
|
66
|
+
return {
|
|
67
|
+
callableIds: [],
|
|
68
|
+
complete: false,
|
|
69
|
+
reason: `Call target alias cycle at ${binding.name}.`,
|
|
70
|
+
};
|
|
71
|
+
if (binding.initializers.length === 0)
|
|
72
|
+
return missingBindingInitializer(binding);
|
|
73
|
+
const candidateContext = {
|
|
74
|
+
...nestedContext(context),
|
|
75
|
+
seenBindings: new Set([...context.seenBindings, binding.bindingId]),
|
|
76
|
+
};
|
|
77
|
+
const candidates = binding.initializers.map(({ node, projection }) => projection.length === 0
|
|
78
|
+
? resolveCallables(node, candidateContext)
|
|
79
|
+
: {
|
|
80
|
+
callableIds: [],
|
|
81
|
+
complete: false,
|
|
82
|
+
reason: `Projected call target ${binding.name} is unresolved.`,
|
|
83
|
+
});
|
|
84
|
+
const callableIds = uniqueCallableIds(candidates.flatMap((candidate) => candidate.callableIds));
|
|
85
|
+
const resolution = {
|
|
86
|
+
callableIds,
|
|
87
|
+
complete: candidates.every(({ complete }) => complete),
|
|
88
|
+
reason: callableIds.length === 1 && candidates.every(({ complete }) => complete)
|
|
89
|
+
? "Unique local callable target."
|
|
90
|
+
: `Call target ${binding.name} has ambiguous or unsupported assignments.`,
|
|
91
|
+
};
|
|
92
|
+
if (resolution.complete)
|
|
93
|
+
context.bindingCache.set(binding.bindingId, resolution);
|
|
94
|
+
return resolution;
|
|
95
|
+
};
|
|
96
|
+
const missingBindingInitializer = (binding) => ({
|
|
97
|
+
callableIds: [],
|
|
98
|
+
complete: false,
|
|
99
|
+
reason: binding.kind === "import"
|
|
100
|
+
? `Call target ${binding.name} is imported from outside this source.`
|
|
101
|
+
: `Call target ${binding.name} has no local initializer.`,
|
|
102
|
+
});
|
|
103
|
+
/** Find all bindings introduced by one positional parameter pattern. */
|
|
104
|
+
export const parameterBindings = (callable, index, state) => {
|
|
105
|
+
if (callable.bodyScopeId === null)
|
|
106
|
+
return [];
|
|
107
|
+
const scope = state.scopesById.get(callable.bodyScopeId);
|
|
108
|
+
if (scope === undefined)
|
|
109
|
+
return [];
|
|
110
|
+
return [...scope.bindings.values()]
|
|
111
|
+
.filter((binding) => binding.kind === "parameter" &&
|
|
112
|
+
binding.definitions.some(({ kind, location }) => kind === "parameter" &&
|
|
113
|
+
parameterIndex(callable, location, state) === index))
|
|
114
|
+
.sort((left, right) => compareCodePoints(left.bindingId, right.bindingId));
|
|
115
|
+
};
|
|
116
|
+
const parameterIndex = (callable, location, state) => {
|
|
117
|
+
const node = state.callableNodesById.get(callable.callableId);
|
|
118
|
+
if (!t.isFunction(node))
|
|
119
|
+
return -1;
|
|
120
|
+
return node.params.findIndex((parameter) => rangeContains(range(parameter), location));
|
|
121
|
+
};
|
|
122
|
+
const rangeContains = (outer, inner) => (outer.start.line < inner.start.line ||
|
|
123
|
+
(outer.start.line === inner.start.line &&
|
|
124
|
+
outer.start.column <= inner.start.column)) &&
|
|
125
|
+
(outer.end.line > inner.end.line ||
|
|
126
|
+
(outer.end.line === inner.end.line &&
|
|
127
|
+
outer.end.column >= inner.end.column));
|
|
128
|
+
const nestedContext = (context) => ({ ...context, depth: context.depth + 1 });
|
|
129
|
+
const uniqueCallableIds = (values) => [...new Set(values)].sort(compareCodePoints);
|