@eventra_dev/eventra-cli 0.3.1 → 0.3.3
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 +2 -13
- package/dist/analysis/cache/evaluationCache.js +1 -3
- package/dist/analysis/cache/resolvedCallCache.js +1 -3
- package/dist/analysis/cache/resolvedExportCache.js +1 -3
- package/dist/analysis/cache/returnPropagationCache.js +19 -0
- package/dist/analysis/cache/symbolInvalidation.js +8 -2
- package/dist/analysis/extractor/extractor.js +5 -13
- package/dist/analysis/extractor/propagationExtractor.js +55 -14
- package/dist/analysis/resolver/callResolver.js +10 -52
- package/dist/analysis/resolver/exportResolver.js +4 -19
- package/dist/analysis/resolver/resolver.js +148 -29
- package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +19 -48
- package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +2 -13
- package/dist/analysis/scanner/scanner.js +16 -44
- package/dist/analysis/scanner/sinkDetector.js +10 -44
- package/dist/analysis/shared/evaluationContext.js +0 -1
- package/dist/analysis/symbols/symbolUtils.js +6 -29
- package/dist/analysis/symbols/wrapperRegistry.js +25 -25
- package/dist/cli/check.js +8 -10
- package/dist/cli/init.js +18 -82
- package/dist/cli/send.js +7 -5
- package/dist/cli/sync.js +4 -39
- package/dist/cli/watch.js +42 -40
- package/dist/compiler/compilerContext.js +18 -41
- package/dist/compiler/documentRegistry.js +5 -15
- package/dist/compiler/importGraph.js +4 -8
- package/dist/compiler/scheduler.js +1 -3
- package/dist/config/config.js +9 -19
- package/dist/core/EventraEngine.js +37 -101
- package/dist/core/projectScanner.js +7 -6
- package/dist/filesystem/processFile.js +1 -4
- package/dist/shared/buildSyntheticTsx.js +1 -2
- package/package.json +1 -1
- package/dist/analysis/cache/semanticDependencyGraph.js +0 -25
- package/dist/analysis/pipeline/semanticPass.js +0 -2
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/analysis/resolver/resolver.ts
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -30,47 +29,111 @@ function concat(left, right) {
|
|
|
30
29
|
}
|
|
31
30
|
return unique(result);
|
|
32
31
|
}
|
|
33
|
-
function
|
|
32
|
+
function getPropertyName(node) {
|
|
33
|
+
if (typescript_1.default.isElementAccessExpression(node)) {
|
|
34
|
+
if (!node.argumentExpression || !typescript_1.default.isStringLiteral(node.argumentExpression)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return node.argumentExpression.text;
|
|
38
|
+
}
|
|
39
|
+
return node.name.text;
|
|
40
|
+
}
|
|
41
|
+
function resolvePropertyAccess(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache) {
|
|
42
|
+
const propertyName = getPropertyName(node);
|
|
43
|
+
if (!propertyName) {
|
|
44
|
+
return empty();
|
|
45
|
+
}
|
|
46
|
+
// payload.event
|
|
47
|
+
// payload["event"]
|
|
48
|
+
const targetExpression = typescript_1.default.isElementAccessExpression(node) ? node.expression : node.expression;
|
|
49
|
+
if (typescript_1.default.isIdentifier(targetExpression)) {
|
|
50
|
+
const symbol = checker.getSymbolAtLocation(targetExpression);
|
|
51
|
+
// parameter binding
|
|
52
|
+
if (symbol && context.parameterBindings.has(symbol)) {
|
|
53
|
+
const bound = context.parameterBindings.get(symbol);
|
|
54
|
+
if (bound && typescript_1.default.isObjectLiteralExpression(bound)) {
|
|
55
|
+
for (const property of bound.properties) {
|
|
56
|
+
if (!typescript_1.default.isPropertyAssignment(property) && !typescript_1.default.isShorthandPropertyAssignment(property)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const objectPropertyName = typescript_1.default.isIdentifier(property.name) ? property.name.text : property.name.getText();
|
|
60
|
+
if (objectPropertyName !== propertyName) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const initializer = typescript_1.default.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
64
|
+
return resolveNodeValue(initializer, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// const EVENTS = {
|
|
69
|
+
// LOGIN: "login"
|
|
70
|
+
// }
|
|
71
|
+
if (symbol) {
|
|
72
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
73
|
+
for (const declaration of declarations) {
|
|
74
|
+
if (!typescript_1.default.isVariableDeclaration(declaration) || !declaration.initializer || !typescript_1.default.isObjectLiteralExpression(declaration.initializer)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
for (const property of declaration.initializer.properties) {
|
|
78
|
+
if (!typescript_1.default.isPropertyAssignment(property) && !typescript_1.default.isShorthandPropertyAssignment(property)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const objectPropertyName = typescript_1.default.isIdentifier(property.name) ? property.name.text : property.name.getText();
|
|
82
|
+
if (objectPropertyName !== propertyName) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const initializer = typescript_1.default.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
86
|
+
return resolveNodeValue(initializer, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return empty();
|
|
92
|
+
}
|
|
93
|
+
function resolveIdentifier(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache) {
|
|
34
94
|
const boundSymbol = checker.getSymbolAtLocation(node);
|
|
35
|
-
if (boundSymbol
|
|
36
|
-
&& context.parameterBindings.has(boundSymbol)) {
|
|
95
|
+
if (boundSymbol && context.parameterBindings.has(boundSymbol)) {
|
|
37
96
|
const bound = context.parameterBindings.get(boundSymbol);
|
|
38
97
|
if (bound) {
|
|
39
|
-
return resolveNodeValue(bound, checker, context, visited, evaluationCache,
|
|
98
|
+
return resolveNodeValue(bound, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
40
99
|
}
|
|
41
100
|
}
|
|
42
101
|
let symbol = checker.getSymbolAtLocation(node);
|
|
43
102
|
if (!symbol) {
|
|
44
103
|
return empty();
|
|
45
104
|
}
|
|
46
|
-
symbol =
|
|
47
|
-
(0, exportResolver_1.resolveExportedSymbol)(symbol, checker, exportCache)
|
|
48
|
-
?? symbol;
|
|
105
|
+
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker, exportCache) ?? symbol;
|
|
49
106
|
const cached = evaluationCache.get(symbol);
|
|
50
107
|
if (cached) {
|
|
51
108
|
return cached;
|
|
52
109
|
}
|
|
53
110
|
const resolving = empty();
|
|
54
111
|
evaluationCache.set(symbol, resolving);
|
|
55
|
-
const declarations = symbol.getDeclarations()
|
|
56
|
-
?? [];
|
|
112
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
57
113
|
for (const declaration of declarations) {
|
|
58
|
-
if (typescript_1.default.isVariableDeclaration(declaration)
|
|
59
|
-
|
|
60
|
-
|
|
114
|
+
if (typescript_1.default.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
115
|
+
const resolved = resolveNodeValue(declaration.initializer, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
116
|
+
evaluationCache.set(symbol, resolved);
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
if (typescript_1.default.isEnumMember(declaration) && declaration.initializer) {
|
|
120
|
+
const resolved = resolveNodeValue(declaration.initializer, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
61
121
|
evaluationCache.set(symbol, resolved);
|
|
62
122
|
return resolved;
|
|
63
123
|
}
|
|
64
124
|
}
|
|
65
125
|
return resolving;
|
|
66
126
|
}
|
|
67
|
-
function resolveCallExpression(node, checker, context, visited, evaluationCache, resolvedCallCache, exportCache) {
|
|
68
|
-
const resolved = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker, resolvedCallCache
|
|
69
|
-
?? new resolvedCallCache_1.ResolvedCallCache(), exportCache);
|
|
127
|
+
function resolveCallExpression(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache) {
|
|
128
|
+
const resolved = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker, resolvedCallCache ?? new resolvedCallCache_1.ResolvedCallCache(), exportCache);
|
|
70
129
|
if (!resolved) {
|
|
71
130
|
return empty();
|
|
72
131
|
}
|
|
73
|
-
|
|
132
|
+
let semantic = returnPropagationCache.get(resolved);
|
|
133
|
+
if (semantic === undefined) {
|
|
134
|
+
semantic = (0, returnPropagationAnalyzer_1.analyzeReturnPropagation)(resolved);
|
|
135
|
+
returnPropagationCache.set(resolved, semantic);
|
|
136
|
+
}
|
|
74
137
|
if (!semantic) {
|
|
75
138
|
return empty();
|
|
76
139
|
}
|
|
@@ -87,7 +150,7 @@ function resolveCallExpression(node, checker, context, visited, evaluationCache,
|
|
|
87
150
|
if (parameterSymbol) {
|
|
88
151
|
nextContext.parameterBindings.set(parameterSymbol, argument);
|
|
89
152
|
}
|
|
90
|
-
const resolvedValue = resolveNodeValue(propagation.returnExpression, checker, nextContext, new Set(visited), evaluationCache, resolvedCallCache, exportCache);
|
|
153
|
+
const resolvedValue = resolveNodeValue(propagation.returnExpression, checker, nextContext, new Set(visited), evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
91
154
|
values.push(...resolvedValue.values);
|
|
92
155
|
}
|
|
93
156
|
return {
|
|
@@ -95,7 +158,7 @@ function resolveCallExpression(node, checker, context, visited, evaluationCache,
|
|
|
95
158
|
dynamic: true,
|
|
96
159
|
};
|
|
97
160
|
}
|
|
98
|
-
function resolveNodeValue(node, checker, context, visited, evaluationCache, resolvedCallCache, exportCache) {
|
|
161
|
+
function resolveNodeValue(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache) {
|
|
99
162
|
if (visited.has(node)) {
|
|
100
163
|
return empty();
|
|
101
164
|
}
|
|
@@ -103,27 +166,83 @@ function resolveNodeValue(node, checker, context, visited, evaluationCache, reso
|
|
|
103
166
|
try {
|
|
104
167
|
if (typescript_1.default.isStringLiteral(node)) {
|
|
105
168
|
return {
|
|
106
|
-
values: [
|
|
107
|
-
node.text,
|
|
108
|
-
],
|
|
169
|
+
values: [node.text],
|
|
109
170
|
dynamic: false,
|
|
110
171
|
};
|
|
111
172
|
}
|
|
173
|
+
if (typescript_1.default.isNoSubstitutionTemplateLiteral(node)) {
|
|
174
|
+
return {
|
|
175
|
+
values: [node.text],
|
|
176
|
+
dynamic: false,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (typescript_1.default.isTemplateExpression(node)) {
|
|
180
|
+
let values = [node.head.text];
|
|
181
|
+
for (const span of node.templateSpans) {
|
|
182
|
+
const resolved = resolveNodeValue(span.expression, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
183
|
+
values = concat(values, resolved.values.map(value => value + span.literal.text));
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
values: unique(values),
|
|
187
|
+
dynamic: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (typescript_1.default.isConditionalExpression(node)) {
|
|
191
|
+
const whenTrue = resolveNodeValue(node.whenTrue, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
192
|
+
const whenFalse = resolveNodeValue(node.whenFalse, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
193
|
+
return {
|
|
194
|
+
values: unique([...whenTrue.values, ...whenFalse.values]),
|
|
195
|
+
dynamic: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (typescript_1.default.isArrayLiteralExpression(node)) {
|
|
199
|
+
const values = [];
|
|
200
|
+
for (const element of node.elements) {
|
|
201
|
+
const resolved = resolveNodeValue(element, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
202
|
+
values.push(...resolved.values);
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
values: unique(values),
|
|
206
|
+
dynamic: true,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (typescript_1.default.isObjectLiteralExpression(node)) {
|
|
210
|
+
const values = [];
|
|
211
|
+
for (const property of node.properties) {
|
|
212
|
+
if (!typescript_1.default.isPropertyAssignment(property) && !typescript_1.default.isShorthandPropertyAssignment(property)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const initializer = typescript_1.default.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
216
|
+
const resolved = resolveNodeValue(initializer, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
217
|
+
values.push(...resolved.values);
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
values: unique(values),
|
|
221
|
+
dynamic: true,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (typescript_1.default.isParenthesizedExpression(node)) {
|
|
225
|
+
return resolveNodeValue(node.expression, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
226
|
+
}
|
|
227
|
+
if (typescript_1.default.isAsExpression(node) || typescript_1.default.isTypeAssertionExpression(node)) {
|
|
228
|
+
return resolveNodeValue(node.expression, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
229
|
+
}
|
|
230
|
+
if (typescript_1.default.isPropertyAccessExpression(node) || typescript_1.default.isPropertyAccessChain(node) || typescript_1.default.isElementAccessExpression(node)) {
|
|
231
|
+
return resolvePropertyAccess(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
232
|
+
}
|
|
112
233
|
if (typescript_1.default.isIdentifier(node)) {
|
|
113
|
-
return resolveIdentifier(node, checker, context, visited, evaluationCache, exportCache);
|
|
234
|
+
return resolveIdentifier(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
114
235
|
}
|
|
115
|
-
if (typescript_1.default.isBinaryExpression(node)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const left = resolveNodeValue(node.left, checker, context, visited, evaluationCache, resolvedCallCache, exportCache);
|
|
119
|
-
const right = resolveNodeValue(node.right, checker, context, visited, evaluationCache, resolvedCallCache, exportCache);
|
|
236
|
+
if (typescript_1.default.isBinaryExpression(node) && node.operatorToken.kind === typescript_1.default.SyntaxKind.PlusToken) {
|
|
237
|
+
const left = resolveNodeValue(node.left, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
238
|
+
const right = resolveNodeValue(node.right, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
120
239
|
return {
|
|
121
240
|
values: concat(left.values, right.values),
|
|
122
241
|
dynamic: true,
|
|
123
242
|
};
|
|
124
243
|
}
|
|
125
244
|
if (typescript_1.default.isCallExpression(node)) {
|
|
126
|
-
return resolveCallExpression(node, checker, context, visited, evaluationCache, resolvedCallCache, exportCache);
|
|
245
|
+
return resolveCallExpression(node, checker, context, visited, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
|
|
127
246
|
}
|
|
128
247
|
return empty();
|
|
129
248
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/analysis/scanner/propagationAnalyzer.ts
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -10,10 +9,9 @@ const symbolUtils_1 = require("../../symbols/symbolUtils");
|
|
|
10
9
|
function extractPropertyPath(expression) {
|
|
11
10
|
const result = [];
|
|
12
11
|
let current = expression;
|
|
13
|
-
while (typescript_1.default.isPropertyAccessExpression(current)) {
|
|
12
|
+
while (typescript_1.default.isPropertyAccessExpression(current) || typescript_1.default.isPropertyAccessChain(current)) {
|
|
14
13
|
result.unshift(current.name.text);
|
|
15
|
-
current =
|
|
16
|
-
current.expression;
|
|
14
|
+
current = current.expression;
|
|
17
15
|
}
|
|
18
16
|
if (!typescript_1.default.isIdentifier(current)) {
|
|
19
17
|
return null;
|
|
@@ -22,12 +20,7 @@ function extractPropertyPath(expression) {
|
|
|
22
20
|
}
|
|
23
21
|
function resolveObjectBinding(parameter, pattern, identifier, parentPath) {
|
|
24
22
|
for (const element of pattern.elements) {
|
|
25
|
-
const propertyName = element.propertyName
|
|
26
|
-
&& typescript_1.default.isIdentifier(element.propertyName)
|
|
27
|
-
? element.propertyName.text
|
|
28
|
-
: typescript_1.default.isIdentifier(element.name)
|
|
29
|
-
? element.name.text
|
|
30
|
-
: null;
|
|
23
|
+
const propertyName = element.propertyName && typescript_1.default.isIdentifier(element.propertyName) ? element.propertyName.text : typescript_1.default.isIdentifier(element.name) ? element.name.text : null;
|
|
31
24
|
if (!propertyName) {
|
|
32
25
|
continue;
|
|
33
26
|
}
|
|
@@ -35,23 +28,18 @@ function resolveObjectBinding(parameter, pattern, identifier, parentPath) {
|
|
|
35
28
|
...parentPath,
|
|
36
29
|
propertyName,
|
|
37
30
|
];
|
|
38
|
-
//
|
|
39
31
|
// alias:
|
|
40
32
|
// { event: name }
|
|
41
|
-
//
|
|
42
33
|
if (typescript_1.default.isIdentifier(element.name)) {
|
|
43
|
-
if (element.name.text
|
|
44
|
-
=== identifier.text) {
|
|
34
|
+
if (element.name.text === identifier.text) {
|
|
45
35
|
return {
|
|
46
36
|
parameter,
|
|
47
37
|
propertyPath: nextPath,
|
|
48
38
|
};
|
|
49
39
|
}
|
|
50
40
|
}
|
|
51
|
-
//
|
|
52
41
|
// nested:
|
|
53
|
-
// { meta: { event }
|
|
54
|
-
//
|
|
42
|
+
// { meta: { event }
|
|
55
43
|
if (typescript_1.default.isObjectBindingPattern(element.name)) {
|
|
56
44
|
const nested = resolveObjectBinding(parameter, element.name, identifier, nextPath);
|
|
57
45
|
if (nested) {
|
|
@@ -63,14 +51,10 @@ function resolveObjectBinding(parameter, pattern, identifier, parentPath) {
|
|
|
63
51
|
}
|
|
64
52
|
function resolveParameter(fn, identifier) {
|
|
65
53
|
for (const parameter of fn.parameters) {
|
|
66
|
-
//
|
|
67
54
|
// simple param
|
|
68
|
-
//
|
|
69
55
|
// function x(name)
|
|
70
|
-
//
|
|
71
56
|
if (typescript_1.default.isIdentifier(parameter.name)) {
|
|
72
|
-
if (parameter.name.text
|
|
73
|
-
=== identifier.text) {
|
|
57
|
+
if (parameter.name.text === identifier.text) {
|
|
74
58
|
return {
|
|
75
59
|
parameter,
|
|
76
60
|
propertyPath: [],
|
|
@@ -78,11 +62,8 @@ function resolveParameter(fn, identifier) {
|
|
|
78
62
|
}
|
|
79
63
|
continue;
|
|
80
64
|
}
|
|
81
|
-
//
|
|
82
65
|
// destructuring
|
|
83
|
-
//
|
|
84
66
|
// function x({ event })
|
|
85
|
-
//
|
|
86
67
|
if (typescript_1.default.isObjectBindingPattern(parameter.name)) {
|
|
87
68
|
const resolved = resolveObjectBinding(parameter, parameter.name, identifier, []);
|
|
88
69
|
if (resolved) {
|
|
@@ -92,7 +73,7 @@ function resolveParameter(fn, identifier) {
|
|
|
92
73
|
}
|
|
93
74
|
return null;
|
|
94
75
|
}
|
|
95
|
-
function collectPropagations(fn,
|
|
76
|
+
function collectPropagations(fn, sinks) {
|
|
96
77
|
const result = [];
|
|
97
78
|
for (const sink of sinks) {
|
|
98
79
|
for (const tracked of sink.trackedArguments) {
|
|
@@ -100,32 +81,24 @@ function collectPropagations(fn, checker, sinks) {
|
|
|
100
81
|
if (!argument) {
|
|
101
82
|
continue;
|
|
102
83
|
}
|
|
103
|
-
//
|
|
104
84
|
// direct parameter
|
|
105
|
-
//
|
|
106
85
|
// track(name)
|
|
107
86
|
// track(props.event)
|
|
108
|
-
//
|
|
109
|
-
if (typescript_1.default.isIdentifier(argument)
|
|
110
|
-
|| typescript_1.default.isPropertyAccessExpression(argument)) {
|
|
87
|
+
// track(props?.event)
|
|
88
|
+
if (typescript_1.default.isIdentifier(argument) || typescript_1.default.isPropertyAccessExpression(argument) || typescript_1.default.isPropertyAccessChain(argument)) {
|
|
111
89
|
let identifier = null;
|
|
112
90
|
let propertyPath = [];
|
|
113
91
|
if (typescript_1.default.isIdentifier(argument)) {
|
|
114
|
-
identifier =
|
|
115
|
-
argument;
|
|
92
|
+
identifier = argument;
|
|
116
93
|
}
|
|
117
94
|
else {
|
|
118
|
-
propertyPath =
|
|
119
|
-
extractPropertyPath(argument)
|
|
120
|
-
?? [];
|
|
95
|
+
propertyPath = extractPropertyPath(argument) ?? [];
|
|
121
96
|
let root = argument;
|
|
122
|
-
while (typescript_1.default.isPropertyAccessExpression(root)) {
|
|
123
|
-
root =
|
|
124
|
-
root.expression;
|
|
97
|
+
while (typescript_1.default.isPropertyAccessExpression(root) || typescript_1.default.isPropertyAccessChain(root)) {
|
|
98
|
+
root = root.expression;
|
|
125
99
|
}
|
|
126
100
|
if (typescript_1.default.isIdentifier(root)) {
|
|
127
|
-
identifier =
|
|
128
|
-
root;
|
|
101
|
+
identifier = root;
|
|
129
102
|
}
|
|
130
103
|
}
|
|
131
104
|
if (!identifier) {
|
|
@@ -146,17 +119,15 @@ function collectPropagations(fn, checker, sinks) {
|
|
|
146
119
|
});
|
|
147
120
|
continue;
|
|
148
121
|
}
|
|
149
|
-
//
|
|
150
122
|
// object literal
|
|
151
|
-
//
|
|
152
123
|
// track({ event: name })
|
|
153
|
-
//
|
|
124
|
+
// track({ event })
|
|
154
125
|
if (typescript_1.default.isObjectLiteralExpression(argument)) {
|
|
155
126
|
for (const property of argument.properties) {
|
|
156
|
-
if (!typescript_1.default.isPropertyAssignment(property)) {
|
|
127
|
+
if (!typescript_1.default.isPropertyAssignment(property) && !typescript_1.default.isShorthandPropertyAssignment(property)) {
|
|
157
128
|
continue;
|
|
158
129
|
}
|
|
159
|
-
const initializer = property.initializer;
|
|
130
|
+
const initializer = typescript_1.default.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
160
131
|
if (!typescript_1.default.isIdentifier(initializer)) {
|
|
161
132
|
continue;
|
|
162
133
|
}
|
|
@@ -176,12 +147,12 @@ function collectPropagations(fn, checker, sinks) {
|
|
|
176
147
|
}
|
|
177
148
|
return result;
|
|
178
149
|
}
|
|
179
|
-
function analyzeWrapperPropagation(fn, checker, sinks
|
|
150
|
+
function analyzeWrapperPropagation(fn, checker, sinks) {
|
|
180
151
|
const symbol = (0, symbolUtils_1.getFunctionSymbol)(fn, checker);
|
|
181
152
|
if (!symbol) {
|
|
182
153
|
return null;
|
|
183
154
|
}
|
|
184
|
-
const propagations = collectPropagations(fn,
|
|
155
|
+
const propagations = collectPropagations(fn, sinks);
|
|
185
156
|
if (propagations.length === 0) {
|
|
186
157
|
return null;
|
|
187
158
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/analysis/scanner/returnPropagationAnalyzer.ts
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -11,8 +10,7 @@ function resolveParameter(fn, identifier) {
|
|
|
11
10
|
if (!typescript_1.default.isIdentifier(parameter.name)) {
|
|
12
11
|
continue;
|
|
13
12
|
}
|
|
14
|
-
if (parameter.name.text
|
|
15
|
-
=== identifier.text) {
|
|
13
|
+
if (parameter.name.text === identifier.text) {
|
|
16
14
|
return parameter;
|
|
17
15
|
}
|
|
18
16
|
}
|
|
@@ -21,20 +19,15 @@ function resolveParameter(fn, identifier) {
|
|
|
21
19
|
function analyzeReturnPropagation(fn) {
|
|
22
20
|
const propagations = [];
|
|
23
21
|
function visit(node) {
|
|
24
|
-
//
|
|
25
22
|
// return value
|
|
26
|
-
|
|
27
|
-
if (typescript_1.default.isReturnStatement(node)
|
|
28
|
-
&& node.expression) {
|
|
23
|
+
if (typescript_1.default.isReturnStatement(node) && node.expression) {
|
|
29
24
|
collectExpression(node.expression);
|
|
30
25
|
}
|
|
31
26
|
typescript_1.default.forEachChild(node, visit);
|
|
32
27
|
}
|
|
33
28
|
function collectExpression(expression) {
|
|
34
|
-
//
|
|
35
29
|
// direct return
|
|
36
30
|
// return event
|
|
37
|
-
//
|
|
38
31
|
if (typescript_1.default.isIdentifier(expression)) {
|
|
39
32
|
const parameter = resolveParameter(fn, expression);
|
|
40
33
|
if (!parameter) {
|
|
@@ -47,10 +40,8 @@ function analyzeReturnPropagation(fn) {
|
|
|
47
40
|
});
|
|
48
41
|
return;
|
|
49
42
|
}
|
|
50
|
-
//
|
|
51
43
|
// object return
|
|
52
44
|
// return { event }
|
|
53
|
-
//
|
|
54
45
|
if (typescript_1.default.isObjectLiteralExpression(expression)) {
|
|
55
46
|
for (const property of expression.properties) {
|
|
56
47
|
if (!typescript_1.default.isPropertyAssignment(property)) {
|
|
@@ -72,9 +63,7 @@ function analyzeReturnPropagation(fn) {
|
|
|
72
63
|
}
|
|
73
64
|
return;
|
|
74
65
|
}
|
|
75
|
-
//
|
|
76
66
|
// template returns
|
|
77
|
-
//
|
|
78
67
|
if (typescript_1.default.isTemplateExpression(expression)) {
|
|
79
68
|
for (const span of expression.templateSpans) {
|
|
80
69
|
if (!typescript_1.default.isIdentifier(span.expression)) {
|
|
@@ -7,67 +7,44 @@ exports.scanSource = scanSource;
|
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
8
|
const sinkDetector_1 = require("./sinkDetector");
|
|
9
9
|
const propagationAnalyzer_1 = require("./analyzer/propagationAnalyzer");
|
|
10
|
-
function scanSource(source, checker,
|
|
10
|
+
function scanSource(source, checker, wrapperRegistry) {
|
|
11
11
|
const sinks = [];
|
|
12
12
|
const wrappers = [];
|
|
13
13
|
const trackCalls = [];
|
|
14
14
|
const functions = [];
|
|
15
15
|
function visit(node) {
|
|
16
|
-
//
|
|
17
16
|
// imports/exports are irrelevant
|
|
18
|
-
|
|
19
|
-
if (typescript_1.default.isImportDeclaration(node)
|
|
20
|
-
|| typescript_1.default.isExportDeclaration(node)) {
|
|
17
|
+
if (typescript_1.default.isImportDeclaration(node) || typescript_1.default.isExportDeclaration(node)) {
|
|
21
18
|
return;
|
|
22
19
|
}
|
|
23
|
-
//
|
|
24
20
|
// function collection
|
|
25
|
-
|
|
26
|
-
if (typescript_1.default.isFunctionDeclaration(node)
|
|
27
|
-
|| typescript_1.default.isArrowFunction(node)
|
|
28
|
-
|| typescript_1.default.isFunctionExpression(node)
|
|
29
|
-
|| typescript_1.default.isMethodDeclaration(node)) {
|
|
21
|
+
if (typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node) || typescript_1.default.isMethodDeclaration(node)) {
|
|
30
22
|
functions.push(node);
|
|
31
23
|
}
|
|
32
|
-
//
|
|
33
24
|
// sink detection
|
|
34
|
-
//
|
|
35
25
|
if (typescript_1.default.isCallExpression(node)) {
|
|
36
26
|
const sink = (0, sinkDetector_1.detectTrackSink)(node);
|
|
37
27
|
if (sink) {
|
|
38
28
|
sinks.push(sink);
|
|
39
|
-
const trackedArguments = sink.trackedArguments
|
|
40
|
-
.map(tracked => {
|
|
29
|
+
const trackedArguments = sink.trackedArguments.map(tracked => {
|
|
41
30
|
const argument = node.arguments[tracked.index];
|
|
42
31
|
if (!argument) {
|
|
43
32
|
return undefined;
|
|
44
33
|
}
|
|
45
|
-
//
|
|
46
34
|
// track({
|
|
47
35
|
// event: name
|
|
48
36
|
// })
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
&& typescript_1.default.isObjectLiteralExpression(argument)) {
|
|
53
|
-
const property = argument.properties
|
|
54
|
-
.find(prop => {
|
|
55
|
-
return (typescript_1.default.isPropertyAssignment(prop)
|
|
56
|
-
&& prop.name.getText()
|
|
57
|
-
=== tracked
|
|
58
|
-
.propertyPath[0]);
|
|
37
|
+
if (tracked.propertyPath.length > 0 && typescript_1.default.isObjectLiteralExpression(argument)) {
|
|
38
|
+
const property = argument.properties.find(prop => {
|
|
39
|
+
return (typescript_1.default.isPropertyAssignment(prop) && prop.name.getText() === tracked.propertyPath[0]);
|
|
59
40
|
});
|
|
60
|
-
if (property
|
|
61
|
-
&& typescript_1.default.isPropertyAssignment(property)) {
|
|
41
|
+
if (property && typescript_1.default.isPropertyAssignment(property)) {
|
|
62
42
|
return property.initializer;
|
|
63
43
|
}
|
|
64
44
|
}
|
|
65
|
-
//
|
|
66
45
|
// track(name)
|
|
67
|
-
//
|
|
68
46
|
return argument;
|
|
69
|
-
})
|
|
70
|
-
.filter((value) => {
|
|
47
|
+
}).filter((value) => {
|
|
71
48
|
return Boolean(value);
|
|
72
49
|
});
|
|
73
50
|
trackCalls.push({
|
|
@@ -80,9 +57,7 @@ function scanSource(source, checker, _config, resolvedCallCache, resolvedExportC
|
|
|
80
57
|
typescript_1.default.forEachChild(node, visit);
|
|
81
58
|
}
|
|
82
59
|
visit(source);
|
|
83
|
-
//
|
|
84
|
-
// generic semantic analysis
|
|
85
|
-
//
|
|
60
|
+
// semantic wrapper analysis
|
|
86
61
|
for (const fn of functions) {
|
|
87
62
|
if (!fn.body) {
|
|
88
63
|
continue;
|
|
@@ -93,19 +68,16 @@ function scanSource(source, checker, _config, resolvedCallCache, resolvedExportC
|
|
|
93
68
|
if (current === fn) {
|
|
94
69
|
return true;
|
|
95
70
|
}
|
|
96
|
-
current =
|
|
97
|
-
current.parent;
|
|
71
|
+
current = current.parent;
|
|
98
72
|
}
|
|
99
73
|
return false;
|
|
100
74
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const wrapper = (0, propagationAnalyzer_1.analyzeWrapperPropagation)(fn, checker, localSinks, wrapperRegistry, resolvedCallCache, resolvedExportCache);
|
|
105
|
-
if (wrapper) {
|
|
106
|
-
wrappers.push(wrapper);
|
|
107
|
-
wrapperRegistry.set(wrapper, checker, resolvedExportCache);
|
|
75
|
+
const wrapper = (0, propagationAnalyzer_1.analyzeWrapperPropagation)(fn, checker, localSinks);
|
|
76
|
+
if (!wrapper) {
|
|
77
|
+
continue;
|
|
108
78
|
}
|
|
79
|
+
wrappers.push(wrapper);
|
|
80
|
+
wrapperRegistry.set(wrapper);
|
|
109
81
|
}
|
|
110
82
|
return {
|
|
111
83
|
fileName: source.fileName,
|