blast-radius-analyzer 1.2.1
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 +108 -0
- package/TEST-REPORT.md +379 -0
- package/dist/core/AnalysisCache.d.ts +59 -0
- package/dist/core/AnalysisCache.js +156 -0
- package/dist/core/BlastRadiusAnalyzer.d.ts +99 -0
- package/dist/core/BlastRadiusAnalyzer.js +510 -0
- package/dist/core/CallStackBuilder.d.ts +63 -0
- package/dist/core/CallStackBuilder.js +269 -0
- package/dist/core/DataFlowAnalyzer.d.ts +215 -0
- package/dist/core/DataFlowAnalyzer.js +1115 -0
- package/dist/core/DependencyGraph.d.ts +55 -0
- package/dist/core/DependencyGraph.js +541 -0
- package/dist/core/ImpactTracer.d.ts +96 -0
- package/dist/core/ImpactTracer.js +398 -0
- package/dist/core/PropagationTracker.d.ts +73 -0
- package/dist/core/PropagationTracker.js +502 -0
- package/dist/core/PropertyAccessTracker.d.ts +56 -0
- package/dist/core/PropertyAccessTracker.js +281 -0
- package/dist/core/SymbolAnalyzer.d.ts +139 -0
- package/dist/core/SymbolAnalyzer.js +608 -0
- package/dist/core/TypeFlowAnalyzer.d.ts +120 -0
- package/dist/core/TypeFlowAnalyzer.js +654 -0
- package/dist/core/TypePropagationAnalyzer.d.ts +58 -0
- package/dist/core/TypePropagationAnalyzer.js +269 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +952 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.js +5 -0
- package/package.json +39 -0
- package/src/core/AnalysisCache.ts +189 -0
- package/src/core/CallStackBuilder.ts +345 -0
- package/src/core/DataFlowAnalyzer.ts +1403 -0
- package/src/core/DependencyGraph.ts +584 -0
- package/src/core/ImpactTracer.ts +521 -0
- package/src/core/PropagationTracker.ts +630 -0
- package/src/core/PropertyAccessTracker.ts +349 -0
- package/src/core/SymbolAnalyzer.ts +746 -0
- package/src/core/TypeFlowAnalyzer.ts +844 -0
- package/src/core/TypePropagationAnalyzer.ts +332 -0
- package/src/index.ts +1071 -0
- package/src/types.ts +163 -0
- package/test-cases/.blast-radius-cache/file-states.json +14 -0
- package/test-cases/config.ts +13 -0
- package/test-cases/consumer.ts +12 -0
- package/test-cases/nested.ts +25 -0
- package/test-cases/simple.ts +62 -0
- package/test-cases/tsconfig.json +11 -0
- package/test-cases/user.ts +32 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CallStackBuilder - 调用栈视图构建器
|
|
3
|
+
*
|
|
4
|
+
* 从改动点向上追踪,构建完整的调用链视图
|
|
5
|
+
*/
|
|
6
|
+
import { Project, Node, SyntaxKind } from 'ts-morph';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
export class CallStackBuilder {
|
|
9
|
+
project;
|
|
10
|
+
projectRoot;
|
|
11
|
+
constructor(projectRoot, tsConfigPath) {
|
|
12
|
+
this.projectRoot = projectRoot;
|
|
13
|
+
this.project = new Project({
|
|
14
|
+
tsConfigFilePath: tsConfigPath,
|
|
15
|
+
skipAddingFilesFromTsConfig: true,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 添加源文件到项目
|
|
20
|
+
*/
|
|
21
|
+
addSourceFiles(patterns) {
|
|
22
|
+
for (const pattern of patterns) {
|
|
23
|
+
this.project.addSourceFilesAtPaths(pattern);
|
|
24
|
+
}
|
|
25
|
+
// 过滤 node_modules
|
|
26
|
+
const sourceFiles = this.project.getSourceFiles();
|
|
27
|
+
for (const sf of sourceFiles) {
|
|
28
|
+
if (sf.getFilePath().includes('node_modules')) {
|
|
29
|
+
sf.forget();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 构建调用栈视图(从改动点向上追踪到入口)
|
|
35
|
+
*/
|
|
36
|
+
buildCallStack(targetSymbol, targetFile) {
|
|
37
|
+
// 找到目标函数的定义
|
|
38
|
+
const definition = this.findSymbolDefinition(targetSymbol, targetFile);
|
|
39
|
+
if (!definition)
|
|
40
|
+
return null;
|
|
41
|
+
const root = {
|
|
42
|
+
name: targetSymbol,
|
|
43
|
+
file: definition.file,
|
|
44
|
+
line: definition.line,
|
|
45
|
+
type: 'function',
|
|
46
|
+
children: [],
|
|
47
|
+
};
|
|
48
|
+
// 递归追踪调用者
|
|
49
|
+
this.traceCallers(root, new Set(), 0, 10);
|
|
50
|
+
// 计算深度
|
|
51
|
+
const depth = this.calculateDepth(root);
|
|
52
|
+
// 构建路径字符串
|
|
53
|
+
const pathStr = this.buildPathString(root);
|
|
54
|
+
return { root, depth, path: pathStr };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 查找符号定义
|
|
58
|
+
*/
|
|
59
|
+
findSymbolDefinition(symbolName, inFile) {
|
|
60
|
+
const sourceFile = this.project.getSourceFile(inFile);
|
|
61
|
+
if (!sourceFile)
|
|
62
|
+
return null;
|
|
63
|
+
let result = null;
|
|
64
|
+
sourceFile.forEachDescendant(node => {
|
|
65
|
+
if (Node.isFunctionDeclaration(node)) {
|
|
66
|
+
const func = node;
|
|
67
|
+
if (func.getName() === symbolName) {
|
|
68
|
+
result = {
|
|
69
|
+
file: inFile,
|
|
70
|
+
line: func.getStartLineNumber(),
|
|
71
|
+
type: 'function',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (Node.isVariableStatement(node)) {
|
|
76
|
+
const varDecl = node.getFirstDescendantByKind(SyntaxKind.VariableDeclaration);
|
|
77
|
+
if (varDecl && varDecl.getName() === symbolName) {
|
|
78
|
+
result = {
|
|
79
|
+
file: inFile,
|
|
80
|
+
line: varDecl.getStartLineNumber(),
|
|
81
|
+
type: 'variable',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 递归追踪调用者
|
|
90
|
+
*/
|
|
91
|
+
traceCallers(node, visited, depth, maxDepth) {
|
|
92
|
+
if (depth > maxDepth)
|
|
93
|
+
return;
|
|
94
|
+
// 找所有调用此函数的地方
|
|
95
|
+
const callers = this.findCallers(node.name, node.file);
|
|
96
|
+
for (const caller of callers) {
|
|
97
|
+
const key = `${caller.file}:${caller.line}:${caller.name}`;
|
|
98
|
+
if (visited.has(key))
|
|
99
|
+
continue;
|
|
100
|
+
visited.add(key);
|
|
101
|
+
// 创建调用者节点
|
|
102
|
+
const callerNode = {
|
|
103
|
+
name: caller.name,
|
|
104
|
+
file: caller.file,
|
|
105
|
+
line: caller.line,
|
|
106
|
+
type: caller.type,
|
|
107
|
+
children: [],
|
|
108
|
+
callSite: {
|
|
109
|
+
line: caller.callLine,
|
|
110
|
+
expression: caller.callExpression,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
node.children.push(callerNode);
|
|
114
|
+
// 递归追踪调用者的调用者
|
|
115
|
+
this.traceCallers(callerNode, visited, depth + 1, maxDepth);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 查找调用某个函数的所有地方
|
|
120
|
+
*/
|
|
121
|
+
findCallers(symbolName, definedInFile) {
|
|
122
|
+
const results = [];
|
|
123
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
124
|
+
const filePath = sourceFile.getFilePath();
|
|
125
|
+
sourceFile.forEachDescendant((node) => {
|
|
126
|
+
// 查找函数定义
|
|
127
|
+
let funcInfo = null;
|
|
128
|
+
if (Node.isFunctionDeclaration(node)) {
|
|
129
|
+
const func = node;
|
|
130
|
+
const name = func.getName();
|
|
131
|
+
if (name) {
|
|
132
|
+
funcInfo = {
|
|
133
|
+
name,
|
|
134
|
+
file: filePath,
|
|
135
|
+
line: func.getStartLineNumber(),
|
|
136
|
+
type: 'function',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (Node.isArrowFunction(node)) {
|
|
141
|
+
// 检查是否是某个 const/let 声明的箭头函数
|
|
142
|
+
const parent = node.getParent();
|
|
143
|
+
if (parent && Node.isVariableDeclaration(parent)) {
|
|
144
|
+
const varDecl = parent;
|
|
145
|
+
const name = varDecl.getName();
|
|
146
|
+
if (name && !name.startsWith('_')) {
|
|
147
|
+
funcInfo = {
|
|
148
|
+
name,
|
|
149
|
+
file: filePath,
|
|
150
|
+
line: node.getStartLineNumber(),
|
|
151
|
+
type: 'arrow',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (Node.isMethodDeclaration(node)) {
|
|
157
|
+
const method = node;
|
|
158
|
+
const name = method.getName();
|
|
159
|
+
funcInfo = {
|
|
160
|
+
name,
|
|
161
|
+
file: filePath,
|
|
162
|
+
line: method.getStartLineNumber(),
|
|
163
|
+
type: 'method',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
else if (Node.isPropertyAssignment(node) && Node.isFunctionExpression(node.getInitializer())) {
|
|
167
|
+
// React 组件: onClick={() => ...}
|
|
168
|
+
const prop = node;
|
|
169
|
+
const name = prop.getName();
|
|
170
|
+
funcInfo = {
|
|
171
|
+
name: name || 'anonymous',
|
|
172
|
+
file: filePath,
|
|
173
|
+
line: node.getStartLineNumber(),
|
|
174
|
+
type: 'component',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// 如果找到了函数定义,检查是否调用了目标符号
|
|
178
|
+
if (funcInfo && funcInfo.name !== symbolName) {
|
|
179
|
+
const calls = this.findCallsInNode(node, symbolName);
|
|
180
|
+
for (const call of calls) {
|
|
181
|
+
results.push({
|
|
182
|
+
...funcInfo,
|
|
183
|
+
callLine: call.line,
|
|
184
|
+
callExpression: call.expression,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 在节点内查找对某个符号的调用
|
|
194
|
+
*/
|
|
195
|
+
findCallsInNode(node, symbolName) {
|
|
196
|
+
const results = [];
|
|
197
|
+
node.forEachDescendant((child) => {
|
|
198
|
+
if (Node.isCallExpression(child)) {
|
|
199
|
+
const callExpr = child;
|
|
200
|
+
const expr = callExpr.getExpression();
|
|
201
|
+
if (Node.isIdentifier(expr)) {
|
|
202
|
+
const name = expr.getText();
|
|
203
|
+
if (name === symbolName) {
|
|
204
|
+
results.push({
|
|
205
|
+
line: callExpr.getStartLineNumber(),
|
|
206
|
+
expression: callExpr.getText().slice(0, 50),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return results;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 计算树深度
|
|
216
|
+
*/
|
|
217
|
+
calculateDepth(node) {
|
|
218
|
+
if (node.children.length === 0)
|
|
219
|
+
return 0;
|
|
220
|
+
return 1 + Math.max(...node.children.map(c => this.calculateDepth(c)));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 构建路径字符串
|
|
224
|
+
*/
|
|
225
|
+
buildPathString(node) {
|
|
226
|
+
const result = [];
|
|
227
|
+
const build = (n) => {
|
|
228
|
+
result.push(`${path.basename(n.file)}:${n.line} (${n.name})`);
|
|
229
|
+
if (n.children.length > 0) {
|
|
230
|
+
build(n.children[0]);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
build(node);
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 生成文本格式的调用栈视图
|
|
238
|
+
*/
|
|
239
|
+
formatAsText(tree, changedSymbol) {
|
|
240
|
+
const lines = [];
|
|
241
|
+
lines.push('');
|
|
242
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
243
|
+
lines.push(' 📞 调用栈视图 (Call Stack) ');
|
|
244
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
245
|
+
lines.push('');
|
|
246
|
+
const renderNode = (n, prefix, isLast, isRoot) => {
|
|
247
|
+
const connector = isLast ? '└─' : '├─';
|
|
248
|
+
const current = isRoot
|
|
249
|
+
? `📍 ${n.name} (改动点)`
|
|
250
|
+
: `${connector} ${n.name}`;
|
|
251
|
+
lines.push(`${prefix}${current} → ${path.basename(n.file)}:${n.line}`);
|
|
252
|
+
if (n.callSite) {
|
|
253
|
+
lines.push(`${prefix} │`);
|
|
254
|
+
lines.push(`${prefix} └── 调用: 第${n.callSite.line}行 "${n.callSite.expression}"`);
|
|
255
|
+
}
|
|
256
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
257
|
+
n.children.forEach((child, idx) => {
|
|
258
|
+
const isChildLast = idx === n.children.length - 1;
|
|
259
|
+
renderNode(child, childPrefix, isChildLast, false);
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
renderNode(tree.root, '', true, true);
|
|
263
|
+
lines.push('');
|
|
264
|
+
lines.push(`深度: ${tree.depth} 层`);
|
|
265
|
+
lines.push(`路径: ${tree.path.join(' → ')}`);
|
|
266
|
+
lines.push('');
|
|
267
|
+
return lines.join('\n');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commercial-Grade Data Flow Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - Interprocedural data flow analysis (cross-function tracking)
|
|
6
|
+
* - Control flow sensitivity (branches, loops, exceptions)
|
|
7
|
+
* - Path sensitivity (different branches = different type states)
|
|
8
|
+
* - Context sensitivity (same function, different call sites = different types)
|
|
9
|
+
* - Worklist algorithm with fixed-point computation
|
|
10
|
+
* - Lattice-based abstract interpretation
|
|
11
|
+
* - Symbolic execution for branch conditions
|
|
12
|
+
* - Points-to analysis for reference tracking
|
|
13
|
+
* - Taint analysis for security-sensitive data
|
|
14
|
+
* - Escape analysis for closure/global escape
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Abstract value in the data flow lattice
|
|
18
|
+
*/
|
|
19
|
+
export interface AbstractValue {
|
|
20
|
+
/** Type representation */
|
|
21
|
+
type: string;
|
|
22
|
+
/** Possible values (constants) */
|
|
23
|
+
constants: Set<string>;
|
|
24
|
+
/** Is null/undefined possible */
|
|
25
|
+
nullable: boolean;
|
|
26
|
+
/** Property types if object */
|
|
27
|
+
properties: Map<string, AbstractValue>;
|
|
28
|
+
/** Array element type if array */
|
|
29
|
+
elementType?: AbstractValue;
|
|
30
|
+
/** Is this value tainted (user input, etc.) */
|
|
31
|
+
tainted: boolean;
|
|
32
|
+
/** Where did this value escape (closure, global, return) */
|
|
33
|
+
escapes: Set<'closure' | 'global' | 'parameter' | 'return'>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Data flow fact at a program point
|
|
37
|
+
*/
|
|
38
|
+
export interface DataFlowFact {
|
|
39
|
+
/** Variable name -> abstract value */
|
|
40
|
+
env: Map<string, AbstractValue>;
|
|
41
|
+
/** Type constraints */
|
|
42
|
+
constraints: TypeConstraint[];
|
|
43
|
+
/** Path condition (branch predicates) */
|
|
44
|
+
pathCondition: PathCondition[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Type constraint
|
|
48
|
+
*/
|
|
49
|
+
export interface TypeConstraint {
|
|
50
|
+
variable: string;
|
|
51
|
+
predicate: string;
|
|
52
|
+
thenTypes?: Map<string, AbstractValue>;
|
|
53
|
+
elseTypes?: Map<string, AbstractValue>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Path condition from branch predicates
|
|
57
|
+
*/
|
|
58
|
+
export interface PathCondition {
|
|
59
|
+
expression: string;
|
|
60
|
+
/** true = then branch, false = else branch */
|
|
61
|
+
polarity: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Analysis result with full flow paths
|
|
65
|
+
*/
|
|
66
|
+
export interface DataFlowResult {
|
|
67
|
+
hasDataLeaks: boolean;
|
|
68
|
+
flowPaths: FlowPath[];
|
|
69
|
+
taintedPaths: TaintedPath[];
|
|
70
|
+
typeNarrowing: Map<string, {
|
|
71
|
+
line: number;
|
|
72
|
+
types: string[];
|
|
73
|
+
}[]>;
|
|
74
|
+
statistics: {
|
|
75
|
+
nodesAnalyzed: number;
|
|
76
|
+
blocksConstructed: number;
|
|
77
|
+
callSitesAnalyzed: number;
|
|
78
|
+
fixedPointIterations: number;
|
|
79
|
+
constraintsGenerated: number;
|
|
80
|
+
typesNarrowed: number;
|
|
81
|
+
promiseUnwraps: number;
|
|
82
|
+
conditionalBranches: number;
|
|
83
|
+
escapedValues: number;
|
|
84
|
+
taintedValues: number;
|
|
85
|
+
pathsTracked: number;
|
|
86
|
+
};
|
|
87
|
+
confidence: 'high' | 'medium' | 'low';
|
|
88
|
+
duration: number;
|
|
89
|
+
/** All facts at exit of each block */
|
|
90
|
+
finalFacts: Map<string, DataFlowFact>;
|
|
91
|
+
}
|
|
92
|
+
export interface FlowPath {
|
|
93
|
+
source: string;
|
|
94
|
+
sink: string;
|
|
95
|
+
path: string[];
|
|
96
|
+
typeAtSink: string;
|
|
97
|
+
typeAtSource: string;
|
|
98
|
+
isTainted: boolean;
|
|
99
|
+
}
|
|
100
|
+
export interface TaintedPath {
|
|
101
|
+
source: string;
|
|
102
|
+
sink: string;
|
|
103
|
+
taintSource: 'user-input' | 'file-read' | 'network' | 'environment';
|
|
104
|
+
path: string[];
|
|
105
|
+
}
|
|
106
|
+
export declare class DataFlowAnalyzer {
|
|
107
|
+
private program;
|
|
108
|
+
private checker;
|
|
109
|
+
private sourceFiles;
|
|
110
|
+
private cfgCache;
|
|
111
|
+
private worklist;
|
|
112
|
+
private analyzedCallSites;
|
|
113
|
+
private maxIterations;
|
|
114
|
+
private maxCallDepth;
|
|
115
|
+
private trackTaint;
|
|
116
|
+
private trackEscapes;
|
|
117
|
+
constructor(projectRoot: string, tsConfigPath: string);
|
|
118
|
+
/**
|
|
119
|
+
* MAIN ENTRY POINT - Full interprocedural data flow analysis
|
|
120
|
+
*/
|
|
121
|
+
analyzeDataFlow(functionName: string, functionFile: string): DataFlowResult;
|
|
122
|
+
/**
|
|
123
|
+
* Find function declaration
|
|
124
|
+
*/
|
|
125
|
+
private findFunction;
|
|
126
|
+
/**
|
|
127
|
+
* Build Control Flow Graph with basic blocks
|
|
128
|
+
*/
|
|
129
|
+
private buildCFG;
|
|
130
|
+
/**
|
|
131
|
+
* Create entry fact with parameter bindings
|
|
132
|
+
*/
|
|
133
|
+
private createEntryFact;
|
|
134
|
+
/**
|
|
135
|
+
* Create abstract value from TypeScript type
|
|
136
|
+
*/
|
|
137
|
+
private createAbstractValue;
|
|
138
|
+
/**
|
|
139
|
+
* WORKLIST ALGORITHM - Lattice-based fixed-point computation
|
|
140
|
+
*
|
|
141
|
+
* This is the core of the data flow analysis.
|
|
142
|
+
* It iterates until no facts change (fixed point is reached).
|
|
143
|
+
*/
|
|
144
|
+
private runWorklistAnalysis;
|
|
145
|
+
/**
|
|
146
|
+
* JOIN operation - combine facts from multiple predecessors
|
|
147
|
+
*/
|
|
148
|
+
private joinFacts;
|
|
149
|
+
/**
|
|
150
|
+
* LATTICE MEET - intersection of abstract values
|
|
151
|
+
*/
|
|
152
|
+
private latticeMeet;
|
|
153
|
+
/**
|
|
154
|
+
* TRANSFER FUNCTION - apply a statement's effect on facts
|
|
155
|
+
*/
|
|
156
|
+
private transfer;
|
|
157
|
+
/**
|
|
158
|
+
* Transfer function for expressions
|
|
159
|
+
*/
|
|
160
|
+
private transferExpr;
|
|
161
|
+
/**
|
|
162
|
+
* Evaluate expression to get abstract value
|
|
163
|
+
*/
|
|
164
|
+
private evaluateExpr;
|
|
165
|
+
/**
|
|
166
|
+
* Analyze a call site (interprocedural analysis)
|
|
167
|
+
*/
|
|
168
|
+
private analyzeCallSite;
|
|
169
|
+
/**
|
|
170
|
+
* Check if expression is a taint source
|
|
171
|
+
*/
|
|
172
|
+
private isTaintedSource;
|
|
173
|
+
/**
|
|
174
|
+
* Check if expression causes escape
|
|
175
|
+
*/
|
|
176
|
+
private doesEscape;
|
|
177
|
+
/**
|
|
178
|
+
* Analyze async patterns (Promise, await)
|
|
179
|
+
*/
|
|
180
|
+
private analyzeAsyncPatterns;
|
|
181
|
+
/**
|
|
182
|
+
* Check for data leaks (tainted -> escape)
|
|
183
|
+
*/
|
|
184
|
+
private checkDataLeaks;
|
|
185
|
+
/**
|
|
186
|
+
* Clone a data flow fact
|
|
187
|
+
*/
|
|
188
|
+
private cloneFact;
|
|
189
|
+
/**
|
|
190
|
+
* Narrow types based on branch condition
|
|
191
|
+
* E.g., if (x != null) narrows x from T | null to T
|
|
192
|
+
* if (x > 1000) narrows the possible range of x
|
|
193
|
+
*/
|
|
194
|
+
private narrowTypesFromCondition;
|
|
195
|
+
/**
|
|
196
|
+
* Check if two facts are equal
|
|
197
|
+
*/
|
|
198
|
+
private factsEqual;
|
|
199
|
+
/**
|
|
200
|
+
* Get line number
|
|
201
|
+
*/
|
|
202
|
+
private getLine;
|
|
203
|
+
/**
|
|
204
|
+
* Calculate confidence level
|
|
205
|
+
*/
|
|
206
|
+
private calculateConfidence;
|
|
207
|
+
/**
|
|
208
|
+
* Create empty result
|
|
209
|
+
*/
|
|
210
|
+
private createEmptyResult;
|
|
211
|
+
/**
|
|
212
|
+
* Format as text
|
|
213
|
+
*/
|
|
214
|
+
formatAsText(result: DataFlowResult, functionName: string): string;
|
|
215
|
+
}
|