@sun-asterisk/sunlint 1.3.1 → 1.3.3

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 (120) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/CONTRIBUTING.md +210 -1691
  3. package/README.md +5 -3
  4. package/config/rule-analysis-strategies.js +17 -1
  5. package/config/rules/enhanced-rules-registry.json +506 -1161
  6. package/config/rules/rules-registry-generated.json +1 -1
  7. package/core/analysis-orchestrator.js +167 -42
  8. package/core/auto-performance-manager.js +243 -0
  9. package/core/cli-action-handler.js +9 -1
  10. package/core/cli-program.js +19 -5
  11. package/core/constants/defaults.js +56 -0
  12. package/core/enhanced-rules-registry.js +2 -1
  13. package/core/performance-optimizer.js +271 -0
  14. package/core/semantic-engine.js +15 -3
  15. package/core/semantic-rule-base.js +4 -2
  16. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
  17. package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
  18. package/docs/PERFORMANCE.md +311 -0
  19. package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
  20. package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
  21. package/docs/QUICK_FILE_LIMITS.md +64 -0
  22. package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
  23. package/engines/heuristic-engine.js +247 -9
  24. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  25. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  26. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  27. package/origin-rules/common-en.md +11 -7
  28. package/package.json +2 -1
  29. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  30. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  31. package/rules/common/C006_function_naming/analyzer.js +29 -3
  32. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  33. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  34. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  35. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  36. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  37. package/rules/common/C013_no_dead_code/config.json +61 -0
  38. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  39. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  40. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  41. package/rules/common/C014_dependency_injection/config.json +26 -0
  42. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  43. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  44. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  45. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  46. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  47. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  48. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  49. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  50. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  51. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  52. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  53. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  54. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  55. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  56. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  57. package/rules/common/C035_error_logging_context/analyzer.js +3 -1
  58. package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
  59. package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
  60. package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
  61. package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
  62. package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
  63. package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
  64. package/rules/index.js +7 -1
  65. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  66. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  67. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  68. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  69. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  70. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  71. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  72. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  73. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  74. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  75. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  76. package/rules/security/S017_use_parameterized_queries/README.md +128 -0
  77. package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
  78. package/rules/security/S017_use_parameterized_queries/config.json +109 -0
  79. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
  80. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
  81. package/rules/security/S031_secure_session_cookies/README.md +127 -0
  82. package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
  83. package/rules/security/S031_secure_session_cookies/config.json +86 -0
  84. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
  85. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
  86. package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
  87. package/rules/security/S032_httponly_session_cookies/README.md +184 -0
  88. package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
  89. package/rules/security/S032_httponly_session_cookies/config.json +96 -0
  90. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
  91. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
  92. package/rules/security/S033_samesite_session_cookies/README.md +227 -0
  93. package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
  94. package/rules/security/S033_samesite_session_cookies/config.json +87 -0
  95. package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
  96. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
  97. package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
  98. package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
  99. package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
  100. package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
  101. package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
  102. package/rules/security/S035_path_session_cookies/README.md +257 -0
  103. package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
  104. package/rules/security/S035_path_session_cookies/config.json +99 -0
  105. package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
  106. package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
  107. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  108. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  109. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  110. package/rules/security/S055_content_type_validation/README.md +176 -0
  111. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  112. package/rules/security/S055_content_type_validation/config.json +48 -0
  113. package/rules/utils/rule-helpers.js +140 -1
  114. package/scripts/batch-processing-demo.js +334 -0
  115. package/scripts/consolidate-config.js +116 -0
  116. package/scripts/performance-test.js +541 -0
  117. package/scripts/quick-performance-test.js +108 -0
  118. package/config/rules/S027-categories.json +0 -122
  119. package/config/rules/rules-registry.json +0 -777
  120. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -0,0 +1,286 @@
1
+ /**
2
+ * S017 Main Analyzer - Always use parameterized queries
3
+ * Primary: Symbol-based analysis (when available)
4
+ * Fallback: Regex-based for all other cases
5
+ * Command: node cli.js --rule=S017 --input=examples/rule-test-fixtures/rules/S017_use_parameterized_queries --engine=heuristic
6
+ */
7
+
8
+ const S017SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
+ const S017RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
+
11
+ class S017Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`🔧 [S017] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `🔧 [S017] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S017";
23
+ this.ruleName = "Always use parameterized queries";
24
+ this.description =
25
+ "Always use parameterized queries instead of string concatenation to build SQL queries. This prevents SQL injection attacks by separating SQL logic from data";
26
+ this.semanticEngine = options.semanticEngine || null;
27
+ this.verbose = options.verbose || false;
28
+
29
+ // Configuration
30
+ this.config = {
31
+ useSymbolBased: true, // Primary approach
32
+ fallbackToRegex: true, // Secondary approach
33
+ regexBasedOnly: false, // Can be set to true for pure mode
34
+ };
35
+
36
+ // Initialize analyzers
37
+ try {
38
+ this.symbolAnalyzer = new S017SymbolBasedAnalyzer(this.semanticEngine);
39
+ if (process.env.SUNLINT_DEBUG) {
40
+ console.log(`🔧 [S017] Symbol analyzer created successfully`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`🔧 [S017] Error creating symbol analyzer:`, error);
44
+ }
45
+
46
+ try {
47
+ this.regexAnalyzer = new S017RegexBasedAnalyzer(this.semanticEngine);
48
+ if (process.env.SUNLINT_DEBUG) {
49
+ console.log(`🔧 [S017] Regex analyzer created successfully`);
50
+ }
51
+ } catch (error) {
52
+ console.error(`🔧 [S017] Error creating regex analyzer:`, error);
53
+ }
54
+
55
+ if (process.env.SUNLINT_DEBUG) {
56
+ console.log(`🔧 [S017] Constructor completed`);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Initialize with semantic engine
62
+ */
63
+ async initialize(semanticEngine = null) {
64
+ if (semanticEngine) {
65
+ this.semanticEngine = semanticEngine;
66
+ }
67
+ this.verbose = semanticEngine?.verbose || false;
68
+
69
+ // Initialize both analyzers
70
+ if (this.symbolAnalyzer) {
71
+ await this.symbolAnalyzer.initialize?.(semanticEngine);
72
+ }
73
+ if (this.regexAnalyzer) {
74
+ await this.regexAnalyzer.initialize?.(semanticEngine);
75
+ }
76
+
77
+ // Ensure verbose flag is propagated
78
+ if (this.regexAnalyzer) {
79
+ this.regexAnalyzer.verbose = this.verbose;
80
+ }
81
+ if (this.symbolAnalyzer) {
82
+ this.symbolAnalyzer.verbose = this.verbose;
83
+ }
84
+
85
+ if (this.verbose) {
86
+ console.log(
87
+ `🔧 [S017 Hybrid] Analyzer initialized - verbose: ${this.verbose}`
88
+ );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Single file analysis method for testing
94
+ */
95
+ analyzeSingle(filePath, options = {}) {
96
+ if (process.env.SUNLINT_DEBUG) {
97
+ console.log(`🔍 [S017] analyzeSingle() called for: ${filePath}`);
98
+ }
99
+
100
+ // Return result using same format as analyze method
101
+ return this.analyze([filePath], "typescript", options);
102
+ }
103
+
104
+ async analyze(files, language, options = {}) {
105
+ if (process.env.SUNLINT_DEBUG) {
106
+ console.log(
107
+ `🔧 [S017] analyze() method called with ${files.length} files, language: ${language}`
108
+ );
109
+ }
110
+
111
+ const violations = [];
112
+
113
+ for (const filePath of files) {
114
+ try {
115
+ if (process.env.SUNLINT_DEBUG) {
116
+ console.log(`🔧 [S017] Processing file: ${filePath}`);
117
+ }
118
+
119
+ const fileViolations = await this.analyzeFile(filePath, options);
120
+ violations.push(...fileViolations);
121
+
122
+ if (process.env.SUNLINT_DEBUG) {
123
+ console.log(
124
+ `🔧 [S017] File ${filePath}: Found ${fileViolations.length} violations`
125
+ );
126
+ }
127
+ } catch (error) {
128
+ console.warn(
129
+ `⚠ [S017] Analysis failed for ${filePath}:`,
130
+ error.message
131
+ );
132
+ }
133
+ }
134
+
135
+ if (process.env.SUNLINT_DEBUG) {
136
+ console.log(`🔧 [S017] Total violations found: ${violations.length}`);
137
+ }
138
+
139
+ return violations;
140
+ }
141
+
142
+ async analyzeFile(filePath, options = {}) {
143
+ if (process.env.SUNLINT_DEBUG) {
144
+ console.log(`🔍 [S017] analyzeFile() called for: ${filePath}`);
145
+ }
146
+
147
+ // Create a Set to track unique violations and prevent duplicates
148
+ const violationMap = new Map();
149
+
150
+ // 1. Try Symbol-based analysis first (primary)
151
+ if (
152
+ this.config.useSymbolBased &&
153
+ this.semanticEngine?.project &&
154
+ this.semanticEngine?.initialized
155
+ ) {
156
+ try {
157
+ if (process.env.SUNLINT_DEBUG) {
158
+ console.log(`🔧 [S017] Trying symbol-based analysis...`);
159
+ }
160
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
161
+ if (sourceFile) {
162
+ if (process.env.SUNLINT_DEBUG) {
163
+ console.log(
164
+ `🔧 [S017] Source file found, analyzing with symbol-based...`
165
+ );
166
+ }
167
+
168
+ // Read file content for symbol analyzer
169
+ const fs = require("fs");
170
+ const fileContent = fs.readFileSync(filePath, "utf8");
171
+
172
+ const violations = await this.symbolAnalyzer.analyzeFile(
173
+ filePath,
174
+ fileContent,
175
+ { ...options, verbose: options.verbose }
176
+ );
177
+
178
+ // Add violations to map to deduplicate
179
+ violations.forEach((v) => {
180
+ const key = `${v.line}:${v.column}:${v.message}`;
181
+ if (!violationMap.has(key)) {
182
+ v.analysisStrategy = "symbol-based";
183
+ violationMap.set(key, v);
184
+ }
185
+ });
186
+
187
+ if (process.env.SUNLINT_DEBUG) {
188
+ console.log(
189
+ `✅ [S017] Symbol-based analysis: ${violations.length} violations`
190
+ );
191
+ }
192
+ return Array.from(violationMap.values()); // Return deduplicated violations
193
+ } else {
194
+ if (process.env.SUNLINT_DEBUG) {
195
+ console.log(`⚠️ [S017] Source file not found in project`);
196
+ }
197
+ }
198
+ } catch (error) {
199
+ console.warn(`⚠️ [S017] Symbol analysis failed: ${error.message}`);
200
+ // Continue to fallback
201
+ }
202
+ } else {
203
+ if (process.env.SUNLINT_DEBUG) {
204
+ console.log(`🔄 [S017] Symbol analysis conditions check:`);
205
+ console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
206
+ console.log(` - semanticEngine: ${!!this.semanticEngine}`);
207
+ console.log(
208
+ ` - semanticEngine.project: ${!!this.semanticEngine?.project}`
209
+ );
210
+ console.log(
211
+ ` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`
212
+ );
213
+ console.log(
214
+ `🔄 [S017] Symbol analysis unavailable, using regex fallback`
215
+ );
216
+ }
217
+ }
218
+
219
+ // 2. Fallback to regex-based analysis (only if symbol-based failed or unavailable)
220
+ if (this.config.fallbackToRegex) {
221
+ try {
222
+ if (process.env.SUNLINT_DEBUG) {
223
+ console.log(`🔧 [S017] Trying regex-based analysis...`);
224
+ }
225
+
226
+ // Read file content for regex analyzer
227
+ const fs = require("fs");
228
+ const fileContent = fs.readFileSync(filePath, "utf8");
229
+
230
+ const violations = await this.regexAnalyzer.analyzeFile(
231
+ filePath,
232
+ fileContent,
233
+ options
234
+ );
235
+
236
+ // Add violations to map to deduplicate
237
+ violations.forEach((v) => {
238
+ const key = `${v.line}:${v.column}:${v.message}`;
239
+ if (!violationMap.has(key)) {
240
+ v.analysisStrategy = "regex-fallback";
241
+ violationMap.set(key, v);
242
+ }
243
+ });
244
+
245
+ if (process.env.SUNLINT_DEBUG) {
246
+ console.log(
247
+ `🔄 [S017] Regex-based analysis: ${violations.length} violations`
248
+ );
249
+ }
250
+ return Array.from(violationMap.values()); // Return deduplicated violations
251
+ } catch (error) {
252
+ console.error(`⚠ [S017] Regex analysis failed: ${error.message}`);
253
+ }
254
+ }
255
+
256
+ console.log(`🔧 [S017] No analysis methods succeeded, returning empty`);
257
+ return [];
258
+ }
259
+
260
+ /**
261
+ * Methods for compatibility with different engine invocation patterns
262
+ */
263
+ async analyzeFileWithSymbols(filePath, options = {}) {
264
+ return this.analyzeFile(filePath, options);
265
+ }
266
+
267
+ async analyzeWithSemantics(filePath, options = {}) {
268
+ return this.analyzeFile(filePath, options);
269
+ }
270
+
271
+ /**
272
+ * Get analyzer metadata
273
+ */
274
+ getMetadata() {
275
+ return {
276
+ rule: "S017",
277
+ name: "Always use parameterized queries",
278
+ category: "security",
279
+ type: "hybrid",
280
+ description:
281
+ "Uses symbol-based and regex analysis to detect SQL injection vulnerabilities",
282
+ };
283
+ }
284
+ }
285
+
286
+ module.exports = S017Analyzer;
@@ -0,0 +1,109 @@
1
+ {
2
+ "id": "S017",
3
+ "name": "Always use parameterized queries",
4
+ "category": "security",
5
+ "description": "S017 - Always use parameterized queries instead of string concatenation to build SQL queries. This prevents SQL injection attacks by separating SQL logic from data",
6
+ "severity": "error",
7
+ "enabled": true,
8
+ "semantic": {
9
+ "enabled": true,
10
+ "priority": "high",
11
+ "fallback": "heuristic"
12
+ },
13
+ "patterns": {
14
+ "include": ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
15
+ "exclude": [
16
+ "**/*.test.js",
17
+ "**/*.test.ts",
18
+ "**/*.spec.js",
19
+ "**/*.spec.ts",
20
+ "**/node_modules/**",
21
+ "**/dist/**",
22
+ "**/build/**"
23
+ ]
24
+ },
25
+ "analysis": {
26
+ "approach": "symbol-based-primary",
27
+ "fallback": "regex-based",
28
+ "depth": 2,
29
+ "timeout": 5000
30
+ },
31
+ "validation": {
32
+ "sqlMethods": [
33
+ "query",
34
+ "execute",
35
+ "exec",
36
+ "run",
37
+ "all",
38
+ "get",
39
+ "prepare",
40
+ "createQuery",
41
+ "executeQuery",
42
+ "executeSql",
43
+ "rawQuery"
44
+ ],
45
+ "dangerousPatterns": [
46
+ "SELECT.*\\+",
47
+ "INSERT.*\\+",
48
+ "UPDATE.*\\+",
49
+ "DELETE.*\\+",
50
+ "WHERE.*\\+",
51
+ "ORDER BY.*\\+",
52
+ "GROUP BY.*\\+",
53
+ "HAVING.*\\+",
54
+ "\\$\\{.*\\}",
55
+ "\\`.*\\$\\{.*\\}.*\\`"
56
+ ],
57
+ "sqlKeywords": [
58
+ "SELECT",
59
+ "INSERT",
60
+ "UPDATE",
61
+ "DELETE",
62
+ "DROP",
63
+ "CREATE",
64
+ "ALTER",
65
+ "UNION",
66
+ "WHERE",
67
+ "ORDER BY",
68
+ "GROUP BY",
69
+ "HAVING",
70
+ "FROM",
71
+ "JOIN",
72
+ "INNER JOIN",
73
+ "LEFT JOIN",
74
+ "RIGHT JOIN",
75
+ "FULL JOIN"
76
+ ],
77
+ "databaseLibraries": [
78
+ "mysql",
79
+ "mysql2",
80
+ "pg",
81
+ "postgres",
82
+ "sqlite3",
83
+ "sqlite",
84
+ "mssql",
85
+ "tedious",
86
+ "oracle",
87
+ "mongodb",
88
+ "mongoose",
89
+ "sequelize",
90
+ "typeorm",
91
+ "prisma",
92
+ "knex",
93
+ "objection"
94
+ ],
95
+ "safePatterns": [
96
+ "\\?",
97
+ "\\$1",
98
+ "\\$2",
99
+ "\\$3",
100
+ "\\$4",
101
+ "\\$5",
102
+ "prepare",
103
+ "bind",
104
+ "params",
105
+ "parameters",
106
+ "values"
107
+ ]
108
+ }
109
+ }