xploitscan-shared-rules 1.1.0 → 1.2.1
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/dist/index.cjs +41 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +41 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -269,26 +269,42 @@ var sqlInjection = {
|
|
|
269
269
|
description: "String concatenation or template literals in SQL queries allow attackers to execute arbitrary database commands.",
|
|
270
270
|
check(content, filePath) {
|
|
271
271
|
const patterns = [
|
|
272
|
-
// Template
|
|
273
|
-
/
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
//
|
|
272
|
+
// Template literal passed as first arg to query/execute/raw/sql/
|
|
273
|
+
// queryRaw/queryRawUnsafe/execute. Examples that SHOULD fire:
|
|
274
|
+
// db.query(`SELECT ... ${x}`)
|
|
275
|
+
// prisma.$queryRawUnsafe(`... ${x}`)
|
|
276
|
+
// knex.raw(`... ${x}`)
|
|
277
|
+
/(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*`[^`]*\$\{/gi,
|
|
278
|
+
// String concatenation in SQL — now includes raw() and sql() and
|
|
279
|
+
// Drizzle's sql.raw() / Prisma's $queryRawUnsafe() variants. Previous
|
|
280
|
+
// version only covered query()/execute() and missed knex.raw("..." + x).
|
|
281
|
+
//
|
|
282
|
+
// The string literal part uses a proper "quoted string" regex that
|
|
283
|
+
// allows the opposite quote type inside (common in SQL — "WHERE x = 'a'").
|
|
284
|
+
// The older `[^"']*` class bailed out at the first inner quote and
|
|
285
|
+
// missed every realistic SQL-containing concat.
|
|
286
|
+
/(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*\+/gi,
|
|
287
|
+
// Direct variable interpolation in a SQL verb context
|
|
277
288
|
/(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\s+.*\$\{(?!.*parameterized)/gi
|
|
278
289
|
];
|
|
279
290
|
const matches = [];
|
|
280
291
|
const usesParams = /\?\s*,|\$\d+|:[\w]+|\bprepare\b|\bplaceholder\b/i.test(content);
|
|
281
292
|
if (usesParams) return [];
|
|
282
293
|
for (const pattern of patterns) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
() => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
|
|
290
|
-
)
|
|
294
|
+
const raw = findMatches(
|
|
295
|
+
content,
|
|
296
|
+
pattern,
|
|
297
|
+
sqlInjection,
|
|
298
|
+
filePath,
|
|
299
|
+
() => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
|
|
291
300
|
);
|
|
301
|
+
for (const m of raw) {
|
|
302
|
+
const lineText = content.split("\n")[m.line - 1] || "";
|
|
303
|
+
if (/\bPrisma\.sql\s*`/.test(lineText)) continue;
|
|
304
|
+
if (/\bsql\s*`/.test(lineText) && !/\bsql\.raw\s*\(/.test(lineText)) continue;
|
|
305
|
+
if (/\$queryRaw\s*\(\s*Prisma\.sql|\$executeRaw\s*\(\s*Prisma\.sql/.test(lineText)) continue;
|
|
306
|
+
matches.push(m);
|
|
307
|
+
}
|
|
292
308
|
}
|
|
293
309
|
return matches;
|
|
294
310
|
}
|
|
@@ -2414,7 +2430,7 @@ function calculateGrade(findings, _totalFiles) {
|
|
|
2414
2430
|
else if (medium >= 1) grade = capGrade("A");
|
|
2415
2431
|
let summary;
|
|
2416
2432
|
if (critical > 0) {
|
|
2417
|
-
summary = `${critical} critical ${critical === 1 ? "vulnerability" : "vulnerabilities"}
|
|
2433
|
+
summary = `${critical} critical ${critical === 1 ? "vulnerability requires" : "vulnerabilities require"} immediate attention.`;
|
|
2418
2434
|
} else if (high >= 3) {
|
|
2419
2435
|
summary = `${high} high-severity issues require urgent attention.`;
|
|
2420
2436
|
} else if (high > 0) {
|
|
@@ -2829,13 +2845,22 @@ var commandInjection = {
|
|
|
2829
2845
|
// Node.js — require standalone exec/execSync, not db.exec() or conn.exec()
|
|
2830
2846
|
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
2831
2847
|
/child_process.*exec\s*\(\s*(?!["'`])/g,
|
|
2848
|
+
// spawn / execFile / exec with shell: true AND a template literal
|
|
2849
|
+
// first arg. `shell: true` converts the first argument into a string
|
|
2850
|
+
// passed to `/bin/sh -c`, so any ${} interpolation is a shell injection
|
|
2851
|
+
// opportunity. Without shell: true, spawn/execFile are safe because
|
|
2852
|
+
// the command and args are kept separate. Previously this class of
|
|
2853
|
+
// bug was missed — the "hasSafe" skip below assumed any spawn in the
|
|
2854
|
+
// file was fine, which is the opposite of true with shell: true.
|
|
2855
|
+
/(?<![.\w])(?:spawn|spawnSync|execFile|execFileSync|exec|execSync)\s*\(\s*`[^`]*\$\{[\s\S]*?shell\s*:\s*(?:true|["'][^"']+["'])/gi,
|
|
2832
2856
|
// Python
|
|
2833
2857
|
/os\.system\s*\(\s*(?!["'`].*["'`]\s*\))/g,
|
|
2834
2858
|
/subprocess\.(?:call|run|Popen)\s*\([^)]*shell\s*=\s*True/gi,
|
|
2835
2859
|
// Ruby
|
|
2836
2860
|
/system\s*\(\s*["'].*#\{/g
|
|
2837
2861
|
];
|
|
2838
|
-
const
|
|
2862
|
+
const hasShellTrue = /shell\s*:\s*(?:true|["'][^"']+["'])/i.test(content);
|
|
2863
|
+
const hasSafe = !hasShellTrue && /execFile|spawn|escapeshellarg|shlex\.quote|shellEscape/i.test(content);
|
|
2839
2864
|
if (hasSafe) return [];
|
|
2840
2865
|
for (const p of patterns) {
|
|
2841
2866
|
const raw = findMatches(
|
|
@@ -4336,7 +4361,7 @@ var hardcodedSupabaseServiceRole = {
|
|
|
4336
4361
|
if (isTestFile(filePath)) return [];
|
|
4337
4362
|
if (!SECRET_FILE_EXT.test(filePath)) return [];
|
|
4338
4363
|
if (LOCK_FILE_RE.test(filePath)) return [];
|
|
4339
|
-
const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\s*[:=]\s*["'`](eyJ[A-Za-z0-9_
|
|
4364
|
+
const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\s*[:=]\s*["'`](eyJ[A-Za-z0-9_\-.]{50,})["'`]/gi;
|
|
4340
4365
|
const findings = [];
|
|
4341
4366
|
let m;
|
|
4342
4367
|
while ((m = pattern.exec(content)) !== null) {
|