@sun-asterisk/sunlint 1.3.18 → 1.3.20
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/config/rules/enhanced-rules-registry.json +77 -18
- package/core/cli-program.js +9 -1
- package/core/github-annotate-service.js +986 -0
- package/core/output-service.js +294 -6
- package/core/summary-report-service.js +30 -30
- package/docs/GITHUB_ACTIONS_INTEGRATION.md +421 -0
- package/package.json +2 -1
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
- package/rules/common/C017_constructor_logic/analyzer.js +137 -503
- package/rules/common/C017_constructor_logic/config.json +50 -0
- package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
- package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
- package/rules/security/S011_secure_guid_generation/README.md +255 -0
- package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
- package/rules/security/S011_secure_guid_generation/config.json +56 -0
- package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
- package/rules/security/S028_file_upload_size_limits/README.md +537 -0
- package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
- package/rules/security/S028_file_upload_size_limits/config.json +186 -0
- package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
- package/rules/security/S041_session_token_invalidation/README.md +303 -0
- package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
- package/rules/security/S041_session_token_invalidation/config.json +175 -0
- package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
- package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
- package/rules/security/S044_re_authentication_required/README.md +136 -0
- package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
- package/rules/security/S044_re_authentication_required/config.json +161 -0
- package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
- package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
- package/rules/security/S045_brute_force_protection/README.md +345 -0
- package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
- package/rules/security/S045_brute_force_protection/config.json +139 -0
- package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +0 -340
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "C017",
|
|
3
|
+
"name": "C017_constructor_logic",
|
|
4
|
+
"category": "architecture",
|
|
5
|
+
"description": "C017 - Do not put business logic inside constructors.",
|
|
6
|
+
"severity": "warning",
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"semantic": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"priority": "high",
|
|
11
|
+
"fallback": "heuristic"
|
|
12
|
+
},
|
|
13
|
+
"patterns": {
|
|
14
|
+
"include": [
|
|
15
|
+
"**/*.js",
|
|
16
|
+
"**/*.ts",
|
|
17
|
+
"**/*.jsx",
|
|
18
|
+
"**/*.tsx"
|
|
19
|
+
],
|
|
20
|
+
"exclude": [
|
|
21
|
+
"**/*.test.*",
|
|
22
|
+
"**/*.spec.*",
|
|
23
|
+
"**/*.mock.*",
|
|
24
|
+
"**/test/**",
|
|
25
|
+
"**/tests/**",
|
|
26
|
+
"**/spec/**"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"options": {
|
|
30
|
+
"strictMode": false,
|
|
31
|
+
"allowedDbMethods": [],
|
|
32
|
+
"repositoryPatterns": [
|
|
33
|
+
"*Repository*",
|
|
34
|
+
"*Repo*",
|
|
35
|
+
"*DAO*",
|
|
36
|
+
"*Store*"
|
|
37
|
+
],
|
|
38
|
+
"servicePatterns": [
|
|
39
|
+
"*Service*",
|
|
40
|
+
"*UseCase*",
|
|
41
|
+
"*Handler*",
|
|
42
|
+
"*Manager*"
|
|
43
|
+
],
|
|
44
|
+
"complexityThreshold": {
|
|
45
|
+
"methodLength": 200,
|
|
46
|
+
"cyclomaticComplexity": 5,
|
|
47
|
+
"nestedDepth": 3
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C017 Symbol-based Analyzer - Do not put business logic inside constructors
|
|
3
|
+
* Purpose: Ensure constructors only initialize objects, not perform business logic, to improve testability.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { SyntaxKind } = require('ts-morph');
|
|
7
|
+
|
|
8
|
+
class C017SymbolBasedAnalyzer {
|
|
9
|
+
constructor(semanticEngine = null) {
|
|
10
|
+
this.ruleId = 'C017';
|
|
11
|
+
this.ruleName = 'Error put business logic inside constructors. (Symbol-Based)';
|
|
12
|
+
this.semanticEngine = semanticEngine;
|
|
13
|
+
this.verbose = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async initialize(semanticEngine = null) {
|
|
17
|
+
if (semanticEngine) {
|
|
18
|
+
this.semanticEngine = semanticEngine;
|
|
19
|
+
}
|
|
20
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
21
|
+
|
|
22
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
23
|
+
console.log(`🔧 [C017 Symbol-Based] Analyzer initialized, verbose: ${this.verbose}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
28
|
+
// This is the main entry point called by the hybrid analyzer
|
|
29
|
+
return await this.analyzeFileWithSymbols(filePath, options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async analyzeFileWithSymbols(filePath, options = {}) {
|
|
33
|
+
const violations = [];
|
|
34
|
+
|
|
35
|
+
// Enable verbose mode if requested
|
|
36
|
+
const verbose = options.verbose || this.verbose;
|
|
37
|
+
|
|
38
|
+
if (!this.semanticEngine?.project) {
|
|
39
|
+
if (verbose) {
|
|
40
|
+
console.warn('[C017 Symbol-Based] No semantic engine available, skipping analysis');
|
|
41
|
+
}
|
|
42
|
+
return violations;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (verbose) {
|
|
46
|
+
console.log(`🔍 [C017 Symbol-Based] Starting analysis for ${filePath}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
51
|
+
if (!sourceFile) {
|
|
52
|
+
return violations;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Find all constructor declarations
|
|
56
|
+
const constructors = sourceFile.getDescendantsOfKind(SyntaxKind.Constructor);
|
|
57
|
+
|
|
58
|
+
for (const constructor of constructors) {
|
|
59
|
+
this.analyzeConstructor(constructor, filePath, violations, verbose);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (verbose) {
|
|
63
|
+
console.log(`🔍 [C017 Symbol-Based] Total violations found: ${violations.length}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return violations;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (verbose) {
|
|
69
|
+
console.warn(`[C017 Symbol-Based] Analysis failed for ${filePath}:`, error.message);
|
|
70
|
+
}
|
|
71
|
+
return violations;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Analyze a constructor for business logic violations
|
|
77
|
+
*/
|
|
78
|
+
analyzeConstructor(constructor, filePath, violations, verbose = false) {
|
|
79
|
+
const body = constructor.getBody();
|
|
80
|
+
if (!body) return;
|
|
81
|
+
|
|
82
|
+
const statements = body.getStatements();
|
|
83
|
+
|
|
84
|
+
for (const statement of statements) {
|
|
85
|
+
// Check for method calls (instance methods)
|
|
86
|
+
if (this.containsMethodCall(statement, verbose)) {
|
|
87
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
88
|
+
|
|
89
|
+
violations.push({
|
|
90
|
+
ruleId: this.ruleId,
|
|
91
|
+
severity: 'warning',
|
|
92
|
+
message: 'Constructor calls instance methods - business logic should not be in constructors',
|
|
93
|
+
source: this.ruleId,
|
|
94
|
+
file: filePath,
|
|
95
|
+
line: line,
|
|
96
|
+
column: column,
|
|
97
|
+
description: `[SYMBOL-BASED] Instance method calls detected in constructor. Calling methods in constructors makes the class harder to test and can cause unexpected behavior if methods are overridden.`,
|
|
98
|
+
suggestion: 'Move method calls to a separate initialization method (e.g., init(), setup()) that can be called after object creation and easily mocked in tests.',
|
|
99
|
+
category: 'TESTABILITY'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check for API calls (fetch, axios, http requests)
|
|
104
|
+
if (this.containsApiCall(statement, verbose)) {
|
|
105
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
106
|
+
|
|
107
|
+
violations.push({
|
|
108
|
+
ruleId: this.ruleId,
|
|
109
|
+
severity: 'warning',
|
|
110
|
+
message: 'Constructor contains API call - business logic should not be in constructors',
|
|
111
|
+
source: this.ruleId,
|
|
112
|
+
file: filePath,
|
|
113
|
+
line: line,
|
|
114
|
+
column: column,
|
|
115
|
+
description: `[SYMBOL-BASED] API calls detected in constructor. Constructors should only initialize fields and assign dependencies.`,
|
|
116
|
+
suggestion: 'Move API calls to a separate initialization method (e.g., init(), load(), or setup()) and call it after object creation.',
|
|
117
|
+
category: 'CODE_QUALITY'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check for complex logic (if/else, loops, switch) - with stricter rules
|
|
122
|
+
if (this.containsComplexLogic(statement, verbose)) {
|
|
123
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
124
|
+
|
|
125
|
+
violations.push({
|
|
126
|
+
ruleId: this.ruleId,
|
|
127
|
+
severity: 'warning',
|
|
128
|
+
message: 'Constructor contains complex business logic - reduces testability',
|
|
129
|
+
source: this.ruleId,
|
|
130
|
+
file: filePath,
|
|
131
|
+
line: line,
|
|
132
|
+
column: column,
|
|
133
|
+
description: `[SYMBOL-BASED] Complex control flow (if/else, loops, switch) detected in constructor. This makes the class harder to test and instantiate.`,
|
|
134
|
+
suggestion: 'Extract complex logic into separate methods that can be called after object creation and mocked during testing.',
|
|
135
|
+
category: 'TESTABILITY'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Check for logging operations
|
|
140
|
+
if (this.containsLogging(statement, verbose)) {
|
|
141
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
142
|
+
|
|
143
|
+
violations.push({
|
|
144
|
+
ruleId: this.ruleId,
|
|
145
|
+
severity: 'info',
|
|
146
|
+
message: 'Constructor contains logging - side effects should be avoided',
|
|
147
|
+
source: this.ruleId,
|
|
148
|
+
file: filePath,
|
|
149
|
+
line: line,
|
|
150
|
+
column: column,
|
|
151
|
+
description: `[SYMBOL-BASED] Logging detected in constructor. Constructors should be side-effect free for predictability.`,
|
|
152
|
+
suggestion: 'Move logging to initialization methods or business logic methods.',
|
|
153
|
+
category: 'RELIABILITY'
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Check for file I/O operations
|
|
158
|
+
if (this.containsFileIO(statement, verbose)) {
|
|
159
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
160
|
+
|
|
161
|
+
violations.push({
|
|
162
|
+
ruleId: this.ruleId,
|
|
163
|
+
severity: 'warning',
|
|
164
|
+
message: 'Constructor contains file I/O operations - business logic violation',
|
|
165
|
+
source: this.ruleId,
|
|
166
|
+
file: filePath,
|
|
167
|
+
line: line,
|
|
168
|
+
column: column,
|
|
169
|
+
description: `[SYMBOL-BASED] File I/O operations detected in constructor. This creates side effects and makes testing difficult.`,
|
|
170
|
+
suggestion: 'Move file operations to separate methods that can be called explicitly and mocked during testing.',
|
|
171
|
+
category: 'INTEGRATION'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check for database operations
|
|
176
|
+
if (this.containsDatabaseOperation(statement, verbose)) {
|
|
177
|
+
const { line, column } = this.getStatementPosition(statement);
|
|
178
|
+
|
|
179
|
+
violations.push({
|
|
180
|
+
ruleId: this.ruleId,
|
|
181
|
+
severity: 'warning',
|
|
182
|
+
message: 'Constructor contains database operations - violates constructor principles',
|
|
183
|
+
source: this.ruleId,
|
|
184
|
+
file: filePath,
|
|
185
|
+
line: line,
|
|
186
|
+
column: column,
|
|
187
|
+
description: `[SYMBOL-BASED] Database operations detected in constructor. This makes object creation slow and unpredictable.`,
|
|
188
|
+
suggestion: 'Move database operations to repository methods or service layer methods.',
|
|
189
|
+
category: 'INTEGRATION'
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get the position of a statement
|
|
197
|
+
*/
|
|
198
|
+
getStatementPosition(statement) {
|
|
199
|
+
try {
|
|
200
|
+
return {
|
|
201
|
+
line: statement.getStartLineNumber(),
|
|
202
|
+
column: statement.getStartLinePos ? statement.getStartLinePos() : 0
|
|
203
|
+
};
|
|
204
|
+
} catch {
|
|
205
|
+
return { line: 0, column: 0 };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check if statement contains instance method calls
|
|
211
|
+
*/
|
|
212
|
+
containsMethodCall(statement, verbose = false) {
|
|
213
|
+
// Get all call expressions in the statement
|
|
214
|
+
const callExpressions = statement.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
215
|
+
|
|
216
|
+
for (const callExpr of callExpressions) {
|
|
217
|
+
const expression = callExpr.getExpression();
|
|
218
|
+
const callText = expression.getText();
|
|
219
|
+
|
|
220
|
+
// Check if it's a 'this.' method call
|
|
221
|
+
if (callText.startsWith('this.')) {
|
|
222
|
+
// Extract method name
|
|
223
|
+
const methodName = callText.replace('this.', '').split('(')[0];
|
|
224
|
+
|
|
225
|
+
// Ignore common safe operations
|
|
226
|
+
const safePatterns = [
|
|
227
|
+
'toString',
|
|
228
|
+
'valueOf',
|
|
229
|
+
'hasOwnProperty',
|
|
230
|
+
'isPrototypeOf',
|
|
231
|
+
'propertyIsEnumerable'
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
if (!safePatterns.includes(methodName)) {
|
|
235
|
+
// Check if it's a config service or environment variable access
|
|
236
|
+
if (!this.isSafeConfigAccess(callExpr)) {
|
|
237
|
+
if (verbose) {
|
|
238
|
+
console.log(` 🔧 Method call detected: ${callText}`);
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Also check for super method calls (not super() constructor call)
|
|
246
|
+
if (callText.startsWith('super.')) {
|
|
247
|
+
if (verbose) {
|
|
248
|
+
console.log(` 🔧 Super method call detected: ${callText}`);
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if a method call is safe config/environment access
|
|
259
|
+
* These are allowed in constructors as they are initialization, not business logic
|
|
260
|
+
*/
|
|
261
|
+
isSafeConfigAccess(callExpr) {
|
|
262
|
+
const expression = callExpr.getExpression();
|
|
263
|
+
const callText = expression.getText();
|
|
264
|
+
|
|
265
|
+
// Pattern: this.configService.get(...) or similar
|
|
266
|
+
// Pattern: this.envService.get(...) or similar
|
|
267
|
+
const configPatterns = [
|
|
268
|
+
/this\.(config|env|environment|settings?)Service\.get/i,
|
|
269
|
+
/this\.(config|env|environment|settings?)\.get/i,
|
|
270
|
+
/this\.(config|env|environment|settings?)Service\.read/i,
|
|
271
|
+
/this\.(config|env|environment|settings?)\.read/i,
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
if (configPatterns.some(pattern => pattern.test(callText))) {
|
|
275
|
+
// Verify it's a simple get/read operation, not complex logic
|
|
276
|
+
const args = callExpr.getArguments();
|
|
277
|
+
// Should have 1-2 arguments (key, optional default value)
|
|
278
|
+
if (args.length <= 2) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Pattern: process.env access wrapped in a method
|
|
284
|
+
// Pattern: this.getString('KEY') where getString is a simple getter
|
|
285
|
+
const simpleGetterPatterns = [
|
|
286
|
+
/this\.get(String|Number|Boolean|Int|Float)/,
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
if (simpleGetterPatterns.some(pattern => pattern.test(callText))) {
|
|
290
|
+
const args = callExpr.getArguments();
|
|
291
|
+
if (args.length <= 2) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Check if statement contains API calls
|
|
301
|
+
*/
|
|
302
|
+
containsApiCall(statement, verbose = false) {
|
|
303
|
+
const text = statement.getText();
|
|
304
|
+
|
|
305
|
+
// Common API call patterns
|
|
306
|
+
const apiPatterns = [
|
|
307
|
+
/\bfetch\s*\(/,
|
|
308
|
+
/\baxios\./,
|
|
309
|
+
/\b(get|post|put|delete|patch)\s*\(/,
|
|
310
|
+
/\.ajax\s*\(/,
|
|
311
|
+
/\bhttps?\./,
|
|
312
|
+
/\bHttpClient\./,
|
|
313
|
+
/\bXMLHttpRequest/,
|
|
314
|
+
/\bsuperagent\./,
|
|
315
|
+
/\brequest\s*\(/
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
const hasApiCall = apiPatterns.some(pattern => pattern.test(text));
|
|
319
|
+
|
|
320
|
+
if (hasApiCall && verbose) {
|
|
321
|
+
console.log(` 📡 API call detected in constructor`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return hasApiCall;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Check if statement contains complex logic
|
|
329
|
+
* Now more strict - flags most conditional logic
|
|
330
|
+
*/
|
|
331
|
+
containsComplexLogic(statement, verbose = false) {
|
|
332
|
+
// Check for control flow structures
|
|
333
|
+
const hasIfStatement = statement.getDescendantsOfKind(SyntaxKind.IfStatement).length > 0;
|
|
334
|
+
const hasLoop = statement.getDescendantsOfKind(SyntaxKind.ForStatement).length > 0 ||
|
|
335
|
+
statement.getDescendantsOfKind(SyntaxKind.WhileStatement).length > 0 ||
|
|
336
|
+
statement.getDescendantsOfKind(SyntaxKind.DoStatement).length > 0 ||
|
|
337
|
+
statement.getDescendantsOfKind(SyntaxKind.ForOfStatement).length > 0 ||
|
|
338
|
+
statement.getDescendantsOfKind(SyntaxKind.ForInStatement).length > 0;
|
|
339
|
+
const hasSwitch = statement.getDescendantsOfKind(SyntaxKind.SwitchStatement).length > 0;
|
|
340
|
+
const hasTryCatch = statement.getDescendantsOfKind(SyntaxKind.TryStatement).length > 0;
|
|
341
|
+
const hasConditional = statement.getDescendantsOfKind(SyntaxKind.ConditionalExpression).length > 0; // ternary operators
|
|
342
|
+
|
|
343
|
+
// Be more strict with if statements
|
|
344
|
+
if (hasIfStatement) {
|
|
345
|
+
const ifStatements = statement.getDescendantsOfKind(SyntaxKind.IfStatement);
|
|
346
|
+
|
|
347
|
+
for (const ifStmt of ifStatements) {
|
|
348
|
+
const condition = ifStmt.getExpression().getText();
|
|
349
|
+
const thenStatement = ifStmt.getThenStatement();
|
|
350
|
+
const elseStatement = ifStmt.getElseStatement();
|
|
351
|
+
|
|
352
|
+
// Only allow very simple null/undefined checks for default parameter values
|
|
353
|
+
// Pattern: if (!param) { this.param = defaultValue; }
|
|
354
|
+
const isSimpleNullDefault =
|
|
355
|
+
/^!?\w+$/.test(condition) && // Simple variable check
|
|
356
|
+
!elseStatement && // No else clause
|
|
357
|
+
thenStatement &&
|
|
358
|
+
thenStatement.getText().match(/^{\s*this\.\w+\s*=\s*[^;]+;\s*}$/);
|
|
359
|
+
|
|
360
|
+
if (!isSimpleNullDefault) {
|
|
361
|
+
if (verbose) {
|
|
362
|
+
console.log(` 🔀 Complex if statement detected: ${condition}`);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const hasComplexLogic = hasLoop || hasSwitch || hasTryCatch || hasConditional;
|
|
370
|
+
|
|
371
|
+
if (hasComplexLogic && verbose) {
|
|
372
|
+
console.log(` 🔀 Complex logic detected in constructor`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return hasComplexLogic;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Check if statement contains logging
|
|
380
|
+
*/
|
|
381
|
+
containsLogging(statement, verbose = false) {
|
|
382
|
+
const text = statement.getText();
|
|
383
|
+
|
|
384
|
+
// Common logging patterns
|
|
385
|
+
const loggingPatterns = [
|
|
386
|
+
/\bconsole\.(log|warn|error|info|debug)/,
|
|
387
|
+
/\blogger\./,
|
|
388
|
+
/\bLog\./,
|
|
389
|
+
/\blogging\./,
|
|
390
|
+
/\bwinston\./,
|
|
391
|
+
/\bbunyan\./,
|
|
392
|
+
/\bpino\./,
|
|
393
|
+
/\.log\s*\(/
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
const hasLogging = loggingPatterns.some(pattern => pattern.test(text));
|
|
397
|
+
|
|
398
|
+
if (hasLogging && verbose) {
|
|
399
|
+
console.log(` 📝 Logging detected in constructor`);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return hasLogging;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Check if statement contains file I/O operations
|
|
407
|
+
*/
|
|
408
|
+
containsFileIO(statement, verbose = false) {
|
|
409
|
+
const text = statement.getText();
|
|
410
|
+
|
|
411
|
+
// File I/O patterns
|
|
412
|
+
const fileIOPatterns = [
|
|
413
|
+
/\bfs\.(readFile|writeFile|read|write|open|close)/,
|
|
414
|
+
/\breadFileSync/,
|
|
415
|
+
/\bwriteFileSync/,
|
|
416
|
+
/\bFileReader/,
|
|
417
|
+
/\bFile\./,
|
|
418
|
+
/\bpath\.read/,
|
|
419
|
+
/\bpath\.write/
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
const hasFileIO = fileIOPatterns.some(pattern => pattern.test(text));
|
|
423
|
+
|
|
424
|
+
if (hasFileIO && verbose) {
|
|
425
|
+
console.log(` 📁 File I/O detected in constructor`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return hasFileIO;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Check if statement contains database operations
|
|
433
|
+
*/
|
|
434
|
+
containsDatabaseOperation(statement, verbose = false) {
|
|
435
|
+
const text = statement.getText();
|
|
436
|
+
|
|
437
|
+
// Database operation patterns
|
|
438
|
+
const dbPatterns = [
|
|
439
|
+
/\b(find|findOne|findById|save|update|delete|insert|create|query|exec)\s*\(/,
|
|
440
|
+
/\bmongoose\./,
|
|
441
|
+
/\bSequelize\./,
|
|
442
|
+
/\bTypeORM\./,
|
|
443
|
+
/\bPrisma\./,
|
|
444
|
+
/\.collection\s*\(/,
|
|
445
|
+
/\bdb\./,
|
|
446
|
+
/\bknex\./,
|
|
447
|
+
/SELECT\s+.*\s+FROM/i,
|
|
448
|
+
/INSERT\s+INTO/i,
|
|
449
|
+
/UPDATE\s+.*\s+SET/i,
|
|
450
|
+
/DELETE\s+FROM/i
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
const hasDbOperation = dbPatterns.some(pattern => pattern.test(text));
|
|
454
|
+
|
|
455
|
+
if (hasDbOperation && verbose) {
|
|
456
|
+
console.log(` 💾 Database operation detected in constructor`);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return hasDbOperation;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
module.exports = C017SymbolBasedAnalyzer;
|