xploitscan 1.0.17 → 1.0.19

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.js CHANGED
@@ -540,26 +540,42 @@ var sqlInjection = {
540
540
  description: "String concatenation or template literals in SQL queries allow attackers to execute arbitrary database commands.",
541
541
  check(content, filePath) {
542
542
  const patterns = [
543
- // Template literals in SQL
544
- /(?:query|execute|raw|sql)\s*\(\s*`[^`]*\$\{/gi,
545
- // String concatenation in SQL
546
- /(?:query|execute)\s*\(\s*["'][^"']*["']\s*\+/gi,
547
- // Direct variable interpolation
543
+ // Template literal passed as first arg to query/execute/raw/sql/
544
+ // queryRaw/queryRawUnsafe/execute. Examples that SHOULD fire:
545
+ // db.query(`SELECT ... ${x}`)
546
+ // prisma.$queryRawUnsafe(`... ${x}`)
547
+ // knex.raw(`... ${x}`)
548
+ /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*`[^`]*\$\{/gi,
549
+ // String concatenation in SQL — now includes raw() and sql() and
550
+ // Drizzle's sql.raw() / Prisma's $queryRawUnsafe() variants. Previous
551
+ // version only covered query()/execute() and missed knex.raw("..." + x).
552
+ //
553
+ // The string literal part uses a proper "quoted string" regex that
554
+ // allows the opposite quote type inside (common in SQL — "WHERE x = 'a'").
555
+ // The older `[^"']*` class bailed out at the first inner quote and
556
+ // missed every realistic SQL-containing concat.
557
+ /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*\+/gi,
558
+ // Direct variable interpolation in a SQL verb context
548
559
  /(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\s+.*\$\{(?!.*parameterized)/gi
549
560
  ];
550
561
  const matches = [];
551
562
  const usesParams = /\?\s*,|\$\d+|:[\w]+|\bprepare\b|\bplaceholder\b/i.test(content);
552
563
  if (usesParams) return [];
553
564
  for (const pattern of patterns) {
554
- matches.push(
555
- ...findMatches(
556
- content,
557
- pattern,
558
- sqlInjection,
559
- filePath,
560
- () => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
561
- )
565
+ const raw = findMatches(
566
+ content,
567
+ pattern,
568
+ sqlInjection,
569
+ filePath,
570
+ () => "Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])"
562
571
  );
572
+ for (const m of raw) {
573
+ const lineText = content.split("\n")[m.line - 1] || "";
574
+ if (/\bPrisma\.sql\s*`/.test(lineText)) continue;
575
+ if (/\bsql\s*`/.test(lineText) && !/\bsql\.raw\s*\(/.test(lineText)) continue;
576
+ if (/\$queryRaw\s*\(\s*Prisma\.sql|\$executeRaw\s*\(\s*Prisma\.sql/.test(lineText)) continue;
577
+ matches.push(m);
578
+ }
563
579
  }
564
580
  return matches;
565
581
  }
@@ -4127,7 +4143,7 @@ async function cursorInstallCommand(opts = {}) {
4127
4143
  var program = new Command();
4128
4144
  program.name("xploitscan").description(
4129
4145
  "AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do."
4130
- ).version("1.0.8");
4146
+ ).version("1.0.19");
4131
4147
  program.command("scan").description("Scan a directory for security vulnerabilities").argument("[directory]", "Directory to scan", ".").option("--no-ai", "Skip AI-powered analysis").option("-f, --format <format>", "Output format: terminal, json, sarif", "terminal").option("-v, --verbose", "Show detailed output", false).option("--diff [base]", "Scan only files changed vs base branch (default: main)").option("-w, --watch", "Watch for file changes and re-scan automatically", false).action(async (directory, opts) => {
4132
4148
  await scanCommand(directory, {
4133
4149
  directory,