@sun-asterisk/sunlint 1.3.2 → 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 (59) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -3
  3. package/config/rules/enhanced-rules-registry.json +144 -33
  4. package/core/analysis-orchestrator.js +167 -42
  5. package/core/auto-performance-manager.js +243 -0
  6. package/core/cli-action-handler.js +9 -1
  7. package/core/cli-program.js +19 -5
  8. package/core/constants/defaults.js +56 -0
  9. package/core/performance-optimizer.js +271 -0
  10. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
  11. package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
  12. package/docs/PERFORMANCE.md +311 -0
  13. package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
  14. package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
  15. package/docs/QUICK_FILE_LIMITS.md +64 -0
  16. package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
  17. package/engines/heuristic-engine.js +182 -5
  18. package/package.json +2 -1
  19. package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
  20. package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
  21. package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
  22. package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
  23. package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
  24. package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
  25. package/rules/index.js +2 -0
  26. package/rules/security/S017_use_parameterized_queries/README.md +128 -0
  27. package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
  28. package/rules/security/S017_use_parameterized_queries/config.json +109 -0
  29. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
  30. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
  31. package/rules/security/S031_secure_session_cookies/README.md +127 -0
  32. package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
  33. package/rules/security/S031_secure_session_cookies/config.json +86 -0
  34. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
  35. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
  36. package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
  37. package/rules/security/S032_httponly_session_cookies/README.md +184 -0
  38. package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
  39. package/rules/security/S032_httponly_session_cookies/config.json +96 -0
  40. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
  41. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
  42. package/rules/security/S033_samesite_session_cookies/README.md +227 -0
  43. package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
  44. package/rules/security/S033_samesite_session_cookies/config.json +87 -0
  45. package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
  46. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
  47. package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
  48. package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
  49. package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
  50. package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
  51. package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
  52. package/rules/security/S035_path_session_cookies/README.md +257 -0
  53. package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
  54. package/rules/security/S035_path_session_cookies/config.json +99 -0
  55. package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
  56. package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
  57. package/scripts/batch-processing-demo.js +334 -0
  58. package/scripts/performance-test.js +541 -0
  59. package/scripts/quick-performance-test.js +108 -0
@@ -0,0 +1,128 @@
1
+ # S017 - Always use parameterized queries
2
+
3
+ ## Rule Description
4
+
5
+ Always use parameterized queries instead of string concatenation to build SQL queries. This prevents SQL injection attacks by separating SQL logic from data.
6
+
7
+ ## Risk Level
8
+
9
+ **HIGH** - SQL injection is one of the most critical security vulnerabilities
10
+
11
+ ## Rule Details
12
+
13
+ This rule detects potentially vulnerable SQL query construction patterns:
14
+
15
+ ### ❌ Violations (Dangerous Patterns)
16
+
17
+ ```javascript
18
+ // String concatenation with user input
19
+ const query = "SELECT * FROM users WHERE id = " + userId;
20
+ db.query(query);
21
+
22
+ // Template literals with interpolation
23
+ const sql = `SELECT * FROM products WHERE name = '${productName}'`;
24
+ connection.execute(sql);
25
+
26
+ // Direct variable insertion
27
+ const deleteQuery = "DELETE FROM orders WHERE status = '" + status + "'";
28
+ mysql.query(deleteQuery);
29
+
30
+ // Complex concatenation
31
+ const updateSql =
32
+ "UPDATE users SET name = '" +
33
+ name +
34
+ "', email = '" +
35
+ email +
36
+ "' WHERE id = " +
37
+ id;
38
+ ```
39
+
40
+ ### ✅ Correct Usage (Safe Patterns)
41
+
42
+ ```javascript
43
+ // Using parameterized queries with placeholders
44
+ const query = "SELECT * FROM users WHERE id = ?";
45
+ db.query(query, [userId]);
46
+
47
+ // Using named parameters
48
+ const sql = "SELECT * FROM products WHERE name = $1";
49
+ client.query(sql, [productName]);
50
+
51
+ // Using prepared statements
52
+ const stmt = db.prepare("DELETE FROM orders WHERE status = ?");
53
+ stmt.run(status);
54
+
55
+ // ORM usage (usually safe)
56
+ const user = await User.findOne({ where: { id: userId } });
57
+
58
+ // Using parameter objects
59
+ const updateSql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
60
+ db.query(updateSql, [name, email, id]);
61
+ ```
62
+
63
+ ## Detected Libraries
64
+
65
+ - **Database drivers**: mysql, mysql2, pg, sqlite3, mssql, oracle
66
+ - **ORMs**: sequelize, typeorm, prisma, mongoose
67
+ - **Query builders**: knex, objection
68
+
69
+ ## Security Impact
70
+
71
+ - **SQL Injection attacks**: Malicious SQL code execution
72
+ - **Data breach**: Unauthorized access to sensitive data
73
+ - **Data manipulation**: Unauthorized data modification/deletion
74
+ - **Authentication bypass**: Circumventing login mechanisms
75
+ - **Privilege escalation**: Gaining admin access
76
+
77
+ ## Best Practices
78
+
79
+ 1. **Always use parameterized queries** with placeholders (?, $1, etc.)
80
+ 2. **Use prepared statements** when available
81
+ 3. **Validate and sanitize** all user inputs
82
+ 4. **Use ORMs** that handle parameterization automatically
83
+ 5. **Apply principle of least privilege** for database accounts
84
+ 6. **Regular security audits** of SQL query patterns
85
+
86
+ ## Examples by Library
87
+
88
+ ### MySQL/MySQL2
89
+
90
+ ```javascript
91
+ // ❌ Vulnerable
92
+ const query = `SELECT * FROM users WHERE email = '${email}'`;
93
+ connection.query(query);
94
+
95
+ // ✅ Safe
96
+ const query = "SELECT * FROM users WHERE email = ?";
97
+ connection.query(query, [email]);
98
+ ```
99
+
100
+ ### PostgreSQL (pg)
101
+
102
+ ```javascript
103
+ // ❌ Vulnerable
104
+ const text = `SELECT * FROM users WHERE id = ${id}`;
105
+ client.query(text);
106
+
107
+ // ✅ Safe
108
+ const text = "SELECT * FROM users WHERE id = $1";
109
+ client.query(text, [id]);
110
+ ```
111
+
112
+ ### SQLite
113
+
114
+ ```javascript
115
+ // ❌ Vulnerable
116
+ const sql = "INSERT INTO logs (message) VALUES ('" + message + "')";
117
+ db.run(sql);
118
+
119
+ // ✅ Safe
120
+ const sql = "INSERT INTO logs (message) VALUES (?)";
121
+ db.run(sql, [message]);
122
+ ```
123
+
124
+ ## References
125
+
126
+ - [OWASP SQL Injection Prevention](https://owasp.org/www-community/attacks/SQL_Injection)
127
+ - [CWE-89: SQL Injection](https://cwe.mitre.org/data/definitions/89.html)
128
+ - [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
@@ -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
+ }