sourceverity 1.0.2 → 1.0.4
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 +91 -3
- package/dist/analysis/arrayBounds.d.ts +4 -0
- package/dist/analysis/arrayBounds.d.ts.map +1 -0
- package/dist/analysis/arrayBounds.js +4 -0
- package/dist/analysis/arrayBounds.js.map +1 -0
- package/dist/analysis/arrayBoundsCore.d.ts +69 -0
- package/dist/analysis/arrayBoundsCore.d.ts.map +1 -0
- package/dist/analysis/arrayBoundsCore.js +863 -0
- package/dist/analysis/arrayBoundsCore.js.map +1 -0
- package/dist/analysis/arrayConstructionBounds.d.ts +14 -0
- package/dist/analysis/arrayConstructionBounds.d.ts.map +1 -0
- package/dist/analysis/arrayConstructionBounds.js +445 -0
- package/dist/analysis/arrayConstructionBounds.js.map +1 -0
- package/dist/analysis/arrayIterationBounds.d.ts +12 -0
- package/dist/analysis/arrayIterationBounds.d.ts.map +1 -0
- package/dist/analysis/arrayIterationBounds.js +578 -0
- package/dist/analysis/arrayIterationBounds.js.map +1 -0
- package/dist/analysis/controlFlowGuards.d.ts +7 -0
- package/dist/analysis/controlFlowGuards.d.ts.map +1 -0
- package/dist/analysis/controlFlowGuards.js +119 -0
- package/dist/analysis/controlFlowGuards.js.map +1 -0
- package/dist/analysis/controlFlowTypes.d.ts +5 -0
- package/dist/analysis/controlFlowTypes.d.ts.map +1 -0
- package/dist/analysis/controlFlowTypes.js +2 -0
- package/dist/analysis/controlFlowTypes.js.map +1 -0
- package/dist/analysis/frameworks.d.ts.map +1 -1
- package/dist/analysis/frameworks.js +95 -35
- package/dist/analysis/frameworks.js.map +1 -1
- package/dist/analysis/functions.d.ts.map +1 -1
- package/dist/analysis/functions.js +120 -36
- package/dist/analysis/functions.js.map +1 -1
- package/dist/analysis/{controlFlow.d.ts → nonNullGuards.d.ts} +2 -5
- package/dist/analysis/nonNullGuards.d.ts.map +1 -0
- package/dist/analysis/nonNullGuards.js +340 -0
- package/dist/analysis/nonNullGuards.js.map +1 -0
- package/dist/analysis/promiseFlow.d.ts.map +1 -1
- package/dist/analysis/promiseFlow.js +85 -11
- package/dist/analysis/promiseFlow.js.map +1 -1
- package/dist/analysis/reactStateBounds.d.ts +8 -0
- package/dist/analysis/reactStateBounds.d.ts.map +1 -0
- package/dist/analysis/reactStateBounds.js +390 -0
- package/dist/analysis/reactStateBounds.js.map +1 -0
- package/dist/baseline/manager.js +1 -1
- package/dist/baseline/manager.js.map +1 -1
- package/dist/baseline/types.d.ts +1 -1
- package/dist/baseline/types.d.ts.map +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +24 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/paths.d.ts +0 -4
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +0 -6
- package/dist/core/paths.js.map +1 -1
- package/dist/engine/runner.d.ts +6 -1
- package/dist/engine/runner.d.ts.map +1 -1
- package/dist/engine/runner.js +38 -21
- package/dist/engine/runner.js.map +1 -1
- package/dist/engine/symbols.d.ts +17 -5
- package/dist/engine/symbols.d.ts.map +1 -1
- package/dist/engine/symbols.js +269 -46
- package/dist/engine/symbols.js.map +1 -1
- package/dist/reporters/sarif.d.ts +2 -0
- package/dist/reporters/sarif.d.ts.map +1 -1
- package/dist/reporters/sarif.js +36 -7
- package/dist/reporters/sarif.js.map +1 -1
- package/dist/repository/detector.d.ts.map +1 -1
- package/dist/repository/detector.js +31 -11
- package/dist/repository/detector.js.map +1 -1
- package/dist/rules/async/floatingPromise.d.ts.map +1 -1
- package/dist/rules/async/floatingPromise.js +24 -2
- package/dist/rules/async/floatingPromise.js.map +1 -1
- package/dist/rules/browser/eventListenerCleanup.d.ts.map +1 -1
- package/dist/rules/browser/eventListenerCleanup.js +2 -9
- package/dist/rules/browser/eventListenerCleanup.js.map +1 -1
- package/dist/rules/browser/observerCleanup.d.ts.map +1 -1
- package/dist/rules/browser/observerCleanup.js +2 -9
- package/dist/rules/browser/observerCleanup.js.map +1 -1
- package/dist/rules/browser/timerCleanup.d.ts.map +1 -1
- package/dist/rules/browser/timerCleanup.js +2 -9
- package/dist/rules/browser/timerCleanup.js.map +1 -1
- package/dist/rules/network/fetchStatusUnchecked.d.ts.map +1 -1
- package/dist/rules/network/fetchStatusUnchecked.js +245 -46
- package/dist/rules/network/fetchStatusUnchecked.js.map +1 -1
- package/dist/rules/react/asyncEffectCallback.d.ts.map +1 -1
- package/dist/rules/react/asyncEffectCallback.js +2 -9
- package/dist/rules/react/asyncEffectCallback.js.map +1 -1
- package/dist/rules/react/derivedStateEffect.d.ts.map +1 -1
- package/dist/rules/react/derivedStateEffect.js +61 -13
- package/dist/rules/react/derivedStateEffect.js.map +1 -1
- package/dist/rules/react/missingEffectCleanup.d.ts.map +1 -1
- package/dist/rules/react/missingEffectCleanup.js +102 -10
- package/dist/rules/react/missingEffectCleanup.js.map +1 -1
- package/dist/rules/typescript/nonNullAssertionRisk.js +1 -1
- package/dist/rules/typescript/nonNullAssertionRisk.js.map +1 -1
- package/dist/rules/typescript/uncheckedIndexAccess.d.ts.map +1 -1
- package/dist/rules/typescript/uncheckedIndexAccess.js +55 -0
- package/dist/rules/typescript/uncheckedIndexAccess.js.map +1 -1
- package/dist/rules/typescript/unsafeUnvalidatedAssertion.d.ts.map +1 -1
- package/dist/rules/typescript/unsafeUnvalidatedAssertion.js +9 -4
- package/dist/rules/typescript/unsafeUnvalidatedAssertion.js.map +1 -1
- package/package.json +1 -1
- package/dist/analysis/controlFlow.d.ts.map +0 -1
- package/dist/analysis/controlFlow.js +0 -2136
- package/dist/analysis/controlFlow.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { unwrapExpression } from "./values.js";
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a statement definitely exits control flow (return, throw, break, continue).
|
|
4
|
+
*/
|
|
5
|
+
export function statementExitsControlFlow(stmt, ts) {
|
|
6
|
+
if (ts.isReturnStatement(stmt) ||
|
|
7
|
+
ts.isThrowStatement(stmt) ||
|
|
8
|
+
ts.isBreakStatement(stmt) ||
|
|
9
|
+
ts.isContinueStatement(stmt)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (ts.isBlock(stmt)) {
|
|
13
|
+
const stmts = stmt.statements;
|
|
14
|
+
if (stmts.length === 0)
|
|
15
|
+
return false;
|
|
16
|
+
const last = stmts[stmts.length - 1];
|
|
17
|
+
return last !== undefined && statementExitsControlFlow(last, ts);
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function isAssignmentOperator(kind, ts) {
|
|
22
|
+
return (kind === ts.SyntaxKind.EqualsToken ||
|
|
23
|
+
kind === ts.SyntaxKind.PlusEqualsToken ||
|
|
24
|
+
kind === ts.SyntaxKind.MinusEqualsToken ||
|
|
25
|
+
kind === ts.SyntaxKind.AsteriskEqualsToken ||
|
|
26
|
+
kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken ||
|
|
27
|
+
kind === ts.SyntaxKind.SlashEqualsToken ||
|
|
28
|
+
kind === ts.SyntaxKind.PercentEqualsToken ||
|
|
29
|
+
kind === ts.SyntaxKind.LessThanLessThanEqualsToken ||
|
|
30
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken ||
|
|
31
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken ||
|
|
32
|
+
kind === ts.SyntaxKind.AmpersandEqualsToken ||
|
|
33
|
+
kind === ts.SyntaxKind.BarEqualsToken ||
|
|
34
|
+
kind === ts.SyntaxKind.CaretEqualsToken ||
|
|
35
|
+
kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
36
|
+
kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
37
|
+
kind === ts.SyntaxKind.QuestionQuestionEqualsToken);
|
|
38
|
+
}
|
|
39
|
+
export function hasVariableReassignment(node, targetName, targetSymbol, ts, checker, beforePos, afterPos) {
|
|
40
|
+
let reassigned = false;
|
|
41
|
+
function checkTargetMatch(ident) {
|
|
42
|
+
if (checker && targetSymbol) {
|
|
43
|
+
let sym = checker.getSymbolAtLocation(ident);
|
|
44
|
+
if (sym && (sym.flags & ts.SymbolFlags.Alias) !== 0) {
|
|
45
|
+
try {
|
|
46
|
+
sym = checker.getAliasedSymbol(sym);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// ignore
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (sym) {
|
|
53
|
+
return sym === targetSymbol;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return targetSymbol === undefined && ident.text === targetName;
|
|
57
|
+
}
|
|
58
|
+
function checkLeftAssignment(leftExpr) {
|
|
59
|
+
const unwrapped = unwrapExpression(leftExpr, ts);
|
|
60
|
+
if (ts.isIdentifier(unwrapped)) {
|
|
61
|
+
return checkTargetMatch(unwrapped);
|
|
62
|
+
}
|
|
63
|
+
if (ts.isArrayLiteralExpression(unwrapped)) {
|
|
64
|
+
return unwrapped.elements.some((el) => {
|
|
65
|
+
if (ts.isOmittedExpression(el))
|
|
66
|
+
return false;
|
|
67
|
+
if (ts.isSpreadElement(el)) {
|
|
68
|
+
return checkLeftAssignment(el.expression);
|
|
69
|
+
}
|
|
70
|
+
return checkLeftAssignment(el);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (ts.isObjectLiteralExpression(unwrapped)) {
|
|
74
|
+
return unwrapped.properties.some((prop) => {
|
|
75
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
76
|
+
return checkTargetMatch(prop.name);
|
|
77
|
+
}
|
|
78
|
+
if (ts.isPropertyAssignment(prop) && ts.isExpression(prop.initializer)) {
|
|
79
|
+
return checkLeftAssignment(prop.initializer);
|
|
80
|
+
}
|
|
81
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
82
|
+
return checkLeftAssignment(prop.expression);
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
function walk(n) {
|
|
90
|
+
if (reassigned)
|
|
91
|
+
return;
|
|
92
|
+
const nStart = n.getStart();
|
|
93
|
+
const nEnd = n.getEnd();
|
|
94
|
+
if (beforePos !== undefined && nStart > beforePos)
|
|
95
|
+
return;
|
|
96
|
+
if (afterPos !== undefined && nEnd < afterPos)
|
|
97
|
+
return;
|
|
98
|
+
const inRange = (afterPos === undefined || nStart >= afterPos) && (beforePos === undefined || nStart <= beforePos);
|
|
99
|
+
if (inRange && ts.isBinaryExpression(n) && isAssignmentOperator(n.operatorToken.kind, ts)) {
|
|
100
|
+
if (checkLeftAssignment(n.left)) {
|
|
101
|
+
reassigned = true;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (inRange &&
|
|
106
|
+
(ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) &&
|
|
107
|
+
(n.operator === ts.SyntaxKind.PlusPlusToken || n.operator === ts.SyntaxKind.MinusMinusToken)) {
|
|
108
|
+
const operand = unwrapExpression(n.operand, ts);
|
|
109
|
+
if (ts.isIdentifier(operand) && checkTargetMatch(operand)) {
|
|
110
|
+
reassigned = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
n.forEachChild(walk);
|
|
115
|
+
}
|
|
116
|
+
walk(node);
|
|
117
|
+
return reassigned;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=controlFlowGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlFlowGuards.js","sourceRoot":"","sources":["../../src/analysis/controlFlowGuards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlFlowTypes.d.ts","sourceRoot":"","sources":["../../src/analysis/controlFlowTypes.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlFlowTypes.js","sourceRoot":"","sources":["../../src/analysis/controlFlowTypes.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../../src/analysis/frameworks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../../src/analysis/frameworks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAiGD;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,CAAC,cAAc,EAC/B,EAAE,EAAE,OAAO,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS,GACvC,uBAAuB,CAgPzB"}
|
|
@@ -45,6 +45,38 @@ function isCallableArgument(arg, ts, checker) {
|
|
|
45
45
|
}
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Determines whether a declaration originates from an authentic React Router / Remix package or import.
|
|
50
|
+
*/
|
|
51
|
+
function isReactRouterDeclaration(decl) {
|
|
52
|
+
const fileName = decl.getSourceFile().fileName.replace(/\\/g, "/");
|
|
53
|
+
if (fileName.includes("react-router/") ||
|
|
54
|
+
fileName.includes("react-router-dom/") ||
|
|
55
|
+
fileName.includes("@remix-run/router/") ||
|
|
56
|
+
fileName.includes("react-router-native/") ||
|
|
57
|
+
fileName.includes("@types/react-router/") ||
|
|
58
|
+
fileName.includes("@types/react-router-dom/")) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
let current = decl;
|
|
62
|
+
while (current) {
|
|
63
|
+
if ("moduleSpecifier" in current &&
|
|
64
|
+
current.moduleSpecifier &&
|
|
65
|
+
typeof current.moduleSpecifier === "object" &&
|
|
66
|
+
"text" in current.moduleSpecifier &&
|
|
67
|
+
typeof current.moduleSpecifier.text === "string") {
|
|
68
|
+
const mod = current.moduleSpecifier.text;
|
|
69
|
+
if (mod === "react-router" ||
|
|
70
|
+
mod === "react-router-dom" ||
|
|
71
|
+
mod === "@remix-run/router" ||
|
|
72
|
+
mod === "react-router-native") {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
current = current.parent;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
48
80
|
/**
|
|
49
81
|
* Evaluates whether a CallExpression represents a verified, handled framework API call.
|
|
50
82
|
*/
|
|
@@ -77,11 +109,49 @@ export function evaluateFrameworkPromiseCall(callExpr, ts, checker) {
|
|
|
77
109
|
}
|
|
78
110
|
}
|
|
79
111
|
const declarations = [...(targetSymbol?.getDeclarations() ?? [])];
|
|
112
|
+
// If target is a variable initialized by a function call (e.g. const goto = useNavigate()),
|
|
113
|
+
// also collect declarations from the initializer's callee symbol
|
|
114
|
+
if (targetSymbol?.valueDeclaration && ts.isVariableDeclaration(targetSymbol.valueDeclaration) && targetSymbol.valueDeclaration.initializer) {
|
|
115
|
+
const initExpr = unwrapExpression(targetSymbol.valueDeclaration.initializer, ts);
|
|
116
|
+
if (ts.isCallExpression(initExpr)) {
|
|
117
|
+
const initCallee = unwrapExpression(initExpr.expression, ts);
|
|
118
|
+
const initSym = checker.getSymbolAtLocation(initCallee);
|
|
119
|
+
let resolvedInitSym = initSym;
|
|
120
|
+
if (resolvedInitSym && (resolvedInitSym.flags & ts.SymbolFlags.Alias)) {
|
|
121
|
+
try {
|
|
122
|
+
resolvedInitSym = checker.getAliasedSymbol(resolvedInitSym);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// ignore
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const initDecls = resolvedInitSym?.getDeclarations() ?? initSym?.getDeclarations();
|
|
129
|
+
if (initDecls) {
|
|
130
|
+
declarations.push(...initDecls);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
80
134
|
// Also collect declarations from the type of the callee/target (e.g. NavigateFunction)
|
|
81
135
|
try {
|
|
82
136
|
const typeAtLoc = checker.getTypeAtLocation(targetIdentifier);
|
|
83
|
-
const
|
|
84
|
-
|
|
137
|
+
const typeSymbols = [];
|
|
138
|
+
const mainSym = typeAtLoc.getSymbol();
|
|
139
|
+
if (mainSym)
|
|
140
|
+
typeSymbols.push(mainSym);
|
|
141
|
+
const aliasSym = typeAtLoc.aliasSymbol;
|
|
142
|
+
if (aliasSym)
|
|
143
|
+
typeSymbols.push(aliasSym);
|
|
144
|
+
if (typeAtLoc.isUnion()) {
|
|
145
|
+
for (const member of typeAtLoc.types) {
|
|
146
|
+
const mSym = member.getSymbol();
|
|
147
|
+
if (mSym)
|
|
148
|
+
typeSymbols.push(mSym);
|
|
149
|
+
const mAlias = member.aliasSymbol;
|
|
150
|
+
if (mAlias)
|
|
151
|
+
typeSymbols.push(mAlias);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const typeSymbol of typeSymbols) {
|
|
85
155
|
let resolvedTypeSymbol = typeSymbol;
|
|
86
156
|
if (resolvedTypeSymbol.flags & ts.SymbolFlags.Alias) {
|
|
87
157
|
try {
|
|
@@ -91,12 +161,6 @@ export function evaluateFrameworkPromiseCall(callExpr, ts, checker) {
|
|
|
91
161
|
// ignore
|
|
92
162
|
}
|
|
93
163
|
}
|
|
94
|
-
if (resolvedTypeSymbol?.name === "NavigateFunction") {
|
|
95
|
-
return {
|
|
96
|
-
isHandled: true,
|
|
97
|
-
reason: "React Router NavigateFunction optional navigation contract.",
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
164
|
const typeDecls = resolvedTypeSymbol?.getDeclarations();
|
|
101
165
|
if (typeDecls) {
|
|
102
166
|
declarations.push(...typeDecls);
|
|
@@ -134,30 +198,18 @@ export function evaluateFrameworkPromiseCall(callExpr, ts, checker) {
|
|
|
134
198
|
}
|
|
135
199
|
}
|
|
136
200
|
// 2. i18next: i18n.init(...)
|
|
137
|
-
const isI18next = fileName.includes("i18next/")
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// 2. i18n.init(options, callback) -> 2 arguments where arg[1] is callable
|
|
147
|
-
const args = callExpr.arguments;
|
|
148
|
-
const hasCallback = (args.length === 1 && args[0] !== undefined && isCallableArgument(args[0], ts, checker)) ||
|
|
149
|
-
(args.length >= 2 && args[1] !== undefined && isCallableArgument(args[1], ts, checker));
|
|
150
|
-
if (hasCallback) {
|
|
151
|
-
return {
|
|
152
|
-
isHandled: true,
|
|
153
|
-
reason: "i18next init called with error/completion callback handler.",
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// Bare i18n.init(options) returns a Promise that can reject on init failure; must not be blanket-suppressed
|
|
201
|
+
const isI18next = fileName.includes("/node_modules/i18next/");
|
|
202
|
+
if (isI18next && methodName === "init") {
|
|
203
|
+
// i18next init has two callback signatures:
|
|
204
|
+
// 1. i18n.init(callback) -> 1 argument where arg[0] is callable
|
|
205
|
+
// 2. i18n.init(options, callback) -> 2 arguments where arg[1] is callable
|
|
206
|
+
const args = callExpr.arguments;
|
|
207
|
+
const hasCallback = (args.length === 1 && args[0] !== undefined && isCallableArgument(args[0], ts, checker)) ||
|
|
208
|
+
(args.length >= 2 && args[1] !== undefined && isCallableArgument(args[1], ts, checker));
|
|
209
|
+
if (hasCallback) {
|
|
157
210
|
return {
|
|
158
|
-
isHandled:
|
|
159
|
-
|
|
160
|
-
reason: "i18next 'init' returns a Promise that rejects on initialization failure, which will escape unhandled if not caught.",
|
|
211
|
+
isHandled: true,
|
|
212
|
+
reason: "i18next init called with error/completion callback handler.",
|
|
161
213
|
};
|
|
162
214
|
}
|
|
163
215
|
}
|
|
@@ -173,10 +225,18 @@ export function evaluateFrameworkPromiseCall(callExpr, ts, checker) {
|
|
|
173
225
|
}
|
|
174
226
|
}
|
|
175
227
|
// 4. React Router / Remix: NavigateFunction
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
228
|
+
if (isReactRouterDeclaration(decl)) {
|
|
229
|
+
const declNamed = decl;
|
|
230
|
+
const declName = declNamed.name?.text ?? "";
|
|
231
|
+
const isNavigateContract = declName === "NavigateFunction" ||
|
|
232
|
+
declName === "useNavigate" ||
|
|
233
|
+
declName === "navigate" ||
|
|
234
|
+
(ts.isInterfaceDeclaration(decl) && decl.name.text === "NavigateFunction") ||
|
|
235
|
+
(ts.isTypeAliasDeclaration(decl) && decl.name.text === "NavigateFunction") ||
|
|
236
|
+
(ts.isFunctionDeclaration(decl) && decl.name?.text === "useNavigate") ||
|
|
237
|
+
methodName === "navigate" ||
|
|
238
|
+
targetSymbol?.name === "NavigateFunction";
|
|
239
|
+
if (isNavigateContract) {
|
|
180
240
|
return {
|
|
181
241
|
isHandled: true,
|
|
182
242
|
reason: "React Router NavigateFunction optional navigation contract.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameworks.js","sourceRoot":"","sources":["../../src/analysis/frameworks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAQrE;;GAEG;AACH,SAAS,qBAAqB,CAC5B,IAAkC,EAClC,EAAiB,EACjB,OAA2B;IAE3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI,EAAE,CAAC,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;gBACxC,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACrC,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;wBAChC,MAAM,GAAG,GAAG,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;wBAChE,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;4BACjE,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;4BACxD,OAAO,IAAI,CAAC;wBACd,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,GAAsB,EACtB,EAAiB,EACjB,OAAwC;IAExC,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;
|
|
1
|
+
{"version":3,"file":"frameworks.js","sourceRoot":"","sources":["../../src/analysis/frameworks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/analysis/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/analysis/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;AAS1C,MAAM,MAAM,eAAe,GACvB,0BAA0B,GAC1B,YAAY,GACZ,YAAY,GACZ,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC;IAC9C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAK/B,QAAQ,CAAC,EAAE,EAAE,OAAO,MAAM;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS;IALnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;IACjE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0B;gBAG1C,EAAE,EAAE,OAAO,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS;IAGnD,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,GAAG,eAAe;CAwBnD;AAioBD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,IAAI,EACrB,EAAE,EAAE,OAAO,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS,EACxC,OAAO,CAAC,EAAE,sBAAsB,GAAG,SAAS,GAC3C,eAAe,CAgBjB;AA2CD,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,MAAM,CAAC,cAAc,EAC/B,EAAE,EAAE,OAAO,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS,GACvC,MAAM,CAAC,IAAI,GAAG,SAAS,CAgBzB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isPromiseLike } from "../engine/symbols.js";
|
|
1
|
+
import { isDOMAbortController, isDOMGlobal, isPromiseLike, resolveReactHook, } from "../engine/symbols.js";
|
|
2
2
|
import { unwrapExpression } from "./values.js";
|
|
3
3
|
import { evaluateFrameworkPromiseCall } from "./frameworks.js";
|
|
4
4
|
/**
|
|
@@ -93,38 +93,54 @@ function containsAwaitOrThrow(node, ts, context) {
|
|
|
93
93
|
return found;
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
|
-
* Checks
|
|
97
|
-
* e.g. console.log/error/warn/info/debug/trace, clearTimeout, clearInterval, clearImmediate,
|
|
98
|
-
* standard AbortController.abort(), or standard React state setters (set[A-Z]...).
|
|
96
|
+
* Checks whether an identifier is the second element returned by React useState.
|
|
99
97
|
*/
|
|
100
|
-
function
|
|
98
|
+
function isReactStateSetter(identifier, ts, checker) {
|
|
99
|
+
if (!checker)
|
|
100
|
+
return false;
|
|
101
|
+
const symbol = checker.getSymbolAtLocation(identifier);
|
|
102
|
+
const declaration = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
103
|
+
if (!declaration || !ts.isBindingElement(declaration))
|
|
104
|
+
return false;
|
|
105
|
+
const bindingPattern = declaration.parent;
|
|
106
|
+
const variableDeclaration = bindingPattern.parent;
|
|
107
|
+
if (!ts.isArrayBindingPattern(bindingPattern) ||
|
|
108
|
+
bindingPattern.elements[1] !== declaration ||
|
|
109
|
+
!ts.isVariableDeclaration(variableDeclaration) ||
|
|
110
|
+
!variableDeclaration.initializer) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const initializer = unwrapExpression(variableDeclaration.initializer, ts);
|
|
114
|
+
return ts.isCallExpression(initializer) && resolveReactHook(initializer, checker, "useState");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Checks whether a call is resolved to a known non-throwing standard or React API.
|
|
118
|
+
*/
|
|
119
|
+
function isKnownSafeSynchronousCall(callExpr, ts, checker) {
|
|
120
|
+
if (!checker)
|
|
121
|
+
return false;
|
|
101
122
|
const callee = unwrapExpression(callExpr.expression, ts);
|
|
102
|
-
// 1. Standard console logging methods: console.log / console.error / console.warn / console.info / console.debug / console.trace
|
|
103
123
|
if (ts.isPropertyAccessExpression(callee)) {
|
|
104
|
-
const
|
|
105
|
-
if (ts.isIdentifier(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// 2. Standard timer and dispatch utilities: clearTimeout, clearInterval, clearImmediate
|
|
116
|
-
if (ts.isIdentifier(callee)) {
|
|
117
|
-
if (callee.text === "clearTimeout" ||
|
|
118
|
-
callee.text === "clearInterval" ||
|
|
119
|
-
callee.text === "clearImmediate") {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
// React state setters: setFoo(false), setScanning(false)
|
|
123
|
-
if (/^set[A-Z]/.test(callee.text)) {
|
|
124
|
+
const object = unwrapExpression(callee.expression, ts);
|
|
125
|
+
if (ts.isIdentifier(object) &&
|
|
126
|
+
isDOMGlobal(object, checker, "console") &&
|
|
127
|
+
(callee.name.text === "log" ||
|
|
128
|
+
callee.name.text === "error" ||
|
|
129
|
+
callee.name.text === "warn" ||
|
|
130
|
+
callee.name.text === "info" ||
|
|
131
|
+
callee.name.text === "debug" ||
|
|
132
|
+
callee.name.text === "trace")) {
|
|
124
133
|
return true;
|
|
125
134
|
}
|
|
135
|
+
return callee.name.text === "abort" && isDOMAbortController(object, checker, ts);
|
|
126
136
|
}
|
|
127
|
-
|
|
137
|
+
if (!ts.isIdentifier(callee))
|
|
138
|
+
return false;
|
|
139
|
+
if ((callee.text === "clearTimeout" || callee.text === "clearInterval") &&
|
|
140
|
+
isDOMGlobal(callee, checker, callee.text)) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return isReactStateSetter(callee, ts, checker);
|
|
128
144
|
}
|
|
129
145
|
/**
|
|
130
146
|
* Checks if an expression is proven non-throwing.
|
|
@@ -141,6 +157,32 @@ function isProvenNonThrowingExpression(expr, ts, checker) {
|
|
|
141
157
|
unwrapped.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
142
158
|
return true;
|
|
143
159
|
}
|
|
160
|
+
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
if (ts.isArrayLiteralExpression(unwrapped)) {
|
|
164
|
+
return unwrapped.elements.every((el) => {
|
|
165
|
+
if (ts.isOmittedExpression(el))
|
|
166
|
+
return true;
|
|
167
|
+
if (ts.isSpreadElement(el))
|
|
168
|
+
return isProvenNonThrowingExpression(el.expression, ts, checker);
|
|
169
|
+
return isProvenNonThrowingExpression(el, ts, checker);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (ts.isObjectLiteralExpression(unwrapped)) {
|
|
173
|
+
return unwrapped.properties.every((prop) => {
|
|
174
|
+
if (ts.isPropertyAssignment(prop) && ts.isExpression(prop.initializer)) {
|
|
175
|
+
return isProvenNonThrowingExpression(prop.initializer, ts, checker);
|
|
176
|
+
}
|
|
177
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
178
|
+
return isProvenNonThrowingExpression(prop.name, ts, checker);
|
|
179
|
+
}
|
|
180
|
+
if (ts.isSpreadAssignment(prop)) {
|
|
181
|
+
return isProvenNonThrowingExpression(prop.expression, ts, checker);
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
144
186
|
if (ts.isBinaryExpression(unwrapped)) {
|
|
145
187
|
return (isProvenNonThrowingExpression(unwrapped.left, ts, checker) &&
|
|
146
188
|
isProvenNonThrowingExpression(unwrapped.right, ts, checker));
|
|
@@ -152,11 +194,16 @@ function isProvenNonThrowingExpression(expr, ts, checker) {
|
|
|
152
194
|
if (checker) {
|
|
153
195
|
try {
|
|
154
196
|
const objType = checker.getTypeAtLocation(unwrapped.expression);
|
|
155
|
-
|
|
197
|
+
const nullishOrUnknown = ts.TypeFlags.Null |
|
|
198
|
+
ts.TypeFlags.Undefined |
|
|
199
|
+
ts.TypeFlags.Void |
|
|
200
|
+
ts.TypeFlags.Any |
|
|
201
|
+
ts.TypeFlags.Unknown;
|
|
202
|
+
if ((objType.flags & nullishOrUnknown) !== 0) {
|
|
156
203
|
return false;
|
|
157
204
|
}
|
|
158
205
|
if (objType.isUnion()) {
|
|
159
|
-
const hasNullish = objType.types.some((t) => (t.flags &
|
|
206
|
+
const hasNullish = objType.types.some((t) => (t.flags & nullishOrUnknown) !== 0);
|
|
160
207
|
if (hasNullish)
|
|
161
208
|
return false;
|
|
162
209
|
}
|
|
@@ -164,11 +211,15 @@ function isProvenNonThrowingExpression(expr, ts, checker) {
|
|
|
164
211
|
catch {
|
|
165
212
|
return false;
|
|
166
213
|
}
|
|
214
|
+
return isProvenNonThrowingExpression(unwrapped.expression, ts, checker);
|
|
167
215
|
}
|
|
168
|
-
return
|
|
216
|
+
return false;
|
|
169
217
|
}
|
|
170
218
|
if (ts.isCallExpression(unwrapped)) {
|
|
171
|
-
|
|
219
|
+
if (!isKnownSafeSynchronousCall(unwrapped, ts, checker)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return unwrapped.arguments.every((arg) => isProvenNonThrowingExpression(arg, ts, checker));
|
|
172
223
|
}
|
|
173
224
|
return false;
|
|
174
225
|
}
|
|
@@ -224,7 +275,7 @@ function isBlockProvenNonThrowing(block, ts, checker) {
|
|
|
224
275
|
return false;
|
|
225
276
|
}
|
|
226
277
|
if (ts.isCallExpression(expr)) {
|
|
227
|
-
if (!isKnownSafeSynchronousCall(expr, ts)) {
|
|
278
|
+
if (!isKnownSafeSynchronousCall(expr, ts, checker)) {
|
|
228
279
|
return false;
|
|
229
280
|
}
|
|
230
281
|
// Verify call arguments are non-throwing
|
|
@@ -282,6 +333,9 @@ function isSafeSynchronousPreamble(stmt, ts, checker) {
|
|
|
282
333
|
if (containsAwaitOrThrow(stmt, ts)) {
|
|
283
334
|
return false;
|
|
284
335
|
}
|
|
336
|
+
if (ts.isFunctionDeclaration(stmt) || ts.isEmptyStatement(stmt)) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
285
339
|
if (ts.isVariableStatement(stmt)) {
|
|
286
340
|
for (const decl of stmt.declarationList.declarations) {
|
|
287
341
|
if (decl.initializer && !isProvenNonThrowingExpression(decl.initializer, ts, checker)) {
|
|
@@ -359,6 +413,30 @@ function containsUncaughtAwaitOrThrow(node, ts, context) {
|
|
|
359
413
|
walk(node);
|
|
360
414
|
return found;
|
|
361
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Distinguishes an explicit asynchronous or throwing path from unproven synchronous work.
|
|
418
|
+
*/
|
|
419
|
+
function containsAwaitOrExplicitThrow(node, ts) {
|
|
420
|
+
let found = false;
|
|
421
|
+
function walk(n) {
|
|
422
|
+
if (found)
|
|
423
|
+
return;
|
|
424
|
+
if (n !== node &&
|
|
425
|
+
(ts.isFunctionDeclaration(n) ||
|
|
426
|
+
ts.isArrowFunction(n) ||
|
|
427
|
+
ts.isFunctionExpression(n) ||
|
|
428
|
+
ts.isMethodDeclaration(n))) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (ts.isAwaitExpression(n) || ts.isThrowStatement(n)) {
|
|
432
|
+
found = true;
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
n.forEachChild(walk);
|
|
436
|
+
}
|
|
437
|
+
walk(node);
|
|
438
|
+
return found;
|
|
439
|
+
}
|
|
362
440
|
/**
|
|
363
441
|
* Analyzes the body of an async function to determine its rejection-handling behavior.
|
|
364
442
|
*/
|
|
@@ -397,13 +475,13 @@ function analyzeAsyncFunctionBody(body, context) {
|
|
|
397
475
|
}
|
|
398
476
|
}
|
|
399
477
|
else {
|
|
400
|
-
if (containsAwaitOrThrow(stmt, ts, context)) {
|
|
478
|
+
if (containsAwaitOrThrow(stmt, ts, context) || !isSafeSynchronousPreamble(stmt, ts, checker)) {
|
|
401
479
|
hasUncaught = true;
|
|
402
480
|
}
|
|
403
481
|
}
|
|
404
482
|
}
|
|
405
483
|
if (hasUncaught) {
|
|
406
|
-
return "propagates";
|
|
484
|
+
return containsAwaitOrExplicitThrow(body, ts) ? "propagates" : "may-reject";
|
|
407
485
|
}
|
|
408
486
|
if (hasTryWithCatch) {
|
|
409
487
|
return "handles-known-rejections";
|
|
@@ -529,7 +607,10 @@ function resolveFunctionFromInitializer(init, ts, checker) {
|
|
|
529
607
|
// useCallback(async () => ...)
|
|
530
608
|
if (ts.isCallExpression(unwrapped)) {
|
|
531
609
|
const callee = unwrapExpression(unwrapped.expression, ts);
|
|
532
|
-
|
|
610
|
+
const isCallback = checker
|
|
611
|
+
? resolveReactHook(unwrapped, checker, "useCallback")
|
|
612
|
+
: (ts.isIdentifier(callee) && callee.text === "useCallback");
|
|
613
|
+
if (isCallback) {
|
|
533
614
|
const cbArg = unwrapped.arguments[0];
|
|
534
615
|
if (cbArg) {
|
|
535
616
|
return resolveFunctionFromInitializer(cbArg, ts, checker);
|
|
@@ -612,7 +693,10 @@ function computeResolveLocalOrProjectFunctionDeclaration(callExpr, ts, checker)
|
|
|
612
693
|
const init = unwrapExpression(decl.initializer, ts);
|
|
613
694
|
if (ts.isCallExpression(init)) {
|
|
614
695
|
const initCallee = unwrapExpression(init.expression, ts);
|
|
615
|
-
|
|
696
|
+
const isRef = checker
|
|
697
|
+
? resolveReactHook(init, checker, "useRef")
|
|
698
|
+
: (ts.isIdentifier(initCallee) && initCallee.text === "useRef");
|
|
699
|
+
if (isRef) {
|
|
616
700
|
const refArg = init.arguments[0];
|
|
617
701
|
if (refArg) {
|
|
618
702
|
if (ts.isIdentifier(refArg)) {
|