@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.
- package/CHANGELOG.md +38 -0
- package/README.md +5 -3
- package/config/rules/enhanced-rules-registry.json +144 -33
- package/core/analysis-orchestrator.js +167 -42
- package/core/auto-performance-manager.js +243 -0
- package/core/cli-action-handler.js +9 -1
- package/core/cli-program.js +19 -5
- package/core/constants/defaults.js +56 -0
- package/core/performance-optimizer.js +271 -0
- package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
- package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
- package/docs/PERFORMANCE.md +311 -0
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
- package/docs/QUICK_FILE_LIMITS.md +64 -0
- package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
- package/engines/heuristic-engine.js +182 -5
- package/package.json +2 -1
- package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
- package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
- package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
- package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
- package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
- package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
- package/rules/index.js +2 -0
- package/rules/security/S017_use_parameterized_queries/README.md +128 -0
- package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
- package/rules/security/S017_use_parameterized_queries/config.json +109 -0
- package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
- package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
- package/rules/security/S031_secure_session_cookies/README.md +127 -0
- package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
- package/rules/security/S031_secure_session_cookies/config.json +86 -0
- package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
- package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
- package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
- package/rules/security/S032_httponly_session_cookies/README.md +184 -0
- package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
- package/rules/security/S032_httponly_session_cookies/config.json +96 -0
- package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
- package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
- package/rules/security/S033_samesite_session_cookies/README.md +227 -0
- package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
- package/rules/security/S033_samesite_session_cookies/config.json +87 -0
- package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
- package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
- package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
- package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
- package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
- package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
- package/rules/security/S035_path_session_cookies/README.md +257 -0
- package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
- package/rules/security/S035_path_session_cookies/config.json +99 -0
- package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
- package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
- package/scripts/batch-processing-demo.js +334 -0
- package/scripts/performance-test.js +541 -0
- 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
|
+
}
|