eslint-plugin-secure-coding 1.0.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/AGENTS.md +196 -0
- package/CHANGELOG.md +105 -0
- package/LICENSE +23 -0
- package/README.md +377 -0
- package/package.json +80 -0
- package/src/index.d.ts +32 -0
- package/src/index.js +345 -0
- package/src/index.js.map +1 -0
- package/src/rules/security/database-injection.d.ts +13 -0
- package/src/rules/security/database-injection.js +407 -0
- package/src/rules/security/database-injection.js.map +1 -0
- package/src/rules/security/detect-child-process.d.ts +11 -0
- package/src/rules/security/detect-child-process.js +460 -0
- package/src/rules/security/detect-child-process.js.map +1 -0
- package/src/rules/security/detect-eval-with-expression.d.ts +9 -0
- package/src/rules/security/detect-eval-with-expression.js +393 -0
- package/src/rules/security/detect-eval-with-expression.js.map +1 -0
- package/src/rules/security/detect-non-literal-fs-filename.d.ts +7 -0
- package/src/rules/security/detect-non-literal-fs-filename.js +322 -0
- package/src/rules/security/detect-non-literal-fs-filename.js.map +1 -0
- package/src/rules/security/detect-non-literal-regexp.d.ts +9 -0
- package/src/rules/security/detect-non-literal-regexp.js +387 -0
- package/src/rules/security/detect-non-literal-regexp.js.map +1 -0
- package/src/rules/security/detect-object-injection.d.ts +11 -0
- package/src/rules/security/detect-object-injection.js +411 -0
- package/src/rules/security/detect-object-injection.js.map +1 -0
- package/src/rules/security/no-buffer-overread.d.ts +14 -0
- package/src/rules/security/no-buffer-overread.js +519 -0
- package/src/rules/security/no-buffer-overread.js.map +1 -0
- package/src/rules/security/no-clickjacking.d.ts +10 -0
- package/src/rules/security/no-clickjacking.js +381 -0
- package/src/rules/security/no-clickjacking.js.map +1 -0
- package/src/rules/security/no-directive-injection.d.ts +12 -0
- package/src/rules/security/no-directive-injection.js +446 -0
- package/src/rules/security/no-directive-injection.js.map +1 -0
- package/src/rules/security/no-document-cookie.d.ts +5 -0
- package/src/rules/security/no-document-cookie.js +90 -0
- package/src/rules/security/no-document-cookie.js.map +1 -0
- package/src/rules/security/no-electron-security-issues.d.ts +10 -0
- package/src/rules/security/no-electron-security-issues.js +421 -0
- package/src/rules/security/no-electron-security-issues.js.map +1 -0
- package/src/rules/security/no-exposed-sensitive-data.d.ts +11 -0
- package/src/rules/security/no-exposed-sensitive-data.js +341 -0
- package/src/rules/security/no-exposed-sensitive-data.js.map +1 -0
- package/src/rules/security/no-format-string-injection.d.ts +17 -0
- package/src/rules/security/no-format-string-injection.js +653 -0
- package/src/rules/security/no-format-string-injection.js.map +1 -0
- package/src/rules/security/no-graphql-injection.d.ts +12 -0
- package/src/rules/security/no-graphql-injection.js +410 -0
- package/src/rules/security/no-graphql-injection.js.map +1 -0
- package/src/rules/security/no-hardcoded-credentials.d.ts +26 -0
- package/src/rules/security/no-hardcoded-credentials.js +377 -0
- package/src/rules/security/no-hardcoded-credentials.js.map +1 -0
- package/src/rules/security/no-improper-sanitization.d.ts +12 -0
- package/src/rules/security/no-improper-sanitization.js +408 -0
- package/src/rules/security/no-improper-sanitization.js.map +1 -0
- package/src/rules/security/no-improper-type-validation.d.ts +10 -0
- package/src/rules/security/no-improper-type-validation.js +420 -0
- package/src/rules/security/no-improper-type-validation.js.map +1 -0
- package/src/rules/security/no-insecure-comparison.d.ts +7 -0
- package/src/rules/security/no-insecure-comparison.js +125 -0
- package/src/rules/security/no-insecure-comparison.js.map +1 -0
- package/src/rules/security/no-insecure-cookie-settings.d.ts +9 -0
- package/src/rules/security/no-insecure-cookie-settings.js +305 -0
- package/src/rules/security/no-insecure-cookie-settings.js.map +1 -0
- package/src/rules/security/no-insecure-jwt.d.ts +10 -0
- package/src/rules/security/no-insecure-jwt.js +338 -0
- package/src/rules/security/no-insecure-jwt.js.map +1 -0
- package/src/rules/security/no-insecure-redirects.d.ts +7 -0
- package/src/rules/security/no-insecure-redirects.js +215 -0
- package/src/rules/security/no-insecure-redirects.js.map +1 -0
- package/src/rules/security/no-insufficient-postmessage-validation.d.ts +14 -0
- package/src/rules/security/no-insufficient-postmessage-validation.js +390 -0
- package/src/rules/security/no-insufficient-postmessage-validation.js.map +1 -0
- package/src/rules/security/no-insufficient-random.d.ts +9 -0
- package/src/rules/security/no-insufficient-random.js +207 -0
- package/src/rules/security/no-insufficient-random.js.map +1 -0
- package/src/rules/security/no-ldap-injection.d.ts +10 -0
- package/src/rules/security/no-ldap-injection.js +449 -0
- package/src/rules/security/no-ldap-injection.js.map +1 -0
- package/src/rules/security/no-missing-authentication.d.ts +13 -0
- package/src/rules/security/no-missing-authentication.js +322 -0
- package/src/rules/security/no-missing-authentication.js.map +1 -0
- package/src/rules/security/no-missing-cors-check.d.ts +9 -0
- package/src/rules/security/no-missing-cors-check.js +449 -0
- package/src/rules/security/no-missing-cors-check.js.map +1 -0
- package/src/rules/security/no-missing-csrf-protection.d.ts +11 -0
- package/src/rules/security/no-missing-csrf-protection.js +183 -0
- package/src/rules/security/no-missing-csrf-protection.js.map +1 -0
- package/src/rules/security/no-missing-security-headers.d.ts +7 -0
- package/src/rules/security/no-missing-security-headers.js +217 -0
- package/src/rules/security/no-missing-security-headers.js.map +1 -0
- package/src/rules/security/no-privilege-escalation.d.ts +13 -0
- package/src/rules/security/no-privilege-escalation.js +321 -0
- package/src/rules/security/no-privilege-escalation.js.map +1 -0
- package/src/rules/security/no-redos-vulnerable-regex.d.ts +7 -0
- package/src/rules/security/no-redos-vulnerable-regex.js +307 -0
- package/src/rules/security/no-redos-vulnerable-regex.js.map +1 -0
- package/src/rules/security/no-sensitive-data-exposure.d.ts +11 -0
- package/src/rules/security/no-sensitive-data-exposure.js +251 -0
- package/src/rules/security/no-sensitive-data-exposure.js.map +1 -0
- package/src/rules/security/no-sql-injection.d.ts +10 -0
- package/src/rules/security/no-sql-injection.js +332 -0
- package/src/rules/security/no-sql-injection.js.map +1 -0
- package/src/rules/security/no-timing-attack.d.ts +10 -0
- package/src/rules/security/no-timing-attack.js +358 -0
- package/src/rules/security/no-timing-attack.js.map +1 -0
- package/src/rules/security/no-toctou-vulnerability.d.ts +7 -0
- package/src/rules/security/no-toctou-vulnerability.js +165 -0
- package/src/rules/security/no-toctou-vulnerability.js.map +1 -0
- package/src/rules/security/no-unchecked-loop-condition.d.ts +12 -0
- package/src/rules/security/no-unchecked-loop-condition.js +635 -0
- package/src/rules/security/no-unchecked-loop-condition.js.map +1 -0
- package/src/rules/security/no-unencrypted-transmission.d.ts +11 -0
- package/src/rules/security/no-unencrypted-transmission.js +237 -0
- package/src/rules/security/no-unencrypted-transmission.js.map +1 -0
- package/src/rules/security/no-unescaped-url-parameter.d.ts +9 -0
- package/src/rules/security/no-unescaped-url-parameter.js +266 -0
- package/src/rules/security/no-unescaped-url-parameter.js.map +1 -0
- package/src/rules/security/no-unlimited-resource-allocation.d.ts +12 -0
- package/src/rules/security/no-unlimited-resource-allocation.js +659 -0
- package/src/rules/security/no-unlimited-resource-allocation.js.map +1 -0
- package/src/rules/security/no-unsafe-deserialization.d.ts +10 -0
- package/src/rules/security/no-unsafe-deserialization.js +501 -0
- package/src/rules/security/no-unsafe-deserialization.js.map +1 -0
- package/src/rules/security/no-unsafe-dynamic-require.d.ts +5 -0
- package/src/rules/security/no-unsafe-dynamic-require.js +107 -0
- package/src/rules/security/no-unsafe-dynamic-require.js.map +1 -0
- package/src/rules/security/no-unsafe-regex-construction.d.ts +9 -0
- package/src/rules/security/no-unsafe-regex-construction.js +292 -0
- package/src/rules/security/no-unsafe-regex-construction.js.map +1 -0
- package/src/rules/security/no-unsanitized-html.d.ts +9 -0
- package/src/rules/security/no-unsanitized-html.js +347 -0
- package/src/rules/security/no-unsanitized-html.js.map +1 -0
- package/src/rules/security/no-unvalidated-user-input.d.ts +9 -0
- package/src/rules/security/no-unvalidated-user-input.js +418 -0
- package/src/rules/security/no-unvalidated-user-input.js.map +1 -0
- package/src/rules/security/no-weak-crypto.d.ts +11 -0
- package/src/rules/security/no-weak-crypto.js +350 -0
- package/src/rules/security/no-weak-crypto.js.map +1 -0
- package/src/rules/security/no-weak-password-recovery.d.ts +12 -0
- package/src/rules/security/no-weak-password-recovery.js +401 -0
- package/src/rules/security/no-weak-password-recovery.js.map +1 -0
- package/src/rules/security/no-xpath-injection.d.ts +10 -0
- package/src/rules/security/no-xpath-injection.js +487 -0
- package/src/rules/security/no-xpath-injection.js.map +1 -0
- package/src/rules/security/no-xxe-injection.d.ts +7 -0
- package/src/rules/security/no-xxe-injection.js +270 -0
- package/src/rules/security/no-xxe-injection.js.map +1 -0
- package/src/rules/security/no-zip-slip.d.ts +9 -0
- package/src/rules/security/no-zip-slip.js +446 -0
- package/src/rules/security/no-zip-slip.js.map +1 -0
- package/src/types/index.d.ts +131 -0
- package/src/types/index.js +18 -0
- package/src/types/index.js.map +1 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noTimingAttack = void 0;
|
|
4
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
+
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
7
|
+
exports.noTimingAttack = (0, eslint_devkit_1.createRule)({
|
|
8
|
+
name: 'no-timing-attack',
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Detects timing attack vulnerabilities in authentication code',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
messages: {
|
|
16
|
+
timingAttack: (0, eslint_devkit_2.formatLLMMessage)({
|
|
17
|
+
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
18
|
+
issueName: 'Timing Attack Vulnerability',
|
|
19
|
+
cwe: 'CWE-208',
|
|
20
|
+
description: 'Timing attack possible - execution time reveals secret information',
|
|
21
|
+
severity: '{{severity}}',
|
|
22
|
+
fix: '{{safeAlternative}}',
|
|
23
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
24
|
+
}),
|
|
25
|
+
insecureStringComparison: (0, eslint_devkit_2.formatLLMMessage)({
|
|
26
|
+
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
27
|
+
issueName: 'Insecure String Comparison',
|
|
28
|
+
cwe: 'CWE-208',
|
|
29
|
+
description: 'String comparison may leak timing information',
|
|
30
|
+
severity: 'HIGH',
|
|
31
|
+
fix: 'Use crypto.timingSafeEqual() for comparing secrets',
|
|
32
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
33
|
+
}),
|
|
34
|
+
earlyReturnLeakage: (0, eslint_devkit_2.formatLLMMessage)({
|
|
35
|
+
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
36
|
+
issueName: 'Early Return Timing Leak',
|
|
37
|
+
cwe: 'CWE-208',
|
|
38
|
+
description: 'Early return may leak information through timing',
|
|
39
|
+
severity: 'MEDIUM',
|
|
40
|
+
fix: 'Process all inputs consistently to avoid timing differences',
|
|
41
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
42
|
+
}),
|
|
43
|
+
useTimingSafeEqual: (0, eslint_devkit_2.formatLLMMessage)({
|
|
44
|
+
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
45
|
+
issueName: 'Use Timing Safe Equal',
|
|
46
|
+
description: 'Use crypto.timingSafeEqual for constant-time comparison',
|
|
47
|
+
severity: 'LOW',
|
|
48
|
+
fix: 'crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))',
|
|
49
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
50
|
+
}),
|
|
51
|
+
useConstantTimeComparison: (0, eslint_devkit_2.formatLLMMessage)({
|
|
52
|
+
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
53
|
+
issueName: 'Constant Time Comparison',
|
|
54
|
+
description: 'Implement constant-time comparison algorithm',
|
|
55
|
+
severity: 'LOW',
|
|
56
|
+
fix: 'Compare all bytes regardless of content',
|
|
57
|
+
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
58
|
+
}),
|
|
59
|
+
avoidEarlyReturns: (0, eslint_devkit_2.formatLLMMessage)({
|
|
60
|
+
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
61
|
+
issueName: 'Avoid Early Returns',
|
|
62
|
+
description: 'Avoid early returns in security-sensitive code',
|
|
63
|
+
severity: 'LOW',
|
|
64
|
+
fix: 'Process all inputs before making decisions',
|
|
65
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
66
|
+
}),
|
|
67
|
+
strategyTimingSafe: (0, eslint_devkit_2.formatLLMMessage)({
|
|
68
|
+
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
69
|
+
issueName: 'Timing Safe Strategy',
|
|
70
|
+
description: 'Use built-in timing-safe comparison functions',
|
|
71
|
+
severity: 'LOW',
|
|
72
|
+
fix: 'Use crypto.timingSafeEqual or equivalent library functions',
|
|
73
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
74
|
+
}),
|
|
75
|
+
strategyConstantTime: (0, eslint_devkit_2.formatLLMMessage)({
|
|
76
|
+
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
77
|
+
issueName: 'Constant Time Strategy',
|
|
78
|
+
description: 'Implement custom constant-time comparison',
|
|
79
|
+
severity: 'LOW',
|
|
80
|
+
fix: 'Compare all characters/bytes with consistent timing',
|
|
81
|
+
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
82
|
+
}),
|
|
83
|
+
strategyConsistentTiming: (0, eslint_devkit_2.formatLLMMessage)({
|
|
84
|
+
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
85
|
+
issueName: 'Consistent Timing Strategy',
|
|
86
|
+
description: 'Ensure consistent execution time for all inputs',
|
|
87
|
+
severity: 'LOW',
|
|
88
|
+
fix: 'Pad inputs or use fixed-time operations',
|
|
89
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
90
|
+
})
|
|
91
|
+
},
|
|
92
|
+
schema: [
|
|
93
|
+
{
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
authFunctions: {
|
|
97
|
+
type: 'array',
|
|
98
|
+
items: { type: 'string' },
|
|
99
|
+
default: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
100
|
+
},
|
|
101
|
+
sensitiveVariables: {
|
|
102
|
+
type: 'array',
|
|
103
|
+
items: { type: 'string' },
|
|
104
|
+
default: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
105
|
+
},
|
|
106
|
+
allowEarlyReturns: {
|
|
107
|
+
type: 'boolean',
|
|
108
|
+
default: false,
|
|
109
|
+
description: 'Allow early returns outside security-sensitive contexts'
|
|
110
|
+
},
|
|
111
|
+
trustedSanitizers: {
|
|
112
|
+
type: 'array',
|
|
113
|
+
items: { type: 'string' },
|
|
114
|
+
default: [],
|
|
115
|
+
description: 'Additional function names to consider as timing-safe',
|
|
116
|
+
},
|
|
117
|
+
trustedAnnotations: {
|
|
118
|
+
type: 'array',
|
|
119
|
+
items: { type: 'string' },
|
|
120
|
+
default: [],
|
|
121
|
+
description: 'Additional JSDoc annotations to consider as timing-safe markers',
|
|
122
|
+
},
|
|
123
|
+
strictMode: {
|
|
124
|
+
type: 'boolean',
|
|
125
|
+
default: false,
|
|
126
|
+
description: 'Disable all false positive detection (strict mode)',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
additionalProperties: false,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
defaultOptions: [
|
|
134
|
+
{
|
|
135
|
+
authFunctions: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
136
|
+
sensitiveVariables: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
137
|
+
allowEarlyReturns: false,
|
|
138
|
+
trustedSanitizers: ['sanitize'],
|
|
139
|
+
trustedAnnotations: ['@timing-safe'],
|
|
140
|
+
strictMode: false,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
create(context) {
|
|
144
|
+
const options = context.options[0] || {};
|
|
145
|
+
const { authFunctions = ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'], sensitiveVariables = ['password', 'token', 'secret', 'key', 'credentials', 'auth'], allowEarlyReturns = false, trustedSanitizers = [], trustedAnnotations = [], strictMode = false, } = options;
|
|
146
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
147
|
+
const filename = context.filename || context.getFilename();
|
|
148
|
+
// Create safety checker for false positive detection
|
|
149
|
+
const safetyChecker = (0, eslint_devkit_3.createSafetyChecker)({
|
|
150
|
+
trustedSanitizers,
|
|
151
|
+
trustedAnnotations,
|
|
152
|
+
trustedOrmPatterns: [],
|
|
153
|
+
strictMode,
|
|
154
|
+
});
|
|
155
|
+
// Track variables that contain sensitive data
|
|
156
|
+
const sensitiveVars = new Set();
|
|
157
|
+
/**
|
|
158
|
+
* Check if a variable name indicates sensitive/auth data
|
|
159
|
+
*/
|
|
160
|
+
const isSensitiveVariable = (varName) => {
|
|
161
|
+
return sensitiveVariables.some(sensitive => varName.toLowerCase().includes(sensitive.toLowerCase()));
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Check if we're in an authentication/security context
|
|
165
|
+
*/
|
|
166
|
+
const isInAuthContext = (node) => {
|
|
167
|
+
// Check if we're inside an authentication function
|
|
168
|
+
let current = node;
|
|
169
|
+
/* c8 ignore start -- defensive auth context detection */
|
|
170
|
+
while (current) {
|
|
171
|
+
if (current.type === 'FunctionDeclaration' || current.type === 'FunctionExpression' || current.type === 'ArrowFunctionExpression') {
|
|
172
|
+
const funcName = current.id?.name;
|
|
173
|
+
if (funcName) {
|
|
174
|
+
// Check exact matches first
|
|
175
|
+
if (authFunctions.includes(funcName)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
// Check pattern matches for common auth function names
|
|
179
|
+
const authPatterns = ['auth', 'login', 'verify', 'token', 'password', 'credential', 'authenticate'];
|
|
180
|
+
if (authPatterns.some(pattern => funcName.toLowerCase().includes(pattern))) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (current.type === 'CallExpression') {
|
|
186
|
+
const callee = current.callee;
|
|
187
|
+
if (callee.type === 'Identifier') {
|
|
188
|
+
if (authFunctions.includes(callee.name)) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
// Check pattern matches
|
|
192
|
+
const authPatterns = ['auth', 'login', 'verify', 'token', 'password', 'credential', 'authenticate'];
|
|
193
|
+
if (authPatterns.some(pattern => callee.name.toLowerCase().includes(pattern))) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
current = current.parent;
|
|
199
|
+
}
|
|
200
|
+
// Check if we're dealing with sensitive variables
|
|
201
|
+
return sensitiveVars.size > 0;
|
|
202
|
+
/* c8 ignore stop */
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Check if a comparison is timing-safe
|
|
206
|
+
*/
|
|
207
|
+
const isTimingSafeComparison = (node) => {
|
|
208
|
+
// Check for crypto.timingSafeEqual calls
|
|
209
|
+
let current = node;
|
|
210
|
+
while (current) {
|
|
211
|
+
if (current.type === 'CallExpression') {
|
|
212
|
+
const callee = current.callee;
|
|
213
|
+
if (callee.type === 'MemberExpression' &&
|
|
214
|
+
callee.object.type === 'Identifier' &&
|
|
215
|
+
callee.object.name === 'crypto' &&
|
|
216
|
+
callee.property.type === 'Identifier' &&
|
|
217
|
+
callee.property.name === 'timingSafeEqual') {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
current = current.parent;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Check if early return is in a security-sensitive context
|
|
227
|
+
*/
|
|
228
|
+
const isEarlyReturnInAuthContext = (node) => {
|
|
229
|
+
// If early returns are explicitly allowed, don't flag them
|
|
230
|
+
if (allowEarlyReturns) {
|
|
231
|
+
/* c8 ignore next */
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return isInAuthContext(node);
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
// Track sensitive variable declarations
|
|
238
|
+
VariableDeclarator(node) {
|
|
239
|
+
if (node.id.type === 'Identifier' && isSensitiveVariable(node.id.name)) {
|
|
240
|
+
sensitiveVars.add(node.id.name);
|
|
241
|
+
}
|
|
242
|
+
// Also check if the variable is assigned sensitive data
|
|
243
|
+
if (node.init && node.id.type === 'Identifier') {
|
|
244
|
+
const initText = sourceCode.getText(node.init).toLowerCase();
|
|
245
|
+
if (sensitiveVariables.some(sensitive => initText.includes(sensitive))) {
|
|
246
|
+
sensitiveVars.add(node.id.name);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
// Check binary expressions for insecure comparisons
|
|
251
|
+
BinaryExpression(node) {
|
|
252
|
+
if (node.operator !== '===' && node.operator !== '==') {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// Skip if already using timing-safe comparison
|
|
256
|
+
if (isTimingSafeComparison(node)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
260
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// Check if either side involves sensitive data
|
|
264
|
+
const leftText = sourceCode.getText(node.left).toLowerCase();
|
|
265
|
+
const rightText = sourceCode.getText(node.right).toLowerCase();
|
|
266
|
+
const involvesSensitiveData = [leftText, rightText].some(text => sensitiveVariables.some(sensitive => text.toLowerCase().includes(sensitive.toLowerCase())));
|
|
267
|
+
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
context.report({
|
|
271
|
+
node,
|
|
272
|
+
messageId: 'insecureStringComparison',
|
|
273
|
+
data: {
|
|
274
|
+
filePath: filename,
|
|
275
|
+
line: String(node.loc?.start.line ?? 0),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
// Check for early returns that could leak timing information
|
|
280
|
+
ReturnStatement(node) {
|
|
281
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
282
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!isEarlyReturnInAuthContext(node)) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
// Look for conditional returns (if/else returns)
|
|
289
|
+
let current = node;
|
|
290
|
+
let isConditionalReturn = false;
|
|
291
|
+
while (current && !isConditionalReturn) {
|
|
292
|
+
if (current.type === 'IfStatement') {
|
|
293
|
+
isConditionalReturn = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
current = current.parent;
|
|
297
|
+
}
|
|
298
|
+
if (!isConditionalReturn) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// Check if this return involves sensitive data
|
|
302
|
+
const returnText = sourceCode.getText(node).toLowerCase();
|
|
303
|
+
const involvesSensitiveData = sensitiveVariables.some(sensitive => returnText.includes(sensitive));
|
|
304
|
+
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
context.report({
|
|
308
|
+
node,
|
|
309
|
+
messageId: 'earlyReturnLeakage',
|
|
310
|
+
data: {
|
|
311
|
+
filePath: filename,
|
|
312
|
+
line: String(node.loc?.start.line ?? 0),
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
// Check function calls for timing-sensitive operations
|
|
317
|
+
CallExpression(node) {
|
|
318
|
+
const callee = node.callee;
|
|
319
|
+
// Check for insecure comparison functions
|
|
320
|
+
if (callee.type === 'MemberExpression' &&
|
|
321
|
+
callee.property.type === 'Identifier' &&
|
|
322
|
+
['equals', 'compare', 'matches'].includes(callee.property.name)) {
|
|
323
|
+
// Skip known timing-safe libraries
|
|
324
|
+
const objectName = callee.object.type === 'Identifier' ? callee.object.name : null;
|
|
325
|
+
if (objectName) {
|
|
326
|
+
// Known timing-safe comparison libraries
|
|
327
|
+
const timingSafeLibraries = ['bcrypt', 'crypto'];
|
|
328
|
+
if (timingSafeLibraries.includes(objectName) && callee.property.name === 'compare') {
|
|
329
|
+
return; // bcrypt.compare and crypto.compare are timing-safe
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Check if arguments involve sensitive data
|
|
333
|
+
const argsText = node.arguments
|
|
334
|
+
.map((arg) => sourceCode.getText(arg).toLowerCase())
|
|
335
|
+
.join(' ');
|
|
336
|
+
const involvesSensitiveData = sensitiveVariables.some(sensitive => argsText.includes(sensitive));
|
|
337
|
+
if (involvesSensitiveData || isInAuthContext(node)) {
|
|
338
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
339
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
context.report({
|
|
343
|
+
node,
|
|
344
|
+
messageId: 'timingAttack',
|
|
345
|
+
data: {
|
|
346
|
+
filePath: filename,
|
|
347
|
+
line: String(node.loc?.start.line ?? 0),
|
|
348
|
+
severity: 'HIGH',
|
|
349
|
+
safeAlternative: 'Use constant-time comparison functions',
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
//# sourceMappingURL=no-timing-attack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-timing-attack.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-secure-coding/src/rules/security/no-timing-attack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface Options {
|
|
2
|
+
/** Ignore in test files. Default: true */
|
|
3
|
+
ignoreInTests?: boolean;
|
|
4
|
+
/** File system methods to check. Default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'] */
|
|
5
|
+
fsMethods?: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare const noToctouVulnerability: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noToctouVulnerability = void 0;
|
|
4
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
+
exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
|
|
7
|
+
name: 'no-toctou-vulnerability',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Detects Time-of-Check-Time-of-Use vulnerabilities',
|
|
12
|
+
},
|
|
13
|
+
hasSuggestions: true,
|
|
14
|
+
messages: {
|
|
15
|
+
toctouVulnerability: (0, eslint_devkit_1.formatLLMMessage)({
|
|
16
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
17
|
+
issueName: 'TOCTOU vulnerability',
|
|
18
|
+
cwe: 'CWE-367',
|
|
19
|
+
description: 'Time-of-check Time-of-use race condition detected',
|
|
20
|
+
severity: 'HIGH',
|
|
21
|
+
fix: 'Use atomic operations or fs.promises for file operations',
|
|
22
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/367.html',
|
|
23
|
+
}),
|
|
24
|
+
useAtomicOperations: (0, eslint_devkit_1.formatLLMMessage)({
|
|
25
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
26
|
+
issueName: 'Use Atomic Operations',
|
|
27
|
+
description: 'Use atomic file operations',
|
|
28
|
+
severity: 'LOW',
|
|
29
|
+
fix: 'fs.promises.access() then fs.promises.readFile()',
|
|
30
|
+
documentationLink: 'https://nodejs.org/api/fs.html#fspromisesaccesspath-mode',
|
|
31
|
+
}),
|
|
32
|
+
useFsPromises: (0, eslint_devkit_1.formatLLMMessage)({
|
|
33
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
34
|
+
issueName: 'Use fs.promises',
|
|
35
|
+
description: 'Use fs.promises API',
|
|
36
|
+
severity: 'LOW',
|
|
37
|
+
fix: 'await fs.promises.readFile() instead of sync operations',
|
|
38
|
+
documentationLink: 'https://nodejs.org/api/fs.html#promises-api',
|
|
39
|
+
}),
|
|
40
|
+
addProperLocking: (0, eslint_devkit_1.formatLLMMessage)({
|
|
41
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
42
|
+
issueName: 'Add File Locking',
|
|
43
|
+
description: 'Add proper locking mechanism',
|
|
44
|
+
severity: 'LOW',
|
|
45
|
+
fix: 'Use proper-lockfile or similar for concurrent access',
|
|
46
|
+
documentationLink: 'https://github.com/moxystudio/node-proper-lockfile',
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
schema: [
|
|
50
|
+
{
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
ignoreInTests: {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
default: true,
|
|
56
|
+
},
|
|
57
|
+
fsMethods: {
|
|
58
|
+
type: 'array',
|
|
59
|
+
items: { type: 'string' },
|
|
60
|
+
default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
defaultOptions: [
|
|
68
|
+
{
|
|
69
|
+
ignoreInTests: true,
|
|
70
|
+
fsMethods: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
create(context, [options = {}]) {
|
|
74
|
+
const { ignoreInTests = true } = options || {};
|
|
75
|
+
const filename = context.getFilename();
|
|
76
|
+
const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
77
|
+
if (isTestFile) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
81
|
+
/**
|
|
82
|
+
* Check for TOCTOU patterns
|
|
83
|
+
*/
|
|
84
|
+
function checkCallExpression(node) {
|
|
85
|
+
const nodeText = sourceCode.getText(node);
|
|
86
|
+
// Only flag file operations (not checks) that are part of check-then-use patterns
|
|
87
|
+
if (!/\b(fs\.readFileSync|fs\.writeFileSync|fs\.openSync|fs\.unlinkSync)\s*\(/.test(nodeText)) {
|
|
88
|
+
return; // Not a file operation we care about
|
|
89
|
+
}
|
|
90
|
+
// Check if this operation is inside an if statement that contains a file check
|
|
91
|
+
let current = node.parent;
|
|
92
|
+
while (current) {
|
|
93
|
+
if (current.type === 'IfStatement') {
|
|
94
|
+
// Check if the test (condition) contains a file check
|
|
95
|
+
const test = current.test;
|
|
96
|
+
if (test.type === 'CallExpression') {
|
|
97
|
+
const testText = sourceCode.getText(test);
|
|
98
|
+
if (/\b(fs\.existsSync|fs\.statSync|fs\.accessSync)\s*\(/.test(testText)) {
|
|
99
|
+
// Check if the file paths match
|
|
100
|
+
const testCall = test;
|
|
101
|
+
const currentCall = node;
|
|
102
|
+
if (testCall.arguments.length > 0 && currentCall.arguments.length > 0) {
|
|
103
|
+
const testArg = testCall.arguments[0];
|
|
104
|
+
const currentArg = currentCall.arguments[0];
|
|
105
|
+
if (testArg.type === 'Literal' && currentArg.type === 'Literal' &&
|
|
106
|
+
testArg.value === currentArg.value) {
|
|
107
|
+
context.report({
|
|
108
|
+
node,
|
|
109
|
+
messageId: 'toctouVulnerability',
|
|
110
|
+
suggest: [
|
|
111
|
+
{
|
|
112
|
+
messageId: 'useAtomicOperations',
|
|
113
|
+
fix: () => null,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
messageId: 'useFsPromises',
|
|
117
|
+
fix: () => null,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
messageId: 'addProperLocking',
|
|
121
|
+
fix: () => null,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
return; // Found a match, stop searching
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Also check for stat-then-use patterns
|
|
131
|
+
if (test.type === 'CallExpression' || test.type === 'MemberExpression') {
|
|
132
|
+
const testText = sourceCode.getText(test);
|
|
133
|
+
// Pattern: if (stats.isFile()) { ... fs.unlinkSync("file") ... }
|
|
134
|
+
if (testText.includes('isFile') && nodeText.includes('fs.unlinkSync')) {
|
|
135
|
+
context.report({
|
|
136
|
+
node,
|
|
137
|
+
messageId: 'toctouVulnerability',
|
|
138
|
+
suggest: [
|
|
139
|
+
{
|
|
140
|
+
messageId: 'useAtomicOperations',
|
|
141
|
+
fix: () => null,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
messageId: 'useFsPromises',
|
|
145
|
+
fix: () => null,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
messageId: 'addProperLocking',
|
|
149
|
+
fix: () => null,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
return; // Found a match, stop searching
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
current = current.parent;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
CallExpression: checkCallExpression,
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=no-toctou-vulnerability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-toctou-vulnerability.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-secure-coding/src/rules/security/no-toctou-vulnerability.ts"],"names":[],"mappings":";;;AASA,4DAA0E;AAC1E,4DAAsD;AAkBzC,QAAA,qBAAqB,GAAG,IAAA,0BAAU,EAA0B;IACvE,IAAI,EAAE,yBAAyB;IAC/B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,mDAAmD;SACjE;QACD,cAAc,EAAE,IAAI;QACpB,QAAQ,EAAE;YACR,mBAAmB,EAAE,IAAA,gCAAgB,EAAC;gBACpC,IAAI,EAAE,4BAAY,CAAC,QAAQ;gBAC3B,SAAS,EAAE,sBAAsB;gBACjC,GAAG,EAAE,SAAS;gBACd,WAAW,EAAE,mDAAmD;gBAChE,QAAQ,EAAE,MAAM;gBAChB,GAAG,EAAE,0DAA0D;gBAC/D,iBAAiB,EAAE,iDAAiD;aACrE,CAAC;YACF,mBAAmB,EAAE,IAAA,gCAAgB,EAAC;gBACpC,IAAI,EAAE,4BAAY,CAAC,IAAI;gBACvB,SAAS,EAAE,uBAAuB;gBAClC,WAAW,EAAE,4BAA4B;gBACzC,QAAQ,EAAE,KAAK;gBACf,GAAG,EAAE,kDAAkD;gBACvD,iBAAiB,EAAE,0DAA0D;aAC9E,CAAC;YACF,aAAa,EAAE,IAAA,gCAAgB,EAAC;gBAC9B,IAAI,EAAE,4BAAY,CAAC,IAAI;gBACvB,SAAS,EAAE,iBAAiB;gBAC5B,WAAW,EAAE,qBAAqB;gBAClC,QAAQ,EAAE,KAAK;gBACf,GAAG,EAAE,yDAAyD;gBAC9D,iBAAiB,EAAE,6CAA6C;aACjE,CAAC;YACF,gBAAgB,EAAE,IAAA,gCAAgB,EAAC;gBACjC,IAAI,EAAE,4BAAY,CAAC,IAAI;gBACvB,SAAS,EAAE,kBAAkB;gBAC7B,WAAW,EAAE,8BAA8B;gBAC3C,QAAQ,EAAE,KAAK;gBACf,GAAG,EAAE,sDAAsD;gBAC3D,iBAAiB,EAAE,oDAAoD;aACxE,CAAC;SACH;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,aAAa,EAAE;wBACb,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,IAAI;qBACd;oBACD,SAAS,EAAE;wBACT,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,OAAO,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,eAAe,CAAC;qBAC3D;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;IACD,cAAc,EAAE;QACd;YACE,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,eAAe,CAAC;SAC7D;KACF;IACD,MAAM,CAAC,OAAsD,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC;QAC3E,MAAM,EACV,aAAa,GAAG,IAAI,EACnB,GAAY,OAAO,IAAI,EAAE,CAAC;QAEvB,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,aAAa,IAAI,iCAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAErF,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;QAE5D;;WAEG;QACH,SAAS,mBAAmB,CAAC,IAA6B;YACxD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAE1C,kFAAkF;YAClF,IAAI,CAAC,yEAAyE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9F,OAAO,CAAC,qCAAqC;YAC/C,CAAC;YAED,+EAA+E;YAC/E,IAAI,OAAO,GAA8B,IAAI,CAAC,MAAM,CAAC;YACrD,OAAO,OAAO,EAAE,CAAC;gBACf,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;oBACnC,sDAAsD;oBACtD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;oBAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;wBACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;wBAC1C,IAAI,qDAAqD,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;4BACzE,gCAAgC;4BAChC,MAAM,QAAQ,GAAG,IAAI,CAAC;4BACtB,MAAM,WAAW,GAAG,IAAI,CAAC;4BAEzB,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gCACtE,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gCACtC,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gCAE5C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS;oCAC3D,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC;oCACvC,OAAO,CAAC,MAAM,CAAC;wCACb,IAAI;wCACJ,SAAS,EAAE,qBAAqB;wCAChC,OAAO,EAAE;4CACP;gDACE,SAAS,EAAE,qBAAqB;gDAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;6CAChB;4CACD;gDACE,SAAS,EAAE,eAAe;gDAC1B,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;6CAChB;4CACD;gDACE,SAAS,EAAE,kBAAkB;gDAC7B,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;6CAChB;yCACF;qCACF,CAAC,CAAC;oCACH,OAAO,CAAC,gCAAgC;gCAC1C,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;oBAED,wCAAwC;oBACxC,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;wBACvE,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;wBAE1C,iEAAiE;wBACjE,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;4BACtE,OAAO,CAAC,MAAM,CAAC;gCACb,IAAI;gCACJ,SAAS,EAAE,qBAAqB;gCAChC,OAAO,EAAE;oCACP;wCACE,SAAS,EAAE,qBAAqB;wCAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;qCAChB;oCACD;wCACE,SAAS,EAAE,eAAe;wCAC1B,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;qCAChB;oCACD;wCACE,SAAS,EAAE,kBAAkB;wCAC7B,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;qCAChB;iCACF;6BACF,CAAC,CAAC;4BACH,OAAO,CAAC,gCAAgC;wBAC1C,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,OAAO,GAAG,OAAO,CAAC,MAAuB,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,OAAO;YACL,cAAc,EAAE,mBAAmB;SACpC,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type SecurityRuleOptions } from '@interlace/eslint-devkit';
|
|
2
|
+
export interface Options extends SecurityRuleOptions {
|
|
3
|
+
/** Maximum allowed loop iterations for static analysis */
|
|
4
|
+
maxStaticIterations?: number;
|
|
5
|
+
/** Variables that contain user input */
|
|
6
|
+
userInputVariables?: string[];
|
|
7
|
+
/** Allow while(true) loops with breaks */
|
|
8
|
+
allowWhileTrueWithBreak?: boolean;
|
|
9
|
+
/** Maximum recursion depth to allow */
|
|
10
|
+
maxRecursionDepth?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const noUncheckedLoopCondition: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|