@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.
Files changed (35) hide show
  1. package/config/rules/enhanced-rules-registry.json +77 -18
  2. package/core/cli-program.js +9 -1
  3. package/core/github-annotate-service.js +986 -0
  4. package/core/output-service.js +294 -6
  5. package/core/summary-report-service.js +30 -30
  6. package/docs/GITHUB_ACTIONS_INTEGRATION.md +421 -0
  7. package/package.json +2 -1
  8. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +392 -280
  9. package/rules/common/C017_constructor_logic/analyzer.js +137 -503
  10. package/rules/common/C017_constructor_logic/config.json +50 -0
  11. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +463 -0
  12. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +463 -21
  13. package/rules/security/S011_secure_guid_generation/README.md +255 -0
  14. package/rules/security/S011_secure_guid_generation/analyzer.js +135 -0
  15. package/rules/security/S011_secure_guid_generation/config.json +56 -0
  16. package/rules/security/S011_secure_guid_generation/symbol-based-analyzer.js +609 -0
  17. package/rules/security/S028_file_upload_size_limits/README.md +537 -0
  18. package/rules/security/S028_file_upload_size_limits/analyzer.js +202 -0
  19. package/rules/security/S028_file_upload_size_limits/config.json +186 -0
  20. package/rules/security/S028_file_upload_size_limits/symbol-based-analyzer.js +530 -0
  21. package/rules/security/S041_session_token_invalidation/README.md +303 -0
  22. package/rules/security/S041_session_token_invalidation/analyzer.js +242 -0
  23. package/rules/security/S041_session_token_invalidation/config.json +175 -0
  24. package/rules/security/S041_session_token_invalidation/regex-based-analyzer.js +411 -0
  25. package/rules/security/S041_session_token_invalidation/symbol-based-analyzer.js +674 -0
  26. package/rules/security/S044_re_authentication_required/README.md +136 -0
  27. package/rules/security/S044_re_authentication_required/analyzer.js +242 -0
  28. package/rules/security/S044_re_authentication_required/config.json +161 -0
  29. package/rules/security/S044_re_authentication_required/regex-based-analyzer.js +329 -0
  30. package/rules/security/S044_re_authentication_required/symbol-based-analyzer.js +537 -0
  31. package/rules/security/S045_brute_force_protection/README.md +345 -0
  32. package/rules/security/S045_brute_force_protection/analyzer.js +336 -0
  33. package/rules/security/S045_brute_force_protection/config.json +139 -0
  34. package/rules/security/S045_brute_force_protection/symbol-based-analyzer.js +646 -0
  35. package/rules/common/C017_constructor_logic/semantic-analyzer.js +0 -340
@@ -1,340 +0,0 @@
1
- /**
2
- * C017 Constructor Logic - Semantic Analyzer (Phase 2)
3
- * Uses ts-morph for precise symbol-based analysis
4
- * Detects complex logic in constructors with high accuracy
5
- */
6
-
7
- const SemanticRuleBase = require('../../../core/semantic-rule-base');
8
- const path = require('path');
9
-
10
- class C017SemanticAnalyzer extends SemanticRuleBase {
11
- constructor(ruleId = 'C017') {
12
- super(ruleId);
13
- this.ruleName = 'C017 - No Complex Logic in Constructors (Semantic)';
14
- this.description = 'Constructors should only handle dependency injection and simple initialization';
15
- }
16
-
17
- /**
18
- * Analyze a single file using ts-morph semantic analysis
19
- * @param {string} filePath - Path to the file to analyze
20
- * @param {Object} options - Analysis options
21
- */
22
- async analyzeFile(filePath, options = {}) {
23
- if (!this.semanticEngine) {
24
- throw new Error('Semantic engine not initialized');
25
- }
26
-
27
- try {
28
- // Get file's absolute path
29
- const absolutePath = path.resolve(filePath);
30
-
31
- // Get source file from semantic engine's project
32
- const sourceFile = this.semanticEngine.project.getSourceFile(absolutePath);
33
- if (!sourceFile) {
34
- if (options.verbose) {
35
- console.log(`⚠️ [C017-Semantic] Could not load source file: ${filePath}`);
36
- }
37
- return;
38
- }
39
-
40
- // Find all class declarations
41
- const classes = sourceFile.getClasses();
42
-
43
- for (const classDecl of classes) {
44
- const constructors = classDecl.getConstructors();
45
-
46
- for (const constructor of constructors) {
47
- await this.analyzeConstructor(constructor, filePath, options);
48
- }
49
- }
50
-
51
- } catch (error) {
52
- if (options.verbose) {
53
- console.warn(`⚠️ [C017-Semantic] Error analyzing ${filePath}:`, error.message);
54
- }
55
- }
56
- }
57
-
58
- /**
59
- * Analyze a constructor node for complex logic
60
- * @param {ts.ConstructorDeclaration} constructor - Constructor node
61
- * @param {string} filePath - File path
62
- * @param {Object} options - Analysis options
63
- */
64
- async analyzeConstructor(constructor, filePath, options) {
65
- const body = constructor.getBody();
66
- if (!body) {
67
- // Constructor without body (interface, abstract, etc.)
68
- return;
69
- }
70
-
71
- const statements = body.getStatements();
72
-
73
- for (const statement of statements) {
74
- const violation = this.analyzeStatement(statement, constructor, filePath);
75
- if (violation) {
76
- this.addViolation(violation);
77
- }
78
- }
79
- }
80
-
81
- /**
82
- * Analyze a statement for complex logic patterns
83
- * @param {ts.Statement} statement - Statement node
84
- * @param {ts.ConstructorDeclaration} constructor - Parent constructor
85
- * @param {string} filePath - File path
86
- * @returns {Object|null} Violation object or null
87
- */
88
- analyzeStatement(statement, constructor, filePath) {
89
- const statementKind = statement.getKind();
90
- const line = statement.getStartLineNumber();
91
- const column = statement.getStart() - statement.getStartLinePos() + 1;
92
-
93
- // Check for complex logic patterns
94
- switch (statementKind) {
95
- case this.SyntaxKind.IfStatement:
96
- return this.createViolation(
97
- filePath, line, column,
98
- 'Conditional logic (if statement) found in constructor',
99
- 'conditional_logic',
100
- statement.getText()
101
- );
102
-
103
- case this.SyntaxKind.ForStatement:
104
- case this.SyntaxKind.ForInStatement:
105
- case this.SyntaxKind.ForOfStatement:
106
- case this.SyntaxKind.WhileStatement:
107
- case this.SyntaxKind.DoStatement:
108
- return this.createViolation(
109
- filePath, line, column,
110
- 'Loop logic found in constructor',
111
- 'loop_logic',
112
- statement.getText()
113
- );
114
-
115
- case this.SyntaxKind.SwitchStatement:
116
- return this.createViolation(
117
- filePath, line, column,
118
- 'Switch statement found in constructor',
119
- 'switch_logic',
120
- statement.getText()
121
- );
122
-
123
- case this.SyntaxKind.TryStatement:
124
- return this.createViolation(
125
- filePath, line, column,
126
- 'Exception handling (try/catch) found in constructor',
127
- 'exception_handling',
128
- statement.getText()
129
- );
130
-
131
- case this.SyntaxKind.ExpressionStatement:
132
- return this.analyzeExpressionStatement(statement, filePath);
133
-
134
- default:
135
- return null;
136
- }
137
- }
138
-
139
- /**
140
- * Analyze expression statements for complex patterns
141
- * @param {ts.ExpressionStatement} statement - Expression statement
142
- * @param {string} filePath - File path
143
- * @returns {Object|null} Violation object or null
144
- */
145
- analyzeExpressionStatement(statement, filePath) {
146
- const expression = statement.getExpression();
147
- const line = statement.getStartLineNumber();
148
- const column = statement.getStart() - statement.getStartLinePos() + 1;
149
-
150
- // Check for async operations
151
- if (this.isAsyncOperation(expression)) {
152
- return this.createViolation(
153
- filePath, line, column,
154
- 'Asynchronous operation found in constructor',
155
- 'async_operation',
156
- statement.getText()
157
- );
158
- }
159
-
160
- // Check for complex method calls
161
- if (this.isComplexMethodCall(expression)) {
162
- return this.createViolation(
163
- filePath, line, column,
164
- 'Complex method call found in constructor',
165
- 'complex_method_call',
166
- statement.getText()
167
- );
168
- }
169
-
170
- // Allow simple assignments and DI setup
171
- if (this.isSimpleAssignment(expression) || this.isConfigurationSetup(expression)) {
172
- return null;
173
- }
174
-
175
- return null;
176
- }
177
-
178
- /**
179
- * Check if expression is an async operation
180
- * @param {ts.Expression} expression - Expression node
181
- * @returns {boolean} True if async operation
182
- */
183
- isAsyncOperation(expression) {
184
- const text = expression.getText();
185
-
186
- // Await expressions
187
- if (expression.getKind() === this.SyntaxKind.AwaitExpression) {
188
- return true;
189
- }
190
-
191
- // Promise chains
192
- if (text.includes('.then(') || text.includes('.catch(') || text.includes('.finally(')) {
193
- return true;
194
- }
195
-
196
- return false;
197
- }
198
-
199
- /**
200
- * Check if expression is a complex method call
201
- * @param {ts.Expression} expression - Expression node
202
- * @returns {boolean} True if complex method call
203
- */
204
- isComplexMethodCall(expression) {
205
- if (expression.getKind() !== this.SyntaxKind.CallExpression) {
206
- return false;
207
- }
208
-
209
- const callExpr = expression;
210
- const methodName = this.getMethodName(callExpr);
211
-
212
- // Allow certain initialization methods
213
- const allowedMethods = [
214
- 'makeObservable', 'makeAutoObservable', // MobX
215
- 'bind', 'bindAll', // Method binding
216
- 'Object.assign', 'Object.create', // Object utilities
217
- 'Array.from', 'Array.of', // Array utilities
218
- ];
219
-
220
- if (allowedMethods.some(method => methodName.includes(method))) {
221
- return false;
222
- }
223
-
224
- // Check for chained calls (more than 2 levels = complex)
225
- const chainLength = this.getChainLength(callExpr);
226
- return chainLength > 2;
227
- }
228
-
229
- /**
230
- * Check if expression is a simple assignment
231
- * @param {ts.Expression} expression - Expression node
232
- * @returns {boolean} True if simple assignment
233
- */
234
- isSimpleAssignment(expression) {
235
- if (expression.getKind() !== this.SyntaxKind.BinaryExpression) {
236
- return false;
237
- }
238
-
239
- const binaryExpr = expression;
240
- const operator = binaryExpr.getOperatorToken();
241
-
242
- return operator.getKind() === this.SyntaxKind.EqualsToken;
243
- }
244
-
245
- /**
246
- * Check if expression is configuration setup
247
- * @param {ts.Expression} expression - Expression node
248
- * @returns {boolean} True if configuration setup
249
- */
250
- isConfigurationSetup(expression) {
251
- const text = expression.getText();
252
-
253
- // Configuration patterns
254
- const configPatterns = [
255
- /new\s+\w+Client\s*\(/, // AWS clients, HTTP clients
256
- /new\s+\w+\s*\(\s*\{/, // Configuration objects
257
- /\.getInstance\s*\(/, // Singleton patterns
258
- /\.create\s*\(/, // Factory patterns
259
- ];
260
-
261
- return configPatterns.some(pattern => pattern.test(text));
262
- }
263
-
264
- /**
265
- * Get method name from call expression
266
- * @param {ts.CallExpression} callExpr - Call expression
267
- * @returns {string} Method name
268
- */
269
- getMethodName(callExpr) {
270
- const expression = callExpr.getExpression();
271
-
272
- if (expression.getKind() === this.SyntaxKind.PropertyAccessExpression) {
273
- return expression.getName();
274
- }
275
-
276
- if (expression.getKind() === this.SyntaxKind.Identifier) {
277
- return expression.getText();
278
- }
279
-
280
- return expression.getText();
281
- }
282
-
283
- /**
284
- * Get chain length of method calls
285
- * @param {ts.CallExpression} callExpr - Call expression
286
- * @returns {number} Chain length
287
- */
288
- getChainLength(callExpr) {
289
- let current = callExpr.getExpression();
290
- let length = 1;
291
-
292
- while (current.getKind() === this.SyntaxKind.PropertyAccessExpression) {
293
- const propAccess = current;
294
- current = propAccess.getExpression();
295
- length++;
296
- }
297
-
298
- return length;
299
- }
300
-
301
- /**
302
- * Create a violation object
303
- * @param {string} filePath - File path
304
- * @param {number} line - Line number
305
- * @param {number} column - Column number
306
- * @param {string} message - Violation message
307
- * @param {string} type - Violation type
308
- * @param {string} code - Code snippet
309
- * @returns {Object} Violation object
310
- */
311
- createViolation(filePath, line, column, message, type, code) {
312
- return {
313
- ruleId: this.ruleId,
314
- file: filePath,
315
- line: line,
316
- column: column,
317
- message: `Constructor contains complex logic: ${message}. Move to initialization methods`,
318
- severity: 'warning',
319
- code: code.trim(),
320
- type: type,
321
- confidence: 95, // High confidence with semantic analysis
322
- analysisMethod: 'semantic',
323
- suggestion: 'Move complex logic to separate initialization methods or lifecycle hooks'
324
- };
325
- }
326
-
327
- /**
328
- * Get SyntaxKind enum from ts-morph
329
- * @returns {Object} SyntaxKind enum
330
- */
331
- get SyntaxKind() {
332
- if (!this.semanticEngine) {
333
- throw new Error('Semantic engine not initialized');
334
- }
335
- return this.semanticEngine.project.getTypeChecker().compilerObject.SyntaxKind ||
336
- require('typescript').SyntaxKind;
337
- }
338
- }
339
-
340
- module.exports = C017SemanticAnalyzer;