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.cjs
CHANGED
|
@@ -472,26 +472,42 @@ var sqlInjection = {
|
|
|
472
472
|
description: "String concatenation or template literals in SQL queries allow attackers to execute arbitrary database commands.",
|
|
473
473
|
check(content, filePath) {
|
|
474
474
|
const patterns = [
|
|
475
|
-
// Template
|
|
476
|
-
/
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
//
|
|
475
|
+
// Template literal passed as first arg to query/execute/raw/sql/
|
|
476
|
+
// queryRaw/queryRawUnsafe/execute. Examples that SHOULD fire:
|
|
477
|
+
// db.query(`SELECT ... ${x}`)
|
|
478
|
+
// prisma.$queryRawUnsafe(`... ${x}`)
|
|
479
|
+
// knex.raw(`... ${x}`)
|
|
480
|
+
/(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*`[^`]*\$\{/gi,
|
|
481
|
+
// String concatenation in SQL — now includes raw() and sql() and
|
|
482
|
+
// Drizzle's sql.raw() / Prisma's $queryRawUnsafe() variants. Previous
|
|
483
|
+
// version only covered query()/execute() and missed knex.raw("..." + x).
|
|
484
|
+
//
|
|
485
|
+
// The string literal part uses a proper "quoted string" regex that
|
|
486
|
+
// allows the opposite quote type inside (common in SQL — "WHERE x = 'a'").
|
|
487
|
+
// The older `[^"']*` class bailed out at the first inner quote and
|
|
488
|
+
// missed every realistic SQL-containing concat.
|
|
489
|
+
/(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*\+/gi,
|
|
490
|
+
// Direct variable interpolation in a SQL verb context
|
|
480
491
|
/(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\s+.*\$\{(?!.*parameterized)/gi
|
|
481
492
|
];
|
|
482
493
|
const matches = [];
|
|
483
494
|
const usesParams = /\?\s*,|\$\d+|:[\w]+|\bprepare\b|\bplaceholder\b/i.test(content);
|
|
484
495
|
if (usesParams) return [];
|
|
485
496
|
for (const pattern of patterns) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
() => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
|
|
493
|
-
)
|
|
497
|
+
const raw = findMatches(
|
|
498
|
+
content,
|
|
499
|
+
pattern,
|
|
500
|
+
sqlInjection,
|
|
501
|
+
filePath,
|
|
502
|
+
() => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
|
|
494
503
|
);
|
|
504
|
+
for (const m of raw) {
|
|
505
|
+
const lineText = content.split("\n")[m.line - 1] || "";
|
|
506
|
+
if (/\bPrisma\.sql\s*`/.test(lineText)) continue;
|
|
507
|
+
if (/\bsql\s*`/.test(lineText) && !/\bsql\.raw\s*\(/.test(lineText)) continue;
|
|
508
|
+
if (/\$queryRaw\s*\(\s*Prisma\.sql|\$executeRaw\s*\(\s*Prisma\.sql/.test(lineText)) continue;
|
|
509
|
+
matches.push(m);
|
|
510
|
+
}
|
|
495
511
|
}
|
|
496
512
|
return matches;
|
|
497
513
|
}
|
|
@@ -2617,7 +2633,7 @@ function calculateGrade(findings, _totalFiles) {
|
|
|
2617
2633
|
else if (medium >= 1) grade = capGrade("A");
|
|
2618
2634
|
let summary;
|
|
2619
2635
|
if (critical > 0) {
|
|
2620
|
-
summary = `${critical} critical ${critical === 1 ? "vulnerability" : "vulnerabilities"}
|
|
2636
|
+
summary = `${critical} critical ${critical === 1 ? "vulnerability requires" : "vulnerabilities require"} immediate attention.`;
|
|
2621
2637
|
} else if (high >= 3) {
|
|
2622
2638
|
summary = `${high} high-severity issues require urgent attention.`;
|
|
2623
2639
|
} else if (high > 0) {
|
|
@@ -3032,13 +3048,22 @@ var commandInjection = {
|
|
|
3032
3048
|
// Node.js — require standalone exec/execSync, not db.exec() or conn.exec()
|
|
3033
3049
|
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
3034
3050
|
/child_process.*exec\s*\(\s*(?!["'`])/g,
|
|
3051
|
+
// spawn / execFile / exec with shell: true AND a template literal
|
|
3052
|
+
// first arg. `shell: true` converts the first argument into a string
|
|
3053
|
+
// passed to `/bin/sh -c`, so any ${} interpolation is a shell injection
|
|
3054
|
+
// opportunity. Without shell: true, spawn/execFile are safe because
|
|
3055
|
+
// the command and args are kept separate. Previously this class of
|
|
3056
|
+
// bug was missed — the "hasSafe" skip below assumed any spawn in the
|
|
3057
|
+
// file was fine, which is the opposite of true with shell: true.
|
|
3058
|
+
/(?<![.\w])(?:spawn|spawnSync|execFile|execFileSync|exec|execSync)\s*\(\s*`[^`]*\$\{[\s\S]*?shell\s*:\s*(?:true|["'][^"']+["'])/gi,
|
|
3035
3059
|
// Python
|
|
3036
3060
|
/os\.system\s*\(\s*(?!["'`].*["'`]\s*\))/g,
|
|
3037
3061
|
/subprocess\.(?:call|run|Popen)\s*\([^)]*shell\s*=\s*True/gi,
|
|
3038
3062
|
// Ruby
|
|
3039
3063
|
/system\s*\(\s*["'].*#\{/g
|
|
3040
3064
|
];
|
|
3041
|
-
const
|
|
3065
|
+
const hasShellTrue = /shell\s*:\s*(?:true|["'][^"']+["'])/i.test(content);
|
|
3066
|
+
const hasSafe = !hasShellTrue && /execFile|spawn|escapeshellarg|shlex\.quote|shellEscape/i.test(content);
|
|
3042
3067
|
if (hasSafe) return [];
|
|
3043
3068
|
for (const p of patterns) {
|
|
3044
3069
|
const raw = findMatches(
|
|
@@ -4539,7 +4564,7 @@ var hardcodedSupabaseServiceRole = {
|
|
|
4539
4564
|
if (isTestFile(filePath)) return [];
|
|
4540
4565
|
if (!SECRET_FILE_EXT.test(filePath)) return [];
|
|
4541
4566
|
if (LOCK_FILE_RE.test(filePath)) return [];
|
|
4542
|
-
const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\s*[:=]\s*["'`](eyJ[A-Za-z0-9_
|
|
4567
|
+
const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\s*[:=]\s*["'`](eyJ[A-Za-z0-9_\-.]{50,})["'`]/gi;
|
|
4543
4568
|
const findings = [];
|
|
4544
4569
|
let m;
|
|
4545
4570
|
while ((m = pattern.exec(content)) !== null) {
|