xploitscan-shared-rules 1.2.1 → 1.3.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/snippet.ts","../src/rules.ts","../src/ai-fp-filter.ts","../src/entropy-scanner.ts"],"sourcesContent":["/**\n * xploitscan-shared-rules\n *\n * Single source of truth for XploitScan's custom security rules. Both the CLI\n * (packages/cli) and the web API (packages/api) import from here so the rule\n * definitions, compliance mappings, and runner logic can't drift between them.\n *\n * The named exports include:\n * - All 158 individual rule objects (e.g. `hardcodedSecrets`, `stripeWebhookUnprotected`)\n * - `freeRules`: the 30 rules bundled into the free CLI\n * - `allRules`: alias for freeRules (used by older call sites)\n * - `complianceMap`: VC### → { owasp, cwe } mapping\n * - `runCustomRules`: the scanner entry point\n * - Types: `CustomRule`, `Finding`, `RuleMatch`, `Severity`, `Confidence`\n * - Helpers: `getSnippet`\n */\n\nexport * from \"./types.js\";\nexport { getSnippet } from \"./snippet.js\";\nexport * from \"./rules.js\";\nexport {\n filterFalsePositives,\n type AIFilterResult,\n type FilteredFinding,\n} from \"./ai-fp-filter.js\";\nexport { scanEntropy } from \"./entropy-scanner.js\";\n","/**\n * Return a small code snippet around a given line number with a `>` marker on the matched line.\n * Pure string manipulation — no fs dependency, so this is safe to use in both the CLI\n * and in serverless/edge environments like the web API.\n */\nexport function getSnippet(\n content: string,\n line: number,\n contextLines = 2,\n): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n\n return lines\n .slice(start, end)\n .map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">\" : \" \";\n return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n","import type { CustomRule, Finding, RuleMatch } from \"./types.js\";\nimport { getSnippet } from \"./snippet.js\";\n\n// ────────────────────────────────────────────\n// GLOBAL PRE-FILTERS\n// Reduces false positives before any rule runs\n// ────────────────────────────────────────────\n\n// Broad test/mock/fixture file detection\nconst TEST_FILE_PATTERN = /(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|\\.stories\\.|\\.story\\.|\\/test\\/|\\/tests\\/|\\/fixtures?\\/|\\/mocks?\\/|\\.mock\\.|test-utils|testing|\\.cy\\.|\\.e2e\\.)/i;\n\nfunction isTestFile(filePath: string): boolean {\n return TEST_FILE_PATTERN.test(filePath);\n}\n\n// Check if a match falls on a comment line (JS/TS/Python/Ruby/YAML/HTML)\nfunction isCommentLine(content: string, matchIndex: number): boolean {\n const lineStart = content.lastIndexOf(\"\\n\", matchIndex - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", matchIndex)).trimStart();\n return (\n lineText.startsWith(\"//\") ||\n lineText.startsWith(\"#\") ||\n lineText.startsWith(\"*\") ||\n lineText.startsWith(\"/*\") ||\n lineText.startsWith(\"<!--\") ||\n lineText.startsWith(\"'\") && lineText.length > 1 && /\\.(vb|bas)$/i.test(\"\") // VB comments\n );\n}\n\n// Check if a match is inside a string literal that's a fix/description message\n// (e.g., inside a findMatches callback or rule description)\nfunction isInsideFixMessage(content: string, matchIndex: number): boolean {\n const lineStart = content.lastIndexOf(\"\\n\", matchIndex - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", matchIndex));\n // Skip if the line looks like a fix suggestion string or rule description\n return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\\s*[:=(]/i.test(lineText) ||\n /return\\s*[\"'`].*(?:Use|Replace|Add|Move|Set|Enable|Disable|Never|Don't|Do not|Instead)/i.test(lineText);\n}\n\n// Context-aware matching: checks if mitigation exists within N lines after the match\nfunction hasMitigationNearby(content: string, matchIndex: number, mitigationPattern: RegExp, linesAhead: number = 5): boolean {\n const lines = content.split(\"\\n\");\n const matchLine = content.substring(0, matchIndex).split(\"\\n\").length - 1;\n const endLine = Math.min(matchLine + linesAhead, lines.length - 1);\n const nearbyContent = lines.slice(matchLine, endLine + 1).join(\"\\n\");\n return mitigationPattern.test(nearbyContent);\n}\n\n// Helper to find all regex matches with line numbers\n// Automatically skips matches on comment lines and fix messages\nfunction findMatches(\n content: string,\n pattern: RegExp,\n rule: Omit<CustomRule, \"check\">,\n filePath: string,\n fixTemplate?: (match: RegExpExecArray) => string,\n): RuleMatch[] {\n const matches: RuleMatch[] = [];\n const lines = content.split(\"\\n\");\n let m: RegExpExecArray | null;\n const re = new RegExp(pattern.source, pattern.flags.includes(\"g\") ? pattern.flags : `${pattern.flags}g`);\n\n while ((m = re.exec(content)) !== null) {\n // Skip matches on comment lines\n if (isCommentLine(content, m.index)) continue;\n // Skip matches inside fix/description strings\n if (isInsideFixMessage(content, m.index)) continue;\n\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: rule.id,\n title: rule.title,\n severity: rule.severity,\n category: rule.category,\n file: filePath,\n line: lineNum,\n snippet: getSnippet(content, lineNum),\n fix: fixTemplate?.(m),\n });\n }\n\n return matches;\n}\n\n// ────────────────────────────────────────────\n// RULE DEFINITIONS\n// ────────────────────────────────────────────\n\nexport const hardcodedSecrets: CustomRule = {\n id: \"VC001\",\n title: \"Hardcoded API Key or Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"API keys, tokens, or secrets hardcoded in source code can be extracted by anyone with access to the code.\",\n check(content, filePath) {\n // Skip .env.example, template, test, and documentation files\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (isTestFile(filePath)) return [];\n if (filePath.match(/\\.(md|txt|rst|adoc)$/)) return [];\n\n const patterns = [\n // Generic API key patterns — require actual value assignment, not variable declarations\n /(?:api[_-]?key|apikey|api[_-]?secret)\\s*[:=]\\s*[\"'`]([a-zA-Z0-9_\\-]{20,})[\"'`]/gi,\n // AWS keys\n /(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/g,\n // Stripe keys\n /(?:sk_live|pk_live|sk_test|pk_test)_[a-zA-Z0-9]{20,}/g,\n // Supabase anon/service keys (JWT format)\n /(?:supabase[_-]?(?:anon|service)[_-]?key|SUPABASE_(?:ANON|SERVICE_ROLE)_KEY)\\s*[:=]\\s*[\"'`](eyJ[a-zA-Z0-9_-]{50,})[\"'`]/gi,\n // OpenAI keys\n /sk-[a-zA-Z0-9]{20,}T3BlbkFJ[a-zA-Z0-9]{20,}/g,\n // Generic tokens in assignments — require standalone word and longer min length\n /(?:^|[\\s,({])(?:token|secret|password|passwd|pwd)\\s*[:=]\\s*[\"'`]([a-zA-Z0-9_\\-!@#$%^&*]{20,})[\"'`]/gim,\n // Private keys\n /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g,\n // Database URLs with credentials\n /(?:postgres|mysql|mongodb(?:\\+srv)?):\\/\\/[^:]+:[^@]+@[^/\\s\"'`]+/gi,\n ];\n\n // Pattern-specific fix messages with risk context\n const fixMessages: string[] = [\n \"Move this API key to an environment variable. If this key has been committed, rotate it immediately — it may have already been scraped by bots.\",\n \"AWS access key detected — may grant full account access (EC2, S3, IAM, billing). Rotate immediately in AWS Console → IAM → Security Credentials. Use IAM roles or environment variables instead.\",\n \"Stripe key detected. sk_live_ keys can process real charges, issue refunds, and access customer payment data. sk_test_ keys are lower risk but should still not be committed. Rotate in Stripe Dashboard → Developers → API Keys.\",\n \"Supabase key detected. Service role keys bypass Row Level Security and grant full database read/write access. Move to a server-side environment variable immediately.\",\n \"OpenAI API key detected — grants full API access and can incur charges. Rotate at platform.openai.com → API Keys.\",\n \"Hardcoded token or password detected. Move to an environment variable and rotate the credential if it has been committed to version control.\",\n \"Private key found in source code. If this has been committed to version control, consider the key compromised — generate a new key pair and revoke the old one.\",\n \"Database credentials in connection string. An attacker with this URL has full database access. Move to an environment variable, restrict network access, and rotate the password.\",\n ];\n\n const matches: RuleMatch[] = [];\n for (let pi = 0; pi < patterns.length; pi++) {\n const pattern = patterns[pi];\n const rawMatches = findMatches(content, pattern, hardcodedSecrets, filePath, () => fixMessages[pi]);\n // Skip matches where the matched secret value is less than 12 characters\n for (const rm of rawMatches) {\n const lineText = content.split(\"\\n\")[rm.line - 1] || \"\";\n // Skip comment lines (additional guard beyond findMatches)\n const trimmed = lineText.trimStart();\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"#\")) continue;\n // Extract the secret value from the match and skip short ones\n const secretMatch = lineText.match(/[:=]\\s*[\"'`]([^\"'`]*)[\"'`]/);\n if (secretMatch && secretMatch[1].length < 12) continue;\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\nexport const exposedEnvFile: CustomRule = {\n id: \"VC002\",\n title: \"Environment File May Be Committed\",\n severity: \"high\",\n category: \"Secrets\",\n description: \".env files containing secrets may be committed to version control.\",\n check(content, filePath) {\n // Only applies to .env files (not .env.example)\n if (!filePath.match(/\\.env(?:\\.[a-z]+)?$/) || filePath.includes(\"example\")) return [];\n\n const hasSecrets = /(?:KEY|SECRET|TOKEN|PASSWORD|PRIVATE|DATABASE_URL)\\s*=/i.test(content);\n if (!hasSecrets) return [];\n\n return [{\n rule: \"VC002\",\n title: exposedEnvFile.title,\n severity: \"high\",\n category: \"Secrets\",\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: 'Add \".env*\" to your .gitignore file and remove this file from git history with: git rm --cached ' + filePath,\n }];\n },\n};\n\nexport const missingAuthMiddleware: CustomRule = {\n id: \"VC003\",\n title: \"API Route Missing Authentication\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"API routes without authentication checks allow unauthorized access.\",\n check(content, filePath) {\n // Only check API route files\n const isApiRoute = /(?:\\/api\\/|routes?\\/|controllers?\\/|endpoints?\\/)/.test(filePath) ||\n filePath.includes(\"server.\");\n if (!isApiRoute) return [];\n\n // Look for route handlers without auth checks\n const routePatterns = [\n // Express/Hono style\n /\\.(get|post|put|patch|delete)\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*(?:async\\s+)?\\(?(?:req|c|ctx)/gi,\n // Next.js API routes\n /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|PATCH|DELETE)\\s*\\(/gi,\n ];\n\n // Skip test files\n if (isTestFile(filePath)) return [];\n // Skip non-code files\n if (filePath.match(/\\.(md|txt|rst|html|css|json|yaml|yml)$/)) return [];\n\n // Skip routes that are intentionally unauthenticated\n const isAuthRoute = /\\/auth\\/|\\/login|\\/signup|\\/register|\\/logout|\\/password\\/|\\/forgot|\\/reset/i.test(filePath);\n if (isAuthRoute) return [];\n\n // Skip webhook receivers (they use their own auth: HMAC, shared secrets, etc.)\n const isWebhookRoute = /\\/webhook/i.test(filePath);\n if (isWebhookRoute) return [];\n\n const authPatterns = [\n /auth/i, /session/i, /jwt/i, /bearer/i, /middleware/i,\n /getUser/i, /currentUser/i, /isAuthenticated/i, /requireAuth/i,\n /requireUser/i, /requireUserForApi/i,\n /clerk/i, /supabase\\.auth/i, /getServerSession/i, /getToken/i,\n /protect/i, /guard/i, /verifyToken/i, /validateToken/i, /withAuth/i,\n /passport/i, /firebase\\.auth/i, /cognito/i,\n /verifyCronSecret/i, /verifySecret/i, /checkApiKey/i,\n ];\n\n const hasAuth = authPatterns.some((p) => p.test(content));\n if (hasAuth) return [];\n\n const matches: RuleMatch[] = [];\n for (const pattern of routePatterns) {\n matches.push(\n ...findMatches(content, pattern, missingAuthMiddleware, filePath, () =>\n \"Add authentication middleware to protect this route. Check the user's session/token before processing the request.\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const supabaseNoRLS: CustomRule = {\n id: \"VC004\",\n title: \"Supabase Client Without Row Level Security\",\n severity: \"critical\",\n category: \"Authorization\",\n description: \"Using Supabase with the service role key or bypassing RLS exposes all database rows to any user.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n\n // Service role key used in client-side code\n if (\n /supabase_service_role|service_role_key/i.test(content) &&\n (/[\"']use client[\"']/.test(content) || filePath.match(/\\.(jsx|tsx|vue|svelte)$/))\n ) {\n matches.push(\n ...findMatches(\n content,\n /service_role/gi,\n supabaseNoRLS,\n filePath,\n () => \"Never expose the service_role key in client-side code. Use the anon key with RLS policies instead.\",\n ),\n );\n }\n\n // .rpc() or direct table access without .auth\n if (/createClient/i.test(content) && /\\.from\\(/.test(content)) {\n const hasRLSBypass = /\\.rpc\\(|auth\\.admin|service_role/i.test(content);\n if (hasRLSBypass) {\n matches.push(\n ...findMatches(\n content,\n /\\.rpc\\(|auth\\.admin/gi,\n { ...supabaseNoRLS, title: \"Supabase RLS Bypass Detected\" },\n filePath,\n () => \"Ensure RLS policies are enabled on all tables and avoid bypassing them with service_role or admin methods in user-facing code.\",\n ),\n );\n }\n }\n\n return matches;\n },\n};\n\nexport const stripeWebhookUnprotected: CustomRule = {\n id: \"VC005\",\n title: \"Unprotected Stripe Webhook Endpoint\",\n severity: \"critical\",\n category: \"Payment Security\",\n description: \"Stripe webhook endpoints without signature verification allow attackers to fake payment events.\",\n check(content, filePath) {\n // Only scan code files, never docs/README/markdown/lock files\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n\n // Must actually reference Stripe (not just generic \"webhook\")\n if (!/stripe/i.test(content)) return [];\n\n // Must have a webhook handler pattern (route definition or event handling)\n const hasStripeWebhookHandler =\n /(?:stripe.*webhook|webhook.*stripe)/i.test(content) ||\n /(?:checkout\\.session\\.completed|invoice\\.paid|payment_intent|customer\\.subscription)/i.test(content);\n if (!hasStripeWebhookHandler) return [];\n\n // Already has signature verification — safe\n if (/constructEvent|verifyHeader|stripe[_-]?signature|webhook[_-]?secret|STRIPE_WEBHOOK_SECRET/i.test(content)) return [];\n\n // Find the actual route handler, not every mention of \"webhook\"\n const handlerPatterns = [\n // POST handler that processes Stripe events\n /export\\s+(?:async\\s+)?function\\s+POST\\s*\\(/g,\n // Express-style Stripe webhook route\n /\\.(post|all)\\s*\\(\\s*[\"'`][^\"'`]*(?:stripe|webhook)[^\"'`]*[\"'`]/gi,\n // Event type checking without prior verification\n /(?:event\\.type|req\\.body\\.type)\\s*===?\\s*[\"'`](?:checkout|invoice|payment|customer)\\./g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of handlerPatterns) {\n matches.push(\n ...findMatches(content, pattern, stripeWebhookUnprotected, filePath, () =>\n \"Verify the Stripe webhook signature using stripe.webhooks.constructEvent(body, sig, webhookSecret) to prevent forged payment events.\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const sqlInjection: CustomRule = {\n id: \"VC006\",\n title: \"Potential SQL Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"String concatenation or template literals in SQL queries allow attackers to execute arbitrary database commands.\",\n check(content, filePath) {\n const patterns = [\n // Template literal passed as first arg to query/execute/raw/sql/\n // queryRaw/queryRawUnsafe/execute. Examples that SHOULD fire:\n // db.query(`SELECT ... ${x}`)\n // prisma.$queryRawUnsafe(`... ${x}`)\n // knex.raw(`... ${x}`)\n /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\\s*\\(\\s*`[^`]*\\$\\{/gi,\n // String concatenation in SQL — now includes raw() and sql() and\n // Drizzle's sql.raw() / Prisma's $queryRawUnsafe() variants. Previous\n // version only covered query()/execute() and missed knex.raw(\"...\" + x).\n //\n // The string literal part uses a proper \"quoted string\" regex that\n // allows the opposite quote type inside (common in SQL — \"WHERE x = 'a'\").\n // The older `[^\"']*` class bailed out at the first inner quote and\n // missed every realistic SQL-containing concat.\n /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\\s*\\(\\s*(?:\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*')\\s*\\+/gi,\n // Direct variable interpolation in a SQL verb context\n /(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\\s+.*\\$\\{(?!.*parameterized)/gi,\n ];\n\n const matches: RuleMatch[] = [];\n\n // Skip if using parameterized queries / prepared statements\n const usesParams = /\\?\\s*,|\\$\\d+|:[\\w]+|\\bprepare\\b|\\bplaceholder\\b/i.test(content);\n if (usesParams) return [];\n\n for (const pattern of patterns) {\n const raw = findMatches(content, pattern, sqlInjection, filePath, () =>\n \"Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])\"\n );\n\n // Filter out safe tagged-template forms. These look like `Prisma.sql`...``\n // or Drizzle's `sql\\`...\\`` — both bind ${...} as query parameters\n // rather than concatenating. Previously the VC006 regex fired a FP\n // when these were nested inside `prisma.$queryRaw(Prisma.sql\\`...\\`)`\n // because the outer call's backtick/template looked like a dynamic SQL\n // string.\n //\n // Heuristic: if the line contains Prisma.sql`` or Drizzle's sql`` OR\n // the enclosing call is a KNOWN SAFE wrapper (e.g. $queryRaw WITHOUT\n // \"Unsafe\" suffix), skip the finding.\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n // Prisma.sql tagged template — safe\n if (/\\bPrisma\\.sql\\s*`/.test(lineText)) continue;\n // Drizzle's sql`...` tagged template — safe (NOT sql.raw(), which is\n // unsafe and flagged by a different pattern above)\n if (/\\bsql\\s*`/.test(lineText) && !/\\bsql\\.raw\\s*\\(/.test(lineText)) continue;\n // Safe Prisma helpers — $queryRaw/$executeRaw (without Unsafe suffix)\n // always take a Prisma.sql tagged template; if the dev used one of\n // the unsafe variants the regex above will still flag them.\n if (/\\$queryRaw\\s*\\(\\s*Prisma\\.sql|\\$executeRaw\\s*\\(\\s*Prisma\\.sql/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\nexport const xssVulnerability: CustomRule = {\n id: \"VC007\",\n title: \"Potential Cross-Site Scripting (XSS)\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Rendering user input without sanitization allows attackers to inject malicious scripts.\",\n check(content, filePath) {\n // Skip if file is a sanitizer utility or already uses DOMPurify\n if (/(?:sanitize|purify|escape|xss)/i.test(filePath)) return [];\n if (/DOMPurify\\.sanitize|sanitizeHtml|xss\\(|escapeHtml/i.test(content)) return [];\n // Skip if the file imports or requires DOMPurify or a sanitize library anywhere\n if (/(?:import|require)\\s*\\(?.*(?:DOMPurify|dompurify|sanitize|sanitizer)/i.test(content)) return [];\n const patterns = [\n // React dangerouslySetInnerHTML\n /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html\\s*:/g,\n // Direct innerHTML assignment\n /\\.innerHTML\\s*=\\s*(?![\"'`]\\s*$)/gm,\n // document.write\n /document\\.write\\s*\\(/g,\n // v-html in Vue\n /v-html\\s*=/g,\n // {@html} in Svelte\n /\\{@html\\s/g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of patterns) {\n const raw = findMatches(content, pattern, xssVulnerability, filePath, () =>\n \"Sanitize user input before rendering as HTML. Use a library like DOMPurify: DOMPurify.sanitize(userInput)\"\n );\n // Filter out innerHTML assignments that use only static strings (no user input)\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n // Skip if innerHTML is assigned a pure string literal with no interpolation\n if (/\\.innerHTML\\s*=\\s*['\"]/.test(lineText) && !/\\$\\{/.test(lineText)) continue;\n // Skip if innerHTML is assigned a string concatenation of only literals (no variables from user input)\n if (/\\.innerHTML\\s*=\\s*['\"][^'\"]*['\"]\\s*$/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\nexport const noRateLimiting: CustomRule = {\n id: \"VC008\",\n title: \"API Endpoint Without Rate Limiting\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"API endpoints without rate limiting are vulnerable to abuse and denial-of-service attacks.\",\n check(content, filePath) {\n // Only check main server/app entry files\n const isEntryFile = /(?:server|app|index|main)\\.[jt]sx?$/.test(filePath) ||\n filePath.includes(\"middleware\");\n if (!isEntryFile) return [];\n\n // Check if this is a server file\n const isServer = /(?:express|hono|fastify|koa|next|createServer|listen\\()/i.test(content);\n if (!isServer) return [];\n\n // Check for rate limiting\n const hasRateLimit = /rate.?limit|throttle|express-rate-limit|@elysiajs\\/rate-limit|hono.*limiter/i.test(content);\n if (hasRateLimit) return [];\n\n return [{\n rule: \"VC008\",\n title: noRateLimiting.title,\n severity: \"medium\",\n category: \"Availability\",\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: \"Add rate limiting middleware to your server. For Express: npm install express-rate-limit. For other frameworks, check their rate limiting plugins.\",\n }];\n },\n};\n\nexport const corsWildcard: CustomRule = {\n id: \"VC009\",\n title: \"CORS Allows All Origins\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Wildcard CORS (*) allows any website to make requests to your API, potentially exposing user data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n const patterns = [\n /cors\\(\\s*\\)/g, // cors() with no options = allow all\n /origin\\s*:\\s*[\"'`]\\*[\"'`]/g,\n /[\"'`]Access-Control-Allow-Origin[\"'`]\\s*,\\s*[\"'`]\\*[\"'`]/g,\n /origin\\s*:\\s*true/g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of patterns) {\n matches.push(\n ...findMatches(content, pattern, corsWildcard, filePath, () =>\n \"Restrict CORS to your specific frontend domain(s): cors({ origin: 'https://yourdomain.com' })\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const clientSideAuth: CustomRule = {\n id: \"VC010\",\n title: \"Client-Side Only Authorization\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Hiding UI elements based on roles without server-side checks lets attackers bypass restrictions using DevTools.\",\n check(content, filePath) {\n // Only check frontend component files\n if (!filePath.match(/\\.(jsx|tsx|vue|svelte)$/)) return [];\n\n const matches: RuleMatch[] = [];\n\n // Pattern: conditional rendering based on role/admin without server check\n const rolePatterns = [\n /\\{.*(?:isAdmin|role\\s*===?\\s*[\"'`]admin[\"'`]|user\\.role).*&&/gi,\n /v-if\\s*=\\s*[\"'`].*(?:isAdmin|role\\s*===?\\s*'admin')/gi,\n ];\n\n for (const pattern of rolePatterns) {\n // Only flag if the file has no server-side fetch for auth verification\n const hasServerCheck = /getServerSession|getUser|server|api\\/auth|middleware/i.test(content);\n if (hasServerCheck) continue;\n\n matches.push(\n ...findMatches(content, pattern, clientSideAuth, filePath, () =>\n \"Client-side role checks only hide UI — they don't prevent access. Always verify permissions on the server/API side too.\"\n ),\n );\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC011 – Secret in NEXT_PUBLIC_ env var\n// ────────────────────────────────────────────\n\nexport const nextPublicSecret: CustomRule = {\n id: \"VC011\",\n title: \"Secret in NEXT_PUBLIC_ Environment Variable\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"NEXT_PUBLIC_ variables are exposed to the browser. Secrets placed here are visible to anyone.\",\n check(content, filePath) {\n if (!filePath.match(/\\.env/) && !filePath.match(/next\\.config/)) return [];\n const patterns = [\n /NEXT_PUBLIC_[A-Z_]*(?:SECRET|KEY|TOKEN|PASSWORD|PRIVATE)[A-Z_]*\\s*=\\s*.+/gi,\n /NEXT_PUBLIC_[A-Z_]*(?:SUPABASE_SERVICE|CLERK_SECRET|STRIPE_SECRET)[A-Z_]*\\s*=\\s*.+/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const raw = findMatches(content, p, nextPublicSecret, filePath, () =>\n \"Remove the NEXT_PUBLIC_ prefix. Only use NEXT_PUBLIC_ for values safe to expose in the browser.\"\n );\n // Filter out publishable/public keys that are DESIGNED to be client-side\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/PUBLISHABLE|ANON_KEY|PUBLIC_KEY/i.test(lineText)) continue;\n // Skip Clerk publishable keys (pk_test_, pk_live_)\n if (/CLERK_PUBLISHABLE/i.test(lineText)) continue;\n // Skip Stripe publishable keys\n if (/STRIPE_PUBLISHABLE/i.test(lineText)) continue;\n // Skip if the value is a placeholder (empty, pk_test_, etc.)\n if (/=\\s*[\"']?\\s*$|=\\s*[\"']?pk_(?:test|live)_[\"']?\\s*$/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC012 – Firebase config in client code\n// ────────────────────────────────────────────\n\nexport const firebaseClientConfig: CustomRule = {\n id: \"VC012\",\n title: \"Firebase Config with API Key in Client Code\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Firebase config objects in client code expose your API key. While Firebase API keys aren't secret, they should be restricted in the Firebase console.\",\n check(content, filePath) {\n if (!/firebase/i.test(content)) return [];\n const patterns = [\n /firebaseConfig\\s*=\\s*\\{[^}]*apiKey\\s*:/gi,\n /initializeApp\\s*\\(\\s*\\{[^}]*apiKey\\s*:/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, firebaseClientConfig, filePath, () =>\n \"Move Firebase config to environment variables. Restrict the API key in Firebase Console > Project Settings > API restrictions.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC013 – Supabase anon key for admin ops\n// ────────────────────────────────────────────\n\nexport const supabaseAnonAdmin: CustomRule = {\n id: \"VC013\",\n title: \"Supabase Anon Key Used for Admin Operations\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Using the Supabase anon key for operations that require elevated privileges is insecure.\",\n check(content, filePath) {\n if (!/supabase/i.test(content)) return [];\n if (!/anon/i.test(content)) return [];\n if (/service_role/i.test(content)) return [];\n const patterns = [\n /supabase[^.]*\\.auth\\.admin/gi,\n /supabase[^.]*\\.rpc\\s*\\(/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, supabaseAnonAdmin, filePath, () =>\n \"Use the service_role key on the server side for admin operations. Never expose it to the client.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC014 – .env not in .gitignore\n// ────────────────────────────────────────────\n\nexport const envNotGitignored: CustomRule = {\n id: \"VC014\",\n title: \".env File Not in .gitignore\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"If .env is not listed in .gitignore, secrets will be committed to version control.\",\n check(content, filePath) {\n if (!filePath.endsWith(\".gitignore\")) return [];\n if (/\\.env/i.test(content)) return [];\n return [{\n rule: \"VC014\", title: envNotGitignored.title, severity: \"high\" as const, category: \"Secrets\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add \".env*\" to your .gitignore file to prevent committing secrets.',\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC015 – eval() / new Function()\n// ────────────────────────────────────────────\n\nexport const evalUsage: CustomRule = {\n id: \"VC015\",\n title: \"Use of eval() or Function Constructor\",\n severity: \"high\",\n category: \"Injection\",\n description: \"eval() and new Function() execute arbitrary code, creating severe injection risks. Common in AI-generated code.\",\n check(content, filePath) {\n if (filePath.includes(\"node_modules\") || filePath.includes(\".min.\")) return [];\n if (filePath.match(/(?:webpack|rollup|vite|jest|babel|tsup|esbuild)\\.config/i)) return [];\n if (isTestFile(filePath)) return [];\n // Skip files that are linters/scanners/rule engines (they reference eval in detection patterns)\n if (filePath.match(/(?:rules?|scanner|lint|check|detect|analyz)/i) && /\\.check\\s*\\(|findMatches/i.test(content)) return [];\n const patterns = [\n // Actual eval() calls — not in strings or regex patterns\n /\\beval\\s*\\(\\s*(?![\"'`](?:eval|source))/g,\n /new\\s+Function\\s*\\(\\s*(?![\"'`])/g,\n ];\n // Skip if eval is only in a string (e.g., devtool: 'eval-source-map')\n const hasEvalInString = /[\"'`]eval(?:-source-map|[\"'`])/i.test(content);\n if (hasEvalInString && !/\\beval\\s*\\([^)]*(?:req\\.|body\\.|input|params|user|data)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const rawMatches = findMatches(content, p, evalUsage, filePath, () =>\n \"Replace eval() with JSON.parse() for data, or a proper parser for expressions. Never pass user input to eval().\"\n );\n // Skip matches on lines containing devtool or source-map config\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const lineText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/devtool|source-map/i.test(lineText)) continue;\n // Skip if eval appears on a line with description/title/message keys (string literal context)\n if (/(?:description|title|message)\\s*[:=]/i.test(lineText)) continue;\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC016 – Unvalidated redirect\n// ────────────────────────────────────────────\n\nexport const unvalidatedRedirect: CustomRule = {\n id: \"VC016\",\n title: \"Unvalidated Redirect\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Redirecting users to URLs from untrusted input enables phishing attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip if the file already validates redirect URLs\n if (/isAllowedRedirect|validateRedirect|isSafeRedirect|allowedDomains|trustedDomains|whitelist.*url|allowlist.*url/i.test(content)) return [];\n const patterns = [\n /window\\.location\\s*=\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.href\\s*=\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.assign\\s*\\(\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.replace\\s*\\(\\s*(?![\"'`]https?:\\/\\/)/g,\n /res\\.redirect\\s*\\(\\s*(?:req\\.|params\\.|query\\.)/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unvalidatedRedirect, filePath, () =>\n \"Validate redirect URLs against an allowlist of trusted domains. Never redirect to user-supplied URLs directly.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC017 – Insecure cookie settings\n// ────────────────────────────────────────────\n\nexport const insecureCookies: CustomRule = {\n id: \"VC017\",\n title: \"Insecure Cookie Settings\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Cookies without httpOnly, secure, or sameSite flags are vulnerable to theft and CSRF attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/cookie/i.test(content)) return [];\n const setCookiePattern = /(?:set-cookie|setCookie|cookie\\s*=|res\\.cookie\\s*\\()/gi;\n if (!setCookiePattern.test(content)) return [];\n const hasHttpOnly = /httpOnly\\s*:\\s*true|httponly/i.test(content);\n const hasSecure = /secure\\s*:\\s*true|;\\s*secure/i.test(content);\n const hasSameSite = /sameSite\\s*:|samesite/i.test(content);\n const matches: RuleMatch[] = [];\n if (!hasHttpOnly || !hasSecure || !hasSameSite) {\n const missing: string[] = [];\n if (!hasHttpOnly) missing.push(\"httpOnly\");\n if (!hasSecure) missing.push(\"secure\");\n if (!hasSameSite) missing.push(\"sameSite\");\n matches.push(...findMatches(content, /(?:set-cookie|setCookie|cookie\\s*=|res\\.cookie\\s*\\()/gi, insecureCookies, filePath, () =>\n `Add missing cookie flags: ${missing.join(\", \")}. Example: { httpOnly: true, secure: true, sameSite: 'lax' }`\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC018 – Exposed auth provider secret key\n// ────────────────────────────────────────────\n\nexport const exposedAuthSecret: CustomRule = {\n id: \"VC018\",\n title: \"Exposed Clerk/Auth Secret Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Auth provider secret keys (Clerk, Auth0, NextAuth) must never be in client-side code or NEXT_PUBLIC_ variables.\",\n check(content, filePath) {\n const isClientFile = filePath.match(/\\.(jsx|tsx|vue|svelte)$/) || /[\"']use client[\"']/.test(content);\n const isEnvFile = filePath.match(/\\.env/);\n if (!isClientFile && !isEnvFile) return [];\n const patterns: RegExp[] = [];\n if (isClientFile) {\n patterns.push(\n /CLERK_SECRET_KEY/g,\n /AUTH0_CLIENT_SECRET/g,\n /NEXTAUTH_SECRET/g,\n );\n }\n if (isEnvFile) {\n patterns.push(\n /NEXT_PUBLIC_CLERK_SECRET/gi,\n /NEXT_PUBLIC_AUTH0_SECRET/gi,\n /NEXT_PUBLIC_NEXTAUTH_SECRET/gi,\n );\n }\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedAuthSecret, filePath, () =>\n \"Move this secret to a server-side environment variable (without the NEXT_PUBLIC_ prefix). Never expose auth secrets to the browser.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC019 – Insecure Electron BrowserWindow\n// ────────────────────────────────────────────\n\nexport const insecureElectronWindow: CustomRule = {\n id: \"VC019\",\n title: \"Insecure Electron BrowserWindow Configuration\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Electron BrowserWindow with nodeIntegration enabled, contextIsolation disabled, or sandbox disabled allows renderer processes to access Node.js APIs, enabling remote code execution.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/BrowserWindow/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /nodeIntegration\\s*:\\s*true/g,\n /contextIsolation\\s*:\\s*false/g,\n /sandbox\\s*:\\s*false/g,\n /webSecurity\\s*:\\s*false/g,\n /allowRunningInsecureContent\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureElectronWindow, filePath, (m) =>\n `Set ${m[0].split(\":\")[0].trim()}: ${m[0].includes(\"true\") ? \"false\" : \"true\"}. Enable contextIsolation, sandbox, and webSecurity; disable nodeIntegration and allowRunningInsecureContent.`\n ));\n }\n // Check for BrowserWindow without sandbox/webSecurity set at all\n if (/new\\s+BrowserWindow\\s*\\(/g.test(content)) {\n if (!/sandbox\\s*:/i.test(content)) {\n matches.push(...findMatches(content, /new\\s+BrowserWindow\\s*\\(/g, { ...insecureElectronWindow, title: \"Electron BrowserWindow Missing sandbox:true\" }, filePath, () =>\n \"Add sandbox: true to BrowserWindow webPreferences for defense in depth.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC020 – Missing Content Security Policy\n// ────────────────────────────────────────────\n\nexport const missingCSP: CustomRule = {\n id: \"VC020\",\n title: \"Missing Content Security Policy (CSP)\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Without a Content-Security-Policy header or meta tag, your app is vulnerable to XSS and data injection attacks.\",\n check(content, filePath) {\n // Check HTML files for missing CSP meta tag\n if (filePath.match(/\\.(html|htm)$/)) {\n if (!/Content-Security-Policy/i.test(content)) {\n return [{\n rule: \"VC020\", title: missingCSP.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add a CSP meta tag: <meta http-equiv=\"Content-Security-Policy\" content=\"default-src \\'self\\'; script-src \\'self\\'\">'\n }];\n }\n }\n // Skip Electron main process — CSP is typically set in the HTML file (already checked above)\n // Flagging main.ts creates false positives since CSP belongs in index.html for Electron apps\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC021 – IPC Handler Without Path Validation\n// ────────────────────────────────────────────\n\nexport const ipcPathTraversal: CustomRule = {\n id: \"VC021\",\n title: \"IPC/File Handler Without Path Validation\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"IPC handlers that read or write files based on renderer-supplied paths without validation allow path traversal attacks, potentially exposing sensitive files like .ssh keys or .env files.\",\n check(content, filePath) {\n if (!/ipcMain\\.handle|ipcMain\\.on/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Look for file read/write in IPC handlers without path validation\n const hasFileOps = /readFile|writeFile|readFileSync|writeFileSync|createReadStream|createWriteStream/i.test(content);\n if (!hasFileOps) return [];\n const hasPathValidation = /(?:path\\.resolve|path\\.normalize|startsWith|isAbsolute|\\.includes\\s*\\(\\s*[\"'`]\\.\\.[\"'`]\\s*\\)|allowedPaths|safePath|validatePath|sanitizePath)/i.test(content);\n if (!hasPathValidation) {\n matches.push(...findMatches(content, /ipcMain\\.(?:handle|on)\\s*\\(\\s*[\"'`][^\"'`]*(?:read|write|file|save|load|open|export)[^\"'`]*[\"'`]/gi, ipcPathTraversal, filePath, () =>\n \"Validate file paths in IPC handlers: ensure paths are within an allowed directory (e.g., app.getPath('userData')), reject paths containing '..', and block access to sensitive directories (.ssh, .env, etc).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC022 – HTML Export Without Sanitization\n// ────────────────────────────────────────────\n\nexport const unsanitizedHTMLExport: CustomRule = {\n id: \"VC022\",\n title: \"HTML Export/Render Without Sanitization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Generating HTML from user content without sanitization (e.g., DOMPurify) allows stored XSS attacks. Malicious content saved in documents could execute scripts when exported or previewed.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip if DOMPurify is imported or used anywhere in the file\n if (/DOMPurify|dompurify/i.test(content)) return [];\n // Skip React/Vue component files — JSX is not unsafe HTML concatenation\n if (filePath.match(/\\.(jsx|tsx|vue|svelte)$/) && !/innerHTML|document\\.write|\\.html\\s*=/i.test(content)) return [];\n // Skip files that have any sanitizer\n const hasSanitizer = /sanitize|escapeHtml|escapeHTML|xss|htmlEncode|purify/i.test(content);\n if (hasSanitizer) return [];\n // Only flag files that actually export/write HTML (not just template rendering)\n if (!/(?:export|download|save|write|send).*(?:html|HTML)|\\.innerHTML\\s*=|document\\.write|res\\.send\\s*\\(/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const htmlBuildPatterns = [\n /`<[^`]*\\$\\{[^}]*(?:content|title|body|text|name|message|description|input|value|data)[^}]*\\}[^`]*>`/gi,\n /[\"']<[^\"']*['\"]\\s*\\+\\s*(?:content|title|body|text|message|data|doc\\.|post\\.|article\\.)/gi,\n ];\n for (const p of htmlBuildPatterns) {\n matches.push(...findMatches(content, p, unsanitizedHTMLExport, filePath, () =>\n \"Sanitize user content before embedding in HTML. Use DOMPurify: DOMPurify.sanitize(content). For plain text, use a function to escape HTML entities (<, >, &, quotes).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC023 – Prototype Pollution via Storage\n// ────────────────────────────────────────────\n\nexport const prototypePollution: CustomRule = {\n id: \"VC023\",\n title: \"Prototype Pollution Risk\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Parsing JSON from localStorage, URL params, or external sources and merging it into objects without validation can lead to prototype pollution, allowing attackers to inject __proto__ or constructor properties.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // JSON.parse from localStorage/sessionStorage without validation\n const storageParsePatterns = [\n /JSON\\.parse\\s*\\(\\s*(?:localStorage|sessionStorage)\\.getItem/g,\n /JSON\\.parse\\s*\\(\\s*window\\.localStorage/g,\n ];\n const hasValidation = /schema|validate|sanitize|whitelist|allowedKeys|pick\\(|Object\\.freeze|zod|yup|joi|ajv/i.test(content);\n if (hasValidation) return [];\n // Check for object spread/assign from parsed storage\n const hasUnsafeMerge = /Object\\.assign\\s*\\([^)]*JSON\\.parse|\\.\\.\\.JSON\\.parse|\\{.*\\.\\.\\.(?:stored|saved|cached|parsed|data)/i.test(content);\n if (hasUnsafeMerge) {\n matches.push(...findMatches(content, /Object\\.assign\\s*\\([^)]*JSON\\.parse|\\.\\.\\.JSON\\.parse/g, prototypePollution, filePath, () =>\n \"Validate parsed data against an expected schema before merging into objects. Use Object.freeze(), a validation library (Zod, Yup), or manually check for __proto__ and constructor keys.\"\n ));\n }\n for (const p of storageParsePatterns) {\n matches.push(...findMatches(content, p, prototypePollution, filePath, () =>\n \"Validate localStorage data against an expected schema before using it. Malicious extensions or XSS can modify localStorage values.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC024 – Missing File Size Limits\n// ────────────────────────────────────────────\n\nexport const missingFileSizeLimits: CustomRule = {\n id: \"VC024\",\n title: \"File Write/Save Without Size Limit\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"File save or upload handlers without size validation can lead to denial-of-service via disk exhaustion or memory exhaustion.\",\n check(content, filePath) {\n if (!/(?:writeFile|save|upload|export)/i.test(filePath) && !/(?:writeFile|writeFileSync|createWriteStream)/i.test(content)) return [];\n // Look for file write operations in handlers\n const hasWriteOps = /(?:ipcMain|app\\.(?:post|put)|router\\.(?:post|put)).*(?:writeFile|save|export)/is.test(content) ||\n /(?:writeFile|writeFileSync)\\s*\\(/g.test(content);\n if (!hasWriteOps) return [];\n const hasSizeCheck = /(?:size|length|byteLength|bytes)\\s*(?:>|>=|<|<=|===)\\s*\\d|maxSize|MAX_SIZE|sizeLimit|content-length/i.test(content);\n if (hasSizeCheck) return [];\n return findMatches(content, /(?:writeFile|writeFileSync)\\s*\\(/g, missingFileSizeLimits, filePath, () =>\n \"Add file size validation before writing. Check content.length or Buffer.byteLength() against a maximum (e.g., 10MB) to prevent disk exhaustion.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC025 – Unsanitized Export Filenames\n// ────────────────────────────────────────────\n\nexport const unsanitizedFilenames: CustomRule = {\n id: \"VC025\",\n title: \"Unsanitized Filename in File Operations\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Using user-supplied filenames without sanitization in file operations can enable path traversal, overwriting system files, or executing commands via special characters.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // Look for file operations using variables as filenames\n const patterns = [\n /(?:writeFile|writeFileSync|createWriteStream|rename|copyFile)\\s*\\(\\s*(?:`[^`]*\\$\\{|[^\"'`\\s,]+\\s*\\+)/g,\n /(?:dialog\\.showSaveDialog|saveDialog).*(?:defaultPath|fileName)\\s*:\\s*(?![\"'`])/g,\n /\\.download\\s*=\\s*(?![\"'`])/g,\n ];\n const hasSanitization = /sanitize|cleanFilename|safeFilename|replace\\s*\\(\\s*\\/\\[.*\\]\\//i.test(content);\n if (hasSanitization) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unsanitizedFilenames, filePath, () =>\n \"Sanitize filenames before use: strip path separators (/ \\\\), special chars, and '..' sequences. Example: name.replace(/[^a-zA-Z0-9._-]/g, '_')\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC026 – Electron Navigation Not Restricted\n// ────────────────────────────────────────────\n\nexport const electronNavigationUnrestricted: CustomRule = {\n id: \"VC026\",\n title: \"Electron: External Navigation Not Blocked\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Electron apps that don't block navigation to external URLs or new window creation are vulnerable to phishing and drive-by downloads. Malicious links in app content can redirect the entire app to an attacker's site.\",\n check(content, filePath) {\n if (!/BrowserWindow|electron/i.test(content)) return [];\n if (!/main|index/i.test(filePath)) return [];\n const hasNavBlock = /will-navigate|new-window|setWindowOpenHandler|webContents\\.on.*navigate/i.test(content);\n if (hasNavBlock) return [];\n if (/new\\s+BrowserWindow/i.test(content)) {\n return findMatches(content, /new\\s+BrowserWindow\\s*\\(/g, electronNavigationUnrestricted, filePath, () =>\n \"Block external navigation: win.webContents.on('will-navigate', (e, url) => { if (!url.startsWith('file://')) e.preventDefault(); }); and use setWindowOpenHandler to block new windows.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC027 – Missing Security Meta Tags\n// ────────────────────────────────────────────\n\nexport const missingSecurityMeta: CustomRule = {\n id: \"VC027\",\n title: \"Missing Security Meta Tags / Headers\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"HTML pages without X-Content-Type-Options, referrer policy, or other security meta tags are more susceptible to MIME-sniffing attacks and information leakage.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(html|htm)$/)) return [];\n const matches: RuleMatch[] = [];\n if (!/X-Content-Type-Options/i.test(content) && !/<meta[^>]*nosniff/i.test(content)) {\n matches.push({\n rule: \"VC027\", title: \"Missing X-Content-Type-Options Header\", severity: \"medium\" as const,\n category: \"Configuration\", file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta http-equiv=\"X-Content-Type-Options\" content=\"nosniff\"> to prevent MIME-type sniffing.'\n });\n }\n if (!/referrer/i.test(content)) {\n matches.push({\n rule: \"VC027\", title: \"Missing Referrer Policy\", severity: \"medium\" as const,\n category: \"Configuration\", file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta name=\"referrer\" content=\"no-referrer\"> or \"strict-origin-when-cross-origin\" to limit referrer leakage.'\n });\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC028 – Unvalidated API Parameters\n// ────────────────────────────────────────────\n\nexport const unvalidatedAPIParams: CustomRule = {\n id: \"VC028\",\n title: \"Unvalidated API Request Parameters\",\n severity: \"high\",\n category: \"Injection\",\n description: \"API requests constructed with unvalidated user input (API keys, model names, URLs) can be exploited for injection attacks or unauthorized access to different API models/endpoints.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // API key passed without format validation\n const apiKeyPatterns = [\n /(?:apiKey|api_key|authorization)\\s*[:=]\\s*(?:req\\.body|req\\.query|params|input|formData|body)\\./gi,\n /headers\\s*:\\s*\\{[^}]*Authorization\\s*:\\s*(?![\"'`]Bearer\\s)/gi,\n ];\n const hasValidation = /validate|sanitize|regex|test\\(|match\\(|pattern|allowList|whitelist|enum|includes\\(/i.test(content);\n if (hasValidation) return [];\n // Model selection without allowlist\n if (/model\\s*[:=]\\s*(?:req\\.body|params|input|body)\\./i.test(content) || /model\\s*[:=]\\s*(?![\"'`])[a-z]/i.test(content)) {\n const hasModelValidation = /allowedModels|validModels|models\\s*\\.\\s*includes|model.*(?:===|!==|includes)/i.test(content);\n if (!hasModelValidation && /(?:openai|anthropic|claude|gpt|llm)/i.test(content)) {\n matches.push(...findMatches(content, /model\\s*[:=]\\s*(?:req\\.body|params|input|body)\\./gi, unvalidatedAPIParams, filePath, () =>\n \"Validate model selection against an allowlist of approved models. Example: const ALLOWED_MODELS = ['gpt-4', 'claude-3']; if (!ALLOWED_MODELS.includes(model)) throw new Error('Invalid model');\"\n ));\n }\n }\n for (const p of apiKeyPatterns) {\n matches.push(...findMatches(content, p, unvalidatedAPIParams, filePath, () =>\n \"Validate API key format before using it (e.g., check prefix and length). Never pass user-supplied API keys directly to third-party services without validation.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC029 – Unvalidated Event/Message Data\n// ────────────────────────────────────────────\n\nexport const unvalidatedEventData: CustomRule = {\n id: \"VC029\",\n title: \"Unvalidated Event or PostMessage Data\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Custom events, postMessage, or IPC message data used without type-checking can lead to injection attacks or unexpected behavior when malicious data is sent through event channels.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // addEventListener('message') without origin check\n if (/addEventListener\\s*\\(\\s*[\"'`]message[\"'`]/i.test(content)) {\n if (!/event\\.origin|e\\.origin|message\\.origin/i.test(content)) {\n matches.push(...findMatches(content, /addEventListener\\s*\\(\\s*[\"'`]message[\"'`]/g, unvalidatedEventData, filePath, () =>\n \"Always verify event.origin in message event handlers to prevent cross-origin attacks. Example: if (event.origin !== 'https://trusted.com') return;\"\n ));\n }\n }\n // dispatchEvent with custom data inserted without validation\n if (/new\\s+CustomEvent\\s*\\(/i.test(content) || /ipcRenderer\\.send/i.test(content)) {\n const hasTypeCheck = /typeof\\s|instanceof|z\\.|schema|validate|Number\\.isFinite|parseInt|parseFloat/i.test(content);\n if (!hasTypeCheck) {\n matches.push(...findMatches(content, /new\\s+CustomEvent\\s*\\(/g, unvalidatedEventData, filePath, () =>\n \"Type-check custom event data before using it. Validate that data.detail contains expected types to prevent injection.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC030 – Insecure Deserialization\n// ────────────────────────────────────────────\n\nexport const insecureDeserialization: CustomRule = {\n id: \"VC030\",\n title: \"Insecure Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Deserializing untrusted data (pickle, unserialize, yaml.load) can execute arbitrary code. Attackers craft malicious payloads to gain remote code execution.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const patterns = [\n // Python pickle\n /pickle\\.loads?\\s*\\(/g,\n /cPickle\\.loads?\\s*\\(/g,\n // PHP unserialize\n /unserialize\\s*\\(/g,\n // Ruby Marshal\n /Marshal\\.load\\s*\\(/g,\n // YAML unsafe load (Python)\n /yaml\\.load\\s*\\([^)]*(?!Loader\\s*=\\s*yaml\\.SafeLoader)/g,\n /yaml\\.unsafe_load\\s*\\(/g,\n // Java ObjectInputStream\n /ObjectInputStream\\s*\\(/g,\n // Node.js node-serialize\n /serialize\\.unserialize\\s*\\(/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureDeserialization, filePath, () =>\n \"Never deserialize untrusted data. Use JSON instead of pickle/Marshal/unserialize. For YAML, use yaml.safe_load(). Validate and sanitize all input before deserialization.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC031 – Hardcoded JWT Secret\n// ────────────────────────────────────────────\n\nexport const hardcodedJWTSecret: CustomRule = {\n id: \"VC031\",\n title: \"Hardcoded JWT Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"JWT tokens signed with a hardcoded string secret can be forged by anyone who reads the source code.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\") || filePath.includes(\"test\")) return [];\n const patterns = [\n /jwt\\.sign\\s*\\([^,]+,\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /jwt\\.verify\\s*\\([^,]+,\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /jsonwebtoken.*secret\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/gi,\n /JWT_SECRET\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, hardcodedJWTSecret, filePath, () =>\n \"Move JWT secret to an environment variable: jwt.sign(payload, process.env.JWT_SECRET). Use a strong, random secret (256+ bits).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC032 – Missing HTTPS Enforcement\n// ────────────────────────────────────────────\n\nexport const missingHTTPS: CustomRule = {\n id: \"VC032\",\n title: \"Missing HTTPS Enforcement\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"HTTP URLs in production code, missing HSTS headers, or insecure redirect configurations expose data to man-in-the-middle attacks.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.includes(\"test\") || filePath.includes(\"README\")) return [];\n if (filePath.match(/\\.(md|txt)$/)) return [];\n const matches: RuleMatch[] = [];\n // Skip standard XML/HTML doctypes, DTDs, schema URIs, namespace URLs\n if (/<!DOCTYPE|xmlns|\\.dtd|\\.xsd/i.test(content) && !/fetch|axios|request|http\\.get/i.test(content)) return [];\n // Hardcoded http:// URLs to non-local hosts (excluding standard schema/namespace URIs)\n const httpPattern = /[\"'`]http:\\/\\/(?!localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|192\\.168\\.|10\\.|172\\.(?:1[6-9]|2\\d|3[01])\\.|www\\.w3\\.org|www\\.apple\\.com\\/DTDs|schemas?\\.|xml\\.org|purl\\.org|ns\\.adobe|xmlpull\\.org|java\\.sun\\.com)[^\"'`\\s]+[\"'`]/g;\n const rawMatches = findMatches(content, httpPattern, missingHTTPS, filePath, () =>\n \"Use https:// instead of http:// for all production URLs. Add HSTS header: Strict-Transport-Security: max-age=31536000; includeSubDomains\"\n );\n // Skip DOCTYPE/DTD URLs\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const matchText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/DTD|DOCTYPE|w3\\.org|apple\\.com\\/DTDs/i.test(matchText)) continue;\n matches.push(rm);\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC033 – Exposed Debug/Dev Mode\n// ────────────────────────────────────────────\n\nexport const exposedDebugMode: CustomRule = {\n id: \"VC033\",\n title: \"Debug/Development Mode Exposed\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Debug mode, verbose logging, or development configuration left in production code exposes internal details and may enable debug endpoints.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.endsWith(\".example\") || filePath.includes(\"node_modules\")) return [];\n if (filePath.match(/\\.env\\.development$/)) return []; // Expected in dev env files\n const matches: RuleMatch[] = [];\n const patterns = [\n // Debug flags set to true\n /DEBUG\\s*[:=]\\s*(?:true|1|[\"'`]true[\"'`]|[\"'`]\\*[\"'`])/g,\n // Django DEBUG\n /DEBUG\\s*=\\s*True/g,\n // Flask/Express debug mode\n /app\\.debug\\s*=\\s*True/g,\n /app\\.run\\s*\\([^)]*debug\\s*=\\s*True/g,\n // Source maps in production\n /devtool\\s*:\\s*[\"'`](?:eval|cheap|source-map|inline-source-map)[\"'`]/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedDebugMode, filePath, () =>\n \"Disable debug mode in production. Use environment variables: DEBUG = process.env.NODE_ENV !== 'production'. Remove source maps from production builds.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC034 – Insecure Randomness\n// ────────────────────────────────────────────\n\nexport const insecureRandomness: CustomRule = {\n id: \"VC034\",\n title: \"Insecure Randomness for Security-Sensitive Values\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"Math.random() is not cryptographically secure. Using it for tokens, session IDs, passwords, or OTPs makes them predictable.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Only flag if Math.random is actually used (not just mentioned)\n if (!/Math\\.random\\s*\\(\\s*\\)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Math.random used in security-critical assignments (narrower: require direct assignment on same line)\n const securityContext = /(?:token|secret|session|password|otp|nonce|salt|csrf|auth)\\s*[:=]\\s*.*Math\\.random/gi;\n const rawMatches = findMatches(content, securityContext, insecureRandomness, filePath, () =>\n \"Use crypto.randomUUID() or crypto.getRandomValues() for security-sensitive values. Math.random() is predictable.\"\n );\n // Skip non-security uses of Math.random (e.g., UI-related randomness)\n const nonSecurityVarNames = /(?:id|key|color|index|delay|position|size|width|height|offset|opacity|rotation|animation|random(?!.*(?:token|secret|key|password)))\\s*[:=]\\s*.*Math\\.random/i;\n for (const rm of rawMatches) {\n const lineText = content.split(\"\\n\")[rm.line - 1] || \"\";\n if (nonSecurityVarNames.test(lineText)) continue;\n matches.push(rm);\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC035 – Open Redirect via URL Params\n// ────────────────────────────────────────────\n\nexport const openRedirectParams: CustomRule = {\n id: \"VC035\",\n title: \"Open Redirect via URL Parameters\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Redirect parameters like ?redirect_url=, ?return_to=, ?next= passed directly to redirects enable phishing attacks.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const patterns = [\n // Reading redirect-like query params and using in redirect\n /(?:redirect_url|redirect_uri|return_to|return_url|next|callback_url|continue|goto|target|dest|destination|forward|redir)\\s*(?:=|:)\\s*(?:req\\.query|req\\.params|searchParams|query|params)\\./gi,\n /redirect\\s*\\(\\s*(?:req\\.query|req\\.params|searchParams\\.get)\\s*\\(\\s*[\"'`](?:redirect|return|next|callback|url|goto)/gi,\n ];\n const hasValidation = /allowedUrls|allowedDomains|allowedHosts|validUrl|safeDomain|whitelist|startsWith.*https|new URL.*hostname/i.test(content);\n if (hasValidation) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, openRedirectParams, filePath, () =>\n \"Validate redirect URLs against an allowlist of trusted domains. Use: const url = new URL(input); if (!ALLOWED_HOSTS.includes(url.hostname)) reject.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC036 – Missing Error Boundary (React)\n// ────────────────────────────────────────────\n\nexport const missingErrorBoundary: CustomRule = {\n id: \"VC036\",\n title: \"React App Missing Error Boundary\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"React apps without error boundaries display raw stack traces and component tree info to users when crashes occur, leaking internal details.\",\n check(content, filePath) {\n // Only check App.tsx, App.jsx, app.tsx, app.jsx files\n const basename = filePath.split(\"/\").pop() || \"\";\n if (!/^[Aa]pp\\.[jt]sx$/i.test(basename)) return [];\n // Skip .ts files (non-JSX)\n if (filePath.match(/\\.ts$/)) return [];\n // Only trigger if file contains JSX (< followed by an uppercase letter)\n if (!/<[A-Z]/.test(content)) return [];\n // Skip Electron main process files\n if (/(?:BrowserWindow|electron|ipcMain|app\\.on\\s*\\(\\s*[\"']ready)/i.test(content)) return [];\n if (/\\/main\\//.test(filePath) && !/react-dom|createRoot/i.test(content)) return [];\n // Must have React imports and render calls\n if (!/(?:import.*react|from\\s+['\"]react|require.*react)/i.test(content)) return [];\n if (!/(?:createRoot|ReactDOM\\.render)/i.test(content)) return [];\n const hasErrorBoundary = /ErrorBoundary|componentDidCatch|getDerivedStateFromError|error-boundary/i.test(content);\n if (hasErrorBoundary) return [];\n if (/createRoot|ReactDOM\\.render/i.test(content)) {\n return [{\n rule: \"VC036\", title: missingErrorBoundary.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Wrap your app in an ErrorBoundary component to catch rendering errors gracefully. Use react-error-boundary or create a class component with componentDidCatch.\"\n }];\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC037 – Exposed Stack Traces in API\n// ────────────────────────────────────────────\n\nexport const exposedStackTraces: CustomRule = {\n id: \"VC037\",\n title: \"Stack Traces Exposed in API Responses\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"Returning error.stack or detailed error messages in API responses reveals internal code paths, file structure, and dependencies to attackers.\",\n check(content, filePath) {\n const isApiFile = /(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|middleware)/i.test(filePath);\n if (!isApiFile) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Sending stack trace in response\n /(?:res\\.(?:json|send|status)|c\\.json|return.*json)\\s*\\([^)]*(?:err\\.stack|error\\.stack|e\\.stack)/gi,\n /(?:res\\.(?:json|send|status)|c\\.json)\\s*\\([^)]*(?:err\\.message|error\\.message|e\\.message)/gi,\n // Express-style error with stack\n /(?:message|error)\\s*:\\s*(?:err|error|e)\\.(?:stack|message)/gi,\n ];\n const hasEnvCheck = /process\\.env\\.NODE_ENV\\s*(?:===|!==)\\s*[\"'`]production[\"'`]|NODE_ENV/i.test(content);\n if (hasEnvCheck) return []; // They're conditionally showing errors\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedStackTraces, filePath, () =>\n \"Never expose error.stack or error.message to clients in production. Return generic error messages: { error: 'Something went wrong' }. Log details server-side only.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC038 – Insecure File Upload Type\n// ────────────────────────────────────────────\n\nexport const insecureFileUpload: CustomRule = {\n id: \"VC038\",\n title: \"Insecure File Upload Validation\",\n severity: \"high\",\n category: \"Injection\",\n description: \"File uploads validated only by extension (not MIME type or content) allow attackers to upload executable files disguised as images or documents.\",\n check(content, filePath) {\n if (!/upload|multer|formidable|busboy|multipart/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Check for extension-only validation\n const hasExtCheck = /\\.(?:endsWith|match|test)\\s*\\([^)]*(?:\\.jpg|\\.png|\\.pdf|\\.doc|ext)/i.test(content);\n const hasMimeCheck = /mimetype|content-type|file\\.type|mime|magic\\.detect|file-type/i.test(content);\n if (hasExtCheck && !hasMimeCheck) {\n matches.push(...findMatches(content, /upload|multer|formidable|busboy/gi, insecureFileUpload, filePath, () =>\n \"Validate file uploads by MIME type AND magic bytes, not just extension. Use the 'file-type' package to detect actual file type from content. Also enforce size limits.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC039 – Missing Dependency Lock File\n// ────────────────────────────────────────────\n\nexport const missingLockFile: CustomRule = {\n id: \"VC039\",\n title: \"Missing Dependency Lock File\",\n severity: \"medium\",\n category: \"Supply Chain\",\n description: \"Without a lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock), dependency versions are unpinned and vulnerable to supply chain attacks via version substitution.\",\n check(content, filePath) {\n // Only check .gitignore for lock files being ignored\n if (!filePath.endsWith(\".gitignore\")) return [];\n const ignoresLock = /package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock/i.test(content);\n if (ignoresLock) {\n return findMatches(content, /(?:package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock)/gi, missingLockFile, filePath, () =>\n \"Remove the lockfile from .gitignore. Lockfiles should be committed to prevent supply chain attacks. They ensure exact versions are installed across all environments.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC040 – Exposed .git Directory\n// ────────────────────────────────────────────\n\nexport const exposedGitDir: CustomRule = {\n id: \"VC040\",\n title: \"Exposed .git Directory via Web Server\",\n severity: \"critical\",\n category: \"Information Leakage\",\n description: \"Web server configs that don't block access to .git directories expose your entire source code, commit history, secrets, and credentials.\",\n check(content, filePath) {\n // Check web server configs\n if (!filePath.match(/(?:nginx|apache|httpd|caddy|\\.htaccess|vercel\\.json|netlify\\.toml|server\\.[jt]s)/i)) return [];\n // For static file servers, check they block .git\n if (/(?:static|serve|express\\.static|serveStatic|public)/i.test(content)) {\n const blocksGit = /\\.git|dotfiles|hidden/i.test(content);\n if (!blocksGit) {\n return findMatches(content, /(?:static|serve|express\\.static|serveStatic)\\s*\\(/g, exposedGitDir, filePath, () =>\n \"Block access to .git and other dotfiles in your static file server config. For Express: app.use('/.git', (req, res) => res.status(403).end()). For Nginx: location ~ /\\\\.git { deny all; }\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC041 – Server-Side Request Forgery (SSRF)\n// ────────────────────────────────────────────\n\nexport const ssrfVulnerability: CustomRule = {\n id: \"VC041\",\n title: \"Potential Server-Side Request Forgery (SSRF)\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Fetching URLs from user input without validation allows attackers to access internal services, cloud metadata endpoints (169.254.169.254), and private networks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Only check server-side files\n const isServerFile = /(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|handler|middleware)/i.test(filePath);\n if (!isServerFile) return [];\n const matches: RuleMatch[] = [];\n // Only flag direct user input passed to fetch — narrower pattern\n const patterns = [\n /(?:fetch|axios\\.get|axios\\.post|axios|got|request|http\\.get|https\\.get)\\s*\\(\\s*(?:req\\.(?:body|query|params))\\./gi,\n ];\n const hasValidation = /allowedHosts|allowedDomains|allowedUrls|safeDomain|whitelist|urlValidator|new URL.*hostname.*includes|isAllowedUrl|validateUrl|isValidUrl/i.test(content);\n if (hasValidation) return [];\n for (const p of patterns) {\n const rawMatches = findMatches(content, p, ssrfVulnerability, filePath, () =>\n \"Validate URLs against an allowlist before fetching. Block internal IPs: 127.0.0.1, 10.x, 172.16-31.x, 192.168.x, 169.254.169.254 (cloud metadata). Use: const url = new URL(input); if (!ALLOWED_HOSTS.includes(url.hostname)) throw new Error('Blocked');\"\n );\n // Skip if the variable passed to fetch is defined as a constant string earlier in the file\n for (const rm of rawMatches) {\n const lineText = content.split(\"\\n\")[rm.line - 1] || \"\";\n const varMatch = lineText.match(/(?:fetch|axios\\.\\w+|got|request|https?\\.get)\\s*\\(\\s*(\\w+)/);\n if (varMatch) {\n const varName = varMatch[1];\n // Check if this variable is defined as a const string literal earlier in the file\n const constDef = new RegExp(`const\\\\s+${varName}\\\\s*=\\\\s*[\"'\\`]https?://`, \"i\");\n if (constDef.test(content)) continue;\n }\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC042 – Mass Assignment\n// ────────────────────────────────────────────\n\nexport const massAssignment: CustomRule = {\n id: \"VC042\",\n title: \"Mass Assignment Vulnerability\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Spreading or assigning request body directly into database models allows attackers to set fields they shouldn't (e.g., isAdmin, role, verified).\",\n check(content, filePath) {\n const isApiFile = /(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|handler)/i.test(filePath);\n if (!isApiFile) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Object.assign(model, req.body)\n /Object\\.assign\\s*\\(\\s*(?:user|account|profile|record|doc|model|entity)[^,]*,\\s*(?:req\\.body|body|input|data)\\s*\\)/gi,\n // Spread req.body into create/update\n /(?:create|update|insert|save|findOneAndUpdate|updateOne|upsert)\\s*\\(\\s*\\{[^}]*\\.\\.\\.(?:req\\.body|body|input|data)/gi,\n // Direct req.body into DB\n /(?:create|insert|save)\\s*\\(\\s*(?:req\\.body|body)\\s*\\)/gi,\n ];\n const hasSanitization = /pick\\(|omit\\(|allowedFields|sanitize|whitelist|permit|strong_params/i.test(content);\n if (hasSanitization) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, massAssignment, filePath, () =>\n \"Never pass req.body directly to database operations. Explicitly pick allowed fields: const { name, email } = req.body; await db.create({ name, email });\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC043 – Timing Attack on Comparison\n// ────────────────────────────────────────────\n\nexport const timingAttack: CustomRule = {\n id: \"VC043\",\n title: \"Timing-Unsafe Secret Comparison\",\n severity: \"medium\",\n category: \"Cryptography\",\n description: \"Using === to compare secrets, tokens, or hashes leaks information via timing side-channels. Attackers can determine the correct value one character at a time.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n // Direct comparison of secrets/tokens/hashes\n const patterns = [\n /(?:token|secret|hash|digest|signature|hmac|apiKey|api_key)\\s*(?:===|!==)\\s*(?:req\\.|body\\.|params\\.|query\\.|input)/gi,\n /(?:^|[^.\\w])(?:req\\.|body\\.|params\\.|query\\.|input)[\\w.]*(?:token|secret|hash|digest|signature|hmac)\\s*(?:===|!==)/gim,\n ];\n const hasTimingSafe = /timingSafeEqual|constantTimeEqual|safeCompare|secureCompare/i.test(content);\n if (hasTimingSafe) return [];\n for (const p of patterns) {\n const raw = findMatches(content, p, timingAttack, filePath, () =>\n \"Use crypto.timingSafeEqual() for comparing secrets: crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)). This prevents timing-based side-channel attacks.\"\n );\n // Skip typeof type guards — they're not secret comparisons\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/typeof\\s/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC044 – Log Injection\n// ────────────────────────────────────────────\n\nexport const logInjection: CustomRule = {\n id: \"VC044\",\n title: \"Potential Log Injection\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Logging unsanitized user input allows attackers to forge log entries, inject malicious content, or exploit log aggregation systems via newlines and special characters.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const isServerFile = /(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|middleware|handler)/i.test(filePath);\n if (!isServerFile) return [];\n const matches: RuleMatch[] = [];\n // console.log/warn/error with req.body/query/params directly\n const patterns = [\n /console\\.(?:log|warn|error|info)\\s*\\([^)]*(?:req\\.body|req\\.query|req\\.params|req\\.headers)\\s*\\)/gi,\n /(?:logger|log)\\.(?:info|warn|error|debug)\\s*\\([^)]*(?:req\\.body|req\\.query|req\\.params)\\s*\\)/gi,\n ];\n const hasSanitization = /sanitize|escape|JSON\\.stringify|replace.*\\\\n/i.test(content);\n if (hasSanitization) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, logInjection, filePath, () =>\n \"Sanitize user input before logging: strip newlines and control characters. Use JSON.stringify() or a structured logger (e.g., pino, winston) that escapes values automatically.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC045 – Weak Password Requirements\n// ────────────────────────────────────────────\n\nexport const weakPasswordRequirements: CustomRule = {\n id: \"VC045\",\n title: \"Weak Password Requirements\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Registration or password-change endpoints without minimum length or complexity validation allow weak passwords that are easily brute-forced.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/(?:password|passwd|pwd)/i.test(content)) return [];\n if (!/(?:register|signup|sign.up|createUser|create.user|changePassword|resetPassword|set.password)/i.test(content) &&\n !/(?:\\/api\\/|routes?\\/|controllers?\\/)/i.test(filePath)) return [];\n // Check for validation anywhere in the file — broader detection of mitigation\n const hasValidation = /(?:password|pwd).*(?:\\.length|minLength|minlength|min_length)\\s*(?:>=?|<|>)\\s*\\d|(?:password|pwd).*(?:match|test|regex|pattern)|zxcvbn|password-validator|passwordStrength|isStrongPassword|joi\\.|yup\\.|zod\\.|validate|schema/i.test(content);\n if (hasValidation) return [];\n const hasPasswordHandling = /(?:password|pwd)\\s*[:=]\\s*(?:req\\.body|body|input|params|args)\\./i.test(content);\n if (!hasPasswordHandling) return [];\n const rawMatches = findMatches(content, /(?:password|pwd)\\s*[:=]\\s*(?:req\\.body|body|input|params|args)\\./gi, weakPasswordRequirements, filePath, () =>\n \"Enforce minimum password requirements: at least 8 characters, mix of letters/numbers/symbols. Use a library like zxcvbn for strength estimation.\"\n );\n // Skip if validation logic exists within 10 lines of the password assignment\n const lines = content.split(\"\\n\");\n const validationPattern = /\\.length|minLength|minlength|min_length|match|test|regex|pattern|validate|schema|zxcvbn|isStrongPassword/i;\n return rawMatches.filter((rm) => {\n const start = Math.max(0, rm.line - 1 - 10);\n const end = Math.min(lines.length, rm.line - 1 + 10);\n const nearby = lines.slice(start, end).join(\"\\n\");\n return !validationPattern.test(nearby);\n });\n },\n};\n\n// ────────────────────────────────────────────\n// VC046 – Session Fixation\n// ────────────────────────────────────────────\n\nexport const sessionFixation: CustomRule = {\n id: \"VC046\",\n title: \"Session Fixation Risk\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Not regenerating session IDs after login allows attackers to pre-set a session ID and hijack the authenticated session.\",\n check(content, filePath) {\n // Only applies to server-side code files, not iOS/Swift, docs, or tests\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/(?:login|signin|sign.in|authenticate)/i.test(content)) return [];\n if (!/session/i.test(content)) return [];\n // Check if session is regenerated after auth\n const hasRegenerate = /regenerate|destroy.*create|req\\.session\\.id\\s*=|session\\.regenerateId|rotateSession|clearCookies/i.test(content);\n if (hasRegenerate) return [];\n // Must have a login function definition, not just a reference or import\n const hasLogin = /(?:function\\s+(?:login|signin|authenticate)|(?:login|signin|authenticate)\\s*(?:=\\s*(?:async\\s*)?\\(|:\\s*(?:async\\s*)?\\())/i.test(content);\n if (!hasLogin) return [];\n return findMatches(content, /(?:function\\s+(?:login|signin|authenticate)|(?:login|signin|authenticate)\\s*(?:=\\s*(?:async\\s*)?\\())/gi, sessionFixation, filePath, () =>\n \"Regenerate the session ID after successful login: req.session.regenerate() (Express) or equivalent. This prevents session fixation attacks.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC047 – Missing Brute Force Protection\n// ────────────────────────────────────────────\n\nexport const missingBruteForce: CustomRule = {\n id: \"VC047\",\n title: \"Login Without Brute Force Protection\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Login endpoints without rate limiting, account lockout, or progressive delays are vulnerable to credential stuffing and brute force attacks.\",\n check(content, filePath) {\n const isLoginFile = /(?:login|signin|sign.in|auth)/i.test(filePath) || /(?:login|signin|authenticate).*(?:post|handler|route)/i.test(content);\n if (!isLoginFile) return [];\n if (!/(?:password|credential)/i.test(content)) return [];\n const hasBruteForce = /rate.?limit|throttle|lockout|maxAttempts|max_attempts|failedAttempts|loginAttempts|brute|express-brute|express-rate-limit|slowDown/i.test(content);\n if (hasBruteForce) return [];\n return findMatches(content, /\\.(post|handler)\\s*\\([^)]*(?:login|signin|auth)/gi, missingBruteForce, filePath, () =>\n \"Add brute force protection to login endpoints: rate limiting (5 attempts/minute), progressive delays, or account lockout after N failures. Use express-rate-limit or similar.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC048 – NoSQL Injection\n// ────────────────────────────────────────────\n\nexport const nosqlInjection: CustomRule = {\n id: \"VC048\",\n title: \"Potential NoSQL Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Passing unsanitized user input directly into MongoDB/NoSQL queries allows attackers to bypass authentication, extract data, or modify queries using operators like $gt, $ne, $regex.\",\n check(content, filePath) {\n if (!/(?:mongo|mongoose|findOne|findById|find\\(|collection|aggregate)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Direct req.body in MongoDB queries\n /\\.find(?:One)?\\s*\\(\\s*(?:req\\.body|body|input|params)\\s*\\)/gi,\n /\\.find(?:One)?\\s*\\(\\s*\\{[^}]*:\\s*(?:req\\.body|body|input|params)\\./gi,\n // $where with user input\n /\\$where\\s*:\\s*(?![\"'`])/g,\n // Direct variable in query without sanitization\n /\\.(?:findOne|findById|deleteOne|updateOne|findOneAndUpdate)\\s*\\(\\s*\\{[^}]*:\\s*(?:req\\.(?:body|query|params))\\./gi,\n ];\n const hasSanitization = /sanitize|escape|mongo-sanitize|express-mongo-sanitize|validator|typeof.*===.*string/i.test(content);\n if (hasSanitization) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, nosqlInjection, filePath, () =>\n \"Sanitize MongoDB query inputs: use express-mongo-sanitize, validate types (ensure strings aren't objects), and avoid $where. Example: if (typeof input !== 'string') throw new Error('Invalid input');\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC049 – Exposed DB Credentials in Config\n// ────────────────────────────────────────────\n\nexport const exposedDBCredentials: CustomRule = {\n id: \"VC049\",\n title: \"Database Credentials in Config File\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Database connection strings with embedded usernames and passwords in committed config files expose credentials to anyone with repo access.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (!filePath.match(/(?:config|setting|database|db|knexfile|sequelize|drizzle|prisma)/i) && !filePath.match(/\\.(json|yaml|yml|toml|js|ts)$/)) return [];\n if (filePath.match(/\\.env/)) return []; // Handled by VC002\n const patterns = [\n // Connection strings with credentials\n /(?:host|server|database|db).*(?:password|passwd|pwd)\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/gi,\n // Inline connection URLs with credentials\n /(?:connection|database|db).*(?:postgres|mysql|mongodb|redis):\\/\\/[^:]+:[^@]+@/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedDBCredentials, filePath, () =>\n \"Move database credentials to environment variables. Use: process.env.DATABASE_URL instead of hardcoding connection strings in config files.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC050 – Missing DB Connection Encryption\n// ────────────────────────────────────────────\n\nexport const missingDBEncryption: CustomRule = {\n id: \"VC050\",\n title: \"Database Connection Without SSL/TLS\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Database connections without SSL/TLS encryption transmit credentials and data in plaintext, allowing eavesdropping on the network.\",\n check(content, filePath) {\n if (!/(?:createConnection|createPool|createClient|connect|new.*Client|knex|sequelize|drizzle)/i.test(content)) return [];\n if (!/(?:postgres|mysql|mariadb|pg|mongo)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // SSL explicitly disabled\n const sslDisabled = [\n /ssl\\s*:\\s*false/gi,\n /sslmode\\s*[:=]\\s*[\"'`]?disable[\"'`]?/gi,\n /rejectUnauthorized\\s*:\\s*false/gi,\n ];\n for (const p of sslDisabled) {\n matches.push(...findMatches(content, p, missingDBEncryption, filePath, () =>\n \"Enable SSL/TLS for database connections: { ssl: { rejectUnauthorized: true } }. In production, always verify server certificates.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC051 – GraphQL Introspection Enabled\n// ────────────────────────────────────────────\n\nexport const graphqlIntrospection: CustomRule = {\n id: \"VC051\",\n title: \"GraphQL Introspection Enabled in Production\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"GraphQL introspection exposes your entire API schema, types, queries, and mutations to attackers, making it easy to find attack vectors.\",\n check(content, filePath) {\n if (!/graphql/i.test(content) && !/graphql/i.test(filePath)) return [];\n const matches: RuleMatch[] = [];\n // Introspection explicitly enabled or not disabled\n if (/introspection\\s*:\\s*true/i.test(content)) {\n matches.push(...findMatches(content, /introspection\\s*:\\s*true/gi, graphqlIntrospection, filePath, () =>\n \"Disable GraphQL introspection in production: introspection: process.env.NODE_ENV !== 'production'. This prevents schema exposure.\"\n ));\n }\n // GraphQL server setup without introspection config\n if (/(?:ApolloServer|GraphQLServer|createYoga|buildSchema|makeExecutableSchema)\\s*\\(/i.test(content)) {\n if (!/introspection/i.test(content)) {\n matches.push(...findMatches(content, /(?:ApolloServer|GraphQLServer|createYoga)\\s*\\(/gi, graphqlIntrospection, filePath, () =>\n \"Explicitly disable introspection in production: new ApolloServer({ introspection: process.env.NODE_ENV !== 'production' })\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC052 – Missing Request Size Limit\n// ────────────────────────────────────────────\n\nexport const missingRequestSizeLimit: CustomRule = {\n id: \"VC052\",\n title: \"Missing Request Body Size Limit\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"Express/Hono/Fastify servers without request body size limits are vulnerable to denial-of-service via oversized payloads that exhaust memory.\",\n check(content, filePath) {\n if (!/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) return [];\n if (!/(?:express|hono|fastify|koa)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // express.json() without limit\n if (/express\\.json\\s*\\(\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /express\\.json\\s*\\(\\s*\\)/g, missingRequestSizeLimit, filePath, () =>\n \"Set a body size limit: express.json({ limit: '1mb' }). Without this, attackers can send huge payloads to crash your server.\"\n ));\n }\n // bodyParser without limit\n if (/bodyParser\\.json\\s*\\(\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /bodyParser\\.json\\s*\\(\\s*\\)/g, missingRequestSizeLimit, filePath, () =>\n \"Set a body size limit: bodyParser.json({ limit: '1mb' }).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC053 – Hardcoded IP/Host Allowlist\n// ────────────────────────────────────────────\n\nexport const hardcodedIPAllowlist: CustomRule = {\n id: \"VC053\",\n title: \"Hardcoded IP or Host Allowlist\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Hardcoded IP addresses or hostnames in allowlists are brittle and hard to update. They should be in environment variables or configuration files.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\") || filePath.match(/\\.(md|txt)$/)) return [];\n const matches: RuleMatch[] = [];\n // Arrays of IPs used in access control\n const patterns = [\n /(?:allowedIPs|allowed_ips|whitelist|allowlist|trustedHosts)\\s*[:=]\\s*\\[\\s*[\"'`]\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/gi,\n /(?:allowedIPs|allowed_ips|whitelist|allowlist|trustedHosts)\\s*[:=]\\s*\\[\\s*[\"'`][\\w.-]+\\.(?:com|net|org|io)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, hardcodedIPAllowlist, filePath, () =>\n \"Move IP/host allowlists to environment variables or a config file: const allowed = process.env.ALLOWED_IPS?.split(',') || [];\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC054 – Sensitive Data in localStorage\n// ────────────────────────────────────────────\n\nexport const sensitiveLocalStorage: CustomRule = {\n id: \"VC054\",\n title: \"Sensitive Data in localStorage\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Storing tokens, passwords, or secrets in localStorage is insecure — it's accessible to any JavaScript on the page (XSS) and persists indefinitely. Use httpOnly cookies instead.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?|vue|svelte)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Skip test-related files (mock, spec in the path)\n if (/mock|spec/i.test(filePath)) return [];\n // Skip if localStorage.removeItem is used (cleanup code, not storage)\n if (!/localStorage\\.setItem|localStorage\\[/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /localStorage\\.setItem\\s*\\(\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret)/gi,\n /localStorage\\s*\\[\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveLocalStorage, filePath, () =>\n \"Don't store tokens/secrets in localStorage — use httpOnly cookies instead. localStorage is accessible to any XSS attack. For session tokens, set them as httpOnly, secure, sameSite cookies.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC055 – Exposed Source Maps in Production\n// ────────────────────────────────────────────\n\nexport const exposedSourceMaps: CustomRule = {\n id: \"VC055\",\n title: \"Source Maps Exposed in Production\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"Source map files (.map) in production expose your original source code, comments, and internal logic to anyone who downloads them.\",\n check(content, filePath) {\n // Check build configs for source maps in production\n if (!filePath.match(/(?:webpack|vite|rollup|next)\\.config|tsconfig/i)) return [];\n const matches: RuleMatch[] = [];\n // Source maps enabled without environment check\n if (/(?:sourceMap|source-map|sourcemap)\\s*[:=]\\s*true/i.test(content)) {\n const hasEnvCheck = /process\\.env\\.NODE_ENV|NODE_ENV|production/i.test(content);\n if (!hasEnvCheck) {\n matches.push(...findMatches(content, /(?:sourceMap|source-map|sourcemap)\\s*[:=]\\s*true/gi, exposedSourceMaps, filePath, () =>\n \"Disable source maps in production builds: sourceMap: process.env.NODE_ENV !== 'production'. Or use 'hidden-source-map' to generate maps without exposing them.\"\n ));\n }\n }\n // productionSourceMap in Vue\n if (/productionSourceMap\\s*:\\s*true/i.test(content)) {\n matches.push(...findMatches(content, /productionSourceMap\\s*:\\s*true/gi, exposedSourceMaps, filePath, () =>\n \"Set productionSourceMap: false to avoid exposing source code in production.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC056 – Clickjacking / Missing X-Frame-Options\n// ────────────────────────────────────────────\n\nexport const clickjacking: CustomRule = {\n id: \"VC056\",\n title: \"Clickjacking — Missing X-Frame-Options\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Without X-Frame-Options or frame-ancestors CSP directive, your page can be embedded in an attacker's iframe for UI redress (clickjacking) attacks.\",\n check(content, filePath) {\n // Check HTML files\n if (filePath.match(/\\.(html|htm)$/)) {\n if (!/X-Frame-Options|frame-ancestors/i.test(content)) {\n return [{\n rule: \"VC056\", title: clickjacking.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta http-equiv=\"X-Frame-Options\" content=\"DENY\"> or set frame-ancestors in CSP to prevent clickjacking.'\n }];\n }\n }\n // Check server configs\n if (/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) {\n if (/(?:express|hono|fastify|koa)/i.test(content)) {\n if (!/X-Frame-Options|frame-ancestors|helmet/i.test(content)) {\n return findMatches(content, /(?:express|hono|fastify|koa)\\s*\\(/gi, clickjacking, filePath, () =>\n \"Add X-Frame-Options header: res.setHeader('X-Frame-Options', 'DENY'). Or use helmet: app.use(helmet()) which sets this and other security headers.\"\n );\n }\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC057 – Overly Permissive IAM/Cloud Roles\n// ────────────────────────────────────────────\n\nexport const overlyPermissiveIAM: CustomRule = {\n id: \"VC057\",\n title: \"Overly Permissive IAM/Cloud Permissions\",\n severity: \"critical\",\n category: \"Authorization\",\n description: \"Wildcard (*) permissions in AWS IAM, GCP, or Terraform configs grant unrestricted access, violating the principle of least privilege.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(tf|hcl|json|yaml|yml)$/) && !filePath.match(/(?:iam|policy|role|permission)/i)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // AWS IAM wildcard\n /[\"'`]Action[\"'`]\\s*:\\s*[\"'`]\\*[\"'`]/g,\n /[\"'`]Resource[\"'`]\\s*:\\s*[\"'`]\\*[\"'`]/g,\n // Terraform aws_iam\n /actions\\s*=\\s*\\[\\s*[\"'`]\\*[\"'`]\\s*\\]/g,\n /resources\\s*=\\s*\\[\\s*[\"'`]\\*[\"'`]\\s*\\]/g,\n // GCP bindings\n /role\\s*[:=]\\s*[\"'`]roles\\/(?:owner|editor)[\"'`]/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, overlyPermissiveIAM, filePath, () =>\n \"Follow the principle of least privilege: replace wildcard (*) with specific actions and resources. Example: 'Action': 's3:GetObject' instead of '*'.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC058 – Docker Running as Root\n// ────────────────────────────────────────────\n\nexport const dockerRunAsRoot: CustomRule = {\n id: \"VC058\",\n title: \"Docker Container Running as Root\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Containers running as root give attackers full system access if they escape the container. Always run as a non-root user.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const hasUser = /^\\s*USER\\s+/m.test(content);\n if (hasUser) return [];\n return [{\n rule: \"VC058\", title: dockerRunAsRoot.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Add a USER directive: RUN addgroup -S app && adduser -S app -G app\\\\nUSER app. Place it after installing dependencies but before COPY/CMD.\"\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC059 – Exposed Ports in Docker Compose\n// ────────────────────────────────────────────\n\nexport const exposedDockerPorts: CustomRule = {\n id: \"VC059\",\n title: \"Docker Compose Binding to All Interfaces\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Binding ports to 0.0.0.0 (default) in Docker Compose exposes services to the entire network. Bind to 127.0.0.1 for local-only access.\",\n check(content, filePath) {\n if (!filePath.match(/docker-compose|compose\\.(yaml|yml)$/i)) return [];\n const matches: RuleMatch[] = [];\n // ports: \"3000:3000\" or \"8080:80\" without binding to 127.0.0.1\n const portPattern = /ports:\\s*\\n(?:\\s*-\\s*[\"'`]?\\d+:\\d+[\"'`]?\\s*\\n?)+/g;\n if (portPattern.test(content) && !/127\\.0\\.0\\.1:/i.test(content)) {\n matches.push(...findMatches(content, /^\\s*-\\s*[\"'`]?\\d+:\\d+[\"'`]?/gm, exposedDockerPorts, filePath, () =>\n \"Bind to localhost only: '127.0.0.1:3000:3000' instead of '3000:3000'. This prevents external network access to the service.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC060 – Weak Hashing Algorithm\n// ────────────────────────────────────────────\n\nexport const weakHashing: CustomRule = {\n id: \"VC060\",\n title: \"Weak Hashing Algorithm for Passwords\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"MD5 and SHA1/SHA256 are too fast for password hashing — they can be brute-forced at billions of attempts per second. Use bcrypt, scrypt, or argon2 instead.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n // Skip if file also uses strong hashing (indicates migration or comparison code)\n if (/bcrypt|scrypt|argon2/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // MD5/SHA used with password context\n const patterns = [\n /(?:md5|sha1|sha256|sha512)\\s*\\([^)]*(?:password|passwd|pwd)/gi,\n /createHash\\s*\\(\\s*[\"'`](?:md5|sha1|sha256)[\"'`]\\).*(?:password|passwd|pwd)/gi,\n /(?:password|passwd|pwd).*createHash\\s*\\(\\s*[\"'`](?:md5|sha1|sha256)[\"'`]\\)/gi,\n /hashlib\\.(?:md5|sha1|sha256)\\s*\\([^)]*(?:password|passwd|pwd)/gi,\n /Digest::(?:MD5|SHA1|SHA256).*(?:password|passwd|pwd)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, weakHashing, filePath, () =>\n \"Use bcrypt, scrypt, or argon2 for password hashing — they're intentionally slow. Example: const hash = await bcrypt.hash(password, 12);\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC061 – Disabled TLS Certificate Verification\n// ────────────────────────────────────────────\n\nexport const disabledTLSVerification: CustomRule = {\n id: \"VC061\",\n title: \"Disabled TLS Certificate Verification\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"Disabling TLS certificate verification (NODE_TLS_REJECT_UNAUTHORIZED=0 or rejectUnauthorized:false) makes all HTTPS connections vulnerable to man-in-the-middle attacks.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const patterns = [\n /NODE_TLS_REJECT_UNAUTHORIZED\\s*[:=]\\s*[\"'`]?0[\"'`]?/g,\n /rejectUnauthorized\\s*:\\s*false/g,\n /verify\\s*[:=]\\s*false.*(?:ssl|tls|cert|https)/gi,\n /PYTHONHTTPSVERIFY\\s*[:=]\\s*[\"'`]?0[\"'`]?/g,\n /ssl_verify\\s*[:=]\\s*false/gi,\n /InsecureSkipVerify\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, disabledTLSVerification, filePath, () =>\n \"Never disable TLS certificate verification in production. Fix the root cause: install the correct CA certificate, or use NODE_EXTRA_CA_CERTS for custom CAs.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC062 – Hardcoded Encryption Key/IV\n// ────────────────────────────────────────────\n\nexport const hardcodedEncryptionKey: CustomRule = {\n id: \"VC062\",\n title: \"Hardcoded Encryption Key or IV\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"Hardcoded encryption keys and initialization vectors (IVs) in source code can be extracted to decrypt all data. IVs must be random per encryption operation.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (isTestFile(filePath)) return [];\n // Skip HTML/XML/config files — meta tags and config values are not encryption keys\n if (filePath.match(/\\.(html|htm|xml|plist|svg|xhtml)$/)) return [];\n // Skip files without any crypto-related code\n if (!/(?:cipher|encrypt|decrypt|crypto|aes|createCipher)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Encryption key as string literal\n /(?:encryption_key|encryptionKey|cipher_key|cipherKey|aes_key|AES_KEY|ENCRYPTION_KEY)\\s*[:=]\\s*[\"'`][^\"'`]{8,}[\"'`]/g,\n // createCipheriv with hardcoded key\n /createCipher(?:iv)?\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*[\"'`][^\"'`]+[\"'`]/g,\n // Buffer.from with hardcoded key near cipher context\n /(?:key|iv|nonce)\\s*[:=]\\s*Buffer\\.from\\s*\\(\\s*[\"'`][^\"'`]{8,}[\"'`]/gi,\n // Static IV (should be random) — only in crypto context\n /(?:^|[\\s,({])(?:iv|nonce|initialVector)\\s*[:=]\\s*[\"'`][0-9a-fA-F]{16,}[\"'`]/gi,\n ];\n for (const p of patterns) {\n const rawMatches = findMatches(content, p, hardcodedEncryptionKey, filePath, () =>\n \"Move encryption keys to environment variables. Generate IVs randomly per operation: crypto.randomBytes(16). Never reuse IVs.\"\n );\n // Skip matches on lines containing CSP meta tags or security headers\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const lineText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/meta\\s+http-equiv|Content-Security-Policy|X-Frame-Options|X-Content-Type/i.test(lineText)) continue;\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC063 – dangerouslySetInnerHTML\n// ────────────────────────────────────────────\n\nexport const dangerousInnerHTML: CustomRule = {\n id: \"VC063\",\n title: \"Unsanitized dangerouslySetInnerHTML\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Using dangerouslySetInnerHTML without sanitization (DOMPurify) enables XSS attacks. User-controlled content injected as raw HTML can execute arbitrary JavaScript.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/dangerouslySetInnerHTML/i.test(content)) return [];\n const hasSanitize = /DOMPurify|sanitize|purify|xss|sanitizeHtml|isomorphic-dompurify/i.test(content);\n if (hasSanitize) return [];\n // Find all dangerouslySetInnerHTML usages, but skip if the value is a\n // static constant (all-caps like THEME_INIT_SCRIPT), a string literal,\n // or a marked safe value — these are not user-controlled input\n const findings: RuleMatch[] = [];\n const re = /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html\\s*:\\s*([^}]+)\\}/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const value = m[1].trim();\n // Skip static constants (UPPER_CASE), string literals, or template strings without interpolation\n if (/^[A-Z][A-Z0-9_]+$/.test(value)) continue;\n if (/^[\"'`][^$]*[\"'`]$/.test(value)) continue;\n // Skip JSON-LD structured data (standard SEO practice, developer-controlled)\n if (/JSON\\.stringify/i.test(value)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC063\", title: dangerousInnerHTML.title,\n severity: \"critical\" as const, category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Sanitize HTML before using dangerouslySetInnerHTML: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}. Install: npm install dompurify\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC064 – Exposed Next.js Server Actions\n// ────────────────────────────────────────────\n\nexport const exposedServerActions: CustomRule = {\n id: \"VC064\",\n title: \"Next.js Server Action Without Auth Check\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Next.js Server Actions ('use server') are publicly callable endpoints. Without authentication checks, anyone can invoke them directly.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n if (!/[\"']use server[\"']/i.test(content)) return [];\n const hasAuth = /getServerSession|auth\\(\\)|currentUser|getUser|requireAuth|requireUser|requireUserForApi|session|clerk|getAuth|verifyCronSecret|checkApiKey/i.test(content);\n if (hasAuth) return [];\n // Check if there are exported async functions (server actions)\n if (/export\\s+async\\s+function/i.test(content)) {\n return findMatches(content, /export\\s+async\\s+function\\s+\\w+/g, exposedServerActions, filePath, () =>\n \"Add authentication to Server Actions: const session = await getServerSession(); if (!session) throw new Error('Unauthorized');\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC065 – Unprotected Next.js API Routes\n// ────────────────────────────────────────────\n\nexport const unprotectedAPIRoutes: CustomRule = {\n id: \"VC065\",\n title: \"Unprotected Next.js API Route\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Next.js API routes under /api/ without authentication middleware can be called by anyone, exposing data or mutations.\",\n check(content, filePath) {\n if (!filePath.match(/\\/api\\/.*\\.(jsx?|tsx?)$/) && !filePath.match(/\\/app\\/api\\/.*route\\.(jsx?|tsx?)$/)) return [];\n // Skip health/public endpoints\n if (/health|status|public|webhook/i.test(filePath)) return [];\n const hasAuth = /getServerSession|auth\\(\\)|currentUser|getUser|requireAuth|session|clerk|getAuth|verifyToken|authenticate|middleware/i.test(content);\n if (hasAuth) return [];\n const hasHandler = /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)|export\\s+default/i.test(content);\n if (hasHandler) {\n return findMatches(content, /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/g, unprotectedAPIRoutes, filePath, () =>\n \"Add authentication to API routes: const session = await getServerSession(authOptions); if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC066 – Client Component Using Secrets\n// ────────────────────────────────────────────\n\nexport const clientComponentSecret: CustomRule = {\n id: \"VC066\",\n title: \"Secret Used in Client Component\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Using server-side secrets (process.env without NEXT_PUBLIC_) in 'use client' components exposes them in the browser bundle.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n if (!/[\"']use client[\"']/i.test(content)) return [];\n // process.env without NEXT_PUBLIC_ prefix in a client component\n const pattern = /process\\.env\\.(?!NEXT_PUBLIC_)[A-Z_]{3,}/g;\n if (pattern.test(content)) {\n return findMatches(content, /process\\.env\\.(?!NEXT_PUBLIC_)[A-Z_]{3,}/g, clientComponentSecret, filePath, () =>\n \"Server-side env vars are not available in client components and may leak in builds. Move this logic to a Server Component or API route.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC067 – Insecure Deep Link Handling\n// ────────────────────────────────────────────\n\nexport const insecureDeepLink: CustomRule = {\n id: \"VC067\",\n title: \"Insecure Deep Link Handling\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Deep links that navigate or execute actions without validating the URL scheme, host, or parameters can be exploited for phishing or unauthorized actions.\",\n check(content, filePath) {\n if (!/(?:Linking|DeepLinking|deep.?link|handleURL|openURL|url.?scheme)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // React Native Linking without validation\n const patterns = [\n /Linking\\.addEventListener\\s*\\([^)]*(?:url|link)/gi,\n /Linking\\.getInitialURL\\s*\\(\\)/g,\n /handleOpenURL|handleDeepLink|onDeepLink/gi,\n ];\n const hasValidation = /allowedSchemes|allowedHosts|validateURL|isAllowedURL|whitelist|URL.*hostname.*includes/i.test(content);\n if (hasValidation) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureDeepLink, filePath, () =>\n \"Validate deep link URLs: check the scheme and host against an allowlist before navigating or executing actions.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC068 – Sensitive Data in AsyncStorage\n// ────────────────────────────────────────────\n\nexport const sensitiveAsyncStorage: CustomRule = {\n id: \"VC068\",\n title: \"Sensitive Data in AsyncStorage\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"React Native AsyncStorage is unencrypted. Storing tokens, passwords, or secrets there makes them readable by other apps or anyone with device access.\",\n check(content, filePath) {\n if (!/AsyncStorage/i.test(content)) return [];\n const patterns = [\n /AsyncStorage\\.setItem\\s*\\(\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret|private_key)/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveAsyncStorage, filePath, () =>\n \"Use react-native-keychain or expo-secure-store instead of AsyncStorage for sensitive data. These use the OS keychain (encrypted).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC069 – Missing Certificate Pinning\n// ────────────────────────────────────────────\n\nexport const missingCertPinning: CustomRule = {\n id: \"VC069\",\n title: \"Missing Certificate Pinning in Mobile App\",\n severity: \"medium\",\n category: \"Cryptography\",\n description: \"Mobile apps without SSL certificate pinning are vulnerable to MITM attacks via compromised or rogue CAs. Pin your API server's certificate.\",\n check(content, filePath) {\n // Only for mobile project files\n if (!/(?:react.native|expo|android|ios|mobile)/i.test(filePath) && !/(?:React.*Native|expo)/i.test(content)) return [];\n if (!/(?:fetch|axios|http|api|request)/i.test(content)) return [];\n // Check for HTTP client setup without pinning\n if (/axios\\.create|new\\s+(?:HttpClient|ApiClient)/i.test(content)) {\n const hasPinning = /pinning|certificate|cert|ssl|TrustKit|react-native-ssl-pinning|cert-pinner/i.test(content);\n if (!hasPinning) {\n return findMatches(content, /axios\\.create|new\\s+(?:HttpClient|ApiClient)/gi, missingCertPinning, filePath, () =>\n \"Add SSL certificate pinning: use react-native-ssl-pinning or TrustKit. This prevents MITM attacks via rogue certificates.\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC070 – Android Debuggable Flag\n// ────────────────────────────────────────────\n\nexport const androidDebuggable: CustomRule = {\n id: \"VC070\",\n title: \"Android App Debuggable in Production\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"android:debuggable='true' in AndroidManifest.xml allows attackers to attach debuggers, inspect memory, and bypass security controls.\",\n check(content, filePath) {\n if (!filePath.match(/AndroidManifest\\.xml$/i)) return [];\n if (/android:debuggable\\s*=\\s*[\"']true[\"']/i.test(content)) {\n return findMatches(content, /android:debuggable\\s*=\\s*[\"']true[\"']/gi, androidDebuggable, filePath, () =>\n \"Remove android:debuggable='true' or set it to false. Debug builds should use build variants, not manifest flags.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC071 – Django DEBUG=True\n// ────────────────────────────────────────────\n\nexport const djangoDebug: CustomRule = {\n id: \"VC071\",\n title: \"Django DEBUG Mode Enabled\",\n severity: \"critical\",\n category: \"Configuration\",\n description: \"Django with DEBUG=True exposes detailed error pages with source code, database queries, environment variables, and installed apps to anyone.\",\n check(content, filePath) {\n if (!filePath.match(/settings\\.py$/i) && !filePath.match(/config.*\\.py$/i)) return [];\n if (/^\\s*DEBUG\\s*=\\s*True\\s*$/m.test(content)) {\n const hasEnvCheck = /os\\.environ|env\\(|config\\(|getenv/i.test(content);\n if (!hasEnvCheck) {\n return findMatches(content, /^\\s*DEBUG\\s*=\\s*True/gm, djangoDebug, filePath, () =>\n \"Use environment variable: DEBUG = os.environ.get('DEBUG', 'False') == 'True'. Never hardcode DEBUG=True.\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC072 – Flask Hardcoded Secret Key\n// ────────────────────────────────────────────\n\nexport const flaskSecretKey: CustomRule = {\n id: \"VC072\",\n title: \"Flask Secret Key Hardcoded\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Hardcoded Flask secret_key allows attackers to forge sessions, CSRF tokens, and signed cookies. Must be a random value from environment variables.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const patterns = [\n /(?:app\\.)?secret_key\\s*=\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /(?:app\\.config)\\s*\\[\\s*[\"']SECRET_KEY[\"']\\s*\\]\\s*=\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n ];\n const hasEnv = /os\\.environ|env\\(|config\\(|getenv/i.test(content);\n if (hasEnv) return [];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, flaskSecretKey, filePath, () =>\n \"Use environment variable: app.secret_key = os.environ['SECRET_KEY']. Generate with: python -c \\\"import secrets; print(secrets.token_hex(32))\\\"\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC073 – Pickle Deserialization\n// ────────────────────────────────────────────\n\nexport const pickleDeserialization: CustomRule = {\n id: \"VC073\",\n title: \"Unsafe Pickle Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"pickle.loads() on untrusted data allows arbitrary code execution. An attacker can craft a pickle payload that runs system commands on your server.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /pickle\\.loads?\\s*\\(/g,\n /cPickle\\.loads?\\s*\\(/g,\n /shelve\\.open\\s*\\(/g,\n /yaml\\.load\\s*\\([^)]*(?!Loader\\s*=\\s*yaml\\.SafeLoader)/g,\n ];\n const hasSafe = /restricted_loads|SafeUnpickler|safe_load|yaml\\.safe_load/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, pickleDeserialization, filePath, () =>\n \"Never unpickle untrusted data — it allows arbitrary code execution. Use JSON for data exchange, or yaml.safe_load() for YAML.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC074 – Missing CSRF Protection (Django)\n// ────────────────────────────────────────────\n\nexport const missingCSRF: CustomRule = {\n id: \"VC074\",\n title: \"CSRF Protection Disabled\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Using @csrf_exempt on state-changing views (POST/PUT/DELETE) allows attackers to forge requests from other sites, performing actions as authenticated users.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const matches: RuleMatch[] = [];\n if (/csrf_exempt/i.test(content)) {\n matches.push(...findMatches(content, /@csrf_exempt/g, missingCSRF, filePath, () =>\n \"Remove @csrf_exempt and use proper CSRF tokens. For APIs, use token-based auth (JWT) instead of session cookies.\"\n ));\n }\n // Also check for CsrfViewMiddleware removal\n if (/MIDDLEWARE.*=.*\\[/s.test(content) && !/CsrfViewMiddleware/i.test(content) && /django/i.test(content)) {\n matches.push(...findMatches(content, /MIDDLEWARE\\s*=/g, missingCSRF, filePath, () =>\n \"Re-add 'django.middleware.csrf.CsrfViewMiddleware' to MIDDLEWARE. CSRF protection is essential for session-based auth.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC075 – GitHub Actions Script Injection\n// ────────────────────────────────────────────\n\nexport const githubActionsInjection: CustomRule = {\n id: \"VC075\",\n title: \"GitHub Actions Script Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Using ${{ github.event.* }} directly in 'run:' steps allows attackers to inject shell commands via PR titles, issue bodies, or branch names.\",\n check(content, filePath) {\n if (!filePath.match(/\\.github\\/workflows\\/.*\\.(yml|yaml)$/i)) return [];\n const matches: RuleMatch[] = [];\n // Direct interpolation in run steps\n const patterns = [\n /run:.*\\$\\{\\{\\s*github\\.event\\.(?:issue|pull_request|comment|review|head_commit)\\.(?:title|body|message)/gi,\n /run:.*\\$\\{\\{\\s*github\\.event\\.(?:inputs|head_ref|base_ref)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, githubActionsInjection, filePath, () =>\n \"Never use ${{ github.event.* }} directly in 'run:'. Pass it as an environment variable: env: TITLE: ${{ github.event.issue.title }} then use $TITLE in the script.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC076 – Secrets in CI Config\n// ────────────────────────────────────────────\n\nexport const secretsInCI: CustomRule = {\n id: \"VC076\",\n title: \"Hardcoded Secrets in CI/CD Config\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Hardcoded tokens, passwords, or API keys in CI/CD workflow files are visible to anyone with repo access. Use encrypted secrets instead.\",\n check(content, filePath) {\n if (!filePath.match(/\\.github\\/workflows\\/|\\.gitlab-ci|Jenkinsfile|\\.circleci|bitbucket-pipelines/i)) return [];\n const matches: RuleMatch[] = [];\n // Hardcoded values that look like secrets (not using ${{ secrets.* }})\n const patterns = [\n /(?:password|token|key|secret|api_key|apikey)\\s*[:=]\\s*[\"'`][A-Za-z0-9+/=_-]{20,}[\"'`]/gi,\n /(?:DOCKER_PASSWORD|NPM_TOKEN|AWS_SECRET_ACCESS_KEY|GH_TOKEN|GITHUB_TOKEN)\\s*[:=]\\s*[\"'`][^\"'`$]+[\"'`]/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, secretsInCI, filePath, () =>\n \"Use repository secrets: ${{ secrets.MY_TOKEN }} (GitHub) or CI/CD variable settings. Never hardcode credentials in workflow files.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC077 – CORS Wildcard in Serverless Config\n// ────────────────────────────────────────────\n\nexport const corsServerless: CustomRule = {\n id: \"VC077\",\n title: \"CORS Wildcard in Serverless Config\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Setting Access-Control-Allow-Origin: * in serverless.yml, vercel.json, or similar configs allows any website to make authenticated requests to your API.\",\n check(content, filePath) {\n if (!filePath.match(/serverless\\.(yml|yaml)|vercel\\.json|netlify\\.toml|amplify\\.yml/i)) return [];\n const matches: RuleMatch[] = [];\n if (/(?:Access-Control-Allow-Origin|allowOrigin|cors).*['\"]\\*['\"]/i.test(content)) {\n matches.push(...findMatches(content, /(?:Access-Control-Allow-Origin|allowOrigin|cors).*['\"]\\*['\"]/gi, corsServerless, filePath, () =>\n \"Replace wildcard CORS with specific origins: allowOrigin: ['https://yourdomain.com']. Wildcard allows any site to call your API.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC078 – Kubernetes Privileged Container\n// ────────────────────────────────────────────\n\nexport const k8sPrivileged: CustomRule = {\n id: \"VC078\",\n title: \"Kubernetes Privileged Container\",\n severity: \"critical\",\n category: \"Configuration\",\n description: \"Running containers with privileged: true or as root in Kubernetes gives full host access, making container escapes trivial.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/) || !/(?:kind|apiVersion|container)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /privileged\\s*:\\s*true/g,\n /runAsUser\\s*:\\s*0\\b/g,\n /runAsNonRoot\\s*:\\s*false/g,\n /allowPrivilegeEscalation\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, k8sPrivileged, filePath, () =>\n \"Set securityContext: { privileged: false, runAsNonRoot: true, allowPrivilegeEscalation: false }. Never run containers as root in production.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// FRAMEWORK DETECTION\n// ────────────────────────────────────────────\n\nexport type DetectedFramework = \"next.js\" | \"react\" | \"react-native\" | \"express\" | \"hono\" | \"fastify\" | \"django\" | \"flask\" | \"electron\" | \"vue\" | \"svelte\" | \"unknown\";\n\nexport function detectFramework(files: { path: string; content: string }[]): DetectedFramework[] {\n const frameworks: Set<DetectedFramework> = new Set();\n for (const { path, content } of files) {\n if (path.match(/next\\.config/i) || /from\\s+[\"']next/i.test(content)) frameworks.add(\"next.js\");\n if (/from\\s+[\"']react-native/i.test(content) || path.match(/react-native\\.config/i)) frameworks.add(\"react-native\");\n else if (/from\\s+[\"']react/i.test(content) || /import\\s+React/i.test(content)) frameworks.add(\"react\");\n if (/from\\s+[\"']express/i.test(content) || /require\\s*\\(\\s*[\"']express/i.test(content)) frameworks.add(\"express\");\n if (/from\\s+[\"']hono/i.test(content)) frameworks.add(\"hono\");\n if (/from\\s+[\"']fastify/i.test(content)) frameworks.add(\"fastify\");\n if (/from\\s+[\"']electron/i.test(content) || path.match(/electron/i)) frameworks.add(\"electron\");\n if (path.match(/settings\\.py$/) || /from\\s+django/i.test(content)) frameworks.add(\"django\");\n if (/from\\s+flask/i.test(content) || /Flask\\s*\\(/i.test(content)) frameworks.add(\"flask\");\n if (/from\\s+[\"']vue/i.test(content) || path.match(/vue\\.config/i)) frameworks.add(\"vue\");\n if (/from\\s+[\"']svelte/i.test(content) || path.match(/svelte\\.config/i)) frameworks.add(\"svelte\");\n }\n if (frameworks.size === 0) frameworks.add(\"unknown\");\n return [...frameworks];\n}\n\n// ────────────────────────────────────────────\n// SEVERITY GRADING\n// ────────────────────────────────────────────\n\nexport type SecurityGrade = \"A+\" | \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n\nexport interface GradeResult {\n grade: SecurityGrade;\n score: number;\n summary: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function calculateGrade(findings: Finding[], _totalFiles: number): GradeResult {\n if (findings.length === 0) {\n return { grade: \"A+\", score: 100, summary: \"No security issues detected. Excellent.\" };\n }\n\n // Single source of truth for severity weights. Matches the dashboard\n // scoring modal and the GitHub Action action.yml so all three surfaces\n // produce the same grade for the same input.\n let critical = 0, high = 0, medium = 0, low = 0;\n for (const f of findings) {\n if (f.severity === \"critical\") critical++;\n else if (f.severity === \"high\") high++;\n else if (f.severity === \"medium\") medium++;\n else if (f.severity === \"low\") low++;\n }\n\n const deductions = critical * 15 + high * 7 + medium * 3 + low * 1;\n const rawScore = Math.max(0, 100 - deductions);\n\n // Score-based grade thresholds\n let grade: SecurityGrade;\n if (rawScore >= 97) grade = \"A+\";\n else if (rawScore >= 90) grade = \"A\";\n else if (rawScore >= 80) grade = \"B\";\n else if (rawScore >= 70) grade = \"C\";\n else if (rawScore >= 60) grade = \"D\";\n else grade = \"F\";\n\n // Severity caps — a scan with serious findings cannot earn an A regardless\n // of what the score math says. Prevents the 'strong security with minor\n // concerns' bug where 2 High findings were rated A.\n const capGrade = (cap: SecurityGrade): SecurityGrade => {\n const order: SecurityGrade[] = [\"A+\", \"A\", \"B\", \"C\", \"D\", \"F\"];\n return order.indexOf(grade) < order.indexOf(cap) ? cap : grade;\n };\n if (critical >= 1) grade = capGrade(\"D\"); // any critical → max D\n else if (high >= 3) grade = capGrade(\"D\"); // 3+ high → max D\n else if (high >= 1) grade = capGrade(\"B\"); // 1–2 high → max B\n else if (medium >= 5) grade = capGrade(\"B\"); // 5+ medium → max B\n else if (medium >= 1) grade = capGrade(\"A\"); // any medium → max A (not A+)\n\n // Summary references what's actually wrong, not just the score bucket.\n let summary: string;\n if (critical > 0) {\n summary = `${critical} critical ${critical === 1 ? \"vulnerability requires\" : \"vulnerabilities require\"} immediate attention.`;\n } else if (high >= 3) {\n summary = `${high} high-severity issues require urgent attention.`;\n } else if (high > 0) {\n summary = `${high} high-severity ${high === 1 ? \"issue needs\" : \"issues need\"} attention.`;\n } else if (medium >= 5) {\n summary = `${medium} medium-severity issues to address.`;\n } else if (medium > 0) {\n summary = `Clean of critical and high issues. ${medium} medium-severity ${medium === 1 ? \"issue\" : \"issues\"} to review.`;\n } else if (low > 0) {\n summary = `Clean of critical, high, and medium issues. ${low} low-severity best-practice ${low === 1 ? \"note\" : \"notes\"}.`;\n } else {\n summary = \"No security issues detected.\";\n }\n\n return { grade, score: rawScore, summary };\n}\n\n// ────────────────────────────────────────────\n// VC079 – JWT Algorithm Confusion\n// ────────────────────────────────────────────\n\nexport const jwtAlgConfusion: CustomRule = {\n id: \"VC079\",\n title: \"JWT Algorithm Confusion (alg:none)\",\n severity: \"critical\",\n category: \"Authentication\",\n description: \"Accepting 'none' as a JWT algorithm allows attackers to forge tokens by removing the signature entirely.\",\n check(content, filePath) {\n if (!/jwt|jsonwebtoken|jose/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /algorithms\\s*:\\s*\\[.*[\"']none[\"']/gi,\n /algorithm\\s*[:=]\\s*[\"']none[\"']/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, jwtAlgConfusion, filePath, () =>\n \"Never allow algorithm 'none'. Explicitly specify: algorithms: ['RS256'] or algorithms: ['HS256']. Reject tokens with alg:none.\"\n ));\n }\n // Also check for missing algorithm restriction\n if (/jwt\\.verify\\s*\\([^)]*\\)\\s*(?!.*algorithms)/i.test(content) && !/algorithms/i.test(content)) {\n matches.push(...findMatches(content, /jwt\\.verify\\s*\\(/g, jwtAlgConfusion, filePath, () =>\n \"Specify allowed algorithms in jwt.verify: jwt.verify(token, secret, { algorithms: ['HS256'] }).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC080 – Regex DoS (ReDoS)\n// ────────────────────────────────────────────\n\nexport const regexDos: CustomRule = {\n id: \"VC080\",\n title: \"Potential Regular Expression DoS (ReDoS)\",\n severity: \"high\",\n category: \"Availability\",\n description: \"Nested quantifiers like (a+)+ or (a*){2,} cause catastrophic backtracking, allowing attackers to freeze your server with crafted input.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n // Detect nested quantifiers in regex\n const patterns = [\n /new\\s+RegExp\\s*\\(\\s*[\"'`].*\\([^)]*[+*]\\)[+*{]/g,\n /\\/.*\\([^)]*[+*]\\)[+*{].*\\//g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, regexDos, filePath, () =>\n \"Avoid nested quantifiers in regex. Use atomic groups, possessive quantifiers, or the 're2' library for safe regex execution.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC081 – XML External Entity (XXE)\n// ────────────────────────────────────────────\n\nexport const xxeVulnerability: CustomRule = {\n id: \"VC081\",\n title: \"XML External Entity (XXE) Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"XML parsers that process external entities allow attackers to read files, perform SSRF, or cause DoS via billion-laughs attacks.\",\n check(content, filePath) {\n if (!/xml|parseXML|DOMParser|SAXParser|etree|lxml/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /\\.parseXML\\s*\\(/g,\n /new\\s+DOMParser\\s*\\(\\)/g,\n /etree\\.parse\\s*\\(/g,\n /lxml\\.etree/g,\n /SAXParserFactory/g,\n /XMLReaderFactory/g,\n ];\n const hasProtection = /noent.*false|resolveExternals.*false|FEATURE_EXTERNAL.*false|defusedxml|disallow-doctype-decl/i.test(content);\n if (hasProtection) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, xxeVulnerability, filePath, () =>\n \"Disable external entities: set noent: false, or use defusedxml (Python). For Java: factory.setFeature('http://apache.org/xml/features/disallow-doctype-decl', true).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC082 – Server-Side Template Injection\n// ────────────────────────────────────────────\n\nexport const ssti: CustomRule = {\n id: \"VC082\",\n title: \"Server-Side Template Injection (SSTI)\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Rendering templates from user-controlled strings allows attackers to execute arbitrary code on the server.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /render_template_string\\s*\\(\\s*(?![\"'`])/g,\n /Template\\s*\\(\\s*(?:req\\.|body\\.|input|params|args|user)/gi,\n /engine\\.render\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n /nunjucks\\.renderString\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n /ejs\\.render\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, ssti, filePath, () =>\n \"Never render templates from user input. Use pre-defined templates and pass data as context variables: render_template('template.html', data=user_data).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC083 – Insecure Java Deserialization\n// ────────────────────────────────────────────\n\nexport const javaDeserialization: CustomRule = {\n id: \"VC083\",\n title: \"Insecure Java Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"ObjectInputStream.readObject() on untrusted data allows arbitrary code execution via gadget chains in the classpath.\",\n check(content, filePath) {\n if (!filePath.match(/\\.java$|\\.kt$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /ObjectInputStream\\s*\\(/g,\n /\\.readObject\\s*\\(\\)/g,\n /XMLDecoder\\s*\\(/g,\n /XStream\\s*\\(\\)/g,\n ];\n const hasSafe = /ValidatingObjectInputStream|ObjectInputFilter|SerialKiller|NotSerializableException/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, javaDeserialization, filePath, () =>\n \"Use ValidatingObjectInputStream with an allowlist of classes, or avoid Java serialization entirely. Use JSON instead.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC084 – Missing Subresource Integrity (SRI)\n// ────────────────────────────────────────────\n\nexport const missingSRI: CustomRule = {\n id: \"VC084\",\n title: \"Missing Subresource Integrity (SRI)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"External scripts and stylesheets loaded without integrity= attributes can be tampered with if the CDN is compromised.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(html|htm|jsx|tsx|ejs|hbs)$/)) return [];\n const matches: RuleMatch[] = [];\n // Script tags with external src but no integrity\n const scriptPattern = /<script\\s+[^>]*src\\s*=\\s*[\"']https?:\\/\\/[^\"']+[\"'][^>]*>/gi;\n let m: RegExpExecArray | null;\n const re = new RegExp(scriptPattern.source, scriptPattern.flags);\n while ((m = re.exec(content)) !== null) {\n if (!m[0].includes(\"integrity\")) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC084\", title: missingSRI.title, severity: \"medium\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: 'Add integrity and crossorigin attributes: <script src=\"...\" integrity=\"sha384-...\" crossorigin=\"anonymous\">'\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC085 – Exposed Admin/Debug Routes\n// ────────────────────────────────────────────\n\nexport const exposedAdminRoutes: CustomRule = {\n id: \"VC085\",\n title: \"Exposed Admin or Debug Route\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Routes like /admin, /debug, /phpinfo, or /actuator without authentication expose sensitive controls and information to attackers.\",\n check(content, filePath) {\n if (!/(?:\\/api\\/|routes?\\/|server\\.|app\\.|index\\.[jt]s)/i.test(filePath)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /[.'\"]\\s*(?:get|use|all)\\s*\\(\\s*[\"'`]\\/(?:admin|debug|_debug|__debug__|phpinfo|actuator|graphiql|playground|swagger|reset|seed|test|dev|mock)[\"'`]/gi,\n ];\n const hasAuth = /auth|requireAuth|isAdmin|requireAdmin|authenticate|middleware.*admin|requireUser/i.test(content);\n if (hasAuth) return [];\n // Skip if there's a dev-only guard\n if (/NODE_ENV\\s*!==\\s*[\"']development[\"']|NODE_ENV\\s*===\\s*[\"']production[\"']/i.test(content)) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedAdminRoutes, filePath, () =>\n \"Protect admin/debug routes with authentication middleware. In production, disable debug endpoints entirely.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC086 – Insecure WebSocket\n// ────────────────────────────────────────────\n\nexport const insecureWebSocket: CustomRule = {\n id: \"VC086\",\n title: \"Insecure WebSocket Connection (ws://)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Using ws:// instead of wss:// transmits data in plaintext, vulnerable to eavesdropping and man-in-the-middle attacks.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n return findMatches(content, /new\\s+WebSocket\\s*\\(\\s*[\"'`]ws:\\/\\//g, insecureWebSocket, filePath, () =>\n \"Use wss:// (WebSocket Secure) instead of ws:// for encrypted connections: new WebSocket('wss://...').\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC087 – Missing HSTS\n// ────────────────────────────────────────────\n\nexport const missingHSTS: CustomRule = {\n id: \"VC087\",\n title: \"Missing HTTP Strict Transport Security (HSTS)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Without HSTS headers, browsers allow downgrade attacks from HTTPS to HTTP, exposing traffic to interception.\",\n check(content, filePath) {\n if (!/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) return [];\n if (!/(?:express|hono|fastify|koa)/i.test(content)) return [];\n if (/Strict-Transport-Security|hsts|helmet/i.test(content)) return [];\n return findMatches(content, /(?:express|hono|fastify|koa)\\s*\\(/gi, missingHSTS, filePath, () =>\n \"Add HSTS header: res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'). Or use helmet().\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC088 – Sensitive Data in URL Parameters\n// ────────────────────────────────────────────\n\nexport const sensitiveURLParams: CustomRule = {\n id: \"VC088\",\n title: \"Sensitive Data in URL Parameters\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Passing passwords, tokens, or API keys in URL query parameters exposes them in server logs, browser history, and referrer headers.\",\n check(content, filePath) {\n // Only scan code files, never docs/README/examples/configs\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Actual code building URLs with sensitive params (template literals or concat)\n /[&?](?:password|secret|api_key|apiKey|access_token|ssn|credit_card)\\s*=\\s*\\$\\{/gi,\n /[&?](?:password|secret|api_key|apiKey|access_token)\\s*=[\"']\\s*\\+/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveURLParams, filePath, () =>\n \"Never pass sensitive data in URL parameters. Use request headers (Authorization: Bearer ...) or POST body instead.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC089 – Missing Content-Disposition\n// ────────────────────────────────────────────\n\nexport const missingContentDisposition: CustomRule = {\n id: \"VC089\",\n title: \"File Download Missing Content-Disposition\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"File download endpoints without Content-Disposition headers may render files inline, leading to XSS if the file contains HTML/JS.\",\n check(content, filePath) {\n if (!/(?:download|sendFile|send_file|pipe|createReadStream)/i.test(content)) return [];\n if (!/(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|handler)/i.test(filePath)) return [];\n if (/Content-Disposition|attachment|download/i.test(content)) return [];\n return findMatches(content, /(?:sendFile|send_file|createReadStream|\\.pipe)\\s*\\(/gi, missingContentDisposition, filePath, () =>\n \"Set Content-Disposition: attachment header on file downloads: res.setHeader('Content-Disposition', 'attachment; filename=\\\"file.pdf\\\"').\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC090 – Open Redirect via Host Header\n// ────────────────────────────────────────────\n\nexport const hostHeaderRedirect: CustomRule = {\n id: \"VC090\",\n title: \"Open Redirect via Host Header\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Using req.headers.host to construct redirect URLs allows attackers to inject a malicious host header, redirecting users to phishing sites.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n if (/req\\.headers\\.host|req\\.get\\s*\\(\\s*[\"']host[\"']\\)/i.test(content) && /redirect|location/i.test(content)) {\n matches.push(...findMatches(content, /req\\.headers\\.host|req\\.get\\s*\\(\\s*[\"']host[\"']\\)/gi, hostHeaderRedirect, filePath, () =>\n \"Don't use req.headers.host for redirects — it's attacker-controlled. Use a hardcoded domain or environment variable.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC091 – Race Condition / TOCTOU\n// ────────────────────────────────────────────\n\nexport const raceCondition: CustomRule = {\n id: \"VC091\",\n title: \"Potential Race Condition (TOCTOU)\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Check-then-act patterns (e.g., checking if a file exists then writing to it) are vulnerable to race conditions where state changes between the check and the action.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /(?:existsSync|exists)\\s*\\([^)]+\\)[\\s\\S]{0,50}(?:writeFileSync|writeFile|unlinkSync|unlink|renameSync)\\s*\\(/g,\n /os\\.path\\.exists\\s*\\([^)]+\\)[\\s\\S]{0,50}open\\s*\\(/g,\n /File\\.exists\\?\\s*\\([^)]+\\)[\\s\\S]{0,50}File\\.(?:write|delete)/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, raceCondition, filePath, () =>\n \"Use atomic operations instead of check-then-act. For files: use fs.open with 'wx' flag (exclusive create), or use file locks.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC092 – Unsafe Object.assign from User Input\n// ────────────────────────────────────────────\n\nexport const unsafeObjectAssign: CustomRule = {\n id: \"VC092\",\n title: \"Unsafe Object Spread from User Input\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Spreading request body into a new object can copy __proto__, constructor, or other dangerous properties, enabling prototype pollution.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /Object\\.assign\\s*\\(\\s*\\{\\s*\\}\\s*,\\s*(?:req\\.(?:body|query|params)|body|input)\\s*\\)/gi,\n /\\{\\s*\\.\\.\\.(?:req\\.(?:body|query|params)|body|input)\\s*\\}/gi,\n ];\n const hasSafe = /omit.*__proto__|sanitize|pick\\(|lodash\\.pick|stripProto/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unsafeObjectAssign, filePath, () =>\n \"Explicitly pick allowed properties instead of spreading: const { name, email } = req.body; const safe = { name, email };\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC093 – Unprotected File Download Endpoint\n// ────────────────────────────────────────────\n\nexport const unprotectedDownload: CustomRule = {\n id: \"VC093\",\n title: \"File Download Without Path Validation\",\n severity: \"medium\",\n category: \"Authorization\",\n description: \"File download endpoints that accept user-controlled filenames without path validation allow directory traversal to read arbitrary files.\",\n check(content, filePath) {\n if (!/(?:download|sendFile|send_file)/i.test(content)) return [];\n if (!/(?:\\/api\\/|routes?\\/|controllers?\\/|server\\.|handler)/i.test(filePath)) return [];\n const matches: RuleMatch[] = [];\n // sendFile/download with user input\n if (/(?:sendFile|download|send_file)\\s*\\([^)]*(?:req\\.|params\\.|query\\.|body\\.)/i.test(content)) {\n const hasValidation = /path\\.resolve|path\\.normalize|path\\.join.*__dirname|realpath|includes\\s*\\(\\s*[\"']\\.\\./i.test(content);\n if (!hasValidation) {\n matches.push(...findMatches(content, /(?:sendFile|download|send_file)\\s*\\(/gi, unprotectedDownload, filePath, () =>\n \"Validate file paths: const safePath = path.resolve(DOWNLOAD_DIR, filename); if (!safePath.startsWith(DOWNLOAD_DIR)) throw new Error('Invalid path');\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC094 – Command Injection\n// ────────────────────────────────────────────\n\nexport const commandInjection: CustomRule = {\n id: \"VC094\",\n title: \"Potential Command Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Passing user input to shell commands (exec, system, child_process) allows attackers to execute arbitrary system commands.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb)$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Node.js — require standalone exec/execSync, not db.exec() or conn.exec()\n /(?<![.\\w])(?:exec|execSync)\\s*\\(\\s*(?:`[^`]*\\$\\{|[\"'][^\"']*\\+\\s*(?:req\\.|body\\.|input|params|args|user))/gi,\n /child_process.*exec\\s*\\(\\s*(?![\"'`])/g,\n // spawn / execFile / exec with shell: true AND a template literal\n // first arg. `shell: true` converts the first argument into a string\n // passed to `/bin/sh -c`, so any ${} interpolation is a shell injection\n // opportunity. Without shell: true, spawn/execFile are safe because\n // the command and args are kept separate. Previously this class of\n // bug was missed — the \"hasSafe\" skip below assumed any spawn in the\n // file was fine, which is the opposite of true with shell: true.\n /(?<![.\\w])(?:spawn|spawnSync|execFile|execFileSync|exec|execSync)\\s*\\(\\s*`[^`]*\\$\\{[\\s\\S]*?shell\\s*:\\s*(?:true|[\"'][^\"']+[\"'])/gi,\n // Python\n /os\\.system\\s*\\(\\s*(?![\"'`].*[\"'`]\\s*\\))/g,\n /subprocess\\.(?:call|run|Popen)\\s*\\([^)]*shell\\s*=\\s*True/gi,\n // Ruby\n /system\\s*\\(\\s*[\"'].*#\\{/g,\n ];\n // Honor known-safe helpers only when shell: true is NOT present. Once\n // shell:true is in the file the spawn \"safety\" is gone, so we can't\n // early-exit on its presence.\n const hasShellTrue = /shell\\s*:\\s*(?:true|[\"'][^\"']+[\"'])/i.test(content);\n const hasSafe = !hasShellTrue && /execFile|spawn|escapeshellarg|shlex\\.quote|shellEscape/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n const raw = findMatches(content, p, commandInjection, filePath, () =>\n \"Use execFile/spawn instead of exec (avoids shell). Never concatenate user input into shell commands. Use parameterized arguments.\"\n );\n // Filter out database .exec() calls (better-sqlite3, knex, sequelize, etc.)\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/(?:conn|db|database|knex|sequelize|prisma|sqlite|pool|client)\\s*\\.exec/i.test(lineText)) continue;\n if (/ALTER\\s+TABLE|CREATE\\s+TABLE|DROP\\s+TABLE|INSERT\\s+INTO/i.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC095 – Hardcoded CORS Origin Localhost\n// ────────────────────────────────────────────\n\nexport const corsLocalhost: CustomRule = {\n id: \"VC095\",\n title: \"Hardcoded Localhost CORS Origin\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Hardcoded localhost CORS origins in production code allow any local process to make authenticated requests to your API.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\") || filePath.includes(\".env\")) return [];\n if (!/origin/i.test(content)) return [];\n return findMatches(content, /origin\\s*[:=]\\s*[\"'`]http:\\/\\/localhost/gi, corsLocalhost, filePath, () =>\n \"Use environment variables for CORS origins: origin: process.env.ALLOWED_ORIGIN. Remove localhost from production configs.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC096 – Unencrypted gRPC\n// ────────────────────────────────────────────\n\nexport const insecureGRPC: CustomRule = {\n id: \"VC096\",\n title: \"Unencrypted gRPC Channel\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Using insecure gRPC channels transmits data including credentials in plaintext.\",\n check(content, filePath) {\n if (!/grpc/i.test(content)) return [];\n return findMatches(content, /(?:insecure_channel|createInsecure|grpc\\.Insecure)/gi, insecureGRPC, filePath, () =>\n \"Use encrypted gRPC channels: grpc.ssl_channel_credentials() or grpc.credentials.createSsl().\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// COMPLIANCE MAPPING (OWASP Top 10 + CWE)\n// ────────────────────────────────────────────\n\nexport const complianceMap: Record<string, { owasp: string; cwe: string }> = {\n VC001: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC002: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC003: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC004: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC005: { owasp: \"A08:2021\", cwe: \"CWE-345\" },\n VC006: { owasp: \"A03:2021\", cwe: \"CWE-89\" },\n VC007: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC008: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC009: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC010: { owasp: \"A01:2021\", cwe: \"CWE-602\" },\n VC011: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC012: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC013: { owasp: \"A01:2021\", cwe: \"CWE-269\" },\n VC014: { owasp: \"A05:2021\", cwe: \"CWE-538\" },\n VC015: { owasp: \"A03:2021\", cwe: \"CWE-95\" },\n VC016: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC017: { owasp: \"A05:2021\", cwe: \"CWE-614\" },\n VC018: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC019: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC020: { owasp: \"A05:2021\", cwe: \"CWE-1021\" },\n VC021: { owasp: \"A01:2021\", cwe: \"CWE-22\" },\n VC022: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC023: { owasp: \"A08:2021\", cwe: \"CWE-1321\" },\n VC024: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC025: { owasp: \"A03:2021\", cwe: \"CWE-22\" },\n VC026: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC027: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC028: { owasp: \"A07:2021\", cwe: \"CWE-20\" },\n VC029: { owasp: \"A08:2021\", cwe: \"CWE-20\" },\n VC030: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC031: { owasp: \"A02:2021\", cwe: \"CWE-321\" },\n VC032: { owasp: \"A05:2021\", cwe: \"CWE-319\" },\n VC033: { owasp: \"A05:2021\", cwe: \"CWE-215\" },\n VC034: { owasp: \"A02:2021\", cwe: \"CWE-338\" },\n VC035: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC036: { owasp: \"A04:2021\", cwe: \"CWE-755\" },\n VC037: { owasp: \"A09:2021\", cwe: \"CWE-209\" },\n VC038: { owasp: \"A04:2021\", cwe: \"CWE-434\" },\n VC039: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC040: { owasp: \"A05:2021\", cwe: \"CWE-538\" },\n VC041: { owasp: \"A10:2021\", cwe: \"CWE-918\" },\n VC042: { owasp: \"A01:2021\", cwe: \"CWE-915\" },\n VC043: { owasp: \"A02:2021\", cwe: \"CWE-208\" },\n VC044: { owasp: \"A09:2021\", cwe: \"CWE-117\" },\n VC045: { owasp: \"A07:2021\", cwe: \"CWE-521\" },\n VC046: { owasp: \"A07:2021\", cwe: \"CWE-384\" },\n VC047: { owasp: \"A07:2021\", cwe: \"CWE-307\" },\n VC048: { owasp: \"A03:2021\", cwe: \"CWE-943\" },\n VC049: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC050: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC051: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC052: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC053: { owasp: \"A05:2021\", cwe: \"CWE-798\" },\n VC054: { owasp: \"A07:2021\", cwe: \"CWE-922\" },\n VC055: { owasp: \"A05:2021\", cwe: \"CWE-540\" },\n VC056: { owasp: \"A05:2021\", cwe: \"CWE-1021\" },\n VC057: { owasp: \"A01:2021\", cwe: \"CWE-269\" },\n VC058: { owasp: \"A05:2021\", cwe: \"CWE-250\" },\n VC059: { owasp: \"A05:2021\", cwe: \"CWE-284\" },\n VC060: { owasp: \"A02:2021\", cwe: \"CWE-328\" },\n VC061: { owasp: \"A02:2021\", cwe: \"CWE-295\" },\n VC062: { owasp: \"A02:2021\", cwe: \"CWE-321\" },\n VC063: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC064: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC065: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC066: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC067: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC068: { owasp: \"A07:2021\", cwe: \"CWE-922\" },\n VC069: { owasp: \"A02:2021\", cwe: \"CWE-295\" },\n VC070: { owasp: \"A05:2021\", cwe: \"CWE-489\" },\n VC071: { owasp: \"A05:2021\", cwe: \"CWE-215\" },\n VC072: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC073: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC074: { owasp: \"A01:2021\", cwe: \"CWE-352\" },\n VC075: { owasp: \"A03:2021\", cwe: \"CWE-78\" },\n VC076: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC077: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC078: { owasp: \"A05:2021\", cwe: \"CWE-250\" },\n VC079: { owasp: \"A02:2021\", cwe: \"CWE-327\" },\n VC080: { owasp: \"A04:2021\", cwe: \"CWE-1333\" },\n VC081: { owasp: \"A03:2021\", cwe: \"CWE-611\" },\n VC082: { owasp: \"A03:2021\", cwe: \"CWE-94\" },\n VC083: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC084: { owasp: \"A06:2021\", cwe: \"CWE-353\" },\n VC085: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC086: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC087: { owasp: \"A05:2021\", cwe: \"CWE-311\" },\n VC088: { owasp: \"A07:2021\", cwe: \"CWE-598\" },\n VC089: { owasp: \"A05:2021\", cwe: \"CWE-430\" },\n VC090: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC091: { owasp: \"A04:2021\", cwe: \"CWE-367\" },\n VC092: { owasp: \"A08:2021\", cwe: \"CWE-1321\" },\n VC093: { owasp: \"A01:2021\", cwe: \"CWE-22\" },\n VC094: { owasp: \"A03:2021\", cwe: \"CWE-78\" },\n VC095: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC096: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC097: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC098: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC099: { owasp: \"A04:2021\", cwe: \"CWE-401\" },\n VC100: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC101: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC102: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC103: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC104: { owasp: \"A04:2021\", cwe: \"CWE-390\" },\n VC105: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC106: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC107: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC108: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC109: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC110: { owasp: \"A09:2021\", cwe: \"CWE-778\" },\n VC111: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC112: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC113: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC114: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC115: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC116: { owasp: \"A05:2021\", cwe: \"CWE-770\" },\n VC117: { owasp: \"A03:2021\", cwe: \"CWE-22\" },\n VC118: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC119: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC120: { owasp: \"A07:2021\", cwe: \"CWE-352\" },\n VC121: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC122: { owasp: \"A02:2021\", cwe: \"CWE-326\" },\n VC123: { owasp: \"A02:2021\", cwe: \"CWE-326\" },\n VC124: { owasp: \"A02:2021\", cwe: \"CWE-327\" },\n VC125: { owasp: \"A07:2021\", cwe: \"CWE-640\" },\n VC126: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC127: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC128: { owasp: \"A05:2021\", cwe: \"CWE-444\" },\n VC129: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC130: { owasp: \"A07:2021\", cwe: \"CWE-307\" },\n VC131: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n // VC132–VC145: Service-specific API key detection\n VC132: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC133: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC134: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC135: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC136: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC137: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC138: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC139: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC140: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC141: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC142: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC143: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC144: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC145: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n // VC146–VC151: Secret exposure path analysis\n VC146: { owasp: \"A07:2021\", cwe: \"CWE-598\" },\n VC147: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC148: { owasp: \"A09:2021\", cwe: \"CWE-209\" },\n VC149: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC150: { owasp: \"A07:2021\", cwe: \"CWE-615\" },\n VC151: { owasp: \"A07:2021\", cwe: \"CWE-214\" },\n // VC152–VC158: New rules\n VC152: { owasp: \"A08:2021\", cwe: \"CWE-345\" },\n VC153: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC154: { owasp: \"A03:2021\", cwe: \"CWE-20\" },\n VC155: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC156: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC157: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC158: { owasp: \"A01:2021\", cwe: \"CWE-639\" },\n};\n\n// ────────────────────────────────────────────\n// VC097 – Console.log in Production\n// ────────────────────────────────────────────\n\nexport const consoleLogProduction: CustomRule = {\n id: \"VC097\",\n title: \"Console.log Left in Production Code\",\n severity: \"low\",\n category: \"Performance\",\n description: \"console.log statements left in production code can leak sensitive data, slow down rendering, and clutter browser consoles.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip server-side scripts, migration files, CLI tools, and seed files\n if (/(?:migrate|seed|script|cli|setup|dev)\\./i.test(filePath)) return [];\n if (!/console\\.log\\s*\\(/g.test(content)) return [];\n const lines = content.split(\"\\n\");\n const logCount = lines.filter(l => /console\\.log\\s*\\(/.test(l.trim()) && !l.trim().startsWith(\"//\")).length;\n // If a file has many console.logs, it's intentional logging, not forgotten debug statements\n if (logCount > 5) return [];\n const matches: RuleMatch[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (/console\\.log\\s*\\(/.test(line) && !line.startsWith(\"//\") && !line.startsWith(\"*\") && !/if\\s*\\(\\s*(?:debug|process\\.env)/i.test(lines[Math.max(0, i-1)] + line)) {\n matches.push({ rule: \"VC097\", title: consoleLogProduction.title, severity: \"low\" as const, category: \"Performance\", file: filePath, line: i + 1, snippet: getSnippet(content, i + 1), fix: \"Remove console.log or use a structured logger that can be disabled in production.\" });\n }\n }\n return matches.slice(0, 1); // Max 1 per file to avoid noise\n },\n};\n\n// ────────────────────────────────────────────\n// VC098 – Synchronous File Operations\n// ────────────────────────────────────────────\n\nexport const syncFileOps: CustomRule = {\n id: \"VC098\",\n title: \"Synchronous File Operations\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Synchronous file operations (readFileSync, writeFileSync) block the event loop, causing all other requests to wait.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|__tests__|fixture|config|\\.config\\./i)) return [];\n return findMatches(content, /(?:readFileSync|writeFileSync|appendFileSync|mkdirSync|rmdirSync|statSync)\\s*\\(/g, syncFileOps, filePath, () =>\n \"Use async file operations (readFile, writeFile) to avoid blocking the event loop.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC099 – Memory Leak: Event Listener\n// ────────────────────────────────────────────\n\nexport const eventListenerLeak: CustomRule = {\n id: \"VC099\",\n title: \"Memory Leak: Event Listener Not Cleaned Up\",\n severity: \"high\",\n category: \"Performance\",\n description: \"Adding event listeners in React useEffect without a cleanup function causes memory leaks as listeners accumulate on re-renders.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx|tsx)$/)) return [];\n if (!/addEventListener/i.test(content)) return [];\n if (/removeEventListener/i.test(content)) return [];\n if (!/useEffect/i.test(content)) return [];\n return findMatches(content, /addEventListener\\s*\\(/g, eventListenerLeak, filePath, () =>\n \"Return a cleanup function from useEffect: useEffect(() => { window.addEventListener('resize', fn); return () => window.removeEventListener('resize', fn); }, []);\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC100 – N+1 Query Pattern\n// ────────────────────────────────────────────\n\nexport const nPlusOneQuery: CustomRule = {\n id: \"VC100\",\n title: \"N+1 Query Pattern Detected\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Database queries inside loops cause N+1 performance problems — one query per iteration instead of a single batch query.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n const hasLoopWithQuery = /(?:for\\s*\\(|\\.forEach\\s*\\(|\\.map\\s*\\(|while\\s*\\()[^}]*(?:\\.find\\(|\\.findOne\\(|\\.findById\\(|\\.query\\(|\\.execute\\(|SELECT\\s)/is.test(content);\n if (!hasLoopWithQuery) return [];\n return findMatches(content, /(?:for\\s*\\(|\\.forEach\\s*\\(|\\.map\\s*\\(|while\\s*\\()/g, nPlusOneQuery, filePath, () =>\n \"Fetch all data in a single query using WHERE IN, JOIN, or batch operations instead of querying per item in a loop.\"\n ).slice(0, 2);\n },\n};\n\n// ────────────────────────────────────────────\n// VC101 – Large Bundle Import\n// ────────────────────────────────────────────\n\nexport const largeBundleImport: CustomRule = {\n id: \"VC101\",\n title: \"Importing Entire Library (Large Bundle)\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Importing entire libraries like lodash or moment.js adds hundreds of KB to your bundle. Import only the functions you need.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /import\\s+_\\s+from\\s+['\"]lodash['\"]/g,\n /import\\s+\\*\\s+as\\s+_\\s+from\\s+['\"]lodash['\"]/g,\n /import\\s+moment\\s+from\\s+['\"]moment['\"]/g,\n /const\\s+_\\s*=\\s*require\\s*\\(\\s*['\"]lodash['\"]\\s*\\)/g,\n /const\\s+moment\\s*=\\s*require\\s*\\(\\s*['\"]moment['\"]\\s*\\)/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, largeBundleImport, filePath, () =>\n \"Import only what you need: import { debounce } from 'lodash/debounce'. Or switch to lighter alternatives like date-fns instead of moment.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC102 – Blocking Main Thread\n// ────────────────────────────────────────────\n\nexport const blockingMainThread: CustomRule = {\n id: \"VC102\",\n title: \"Blocking Main Thread with Heavy Computation\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Infinite loops or deeply nested iterations on the main thread freeze the UI and cause unresponsiveness.\",\n check(content, filePath) {\n if (filePath.match(/worker|test|spec|mock/i)) return [];\n const matches: RuleMatch[] = [];\n if (/while\\s*\\(\\s*true\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /while\\s*\\(\\s*true\\s*\\)/g, blockingMainThread, filePath, () =>\n \"Avoid while(true) on the main thread. Use Web Workers for heavy computation or requestIdleCallback for non-urgent work.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC103 – TODO/FIXME Left in Code\n// ────────────────────────────────────────────\n\nexport const todoLeftInCode: CustomRule = {\n id: \"VC103\",\n title: \"TODO/FIXME Left in Code\",\n severity: \"low\",\n category: \"Code Quality\",\n description: \"TODO, FIXME, HACK, and XXX comments indicate unfinished work that should be resolved before shipping to production.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|__tests__|fixture|node_modules/i)) return [];\n return findMatches(content, /\\/\\/\\s*(?:TODO|FIXME|HACK|XXX)\\b/gi, todoLeftInCode, filePath, () =>\n \"Resolve TODO/FIXME comments before shipping. If it's intentional tech debt, track it in your issue tracker instead.\"\n ).slice(0, 5);\n },\n};\n\n// ────────────────────────────────────────────\n// VC104 – Empty Catch Block\n// ────────────────────────────────────────────\n\nexport const emptyCatchBlock: CustomRule = {\n id: \"VC104\",\n title: \"Empty Catch Block\",\n severity: \"medium\",\n category: \"Code Quality\",\n description: \"Empty catch blocks silently swallow errors, making bugs impossible to diagnose. At minimum, log the error.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n return findMatches(content, /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*\\}/g, emptyCatchBlock, filePath, () =>\n \"Handle errors in catch blocks: catch(err) { console.error('Operation failed:', err); } or re-throw if appropriate.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC105 – Callback Hell\n// ────────────────────────────────────────────\n\nexport const callbackHell: CustomRule = {\n id: \"VC105\",\n title: \"Deeply Nested Callbacks (Promise Chain)\",\n severity: \"medium\",\n category: \"Code Quality\",\n description: \"Long .then() chains or deeply nested callbacks are hard to read, debug, and maintain. Refactor to async/await.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n return findMatches(content, /\\.then\\s*\\([^)]*\\)\\s*\\.then\\s*\\([^)]*\\)\\s*\\.then/g, callbackHell, filePath, () =>\n \"Refactor .then() chains to async/await for cleaner, more readable code.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC106 – Magic Numbers\n// ────────────────────────────────────────────\n\nexport const magicNumbers: CustomRule = {\n id: \"VC106\",\n title: \"Magic Numbers in Code\",\n severity: \"low\",\n category: \"Code Quality\",\n description: \"Unnamed numeric constants in conditions or calculations make code hard to understand. Extract them into named constants.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|config|\\.config\\.|constant|enum|migration/i)) return [];\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n const matches: RuleMatch[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (line.startsWith(\"//\") || line.startsWith(\"*\")) continue;\n // Look for conditions with magic numbers > 1 (skip 0, 1, -1, 2)\n if (/(?:===|!==|>=?|<=?)\\s*\\d{3,}/.test(line) || /(?:setTimeout|setInterval)\\s*\\([^,]+,\\s*\\d{4,}/.test(line)) {\n matches.push({ rule: \"VC106\", title: magicNumbers.title, severity: \"low\" as const, category: \"Code Quality\", file: filePath, line: i + 1, snippet: getSnippet(content, i + 1), fix: \"Extract magic numbers into named constants: const MAX_RETRIES = 3; const TIMEOUT_MS = 5000;\" });\n }\n }\n return matches.slice(0, 3);\n },\n};\n\n// ────────────────────────────────────────────\n// VC107 – S3 Bucket Without Encryption\n// ────────────────────────────────────────────\n\nexport const s3BucketNoEncryption: CustomRule = {\n id: \"VC107\",\n title: \"S3 Bucket Without Encryption\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"AWS S3 buckets without server-side encryption leave data at rest unprotected. Enable encryption to protect sensitive data.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_s3_bucket\"/.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Find S3 bucket resources without encryption configuration nearby\n const bucketPattern = /resource\\s+\"aws_s3_bucket\"\\s+\"(\\w+)\"/g;\n let m: RegExpExecArray | null;\n while ((m = bucketPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check if there's a server_side_encryption_configuration within a reasonable range\n const blockEnd = Math.min(m.index + 2000, content.length);\n const blockContent = content.substring(m.index, blockEnd);\n if (!/server_side_encryption/.test(blockContent)) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC107\", title: s3BucketNoEncryption.title, severity: \"high\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Enable S3 bucket encryption: server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { sse_algorithm = 'aws:kms' } } }\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC108 – Security Group Allows All Inbound\n// ────────────────────────────────────────────\n\nexport const securityGroupAllInbound: CustomRule = {\n id: \"VC108\",\n title: \"Security Group Allows All Inbound\",\n severity: \"critical\",\n category: \"Infrastructure\",\n description: \"Security groups allowing all inbound traffic (0.0.0.0/0 on all ports) expose resources to the entire internet.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(tf|json|yaml|yml)$/)) return [];\n const matches: RuleMatch[] = [];\n // Terraform: ingress with 0.0.0.0/0 and port 0 or no port restriction\n if (filePath.match(/\\.tf$/)) {\n const ingressPattern = /ingress\\s*\\{[^}]*cidr_blocks\\s*=\\s*\\[\\s*\"0\\.0\\.0\\.0\\/0\"\\s*\\][^}]*\\}/gs;\n let m: RegExpExecArray | null;\n while ((m = ingressPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check for unrestricted ports (from_port = 0 or no port spec)\n if (/from_port\\s*=\\s*0/.test(m[0]) || !/from_port/.test(m[0])) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC108\", title: securityGroupAllInbound.title, severity: \"critical\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Restrict security group ingress to specific IP ranges and ports.\",\n });\n }\n }\n }\n // CloudFormation: AWS::EC2::SecurityGroup with CidrIp: 0.0.0.0/0\n if (filePath.match(/\\.(json|yaml|yml)$/)) {\n const cfnPattern = /AWS::EC2::SecurityGroup/g;\n if (cfnPattern.test(content) && /CidrIp\\s*:\\s*[\"']?0\\.0\\.0\\.0\\/0/.test(content)) {\n matches.push(...findMatches(content, /CidrIp\\s*:\\s*[\"']?0\\.0\\.0\\.0\\/0/g, securityGroupAllInbound, filePath, () =>\n \"Restrict security group ingress to specific IP ranges and ports.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC109 – RDS Instance Publicly Accessible\n// ────────────────────────────────────────────\n\nexport const rdsPubliclyAccessible: CustomRule = {\n id: \"VC109\",\n title: \"RDS Instance Publicly Accessible\",\n severity: \"critical\",\n category: \"Infrastructure\",\n description: \"RDS instances with publicly_accessible = true are exposed to the internet, risking unauthorized database access.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_db_instance\"/.test(content)) return [];\n return findMatches(content, /publicly_accessible\\s*=\\s*true/g, rdsPubliclyAccessible, filePath, () =>\n \"Set publicly_accessible = false. Access RDS through VPC private subnets.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC110 – Missing CloudTrail Logging\n// ────────────────────────────────────────────\n\nexport const missingCloudTrail: CustomRule = {\n id: \"VC110\",\n title: \"Missing CloudTrail Logging\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"AWS environments without CloudTrail lack audit logging of API calls, making it difficult to detect unauthorized activity.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n // Only flag if the file uses AWS provider but has no cloudtrail resource\n if (!/provider\\s+\"aws\"/.test(content)) return [];\n if (/aws_cloudtrail/.test(content)) return [];\n const lineNum = content.substring(0, content.search(/provider\\s+\"aws\"/)).split(\"\\n\").length;\n return [{\n rule: \"VC110\", title: missingCloudTrail.title, severity: \"high\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Enable CloudTrail for audit logging of all AWS API calls.\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC111 – Lambda Without VPC\n// ────────────────────────────────────────────\n\nexport const lambdaWithoutVPC: CustomRule = {\n id: \"VC111\",\n title: \"Lambda Without VPC\",\n severity: \"medium\",\n category: \"Infrastructure\",\n description: \"Lambda functions not placed in a VPC lack network isolation and cannot access VPC-only resources securely.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_lambda_function\"/.test(content)) return [];\n const matches: RuleMatch[] = [];\n const lambdaPattern = /resource\\s+\"aws_lambda_function\"\\s+\"(\\w+)\"/g;\n let m: RegExpExecArray | null;\n while ((m = lambdaPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const blockEnd = Math.min(m.index + 2000, content.length);\n const blockContent = content.substring(m.index, blockEnd);\n if (!/vpc_config\\s*\\{/.test(blockContent)) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC111\", title: lambdaWithoutVPC.title, severity: \"medium\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Place Lambda functions in a VPC for network isolation.\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC112 – Docker Image Using Latest Tag\n// ────────────────────────────────────────────\n\nexport const dockerLatestTag: CustomRule = {\n id: \"VC112\",\n title: \"Docker Image Using Latest Tag\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Using :latest or no tag in Docker FROM directives leads to non-reproducible builds and potential security regressions.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const matches: RuleMatch[] = [];\n // Match FROM image:latest or FROM image (no tag, no AS, no scratch)\n const fromPattern = /^FROM\\s+(?!scratch)(\\S+?)(?:\\s+AS\\s+\\S+)?\\s*$/gm;\n let m: RegExpExecArray | null;\n while ((m = fromPattern.exec(content)) !== null) {\n const image = m[1];\n // Flag if using :latest or no tag at all (no colon)\n if (image.endsWith(\":latest\") || (!image.includes(\":\") && !image.startsWith(\"$\"))) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC112\", title: dockerLatestTag.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pin Docker image versions: FROM node:20-alpine instead of FROM node:latest\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC113 – Docker COPY With Sensitive Files\n// ────────────────────────────────────────────\n\nexport const dockerCopySensitive: CustomRule = {\n id: \"VC113\",\n title: \"Docker COPY With Sensitive Files\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Using COPY . . or ADD . . without a .dockerignore can leak .env files, .git history, and other sensitive data into the Docker image.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n // Check for COPY . . or ADD . .\n if (!/(?:COPY|ADD)\\s+\\.\\s+\\./.test(content)) return [];\n return findMatches(content, /(?:COPY|ADD)\\s+\\.\\s+\\./g, dockerCopySensitive, filePath, () =>\n \"Use .dockerignore to exclude .env, .git, node_modules, and sensitive files from the Docker build context.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC114 – Docker Exposing Too Many Ports\n// ────────────────────────────────────────────\n\nexport const dockerTooManyPorts: CustomRule = {\n id: \"VC114\",\n title: \"Docker Exposing Too Many Ports\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Exposing many ports or wide port ranges increases the attack surface of a container.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const matches: RuleMatch[] = [];\n // Detect port ranges like EXPOSE 3000-9000\n const rangePattern = /^EXPOSE\\s+\\d+-\\d+/gm;\n matches.push(...findMatches(content, rangePattern, dockerTooManyPorts, filePath, () =>\n \"Only expose necessary ports. Each EXPOSE should have a clear purpose.\"\n ));\n // Detect multiple EXPOSE directives (more than 3)\n const exposeLines = content.split(\"\\n\").filter(l => /^\\s*EXPOSE\\s+/.test(l));\n if (exposeLines.length > 3) {\n const firstExpose = content.search(/^\\s*EXPOSE\\s+/m);\n if (firstExpose >= 0) {\n const lineNum = content.substring(0, firstExpose).split(\"\\n\").length;\n matches.push({\n rule: \"VC114\", title: dockerTooManyPorts.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Only expose necessary ports. Each EXPOSE should have a clear purpose.\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC115 – Kubernetes Secret Not Encrypted\n// ────────────────────────────────────────────\n\nexport const k8sSecretNotEncrypted: CustomRule = {\n id: \"VC115\",\n title: \"Kubernetes Secret Not Encrypted\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"Kubernetes Secrets stored as plain base64 in manifests are not encrypted and can be trivially decoded. Use sealed-secrets or external-secrets.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/)) return [];\n if (!/kind\\s*:\\s*Secret/i.test(content)) return [];\n // Skip if using sealed-secrets or external-secrets annotations\n if (/sealedsecrets\\.bitnami\\.com|external-secrets\\.io/i.test(content)) return [];\n return findMatches(content, /kind\\s*:\\s*Secret/g, k8sSecretNotEncrypted, filePath, () =>\n \"Use sealed-secrets or external-secrets-operator. Never store plain secrets in manifests.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC116 – Kubernetes Pod Without Resource Limits\n// ────────────────────────────────────────────\n\nexport const k8sNoResourceLimits: CustomRule = {\n id: \"VC116\",\n title: \"Kubernetes Pod Without Resource Limits\",\n severity: \"medium\",\n category: \"Infrastructure\",\n description: \"Pods or deployments without resource limits can consume excessive CPU/memory, causing cluster instability.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/)) return [];\n if (!/kind\\s*:\\s*(?:Pod|Deployment|StatefulSet|DaemonSet|Job|CronJob)/i.test(content)) return [];\n // Check if any container spec has resources.limits\n if (/resources\\s*:\\s*\\n\\s+limits\\s*:/m.test(content)) return [];\n // Also accept inline resources: { limits: ... }\n if (/resources\\s*:.*limits/i.test(content)) return [];\n const kindMatch = content.match(/kind\\s*:\\s*(?:Pod|Deployment|StatefulSet|DaemonSet|Job|CronJob)/i);\n if (!kindMatch) return [];\n const lineNum = content.substring(0, kindMatch.index).split(\"\\n\").length;\n return [{\n rule: \"VC116\", title: k8sNoResourceLimits.title, severity: \"medium\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Set resource limits to prevent pods from consuming excessive CPU/memory.\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC117 – Path Traversal Vulnerability\n// ────────────────────────────────────────────\n\nexport const pathTraversal: CustomRule = {\n id: \"VC117\",\n title: \"Path Traversal Vulnerability\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"User input is used to construct file paths without sanitization, allowing attackers to read/write arbitrary files (e.g., ../../etc/passwd).\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|php|java)$/)) return [];\n const findings: RuleMatch[] = [];\n // Pattern: file operations using user input (req.params, req.query, req.body) in path\n const patterns = [\n /(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync|appendFile|unlink|unlinkSync|access|stat|statSync|existsSync)\\s*\\(\\s*(?:req\\.|request\\.|ctx\\.|params\\.|query\\.)/gi,\n /(?:path\\.join|path\\.resolve)\\s*\\([^)]*(?:req\\.|request\\.|ctx\\.|params\\.|query\\.)[^)]*\\)/gi,\n /(?:res\\.sendFile|res\\.download)\\s*\\([^)]*(?:req\\.|request\\.)/gi,\n /open\\s*\\(\\s*(?:request\\.|params\\[|args\\.)/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Skip if path.normalize or sanitization is near\n const surrounding = content.substring(Math.max(0, m.index - 200), m.index + 200);\n if (/path\\.normalize|sanitize|\\.replace\\(\\s*['\"]\\.\\./i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC117\", title: pathTraversal.title, severity: \"critical\" as const, category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Sanitize file paths: use path.normalize(), reject paths containing '..', and validate against an allowed base directory.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC118 – PII in Log Output\n// ────────────────────────────────────────────\n\nexport const piiInLogs: CustomRule = {\n id: \"VC118\",\n title: \"Personally Identifiable Information in Logs\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Logging statements that include email addresses, passwords, SSNs, credit card numbers, or other PII can leak sensitive data to log aggregation systems.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match console.log/logger.info etc. that reference sensitive fields\n const logPattern = /(?:console\\.(?:log|info|warn|error|debug)|logger\\.(?:info|warn|error|debug|log)|log\\.(?:info|warn|error|debug)|logging\\.(?:info|warn|error|debug))\\s*\\([^)]*(?:password|passwd|secret|ssn|social.?security|credit.?card|cardNumber|cvv|token|bearer|authorization)\\b/gi;\n let m;\n while ((m = logPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC118\", title: piiInLogs.title, severity: \"high\" as const, category: \"Information Leakage\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never log sensitive data. Redact or mask PII before logging: log('user login', { email: maskEmail(email) }).\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC119 – Hardcoded OAuth Client Secret\n// ────────────────────────────────────────────\n\nexport const hardcodedOAuthSecret: CustomRule = {\n id: \"VC119\",\n title: \"Hardcoded OAuth Client Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"OAuth client secrets hardcoded in source code can be extracted and used to impersonate your application.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php|env|json|yaml|yml)$/)) return [];\n // Skip lockfiles and generated files\n if (/package-lock|yarn\\.lock|pnpm-lock|composer\\.lock/i.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /client[_-]?secret\\s*[:=]\\s*[\"'][a-zA-Z0-9_\\-]{20,}[\"']/gi,\n /GOOGLE_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /GITHUB_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /FACEBOOK_APP_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /AUTH0_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Skip if value is a placeholder or env ref\n if (/process\\.env|os\\.environ|ENV\\[|getenv|\\$\\{|<your|CHANGE_ME|xxx|placeholder/i.test(m[0])) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC119\", title: hardcodedOAuthSecret.title, severity: \"critical\" as const, category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move OAuth client secrets to environment variables. Never commit secrets to source control.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC120 – Missing OAuth State Parameter\n// ────────────────────────────────────────────\n\nexport const missingOAuthState: CustomRule = {\n id: \"VC120\",\n title: \"OAuth Flow Missing State Parameter\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"OAuth authorization requests without a state parameter are vulnerable to CSRF attacks, allowing attackers to link their account to a victim's session.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // OAuth authorization URL construction without state param\n const oauthUrlPattern = /(?:authorize\\?|\\/oauth\\/authorize|\\/auth\\?|authorization_endpoint)[^}]*(?:client_id|response_type)/gi;\n let m;\n while ((m = oauthUrlPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const surrounding = content.substring(m.index, Math.min(content.length, m.index + 500));\n if (/state\\s*[=:]/i.test(surrounding)) continue; // Has state param\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC120\", title: missingOAuthState.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Always include a cryptographically random 'state' parameter in OAuth authorization requests and validate it on callback.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC121 – Unpinned GitHub Actions Version\n// ────────────────────────────────────────────\n\nexport const unpinnedGitHubAction: CustomRule = {\n id: \"VC121\",\n title: \"Unpinned GitHub Actions Version\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"GitHub Actions using branch references (@main, @master) instead of commit SHAs can be compromised via supply-chain attacks.\",\n check(content, filePath) {\n if (!filePath.match(/\\.github\\/workflows\\/.*\\.(yml|yaml)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match uses: org/action@branch (not @sha or @v1.2.3)\n const usesPattern = /uses\\s*:\\s*[\\w\\-]+\\/[\\w\\-]+@(main|master|dev|develop|latest)\\b/gi;\n let m;\n while ((m = usesPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC121\", title: unpinnedGitHubAction.title, severity: \"high\" as const, category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pin GitHub Actions to a specific commit SHA instead of a branch name: uses: owner/action@<full-sha>\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC122 – Deprecated TLS Version\n// ────────────────────────────────────────────\n\nexport const deprecatedTLS: CustomRule = {\n id: \"VC122\",\n title: \"Deprecated TLS Version Configured\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"TLS 1.0 and 1.1 are deprecated and have known vulnerabilities. Only TLS 1.2+ should be used.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|yaml|yml|json|conf|cfg|xml)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /(?:TLSv1_METHOD|TLSv1\\.0|TLSv1\\.1|ssl\\.PROTOCOL_TLSv1|SSLv3|SSLv2|TLS_1_0|TLS_1_1|minVersion\\s*[:=]\\s*[\"']TLSv1(?:\\.1)?[\"'])/gi,\n /(?:tls\\.DEFAULT_MIN_VERSION|secureProtocol)\\s*[:=]\\s*[\"'](?:TLSv1_method|TLSv1_1_method|SSLv3_method)[\"']/gi,\n /ssl_protocols\\s+.*(?:TLSv1(?:\\.1)?(?:\\s|;))/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC122\", title: deprecatedTLS.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use TLS 1.2 or higher. Set minVersion to 'TLSv1.2' and remove support for TLS 1.0/1.1.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC123 – Weak RSA Key Size\n// ────────────────────────────────────────────\n\nexport const weakRSAKeySize: CustomRule = {\n id: \"VC123\",\n title: \"Weak RSA Key Size\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"RSA keys smaller than 2048 bits are considered insecure and can be factored with modern hardware.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match RSA key generation with explicit small sizes\n const patterns = [\n /generateKeyPair\\s*\\(\\s*[\"']rsa[\"']\\s*,\\s*\\{[^}]*modulusLength\\s*:\\s*(512|768|1024)\\b/gi,\n /RSA\\.generate\\s*\\(\\s*(512|768|1024)\\b/gi,\n /rsa\\.GenerateKey\\s*\\([^,]*,\\s*(512|768|1024)\\b/gi,\n /KeyPairGenerator.*initialize\\s*\\(\\s*(512|768|1024)\\b/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC123\", title: weakRSAKeySize.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use RSA key sizes of at least 2048 bits. 4096 bits is recommended for long-term security.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC124 – ECB Mode Encryption\n// ────────────────────────────────────────────\n\nexport const ecbModeEncryption: CustomRule = {\n id: \"VC124\",\n title: \"Insecure ECB Mode Encryption\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"ECB (Electronic Codebook) mode encrypts identical plaintext blocks to identical ciphertext, leaking patterns in the data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /createCipher(?:iv)?\\s*\\(\\s*[\"'](?:aes-(?:128|192|256)-ecb|des-ecb)[\"']/gi,\n /AES\\.(?:new|MODE_ECB)|MODE_ECB/gi,\n /Cipher\\.getInstance\\s*\\(\\s*[\"']AES\\/ECB/gi,\n /aes\\.NewCipher|cipher\\.NewECBEncrypter/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC124\", title: ecbModeEncryption.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use AES-GCM or AES-CBC with HMAC instead of ECB mode. GCM provides both encryption and authentication.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC125 – Insecure Password Reset Flow\n// ────────────────────────────────────────────\n\nexport const insecurePasswordReset: CustomRule = {\n id: \"VC125\",\n title: \"Insecure Password Reset Implementation\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Password reset using predictable tokens, no expiration, or user-enumeration leaks can be exploited to take over accounts.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (!/(?:reset|forgot).*(?:password|passwd)/i.test(content)) return [];\n const findings: RuleMatch[] = [];\n // Predictable reset tokens (using Date.now, Math.random, uuid without crypto)\n const weakTokens = /(?:reset|forgot).*(?:token|code)\\s*[:=].*(?:Date\\.now|Math\\.random|uuid\\(\\)|nanoid\\(\\))/gi;\n let m;\n while ((m = weakTokens.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC125\", title: insecurePasswordReset.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use crypto.randomBytes(32).toString('hex') for reset tokens. Set expiration (15-60 minutes) and single-use enforcement.\",\n });\n }\n // User enumeration: different responses for valid vs invalid emails\n const enumeration = /(?:user|email|account)\\s*(?:not\\s*found|does\\s*not\\s*exist|invalid)/gi;\n while ((m = enumeration.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Only flag if in a password reset context\n const surrounding = content.substring(Math.max(0, m.index - 500), m.index);\n if (!/(?:reset|forgot).*password/i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC125\", title: insecurePasswordReset.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Always return the same response regardless of whether the email exists. Say 'If an account exists, a reset link was sent.'\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC126 – Terraform State File Exposed\n// ────────────────────────────────────────────\n\nexport const terraformStateExposed: CustomRule = {\n id: \"VC126\",\n title: \"Terraform State File Committed\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Terraform state files contain sensitive infrastructure details, secrets, and access credentials in plaintext. They must never be committed to version control.\",\n check(content, filePath) {\n const findings: RuleMatch[] = [];\n // Direct detection of terraform state files\n if (/terraform\\.tfstate(\\.backup)?$/.test(filePath)) {\n findings.push({\n rule: \"VC126\", title: terraformStateExposed.title, severity: \"critical\" as const, category: \"Secrets\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Add '*.tfstate' and '*.tfstate.backup' to .gitignore. Use remote state backends (S3, GCS, Terraform Cloud) instead.\",\n });\n }\n // Check .gitignore for missing tfstate\n if (filePath.endsWith(\".gitignore\") && !content.includes(\"tfstate\")) {\n // Only flag if the repo has terraform files\n if (/\\.tf$/.test(filePath) || content.includes(\".tf\")) {\n // This is a weak signal, skip\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC127 – Insecure HTTP Method Handling\n// ────────────────────────────────────────────\n\nexport const insecureHTTPMethods: CustomRule = {\n id: \"VC127\",\n title: \"Dangerous HTTP Methods Without Auth\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"DELETE, PUT, and PATCH endpoints without authentication checks allow unauthorized data modification or deletion.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Express/Hono/Fastify route handlers for dangerous methods\n const routePatterns = [\n /(?:app|router|server)\\.(delete|put|patch)\\s*\\(\\s*[\"']/gi,\n ];\n for (const pat of routePatterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check the handler for auth middleware or auth checks\n const handlerBlock = content.substring(m.index, Math.min(content.length, m.index + 500));\n if (/auth|authenticate|authorize|requireAuth|isAuthenticated|protect|guard|middleware|session|jwt|verify|clerk|getAuth/i.test(handlerBlock)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC127\", title: insecureHTTPMethods.title, severity: \"high\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add authentication middleware to DELETE, PUT, and PATCH routes. Verify the user has permission to modify the resource.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC128 – HTTP Request Smuggling Risk\n// ────────────────────────────────────────────\n\nexport const httpRequestSmuggling: CustomRule = {\n id: \"VC128\",\n title: \"HTTP Request Smuggling Risk\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Manually parsing Content-Length or Transfer-Encoding headers can lead to request smuggling when behind a reverse proxy.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /(?:headers?\\[?|getHeader\\s*\\(\\s*)[\"'](?:content-length|transfer-encoding)[\"']\\s*\\]?\\s*\\)/gi,\n /parseInt\\s*\\(\\s*(?:req|request)\\.headers?\\[?\\s*[\"']content-length[\"']/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC128\", title: httpRequestSmuggling.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Let your framework handle Content-Length and Transfer-Encoding headers. Do not manually parse or trust these values.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC129 – Unencrypted PII in Database Schema\n// ────────────────────────────────────────────\n\nexport const unencryptedPII: CustomRule = {\n id: \"VC129\",\n title: \"Sensitive Data Stored Without Encryption\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Database schemas storing PII (SSN, credit cards, health records) in plaintext violate compliance requirements and expose data in breaches.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(sql|prisma|py|ts|js|rb)$/)) return [];\n const findings: RuleMatch[] = [];\n // Schema definitions with sensitive field names stored as plain text/varchar\n const schemaPatterns = [\n /(?:column|field|Column|Field)\\s*\\(\\s*[\"'](?:ssn|social_security|tax_id|credit_card|card_number|cvv|passport|driver_license|bank_account|routing_number)[\"']\\s*,\\s*(?:String|TEXT|VARCHAR|Text|text)/gi,\n /(?:ssn|social_security|tax_id|credit_card|card_number|cvv|passport_number|driver_license|bank_account|routing_number)\\s+(?:TEXT|VARCHAR|String|text|character varying)/gi,\n ];\n for (const pat of schemaPatterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC129\", title: unencryptedPII.title, severity: \"high\" as const, category: \"Information Leakage\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Encrypt sensitive fields at the application level before storing. Use field-level encryption with a KMS for SSN, credit cards, and health data.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC130 – Missing Rate Limit on Auth Endpoints\n// ────────────────────────────────────────────\n\nexport const missingAuthRateLimit: CustomRule = {\n id: \"VC130\",\n title: \"Authentication Endpoint Without Rate Limiting\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Login, registration, and password reset endpoints without rate limiting are vulnerable to credential stuffing and brute-force attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Routes that handle auth operations\n const authRoutePattern = /(?:app|router|server)\\.(?:post|put)\\s*\\(\\s*[\"'](?:\\/(?:api\\/)?(?:auth\\/)?(?:login|signin|sign-in|register|signup|sign-up|forgot-password|reset-password))[\"']/gi;\n let m;\n while ((m = authRoutePattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check surrounding code for rate limiting\n const surrounding = content.substring(Math.max(0, m.index - 300), Math.min(content.length, m.index + 300));\n if (/rateLimit|rateLimiter|throttle|slowDown|express-rate-limit|rate_limit|RateLimiter|limiter/i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC130\", title: missingAuthRateLimit.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add rate limiting to authentication endpoints. Limit to 5-10 attempts per minute per IP. Use express-rate-limit or similar.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC131 – Known Vulnerable Dependency Pattern\n// ────────────────────────────────────────────\n\nexport const vulnerableDependencies: CustomRule = {\n id: \"VC131\",\n title: \"Potentially Vulnerable Dependency\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"Dependencies with known security issues that are commonly found in AI-generated codebases.\",\n check(content, filePath) {\n if (!filePath.endsWith(\"package.json\")) return [];\n // Skip nested package.json (node_modules, etc)\n if (/node_modules|\\.next|dist|build/.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Known problematic packages and version ranges\n const vulnerablePackages: [RegExp, string][] = [\n [/\"jsonwebtoken\"\\s*:\\s*\"[\\^~]?[0-7]\\./g, \"jsonwebtoken < 8.x has signature bypass vulnerabilities\"],\n [/\"lodash\"\\s*:\\s*\"[\\^~]?[0-3]\\./g, \"lodash < 4.x has prototype pollution vulnerabilities\"],\n [/\"minimist\"\\s*:\\s*\"[\\^~]?[01]\\.[01]\\./g, \"minimist < 1.2.6 has prototype pollution\"],\n [/\"node-fetch\"\\s*:\\s*\"[\\^~]?[12]\\./g, \"node-fetch < 3.x has data exposure issues\"],\n [/\"express\"\\s*:\\s*\"[\\^~]?[0-3]\\./g, \"express < 4.x has multiple known vulnerabilities\"],\n [/\"axios\"\\s*:\\s*\"[\\^~]?0\\.[0-9]\\./g, \"axios < 0.21 has SSRF vulnerabilities\"],\n [/\"tar\"\\s*:\\s*\"[\\^~]?[0-5]\\./g, \"tar < 6.x has path traversal vulnerabilities\"],\n [/\"glob-parent\"\\s*:\\s*\"[\\^~]?[0-4]\\./g, \"glob-parent < 5.1.2 has ReDoS vulnerability\"],\n ];\n for (const [pat, message] of vulnerablePackages) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC131\", title: vulnerableDependencies.title, severity: \"high\" as const, category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: `${message}. Update to the latest version and run 'npm audit' regularly.`,\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC132–VC145: SERVICE-SPECIFIC API KEY DETECTION\n// Each rule targets a service with a known key prefix/format.\n// Descriptions include permission context (what the key can do).\n// ────────────────────────────────────────────\n\nconst SECRET_FILE_EXT = /\\.(js|ts|jsx|tsx|py|rb|go|java|php|env|json|yaml|yml|toml|cfg|conf|ini|sh|bash|zsh)$/;\nconst PLACEHOLDER_RE = /process\\.env|os\\.environ|ENV\\[|getenv|System\\.getenv|env\\(|CHANGE_ME|YOUR_|xxx|placeholder|example|TODO|FIXME|<your|dummy|fake_?key|test_?key/i;\nconst LOCK_FILE_RE = /package-lock|yarn\\.lock|pnpm-lock|Gemfile\\.lock|Pipfile\\.lock|composer\\.lock/i;\n\nfunction secretRuleCheck(\n content: string,\n filePath: string,\n pattern: RegExp,\n ruleId: string,\n title: string,\n severity: \"critical\" | \"high\",\n fix: string,\n): RuleMatch[] {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const re = new RegExp(pattern.source, pattern.flags.includes(\"g\") ? pattern.flags : `${pattern.flags}g`);\n let m: RegExpExecArray | null;\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n // Skip if this looks like an env var reference, not a literal\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: ruleId, title, severity, category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum), fix,\n });\n }\n return findings;\n}\n\nexport const hardcodedAnthropicKey: CustomRule = {\n id: \"VC132\",\n title: \"Hardcoded Anthropic API Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Anthropic API keys (sk-ant-*) grant full API access: running models, incurring charges, and accessing usage history. A leaked key can be used to make unlimited API calls at your expense.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /sk-ant-api03-[A-Za-z0-9_\\-]{80,}/g, \"VC132\", this.title, \"critical\",\n \"Move the Anthropic API key to an environment variable (ANTHROPIC_API_KEY). Rotate the exposed key immediately at console.anthropic.com → API Keys.\");\n },\n};\n\nexport const hardcodedGitHubPAT: CustomRule = {\n id: \"VC133\",\n title: \"Hardcoded GitHub Personal Access Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_) grant repository access: reading/writing code, managing issues, triggering Actions, and accessing private repos depending on scopes.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}/g, \"VC133\", this.title, \"critical\",\n \"Move the GitHub token to an environment variable (GITHUB_TOKEN). Rotate immediately at github.com → Settings → Developer settings → Personal access tokens. Consider using fine-grained tokens with minimal permissions.\");\n },\n};\n\nexport const hardcodedSendGridKey: CustomRule = {\n id: \"VC134\",\n title: \"Hardcoded SendGrid API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"SendGrid API keys (SG.*) can send emails as your domain, access contact lists, view email activity, and modify sender reputation. A leaked key enables phishing from your verified domain.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /SG\\.[A-Za-z0-9_\\-]{22}\\.[A-Za-z0-9_\\-]{43}/g, \"VC134\", this.title, \"high\",\n \"Move the SendGrid API key to an environment variable (SENDGRID_API_KEY). Rotate at app.sendgrid.com → Settings → API Keys.\");\n },\n};\n\nexport const hardcodedSlackToken: CustomRule = {\n id: \"VC135\",\n title: \"Hardcoded Slack Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Slack tokens (xoxb-, xoxp-, xapp-) can read/send messages, access channels, list users, and manage workspace settings depending on scopes. Bot tokens (xoxb) have app-level access; user tokens (xoxp) act as the installing user.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /xox[bpas]-[0-9A-Za-z\\-]{10,}/g, \"VC135\", this.title, \"high\",\n \"Move the Slack token to an environment variable (SLACK_BOT_TOKEN). Rotate at api.slack.com → Your Apps → OAuth & Permissions → Reinstall.\");\n },\n};\n\nexport const hardcodedGCPServiceAccount: CustomRule = {\n id: \"VC136\",\n title: \"Hardcoded GCP Service Account Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GCP service account JSON keys grant project-level access: compute, storage, databases, IAM, and billing depending on assigned roles. A leaked key can compromise your entire Google Cloud project.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(json|js|ts|py|yaml|yml)$/)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for the combination of service_account type + private key in the same file\n if (!/\"type\"\\s*:\\s*\"service_account\"/.test(content)) return [];\n if (!/-----BEGIN (?:RSA )?PRIVATE KEY-----/.test(content)) return [];\n const findings: RuleMatch[] = [];\n const m = content.match(/\"type\"\\s*:\\s*\"service_account\"/);\n if (m && m.index !== undefined) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC136\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"GCP service account keys should never be committed. Use Workload Identity Federation or store the key in a secrets manager. Delete this key in Google Cloud Console → IAM → Service Accounts and generate a new one.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedShopifyToken: CustomRule = {\n id: \"VC137\",\n title: \"Hardcoded Shopify Access Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Shopify access tokens (shpat_, shpca_, shppa_) grant full store access: orders, customers, products, and payment information. A leaked token can expose customer PII and modify store data.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /shp(?:at|ca|pa)_[a-fA-F0-9]{32,}/g, \"VC137\", this.title, \"critical\",\n \"Move the Shopify token to an environment variable. Rotate in Shopify Admin → Apps → Develop apps → API credentials.\");\n },\n};\n\nexport const hardcodedGitLabToken: CustomRule = {\n id: \"VC138\",\n title: \"Hardcoded GitLab Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GitLab personal/project tokens (glpat-) grant repository and CI/CD access: reading/writing code, triggering pipelines, and managing project settings.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /glpat-[A-Za-z0-9_\\-]{20,}/g, \"VC138\", this.title, \"critical\",\n \"Move the GitLab token to an environment variable. Revoke and regenerate at gitlab.com → Preferences → Access Tokens.\");\n },\n};\n\nexport const hardcodedTwilioKey: CustomRule = {\n id: \"VC139\",\n title: \"Hardcoded Twilio API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Twilio API key SIDs (SK*) can send SMS/calls, access call recordings and logs, and modify account configuration. A leaked key enables toll fraud and unauthorized communications.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /SK[0-9a-fA-F]{32}/g, \"VC139\", this.title, \"high\",\n \"Move the Twilio API key to an environment variable (TWILIO_API_KEY). Rotate at twilio.com → Console → API Keys.\");\n },\n};\n\nexport const hardcodedMailgunKey: CustomRule = {\n id: \"VC140\",\n title: \"Hardcoded Mailgun API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Mailgun API keys (key-*) can send emails as your domain, access email logs, and manage routes. A leaked key enables phishing from your verified sending domain.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /key-[0-9a-zA-Z]{32}/g, \"VC140\", this.title, \"high\",\n \"Move the Mailgun key to an environment variable (MAILGUN_API_KEY). Rotate at app.mailgun.com → Settings → API Security.\");\n },\n};\n\nexport const hardcodedDatadogKey: CustomRule = {\n id: \"VC141\",\n title: \"Hardcoded Datadog API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Datadog API/app keys grant access to all monitoring data and can modify dashboards, alerts, and integrations. A leaked key exposes your entire infrastructure topology.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for DD_API_KEY or DD_APP_KEY assignments with hex values\n const pattern = /(?:DD_API_KEY|DD_APP_KEY|datadog[_-]?(?:api|app)[_-]?key)\\s*[:=]\\s*[\"'`]([a-f0-9]{32})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC141\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Datadog key to an environment variable (DD_API_KEY). Rotate at app.datadoghq.com → Organization Settings → API Keys.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedVercelToken: CustomRule = {\n id: \"VC142\",\n title: \"Hardcoded Vercel Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Vercel tokens can deploy projects, manage environment variables (which may contain other secrets), and access project settings. A leaked token can modify your production deployments.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n const pattern = /(?:VERCEL_TOKEN|vercel[_-]?token|vercel[_-]?api[_-]?token)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{24,})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC142\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Vercel token to an environment variable (VERCEL_TOKEN). Rotate at vercel.com → Settings → Tokens.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedSupabaseServiceRole: CustomRule = {\n id: \"VC143\",\n title: \"Hardcoded Supabase Service Role Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Supabase service role keys bypass Row Level Security and grant full database read/write access, auth admin, and storage admin. A leaked service role key exposes all user data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for service_role key assignments (JWT format near service_role context).\n // Supabase service role keys are JWTs — header.payload.signature. The `.`\n // between the three base64url segments MUST be in the character class, or\n // we miss every real token. Previously this regex excluded the dots and\n // silently passed right over real service-role keys.\n const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\\s*[:=]\\s*[\"'`](eyJ[A-Za-z0-9_\\-.]{50,})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC143\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Supabase service role key to a server-side environment variable. This key bypasses RLS — it must NEVER be exposed to the client. Rotate at supabase.com → Project Settings → API.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedVaultToken: CustomRule = {\n id: \"VC144\",\n title: \"Hardcoded HashiCorp Vault Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Vault tokens (hvs.*, s.*) grant access to secrets stored in HashiCorp Vault, potentially exposing database credentials, API keys, and certificates across your entire infrastructure.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /(?:hvs\\.[A-Za-z0-9_\\-]{24,}|(?:VAULT_TOKEN|vault[_-]?token)\\s*[:=]\\s*[\"'`]s\\.[A-Za-z0-9]{24,}[\"'`])/g, \"VC144\", this.title, \"critical\",\n \"Move the Vault token to an environment variable (VAULT_TOKEN). Revoke the exposed token immediately with `vault token revoke`. Use short-lived tokens with minimal policies.\");\n },\n};\n\nexport const hardcodedPineconeKey: CustomRule = {\n id: \"VC145\",\n title: \"Hardcoded Pinecone API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Pinecone API keys can read, write, and delete all vector data and manage indexes. A leaked key exposes your entire vector database and the embeddings stored in it.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /pcsk_[A-Za-z0-9_\\-]{50,}/g, \"VC145\", this.title, \"high\",\n \"Move the Pinecone API key to an environment variable (PINECONE_API_KEY). Rotate at app.pinecone.io → API Keys.\");\n },\n};\n\n// ────────────────────────────────────────────\n// VC146–VC151: SECRET EXPOSURE PATH ANALYSIS\n// Detects when secrets are used in ways that amplify exposure.\n// ────────────────────────────────────────────\n\nconst SECRET_VAR_RE = /(?:api[_-]?key|apikey|api[_-]?secret|secret[_-]?key|access[_-]?token|auth[_-]?token|private[_-]?key|password|passwd|pwd|credentials|client[_-]?secret|app[_-]?secret|master[_-]?key)/i;\n\nexport const secretInURLParam: CustomRule = {\n id: \"VC146\",\n title: \"Secret Passed in URL Query Parameter\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"API keys or tokens in URL query parameters are logged in server access logs, browser history, referrer headers, and proxy logs. Secrets in URLs are one of the most common causes of credential exposure.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // Template literals: `?api_key=${secret}` or `&token=${token}`\n /[?&](?:api_key|apikey|api_secret|token|access_token|secret|key|password|auth|authorization)=\\$\\{/gi,\n // String concat: '?key=' + apiKey\n /[?&](?:api_key|apikey|api_secret|token|access_token|secret|key|password|auth)=[\"']\\s*\\+/gi,\n // Python f-strings: f\"?token={token}\"\n /[?&](?:api_key|apikey|token|access_token|secret|key|password)=\\{[a-zA-Z_]/g,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC146\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pass secrets in the Authorization header (Bearer token) or request body, never in URL parameters. URL parameters are logged in server access logs, browser history, and referrer headers.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretLoggedToConsole: CustomRule = {\n id: \"VC147\",\n title: \"Secret Logged to Console\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Logging variables that appear to contain secrets (key, token, password, secret) writes credentials to stdout, log files, and monitoring services where they can be harvested.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n const findings: RuleMatch[] = [];\n // console.log/debug/info/warn/error with a secret-named variable\n const pattern = /console\\.(?:log|debug|info|warn|error)\\s*\\([^)]*\\b(api[_-]?key|apikey|secret|token|password|passwd|credentials|private[_-]?key|auth[_-]?token|access[_-]?token)\\b/gi;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC147\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Remove this console statement or redact the secret before logging. Use structured logging with a secret-redaction filter in production.\",\n });\n }\n return findings;\n },\n};\n\nexport const secretInErrorResponse: CustomRule = {\n id: \"VC148\",\n title: \"Secret Leaked in Error Response\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Returning secrets or secret-named variables in API error responses exposes credentials to anyone who can trigger the error, including unauthenticated users.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n const findings: RuleMatch[] = [];\n // res.json/send/status().json containing secret-named variables in catch/error blocks\n const pattern = /(?:res\\.(?:json|send|status\\s*\\([^)]*\\)\\s*\\.json))\\s*\\(\\s*\\{[^}]*\\b(api[_-]?key|secret|token|password|credentials|private[_-]?key|process\\.env\\.[A-Z_]*(?:KEY|SECRET|TOKEN|PASSWORD))\\b/gi;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC148\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never include secrets in API responses. Return a generic error message and log the detailed error server-side only.\",\n });\n }\n return findings;\n },\n};\n\nexport const secretInBundleConfig: CustomRule = {\n id: \"VC149\",\n title: \"Secret in Client-Side Bundle Configuration\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Secrets defined in Webpack DefinePlugin, Vite define, or Next.js publicRuntimeConfig are embedded into the client-side JavaScript bundle and visible to anyone viewing the page source.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/(?:webpack|vite|next)\\.config\\.|\\.config\\.(js|ts|mjs)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // Webpack DefinePlugin with secret-named key\n /DefinePlugin\\s*\\(\\s*\\{[^}]*\\b(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY|CLIENT_SECRET)\\b/gi,\n // Vite define with secret-named key\n /define\\s*:\\s*\\{[^}]*\\b(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY|CLIENT_SECRET)\\b/gi,\n // Next.js publicRuntimeConfig with secret-named key\n /publicRuntimeConfig\\s*:\\s*\\{[^}]*\\b(?:apiKey|secret|token|password|privateKey|clientSecret)\\b/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC149\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move secrets to server-side environment variables. Only expose public configuration (like API base URLs) in client-side bundle config. Use serverRuntimeConfig (Next.js) or server-only modules.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretInHTMLAttribute: CustomRule = {\n id: \"VC150\",\n title: \"Secret in HTML Meta Tag or Data Attribute\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"API keys or tokens embedded in HTML meta tags or data-* attributes are visible in the page source to anyone who visits the page, including search engine crawlers.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(html|htm|jsx|tsx|vue|svelte|ejs|hbs|pug)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // <meta name=\"api-key\" content=\"...\">\n /<meta\\s[^>]*name\\s*=\\s*[\"'](?:api[_-]?key|secret|token|password)[^>]*>/gi,\n // data-api-key=\"actual-value\" (not a template/binding)\n /data-(?:api[_-]?key|secret|token|password|auth)\\s*=\\s*[\"'][a-zA-Z0-9_\\-]{12,}[\"']/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC150\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never embed secrets in HTML attributes or meta tags. Load configuration from server-side APIs or use server-rendered environment injection with non-secret values only.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretInCLIArgument: CustomRule = {\n id: \"VC151\",\n title: \"Secret Passed as Command-Line Argument\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Secrets interpolated into exec/spawn/execSync command strings are visible in process listings (ps aux), shell history, and system audit logs to any user on the same machine.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb)$/)) return [];\n const findings: RuleMatch[] = [];\n // exec/spawn/execSync with secret-named variable interpolated\n const patterns = [\n /(?:exec|execSync|spawn|spawnSync|child_process)\\s*\\([^)]*\\$\\{[^}]*(?:api[_-]?key|secret|token|password|credentials)/gi,\n /(?:exec|execSync|spawn|spawnSync|child_process)\\s*\\([^)]*[\"']\\s*\\+\\s*(?:api[_-]?key|secret|token|password|credentials)/gi,\n /(?:subprocess|os\\.system|os\\.popen)\\s*\\([^)]*\\{[^}]*(?:api[_-]?key|secret|token|password|credentials)/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC151\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pass secrets via environment variables (env option in spawn/exec) instead of command-line arguments. CLI arguments are visible to all users via `ps aux`.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC152–VC158: NEW RULES (Webhook Verification, CORS, Validation, etc.)\n// ────────────────────────────────────────────\n\nexport const webhookSignatureVerification: CustomRule = {\n id: \"VC152\",\n title: \"Missing Webhook Signature Verification (Non-Stripe)\",\n severity: \"critical\",\n category: \"Payment Security\",\n description: \"Webhook endpoints for Clerk, GitHub, Resend, SendGrid, Slack, or Twilio that don't verify the request signature allow attackers to forge events.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\/|\\/webhook/i.test(filePath)) return [];\n // Must have a POST handler\n if (!/export\\s+(?:async\\s+)?function\\s+POST/i.test(content)) return [];\n\n const services = [\n { name: \"Clerk\", content: /clerk/i, events: /user\\.created|user\\.updated|user\\.deleted|session\\./i, verify: /svix|Webhook\\(\\)\\.verify|webhook-id|wh_secret/i },\n { name: \"GitHub\", content: /github/i, events: /push|pull_request|issues|installation/i, verify: /createHmac|x-hub-signature|verify.*signature|GITHUB_WEBHOOK_SECRET/i },\n { name: \"Resend\", content: /resend/i, events: /email\\.sent|email\\.delivered|email\\.bounced/i, verify: /svix|webhook.*verify|x-webhook-signature|RESEND_WEBHOOK_SECRET/i },\n { name: \"SendGrid\", content: /sendgrid/i, events: /inbound.*parse|event.*webhook/i, verify: /EventWebhook|x-twilio-email|verifySignature|SENDGRID_WEBHOOK_VERIFICATION/i },\n { name: \"Slack\", content: /slack/i, events: /event_callback|challenge|url_verification/i, verify: /signing_secret|createHmac|x-slack-signature|SLACK_SIGNING_SECRET/i },\n { name: \"Twilio\", content: /twilio/i, events: /sms|voice|StatusCallback|MessagingResponse/i, verify: /validateRequest|X-Twilio-Signature|authToken|TWILIO_AUTH_TOKEN/i },\n ];\n\n const findings: RuleMatch[] = [];\n for (const svc of services) {\n if (!svc.content.test(content)) continue;\n if (!svc.events.test(content)) continue;\n if (svc.verify.test(content)) continue;\n // Found a handler for this service without verification\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+POST/);\n if (!m || m.index === undefined) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC152\", title: `${svc.name} Webhook Missing Signature Verification`,\n severity: \"critical\", category: \"Payment Security\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: `Verify the ${svc.name} webhook signature before processing events. Check the ${svc.name} documentation for their signature verification method.`,\n });\n break; // Max 1 finding per file\n }\n return findings;\n },\n};\n\nexport const reflectedCORSOrigin: CustomRule = {\n id: \"VC153\",\n title: \"Reflected CORS Origin with Credentials\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Echoing req.headers.origin into Access-Control-Allow-Origin with credentials enabled lets any website read authenticated API responses. This is worse than wildcard CORS because browsers actually send cookies.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Must have credentials enabled\n if (!/credentials\\s*[=:]\\s*true|Allow-Credentials.*true/i.test(content)) return [];\n // Must have origin reflection pattern\n if (!/req\\.headers\\.origin|callback\\s*\\(\\s*null\\s*,\\s*origin\\s*\\)|origin:\\s*true/i.test(content)) return [];\n // Skip if there's an allowlist check\n if (/ALLOWED_ORIGINS|allowedOrigins|whitelist|isAllowed|\\.includes\\s*\\(\\s*origin|\\.has\\s*\\(\\s*origin/i.test(content)) return [];\n\n const patterns = [\n /(?:Allow-Origin[\"'],\\s*req\\.headers\\.origin)/gi,\n /origin\\s*:\\s*\\(\\s*origin\\s*,\\s*callback\\s*\\)\\s*=>\\s*callback\\s*\\(\\s*null\\s*,\\s*origin\\s*\\)/gi,\n /origin\\s*:\\s*true/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const raw = findMatches(content, p, reflectedCORSOrigin, filePath, () =>\n \"Use an explicit origin allowlist instead of reflecting the request origin. Only allow origins you control.\"\n );\n if (raw.length > 0) { matches.push(raw[0]); break; } // Max 1 per file\n }\n return matches;\n },\n};\n\nexport const missingRequestValidation: CustomRule = {\n id: \"VC154\",\n title: \"API Route Without Request Body Validation\",\n severity: \"medium\",\n category: \"Authorization\",\n description: \"API route reads the request body without any schema validation (zod, joi, yup, etc.). Unvalidated input can lead to unexpected behavior, injection, or data corruption.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n if (/\\/webhook/i.test(filePath)) return []; // Webhooks validate via signature\n // Must have a mutating handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:POST|PUT|PATCH)/i.test(content)) return [];\n // Must read the body\n if (!/req\\.body|request\\.json\\(\\)|c\\.req\\.json\\(\\)|await.*\\.json\\(\\)/i.test(content)) return [];\n // Skip if any validation library or manual checks exist\n if (/zod|joi|yup|ajv|class-validator|superstruct|valibot|\\.safeParse|\\.validate\\(|z\\.object|Joi\\.object|yup\\.object|schema\\.parse|zodResolver/i.test(content)) return [];\n if (/if\\s*\\(\\s*!(?:body|parsed|data|payload)\\.|throw.*(?:missing|invalid|required)/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+(?:POST|PUT|PATCH)/);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC154\", title: this.title, severity: \"medium\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Validate request bodies with a schema library like zod: const parsed = schema.safeParse(await request.json()); if (!parsed.success) return Response.json({ error: 'Invalid input' }, { status: 400 });\",\n }];\n },\n};\n\nexport const missingAIRateLimit: CustomRule = {\n id: \"VC155\",\n title: \"Missing Rate Limiting on AI/LLM API Call\",\n severity: \"high\",\n category: \"Availability\",\n description: \"API route calls an AI/LLM service (OpenAI, Anthropic, Cohere) without rate limiting. A single user can burn through your entire API budget in minutes.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Must have a route handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|PATCH|DELETE)/i.test(content) &&\n !/\\.(get|post|put|patch|delete)\\s*\\(/i.test(content)) return [];\n // Must reference an AI SDK\n if (!/openai|anthropic|@anthropic-ai|cohere|@google\\/generative-ai|chat\\.completions|messages\\.create/i.test(content)) return [];\n // Skip if rate limiting exists\n if (/rateLimit|rateLimiter|throttle|checkRateLimit|limiter|slowDown|express-rate-limit/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+(?:POST|GET)/i) || content.match(/\\.(post|get)\\s*\\(/i);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC155\", title: this.title, severity: \"high\" as const, category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add rate limiting to AI/LLM endpoints to prevent budget abuse. Use express-rate-limit or a custom limiter with per-user and per-IP limits.\",\n }];\n },\n};\n\nexport const missingPagination: CustomRule = {\n id: \"VC156\",\n title: \"Missing Pagination on List Endpoint\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"API endpoint returns database records without a LIMIT or pagination. A single request can dump the entire table, crash the server, or cause an out-of-memory error.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n // Must have a GET handler (list endpoints)\n if (!/export\\s+(?:async\\s+)?function\\s+GET/i.test(content)) return [];\n // Must have a database query\n if (!/findMany|\\.find\\(\\s*\\{|\\.find\\(\\s*\\)|SELECT\\s.*FROM|\\.all\\(\\)|\\.query\\(/i.test(content)) return [];\n // Skip if any pagination exists\n if (/LIMIT|\\.take\\(|\\.limit\\(|\\.skip\\(|offset|cursor|page|perPage|pageSize|paginate|\\.slice\\(/i.test(content)) return [];\n // Skip if it filters by specific ID (not a list endpoint)\n if (/findUnique|findFirst|findById|\\.findOne|WHERE.*id\\s*=/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+GET/);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC156\", title: this.title, severity: \"medium\" as const, category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add pagination to list endpoints: .findMany({ take: limit, skip: offset }) or SELECT ... LIMIT ? OFFSET ?\",\n }];\n },\n};\n\nexport const exposedDatabaseStudio: CustomRule = {\n id: \"VC157\",\n title: \"Database Studio Exposed in Production\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Prisma Studio or Drizzle Studio is configured in a production script or bound to 0.0.0.0, giving anyone with the URL full database admin access.\",\n check(content, filePath) {\n const basename = filePath.split(\"/\").pop() || \"\";\n if (basename !== \"package.json\" && !/docker-compose/i.test(basename)) return [];\n // Check for studio in production-facing scripts\n if (!/prisma\\s+studio|drizzle-kit\\s+studio/i.test(content)) return [];\n // Skip if only in dev/db:studio scripts (that's fine)\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (!/prisma\\s+studio|drizzle-kit\\s+studio/i.test(line)) continue;\n // Flag if in start/production script or bound to 0.0.0.0\n if (/\"start\"|\"production\"|\"build.*&&.*studio\"|0\\.0\\.0\\.0/i.test(line)) {\n return [{\n rule: \"VC157\", title: this.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: i + 1, snippet: getSnippet(content, i + 1),\n fix: \"Only run database studio in development. Move the studio command to a dev-only script and never bind to 0.0.0.0 in production.\",\n }];\n }\n }\n return [];\n },\n};\n\nexport const insecureDirectObjectReference: CustomRule = {\n id: \"VC158\",\n title: \"Potential Insecure Direct Object Reference (IDOR)\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"API route fetches a record by ID from URL parameters without checking if the requesting user owns that record. An attacker can access other users' data by guessing or enumerating IDs.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n // Must have a route handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:GET|PUT|PATCH|DELETE)/i.test(content)) return [];\n // Must read an ID from params\n if (!/params\\.id|params\\.(?:slug|uuid)|req\\.params\\./i.test(content)) return [];\n // Must query a database with that param\n if (!/findUnique|findFirst|findById|\\.findOne|WHERE.*=.*params|\\.get\\(.*params/i.test(content)) return [];\n // Skip if ownership check exists\n if (/userId|ownerId|createdBy|user_id|owner_id|AND.*user|where.*user|authorize|canAccess|checkPermission|isOwner|belongsTo/i.test(content)) return [];\n // Skip admin routes\n if (/admin/i.test(filePath)) return [];\n // Skip if auth is present (may be checking ownership via middleware)\n if (/requireUser|requireUserForApi|getServerSession|auth\\(\\)/i.test(content)) return [];\n const m = content.match(/params\\.id|params\\.(?:slug|uuid)|req\\.params\\./);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC158\", title: this.title, severity: \"high\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add an ownership check: verify the requesting user owns the record before returning it. Example: WHERE id = params.id AND userId = session.userId\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// RULE TIERS: FREE (30 rules) + PRO (all 158)\n// VC152-VC158 are Pro rules (not in freeRules)\n// ────────────────────────────────────────────\n\n// Free tier: core security rules available to everyone (30 of 151)\nexport const freeRules: CustomRule[] = [\n hardcodedSecrets, // VC001\n exposedEnvFile, // VC002\n missingAuthMiddleware, // VC003\n supabaseNoRLS, // VC004\n stripeWebhookUnprotected, // VC005\n sqlInjection, // VC006\n xssVulnerability, // VC007\n noRateLimiting, // VC008\n corsWildcard, // VC009\n clientSideAuth, // VC010\n nextPublicSecret, // VC011\n envNotGitignored, // VC014\n evalUsage, // VC015\n unvalidatedRedirect, // VC016\n insecureCookies, // VC017\n exposedAuthSecret, // VC018\n missingCSP, // VC020\n hardcodedJWTSecret, // VC031\n missingHTTPS, // VC032\n exposedDebugMode, // VC033\n insecureRandomness, // VC034\n missingErrorBoundary, // VC036\n exposedStackTraces, // VC037\n missingLockFile, // VC039\n dangerousInnerHTML, // VC063\n consoleLogProduction, // VC097\n emptyCatchBlock, // VC104\n todoLeftInCode, // VC103\n weakHashing, // VC060\n disabledTLSVerification, // VC061\n];\n\n// Pro rules are delivered server-side — not shipped in npm package\n// See: /api/cli/rules-bundle endpoint\nexport const allRules = freeRules;\n\n// Every rule defined in this file, in ID order. The web API runs the full set\n// on each scan; the CLI uses this as the catalog for `/rules` docs and admin\n// tooling. Keep this in sync when adding new rules (tests will catch gaps).\nexport const allCustomRules: CustomRule[] = [\n // Free tier (VC001–VC039 plus legacy free IDs)\n hardcodedSecrets,\n exposedEnvFile,\n missingAuthMiddleware,\n supabaseNoRLS,\n stripeWebhookUnprotected,\n sqlInjection,\n xssVulnerability,\n noRateLimiting,\n corsWildcard,\n clientSideAuth,\n nextPublicSecret,\n envNotGitignored,\n evalUsage,\n unvalidatedRedirect,\n insecureCookies,\n exposedAuthSecret,\n missingCSP,\n hardcodedJWTSecret,\n missingHTTPS,\n exposedDebugMode,\n insecureRandomness,\n missingErrorBoundary,\n exposedStackTraces,\n missingLockFile,\n dangerousInnerHTML,\n consoleLogProduction,\n emptyCatchBlock,\n todoLeftInCode,\n weakHashing,\n disabledTLSVerification,\n // Pro tier: infrastructure, cloud, deserialization, auth, mobile, etc.\n firebaseClientConfig,\n supabaseAnonAdmin,\n insecureElectronWindow,\n ipcPathTraversal,\n unsanitizedHTMLExport,\n prototypePollution,\n missingFileSizeLimits,\n unsanitizedFilenames,\n electronNavigationUnrestricted,\n missingSecurityMeta,\n unvalidatedAPIParams,\n unvalidatedEventData,\n insecureDeserialization,\n openRedirectParams,\n insecureFileUpload,\n exposedGitDir,\n ssrfVulnerability,\n massAssignment,\n timingAttack,\n logInjection,\n weakPasswordRequirements,\n sessionFixation,\n missingBruteForce,\n nosqlInjection,\n exposedDBCredentials,\n missingDBEncryption,\n graphqlIntrospection,\n missingRequestSizeLimit,\n hardcodedIPAllowlist,\n sensitiveLocalStorage,\n exposedSourceMaps,\n clickjacking,\n overlyPermissiveIAM,\n dockerRunAsRoot,\n exposedDockerPorts,\n hardcodedEncryptionKey,\n exposedServerActions,\n unprotectedAPIRoutes,\n clientComponentSecret,\n insecureDeepLink,\n sensitiveAsyncStorage,\n missingCertPinning,\n androidDebuggable,\n djangoDebug,\n flaskSecretKey,\n pickleDeserialization,\n missingCSRF,\n githubActionsInjection,\n secretsInCI,\n corsServerless,\n k8sPrivileged,\n jwtAlgConfusion,\n regexDos,\n xxeVulnerability,\n ssti,\n javaDeserialization,\n missingSRI,\n exposedAdminRoutes,\n insecureWebSocket,\n missingHSTS,\n sensitiveURLParams,\n missingContentDisposition,\n hostHeaderRedirect,\n raceCondition,\n unsafeObjectAssign,\n unprotectedDownload,\n commandInjection,\n corsLocalhost,\n insecureGRPC,\n syncFileOps,\n eventListenerLeak,\n nPlusOneQuery,\n largeBundleImport,\n blockingMainThread,\n callbackHell,\n magicNumbers,\n s3BucketNoEncryption,\n securityGroupAllInbound,\n rdsPubliclyAccessible,\n missingCloudTrail,\n lambdaWithoutVPC,\n dockerLatestTag,\n dockerCopySensitive,\n dockerTooManyPorts,\n k8sSecretNotEncrypted,\n k8sNoResourceLimits,\n pathTraversal,\n piiInLogs,\n hardcodedOAuthSecret,\n missingOAuthState,\n unpinnedGitHubAction,\n deprecatedTLS,\n weakRSAKeySize,\n ecbModeEncryption,\n insecurePasswordReset,\n terraformStateExposed,\n insecureHTTPMethods,\n httpRequestSmuggling,\n unencryptedPII,\n missingAuthRateLimit,\n vulnerableDependencies,\n // VC132–VC151: expanded secret detection\n hardcodedAnthropicKey,\n hardcodedGitHubPAT,\n hardcodedSendGridKey,\n hardcodedSlackToken,\n hardcodedGCPServiceAccount,\n hardcodedShopifyToken,\n hardcodedGitLabToken,\n hardcodedTwilioKey,\n hardcodedMailgunKey,\n hardcodedDatadogKey,\n hardcodedVercelToken,\n hardcodedSupabaseServiceRole,\n hardcodedVaultToken,\n hardcodedPineconeKey,\n secretInURLParam,\n secretLoggedToConsole,\n secretInErrorResponse,\n secretInBundleConfig,\n secretInHTMLAttribute,\n secretInCLIArgument,\n // VC152–VC158\n webhookSignatureVerification,\n reflectedCORSOrigin,\n missingRequestValidation,\n missingAIRateLimit,\n missingPagination,\n exposedDatabaseStudio,\n insecureDirectObjectReference,\n];\n\nexport function runCustomRules(\n content: string,\n filePath: string,\n disabledRules: string[] = [],\n tier: \"free\" | \"pro\" = \"free\",\n extraRules: CustomRule[] = [],\n): Finding[] {\n const findings: Finding[] = [];\n\n // Skip files that ARE security scanners (avoid scanning ourselves)\n if (/function runScan\\(files\\)|export function runCustomRules/.test(content) && /const (?:rules|allRules)\\s*[:=]/.test(content) && /findMatches/.test(content)) {\n return findings;\n }\n\n // Skip compiled bundles (pro-rules-bundle.cjs, webpack output, etc.)\n if (/pro-rules-bundle|\\.bundle\\./i.test(filePath)) return findings;\n\n // Skip files that contain intentional example/demo vulnerable code\n // (onboarding tutorials, blog code samples, documentation examples)\n if (/onboarding|demo-data|example-vulnerable|code-sample/i.test(filePath)) return findings;\n\n // Free tier uses bundled rules; Pro tier uses free + server-delivered extra rules\n const ruleset = tier === \"pro\" && extraRules.length > 0\n ? [...freeRules, ...extraRules]\n : freeRules;\n for (const rule of ruleset) {\n if (disabledRules.includes(rule.id)) continue;\n\n const matches = rule.check(content, filePath);\n const compliance = complianceMap[rule.id];\n for (const match of matches) {\n findings.push({\n id: `${match.rule}-${match.file}:${match.line}`,\n rule: match.rule,\n severity: match.severity,\n title: match.title,\n description: rule.description,\n file: match.file,\n line: match.line,\n column: match.column,\n snippet: match.snippet,\n fix: match.fix,\n category: match.category,\n source: \"custom\",\n owasp: compliance?.owasp,\n cwe: compliance?.cwe,\n });\n }\n }\n\n return findings;\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Finding } from \"./types.js\";\n\n/**\n * AI-powered false positive filter.\n *\n * Takes findings from the regex/entropy scanners and asks Claude Haiku\n * to classify each one as \"real\" or \"false positive\" based on the\n * surrounding code context. Findings marked FP are removed before the\n * user sees them, but preserved in filteredFindings so users can\n * review the AI's decisions.\n */\n\nconst REVIEW_SYSTEM_PROMPT = `You are reviewing security scan findings for false positives. Your job is to look at each finding and the surrounding code to determine if it's a REAL security vulnerability or a FALSE POSITIVE.\n\nCommon false positive patterns you should catch:\n- Auth check exists inside the function body (requireUser, requireUserForApi, getSession, etc.) but the scanner only checked the function signature\n- The flagged pattern is in example/documentation/tutorial code, not production code\n- The variable is developer-controlled (constants, config values, static strings), not user input\n- The flagged function is a database method (conn.exec, db.exec, prisma.$executeRaw) not a shell command (child_process.exec)\n- The comparison is a type check (typeof x === \"string\") not a secret comparison\n- The file is a test, mock, or fixture file\n- The \"secret\" is a publishable/public key (pk_test_, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) designed to be client-side\n- The innerHTML/dangerouslySetInnerHTML uses a static constant or JSON.stringify, not user input\n- The redirect URL has already been validated (isAllowedRedirect, validateRedirect)\n- The webhook endpoint is for a non-Stripe service but flagged as \"Stripe webhook\"\n- The \"sensitive data in URL\" is in a comment or documentation, not actual code\n- Package-lock.json URLs flagged as secrets (they're npm registry URLs, not secrets)\n\nFor each finding, respond ONLY with a JSON array. No other text.\nEach element: {\"index\": <number>, \"verdict\": \"real\" or \"fp\", \"reason\": \"<1 sentence>\"}`;\n\nconst MAX_FINDINGS_PER_BATCH = 15;\nconst MAX_CONTEXT_LINES = 10;\nconst MAX_TOTAL_FINDINGS = 50;\n\ninterface ReviewResult {\n index: number;\n verdict: \"real\" | \"fp\";\n reason: string;\n}\n\nexport interface FilteredFinding {\n finding: Finding;\n reason: string;\n}\n\nexport interface AIFilterResult {\n findings: Finding[];\n filteredFindings: FilteredFinding[];\n aiReviewed: boolean;\n removedCount: number;\n totalBefore: number;\n}\n\nfunction getExpandedContext(content: string, line: number, contextLines: number = MAX_CONTEXT_LINES): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">>>\" : \" \";\n return `${marker} ${lineNum} | ${l}`;\n }).join(\"\\n\");\n}\n\nfunction buildReviewPrompt(findings: Finding[], fileContent: string): string {\n const parts: string[] = [];\n for (let i = 0; i < findings.length; i++) {\n const f = findings[i];\n const context = getExpandedContext(fileContent, f.line);\n parts.push(`--- Finding ${i} ---\nRule: ${f.rule} (${f.title})\nSeverity: ${f.severity}\nFile: ${f.file}\nLine: ${f.line}\nDescription: ${f.description}\nSuggested fix: ${f.fix || \"N/A\"}\n\nCode context:\n${context}\n`);\n }\n return `Review these ${findings.length} security scan findings. For each one, determine if it's a real vulnerability or a false positive based on the code context.\\n\\n${parts.join(\"\\n\")}`;\n}\n\nfunction parseReviewResponse(text: string): ReviewResult[] {\n try {\n const cleaned = text.replace(/```json\\n?/g, \"\").replace(/```\\n?/g, \"\").trim();\n const parsed = JSON.parse(cleaned);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (r: unknown): r is ReviewResult =>\n typeof r === \"object\" && r !== null &&\n \"index\" in r && \"verdict\" in r &&\n ((r as ReviewResult).verdict === \"real\" || (r as ReviewResult).verdict === \"fp\")\n );\n } catch {\n return [];\n }\n}\n\n/**\n * Filter false positives from findings using Claude Haiku.\n *\n * Returns filtered findings, the removed findings with AI reasons,\n * and metadata about what the AI did.\n */\nexport async function filterFalsePositives(\n findings: Finding[],\n fileContents: Map<string, string>,\n): Promise<AIFilterResult> {\n const empty: AIFilterResult = { findings, filteredFindings: [], aiReviewed: false, removedCount: 0, totalBefore: findings.length };\n if (!process.env.ANTHROPIC_API_KEY) return empty;\n if (findings.length === 0) return empty;\n\n const toReview = findings.slice(0, MAX_TOTAL_FINDINGS);\n const overflow = findings.slice(MAX_TOTAL_FINDINGS);\n const totalBefore = findings.length;\n\n const byFile = new Map<string, Finding[]>();\n for (const f of toReview) {\n const group = byFile.get(f.file) || [];\n group.push(f);\n byFile.set(f.file, group);\n }\n\n let client: Anthropic;\n try {\n client = new Anthropic();\n } catch {\n return empty;\n }\n\n // Track which findings are FP and why\n const fpMap = new Map<number, string>(); // globalIndex → reason\n\n for (const [file, fileFindings] of byFile) {\n const content = fileContents.get(file);\n if (!content) continue;\n\n for (let i = 0; i < fileFindings.length; i += MAX_FINDINGS_PER_BATCH) {\n const batch = fileFindings.slice(i, i + MAX_FINDINGS_PER_BATCH);\n const prompt = buildReviewPrompt(batch, content);\n\n try {\n const response = await client.messages.create({\n model: \"claude-haiku-4-5-20251001\",\n max_tokens: 1024,\n system: REVIEW_SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: prompt }],\n });\n\n const text = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\");\n\n const results = parseReviewResponse(text);\n\n for (const r of results) {\n if (r.verdict === \"fp\" && r.index >= 0 && r.index < batch.length) {\n const globalIndex = toReview.indexOf(batch[r.index]);\n if (globalIndex !== -1) {\n fpMap.set(globalIndex, r.reason);\n }\n }\n }\n } catch {\n continue;\n }\n }\n }\n\n const filtered = toReview.filter((_, i) => !fpMap.has(i));\n const filteredFindings: FilteredFinding[] = [];\n for (const [idx, reason] of fpMap) {\n filteredFindings.push({ finding: toReview[idx], reason });\n }\n\n return {\n findings: [...filtered, ...overflow],\n filteredFindings,\n aiReviewed: true,\n removedCount: fpMap.size,\n totalBefore,\n };\n}\n","/**\n * Shannon-entropy based secret detection.\n *\n * Catches high-entropy string literals that look like API keys / tokens /\n * credentials even when they don't match any of the service-specific\n * hardcoded-secret rules (VC001, VC132, etc). Fires a single `ENTROPY`\n * finding per suspicious literal.\n *\n * The trick to keeping this useful is keeping false positives down. Real\n * codebases are full of high-entropy strings that aren't secrets — SHA-256\n * hashes, UUIDs, base64 integrity hashes, Tailwind-generated class names,\n * SVG path data, Next.js content-addressed filenames, publishable\n * (intentionally-public) keys from Clerk/Stripe/etc. This scanner has three\n * layers of suppression before emitting a finding:\n *\n * 1. File-level: skip lockfiles, CSS/SVG/image/map files, node_modules,\n * minified/bundled output.\n * 2. Shape-level: if the string matches a known-safe shape (UUID, Git SHA,\n * integrity hash, publishable key prefix, Tailwind fingerprint, SVG\n * path data, Next.js content hash), skip.\n * 3. Context-level: inspect the assignment context — if the variable name\n * implies \"this holds a hash/digest/fingerprint\" (not a secret), skip.\n * If the variable name implies a secret (key/token/etc), lower the\n * entropy bar. Otherwise require high entropy AND no safe signals.\n *\n * When in doubt we bias toward suppressing — one FP every 100 scans is much\n * more costly to the product than one missed secret, because the expanded\n * VC132-VC151 service-specific rules already cover the most common key\n * formats.\n */\n\nimport type { Finding } from \"./types.js\";\n\nfunction shannonEntropy(str: string): number {\n const freq: Record<string, number> = {};\n for (const ch of str) {\n freq[ch] = (freq[ch] || 0) + 1;\n }\n const len = str.length;\n let entropy = 0;\n for (const count of Object.values(freq)) {\n const p = count / len;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\n// ────────────────────────────────────────────\n// Shape-based allowlist — non-secret strings that look high-entropy\n// ────────────────────────────────────────────\n\nconst SAFE_PATTERNS: RegExp[] = [\n // UUIDs (v1-v5)\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n // Git commit hashes (long and short)\n /^[0-9a-f]{40}$/i,\n /^[0-9a-f]{7,8}$/i,\n // Hex colors\n /^#?[0-9a-fA-F]{3,8}$/,\n // Small base64 (< 20 chars can't be a real key anyway)\n /^[A-Za-z0-9+/]{1,19}={0,2}$/,\n // Package versions\n /^\\d+\\.\\d+\\.\\d+/,\n // Integrity-hash prefix (sha256-, sha512-, ...)\n /^sha\\d+-/i,\n // URLs without credentials in them\n /^https?:\\/\\/[^:@]*$/,\n /^https?:\\/\\/registry\\.npmjs\\.org\\//,\n /^https?:\\/\\/registry\\.yarnpkg\\.com\\//,\n // Full package integrity hash (sha512-[base64]=)\n /^sha\\d+-[A-Za-z0-9+/=]+$/,\n // Package tarball URLs\n /\\.tgz$/,\n /registry.*\\/-\\/.*\\.tgz$/,\n // ISO dates / times\n /^\\d{4}-\\d{2}-\\d{2}/,\n // Locale tags\n /^[a-z]{2}-[A-Z]{2}$/,\n // Text encodings\n /^utf-?8|ascii|latin|iso-8859/i,\n // MIME types\n /^(?:application|text|image|audio|video)\\//,\n // CSS keyword values\n /^(?:inherit|none|auto|block|flex|grid|absolute|relative|fixed|px|em|rem|%)/,\n // Developer-placeholder tokens\n /^(?:test|example|sample|demo|placeholder|temp|tmp|foo|bar|baz|lorem|ipsum)/i,\n // XML / DTD markers\n /DTD|DOCTYPE|w3\\.org|apple\\.com\\/DTDs/i,\n /xmlns|schema|xsd|xsi/i,\n\n // NEW — added in Wave 3.2 for context-aware FP reduction\n // ──────────────────────────────────────────────────────\n\n // Tailwind JIT / CSS-in-JS class-name fingerprints, e.g. \"css-2kx3yr8\",\n // \"tw-abc12def\", \"jss-1a2b3c4d\". Typically prefix + 6-12 hex-ish chars.\n /^(?:css|tw|jss|emotion|styled|mui|chakra)-[a-z0-9]{4,14}$/i,\n\n // SVG path data — starts with a path command letter followed by coords.\n // These can get very long and high-entropy but are never secrets.\n /^[MmLlHhVvCcSsQqTtAaZz][\\d.,\\s\\-MmLlHhVvCcSsQqTtAaZz]{10,}$/,\n\n // Next.js / Vite / webpack content-addressed asset filenames, e.g.\n // \"main.4e5f6a78.js\", \"chunk-2a3b.js\", \"_next/static/chunks/pages-xyz\".\n /\\.[0-9a-f]{6,16}\\.(?:js|css|mjs|woff2?|ttf|png|jpg|svg)(?:\\?.*)?$/i,\n /^_next\\//,\n\n // Publishable / client-side keys from common vendors. Flagged by their\n // own service-specific rules if they look wrong, but entropy should NOT\n // double-flag these. They are designed to ship to the browser.\n /^pk_(?:live|test|[a-z0-9]+)_/, // Stripe / Clerk publishable\n /^NEXT_PUBLIC_|^VITE_|^REACT_APP_/, // Build-time public env vars\n /^pub_/, // Segment etc.\n /^ey[A-Za-z0-9_-]+\\.ey[A-Za-z0-9_-]+\\./, // JWT header.payload prefix — don't flag solely on entropy\n\n // Content-Security-Policy hashes / nonces in HTML/JSON\n /^'sha\\d+-/,\n /^nonce-/i,\n\n // Embedded data URIs\n /^data:[a-z]+\\//i,\n];\n\n// File types that shouldn't be scanned for entropy at all\nconst SKIP_FILES = /\\.(css|scss|less|svg|md|txt|html?|xml|yml|yaml|toml|lock|map|woff2?|ttf|eot|ico|png|jpg|gif|webp)$/i;\nconst SKIP_FILENAMES = /(?:package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock|composer\\.lock|Gemfile\\.lock|Cargo\\.lock|poetry\\.lock|Pipfile\\.lock|shrinkwrap\\.json)$/i;\n\n// Variable names whose contents are almost never secrets\nconst SAFE_VAR_NAMES = /(?:description|message|text|label|title|content|template|html|svg|css|style|class(?:Name)?|query|mutation|schema|regex|pattern|format|placeholder|comment|url|path|route|endpoint|href|src|alt|name|type|version|encoding|charset|locale|translation|copy|prose|markdown|slug|handle)/i;\n\n// Variable names that specifically imply hashing / fingerprinting rather than\n// secret storage. If a hex/base64 blob lands in one of these, suppress.\nconst HASH_LIKE_VAR_NAMES = /(?:^|[^a-z])(?:hash|digest|checksum|fingerprint|etag|crc|md5|sha1|sha256|sha512|contenthash|buildid|revision|commit|sri|integrity|cacheKey|fileHash|assetId|versionId)(?:$|[^a-z])/i;\n\n// Value prefixes / shapes that imply \"algorithm-name:hex-blob\" hash literals\n// — e.g. \"sha256:a94a8fe5ccb19...\", \"md5:5d41402abc4b2a76b9719d911017c592\"\nconst HASH_PREFIX_RE = /^(?:sha\\d+|md5|crc32|base64|bcrypt|argon2|pbkdf2|blake2b?)[:_]/i;\n\n// Variable names that IMPLY a credential. If one of these catches a\n// high-entropy literal, we emit the finding at a lower entropy bar and a\n// higher severity.\nconst SECRET_VAR_NAMES = /(?:^|[^a-z])(?:key|secret|token|password|passwd|pwd|api[_-]?key|auth|credential|private|signing|bearer|access[_-]?token|refresh[_-]?token|session[_-]?id)(?:$|[^a-z])/i;\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\n/**\n * Scan a batch of files for high-entropy string literals.\n *\n * Each element in `files` must be `{ path, content }`. Returns a list of\n * `Finding` objects for suspicious string literals. See the file header for\n * the suppression layers.\n */\nexport function scanEntropy(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n if (SKIP_FILES.test(filePath)) continue;\n if (SKIP_FILENAMES.test(filePath)) continue;\n const basename = filePath.split(\"/\").pop() || \"\";\n if (SKIP_FILENAMES.test(basename)) continue;\n if (filePath.includes(\"node_modules\")) continue;\n if (filePath.includes(\".min.\")) continue;\n if (/pro-rules-bundle|\\.bundle\\.|\\.chunk\\./i.test(filePath)) continue;\n if (/(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|fixtures?\\/)/i.test(filePath)) continue;\n\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Skip comment lines\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"#\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\")) continue;\n\n // Find string assignments (quotes or backticks)\n const stringPattern = /(?:[:=]\\s*)([\"'`])([^\"'`\\n]{20,120})\\1/g;\n let match: RegExpExecArray | null;\n\n while ((match = stringPattern.exec(line)) !== null) {\n const value = match[2];\n\n // Layer 2 — shape-based allowlist\n if (SAFE_PATTERNS.some(p => p.test(value))) continue;\n if (HASH_PREFIX_RE.test(value)) continue;\n\n // Layer 3 — context\n const beforeAssign = line.substring(0, match.index);\n if (SAFE_VAR_NAMES.test(beforeAssign)) continue;\n\n // Skip URL-ish strings without credentials\n if (/^https?:\\/\\/[^:@]*$/.test(value)) continue;\n\n // Skip sentence-like strings (more than 2 spaces => likely prose)\n if ((value.match(/\\s/g) || []).length > 2) continue;\n\n // Adaptive entropy thresholds depending on the charset of the value\n const isHex = /^[0-9a-fA-F]+$/.test(value);\n const isBase64 = /^[A-Za-z0-9+/]+=*$/.test(value);\n\n let threshold = 4.0;\n if (isHex) threshold = 3.0;\n else if (isBase64) threshold = 4.5;\n\n if (value.length < 20) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy < threshold) continue;\n\n // Pull out the variable name for context checks\n const varName = beforeAssign.match(/(\\w+)\\s*[:=]\\s*$/)?.[1] || \"\";\n\n // Context suppression: if the variable name clearly describes a\n // hash/digest/fingerprint AND the value is hex-ish or base64-ish,\n // suppress. This catches things like:\n // const contentHash = \"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\";\n // const etag = \"W/\\\"686897696a7c876b7e\\\"\";\n // config.buildId = \"9a4f2c8b3d1e5f7a8c9b0d1e2f3a4b5c\";\n if (HASH_LIKE_VAR_NAMES.test(varName) && (isHex || isBase64)) continue;\n\n const isLikelySecret = SECRET_VAR_NAMES.test(varName);\n\n // Require EITHER very-high entropy OR a secret-suggesting var name\n // before actually emitting a finding. This is the final gate.\n if (entropy < 4.5 && !isLikelySecret) continue;\n\n const masked = value.substring(0, 6) + \"...\" + value.substring(value.length - 4);\n\n findings.push({\n id: `ENTROPY-${filePath}:${i + 1}`,\n rule: \"ENTROPY\",\n severity: isLikelySecret ? \"critical\" : \"high\",\n title: \"High-Entropy String Detected (Possible Secret)\",\n description: `Found a high-entropy string (${entropy.toFixed(1)} bits) that may be a hardcoded secret or API key: \"${masked}\"`,\n file: filePath,\n line: i + 1,\n snippet: getSnippet(content, i + 1),\n fix: \"If this is a secret, move it to an environment variable. If it's not a secret (e.g., hash, encoded data), add it to .xploitscanignore.\",\n category: \"Secrets\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-798\",\n });\n }\n }\n }\n\n return findings;\n}\n"],"mappings":"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","names":["Anthropic","getSnippet"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/snippet.ts","../src/ast/parse.ts","../src/ast/taint.ts","../src/ast/traverse.ts","../src/rules.ts","../src/ai-fp-filter.ts","../src/entropy-scanner.ts"],"sourcesContent":["/**\n * xploitscan-shared-rules\n *\n * Single source of truth for XploitScan's custom security rules. Both the CLI\n * (packages/cli) and the web API (packages/api) import from here so the rule\n * definitions, compliance mappings, and runner logic can't drift between them.\n *\n * The named exports include:\n * - All 206 individual rule objects (e.g. `hardcodedSecrets`, `stripeWebhookUnprotected`)\n * - `freeRules`: the 30 rules bundled into the free CLI\n * - `allRules`: alias for freeRules (used by older call sites)\n * - `complianceMap`: VC### → { owasp, cwe } mapping\n * - `runCustomRules`: the scanner entry point\n * - Types: `CustomRule`, `Finding`, `RuleMatch`, `Severity`, `Confidence`\n * - Helpers: `getSnippet`\n */\n\nexport * from \"./types.js\";\nexport { getSnippet } from \"./snippet.js\";\nexport * from \"./rules.js\";\nexport {\n filterFalsePositives,\n type AIFilterResult,\n type FilteredFinding,\n} from \"./ai-fp-filter.js\";\nexport { scanEntropy } from \"./entropy-scanner.js\";\n// AST primitives — exposed for external rule authors and test harnesses.\nexport {\n parseFile,\n buildTaintMap,\n visitCalls,\n visitBinary,\n isCalleeNamed,\n isMethodCall,\n getObjectProperty,\n callSpreads,\n type ParsedFile,\n type TaintMap,\n} from \"./ast/index.js\";\n","/**\n * Return a small code snippet around a given line number with a `>` marker on the matched line.\n * Pure string manipulation — no fs dependency, so this is safe to use in both the CLI\n * and in serverless/edge environments like the web API.\n */\nexport function getSnippet(\n content: string,\n line: number,\n contextLines = 2,\n): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n\n return lines\n .slice(start, end)\n .map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">\" : \" \";\n return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n","/**\n * Centralized Babel parser with per-file caching.\n *\n * Rules that opt in to AST analysis call parseFile(content, filename) to get\n * a Program node back. Parsing is expensive (~5-50ms per KB of source); the\n * LRU cache ensures a single scan pass touches each file at most once even\n * when multiple rules walk the same AST.\n *\n * Failure mode: returns null on parse errors. Callers fall back to regex-only\n * behavior so a syntax-error file in the user's repo can never break a scan.\n */\nimport { parse, type ParseResult } from \"@babel/parser\";\nimport type { File } from \"@babel/types\";\n\nexport interface ParsedFile {\n ast: ParseResult<File>;\n language: \"js\" | \"ts\" | \"jsx\" | \"tsx\";\n}\n\n// Bounded LRU so a 10k-file repo scan doesn't blow memory. Most scans\n// finish well under this cap.\nconst MAX_CACHE = 256;\nconst cache = new Map<string, ParsedFile | null>();\n\nfunction cacheKey(filename: string, contentHash: number): string {\n return `${filename}:${contentHash}`;\n}\n\n// Cheap content hash — collisions would cause a stale AST on the rare case\n// two identically-named files have distinct content during the same scan.\n// We add length + first/last byte to make that almost impossible in practice.\nfunction quickHash(s: string): number {\n let h = 5381;\n const step = Math.max(1, Math.floor(s.length / 32));\n for (let i = 0; i < s.length; i += step) {\n h = ((h << 5) + h + s.charCodeAt(i)) | 0;\n }\n return (h * 31 + s.length) | 0;\n}\n\nfunction pickLanguage(filename: string): ParsedFile[\"language\"] | null {\n if (/\\.tsx$/i.test(filename)) return \"tsx\";\n if (/\\.jsx$/i.test(filename)) return \"jsx\";\n if (/\\.(ts|cts|mts)$/i.test(filename)) return \"ts\";\n if (/\\.(js|cjs|mjs)$/i.test(filename)) return \"js\";\n return null;\n}\n\n/**\n * Parse a JS/TS file. Returns null for unsupported extensions, parse errors,\n * or empty content. Callers MUST handle null and fall back to regex-only.\n */\nexport function parseFile(content: string, filename: string): ParsedFile | null {\n const lang = pickLanguage(filename);\n if (!lang) return null;\n if (!content || content.length === 0) return null;\n\n const key = cacheKey(filename, quickHash(content));\n if (cache.has(key)) return cache.get(key) ?? null;\n\n const plugins: Array<string | [string, unknown]> = [];\n if (lang === \"ts\" || lang === \"tsx\") plugins.push(\"typescript\");\n if (lang === \"jsx\" || lang === \"tsx\") plugins.push(\"jsx\");\n plugins.push(\"decorators-legacy\", \"classProperties\", \"dynamicImport\", \"topLevelAwait\");\n\n let ast: ParseResult<File> | null = null;\n try {\n ast = parse(content, {\n sourceType: \"unambiguous\",\n allowImportExportEverywhere: true,\n allowReturnOutsideFunction: true,\n allowAwaitOutsideFunction: true,\n allowUndeclaredExports: true,\n errorRecovery: true,\n plugins: plugins as never,\n });\n } catch {\n // Parse error — treat as unparseable. Regex rules will still fire.\n cache.set(key, null);\n if (cache.size > MAX_CACHE) {\n const firstKey = cache.keys().next().value;\n if (firstKey !== undefined) cache.delete(firstKey);\n }\n return null;\n }\n\n const entry: ParsedFile = { ast, language: lang };\n cache.set(key, entry);\n if (cache.size > MAX_CACHE) {\n const firstKey = cache.keys().next().value;\n if (firstKey !== undefined) cache.delete(firstKey);\n }\n return entry;\n}\n\n/** Exported for tests only. Drops the AST cache between test runs. */\nexport function _resetParseCache(): void {\n cache.clear();\n}\n","/**\n * Lightweight taint tracking for AST-based rules.\n *\n * \"Tainted\" means \"derived from untrusted input\" — an Express/Next request\n * body, query string, params, or headers; process.argv; environment\n * variables known to be user-controlled; or file contents read at runtime.\n *\n * This is deliberately a local-only analysis: we walk the AST of one file,\n * track which identifiers bind to a tainted source, and propagate through\n * direct assignments and destructuring. Cross-function and cross-file flow\n * is out of scope for Phase A — those require a module graph we don't build\n * yet. A conservative local analysis is still enough to flip the benchmark\n * recall on the data-flow rules.\n *\n * The analysis returns a TaintMap the rules can query:\n * isTainted(node) → \"is this AST expression tainted?\"\n * isTaintedIdent(name) → \"is this identifier currently bound to tainted\n * data in the file-level scope?\"\n *\n * Shape of recognized sources:\n * req.body, req.query, req.params, req.headers, req.cookies\n * request.body, request.query, ... (Fastify)\n * ctx.request.body, ctx.query (Koa)\n * process.argv, process.env[Keys known to be user-controllable]\n * event.queryStringParameters, event.body (AWS Lambda / API Gateway)\n * searchParams.get(...), url.searchParams (Next.js / Web fetch API)\n */\nimport type { Node } from \"@babel/types\";\nimport type { NodePath } from \"@babel/traverse\";\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nimport _traverse from \"@babel/traverse\";\nimport type { ParsedFile } from \"./parse.js\";\n\n// @babel/traverse ships CJS-default; the ESM interop gives us an object with\n// a default property in some bundlers.\nconst traverse = (\n typeof _traverse === \"function\" ? _traverse : (_traverse as { default: typeof _traverse }).default\n) as typeof _traverse;\n\n/** User-controlled property paths on Express/Fastify/Koa/Lambda request objects. */\nconst TAINTED_PROP_SUFFIXES = new Set([\n \"body\",\n \"query\",\n \"params\",\n \"headers\",\n \"cookies\",\n \"queryStringParameters\",\n \"pathParameters\",\n \"rawBody\",\n \"searchParams\",\n]);\n\n/** Objects whose request-like properties we treat as taint sources. */\nconst TAINTED_REQUEST_OBJECTS = new Set([\n \"req\",\n \"request\",\n \"ctx\", // Koa\n \"context\", // Koa alt\n \"event\", // Lambda\n]);\n\nexport interface TaintMap {\n /** true iff the given expression node evaluates to tainted data. */\n isTainted(node: Node | null | undefined): boolean;\n /** true iff the named identifier is bound to tainted data somewhere visible. */\n isTaintedIdent(name: string): boolean;\n /** Debug: all identifiers the analysis marked tainted, in discovery order. */\n taintedNames(): string[];\n}\n\n/**\n * Build a TaintMap for one parsed file. Single traversal, propagates taint\n * through:\n * - const/let bindings: const x = req.body.foo → x tainted\n * - destructuring: const { foo } = req.body → foo tainted\n * - assignment expressions: y = x; x is tainted → y tainted\n * - function params named req/request/ctx (inside handlers, `req.body` is\n * tainted even if we didn't see the outer binding)\n */\nexport function buildTaintMap(parsed: ParsedFile): TaintMap {\n const tainted = new Set<string>();\n\n /** Does this expression reach a request-like identifier through `.request`? */\n function reachesRequestIdent(node: Node | null | undefined): boolean {\n if (!node) return false;\n if (node.type === \"Identifier\") return TAINTED_REQUEST_OBJECTS.has(node.name);\n if (node.type === \"MemberExpression\") {\n // ctx.request, context.request — step through\n if (\n node.property.type === \"Identifier\" &&\n node.property.name === \"request\" &&\n node.object.type === \"Identifier\" &&\n TAINTED_REQUEST_OBJECTS.has(node.object.name)\n ) {\n return true;\n }\n return reachesRequestIdent(node.object);\n }\n return false;\n }\n\n function nodeIsTaintedSource(node: Node | null | undefined): boolean {\n if (!node) return false;\n // req.body / request.query / ctx.request.body / event.body / searchParams\n if (node.type === \"MemberExpression\") {\n const prop = node.property;\n const propName =\n prop.type === \"Identifier\"\n ? prop.name\n : prop.type === \"StringLiteral\"\n ? prop.value\n : \"\";\n\n if (TAINTED_PROP_SUFFIXES.has(propName)) {\n // Anchor object must be a known request-like identifier, a\n // ctx.request-style two-hop path, or another tainted-source\n // expression (req.query.x, ctx.request.body.user).\n const obj = node.object;\n if (obj.type === \"Identifier\" && TAINTED_REQUEST_OBJECTS.has(obj.name)) {\n return true;\n }\n if (reachesRequestIdent(obj)) return true;\n if (nodeIsTaintedSource(obj)) return true;\n }\n\n // process.argv handled as a special case here since it doesn't fit\n // the request-object / tainted-suffix shape.\n if (\n node.object.type === \"Identifier\" &&\n node.object.name === \"process\" &&\n prop.type === \"Identifier\" &&\n prop.name === \"argv\"\n ) {\n return true;\n }\n\n // req.body.foo — any descendant of a tainted source is tainted.\n if (nodeIsTaintedSource(node.object)) return true;\n return false;\n }\n\n // Method calls: e.g. searchParams.get(\"foo\"), url.searchParams.get(...)\n if (node.type === \"CallExpression\") {\n const callee = node.callee;\n if (\n callee.type === \"MemberExpression\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"get\" &&\n nodeIsTaintedSource(callee.object)\n ) {\n return true;\n }\n\n // Next.js / Web Fetch API body readers:\n // await request.json()\n // await request.formData()\n // await request.text()\n // await request.arrayBuffer()\n // await request.blob()\n // The request object is the one Next.js hands to a route handler; its\n // body is attacker-controlled.\n const BODY_READERS = new Set([\"json\", \"formData\", \"text\", \"arrayBuffer\", \"blob\"]);\n if (\n callee.type === \"MemberExpression\" &&\n callee.property.type === \"Identifier\" &&\n BODY_READERS.has(callee.property.name)\n ) {\n const obj = callee.object;\n if (obj.type === \"Identifier\" && TAINTED_REQUEST_OBJECTS.has(obj.name)) {\n return true;\n }\n if (reachesRequestIdent(obj)) return true;\n }\n }\n\n // `await <tainted-expr>` — unwrap the AwaitExpression and propagate.\n if (node.type === \"AwaitExpression\") {\n return nodeIsTaintedSource(node.argument);\n }\n\n return false;\n }\n\n /** Does this expression (not just a MemberExpression) produce tainted data? */\n function exprIsTainted(node: Node | null | undefined): boolean {\n if (!node) return false;\n if (nodeIsTaintedSource(node)) return true;\n if (node.type === \"Identifier\") return tainted.has(node.name);\n if (node.type === \"TemplateLiteral\") {\n return node.expressions.some((e) => exprIsTainted(e as Node));\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n return exprIsTainted(node.left) || exprIsTainted(node.right);\n }\n if (node.type === \"LogicalExpression\" && (node.operator === \"||\" || node.operator === \"??\")) {\n return exprIsTainted(node.left) || exprIsTainted(node.right);\n }\n if (node.type === \"ConditionalExpression\") {\n return exprIsTainted(node.consequent) || exprIsTainted(node.alternate);\n }\n if (node.type === \"MemberExpression\") {\n return exprIsTainted(node.object);\n }\n if (node.type === \"CallExpression\") {\n // Body-reader calls: await request.json() — taint the whole call.\n if (nodeIsTaintedSource(node)) return true;\n if (node.callee.type === \"MemberExpression\") {\n if (exprIsTainted(node.callee.object)) return true;\n // Known passthroughs that propagate taint from args to return:\n // path.join / resolve / normalize / format / parse / relative.\n const obj = node.callee.object;\n const prop = node.callee.property;\n if (\n prop.type === \"Identifier\" &&\n obj.type === \"Identifier\" &&\n obj.name === \"path\" &&\n [\"join\", \"resolve\", \"normalize\", \"format\", \"parse\", \"relative\"].includes(prop.name)\n ) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && exprIsTainted(a as Node));\n }\n // Buffer.from(x) / Buffer.concat([tainted]) etc. — propagate.\n if (\n prop.type === \"Identifier\" &&\n obj.type === \"Identifier\" &&\n obj.name === \"Buffer\" &&\n [\"from\", \"concat\", \"alloc\", \"allocUnsafe\"].includes(prop.name)\n ) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && exprIsTainted(a as Node));\n }\n }\n // Coercion wrappers (String/Number/URL/etc. called as bare identifiers)\n if (node.callee.type === \"Identifier\") {\n if ([\"String\", \"Number\", \"Boolean\", \"URL\", \"URLSearchParams\"].includes(node.callee.name)) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && exprIsTainted(a as Node));\n }\n }\n }\n if (node.type === \"AwaitExpression\") {\n return exprIsTainted(node.argument);\n }\n return false;\n }\n\n // Walk once and seed `tainted` with identifier names bound to tainted\n // expressions. We also collect assignment-propagation so `y = x` after\n // `x = req.body.foo` marks y tainted — this takes one pass because Babel\n // visits assignments in source order.\n traverse(parsed.ast, {\n VariableDeclarator(path: NodePath) {\n const node = path.node;\n if (node.type !== \"VariableDeclarator\") return;\n const init = node.init;\n if (!init) return;\n\n // const x = <tainted> → mark x\n if (node.id.type === \"Identifier\") {\n if (exprIsTainted(init)) {\n tainted.add(node.id.name);\n }\n }\n\n // const { a, b: c } = <tainted> → mark a, c\n if (node.id.type === \"ObjectPattern\") {\n const isTainted =\n nodeIsTaintedSource(init) ||\n (init.type === \"Identifier\" && tainted.has(init.name));\n if (isTainted) {\n for (const prop of node.id.properties) {\n if (prop.type === \"ObjectProperty\") {\n if (prop.value.type === \"Identifier\") tainted.add(prop.value.name);\n } else if (prop.type === \"RestElement\") {\n if (prop.argument.type === \"Identifier\") tainted.add(prop.argument.name);\n }\n }\n }\n }\n },\n\n AssignmentExpression(path: NodePath) {\n const node = path.node;\n if (node.type !== \"AssignmentExpression\") return;\n if (node.operator !== \"=\" && node.operator !== \"||=\" && node.operator !== \"??=\") return;\n if (node.left.type !== \"Identifier\") return;\n if (exprIsTainted(node.right)) {\n tainted.add(node.left.name);\n }\n },\n });\n\n const isTainted = (node: Node | null | undefined): boolean => {\n if (!node) return false;\n if (node.type === \"Identifier\") return tainted.has(node.name);\n if (nodeIsTaintedSource(node)) return true;\n // Template literals / string concat with a tainted piece.\n if (node.type === \"TemplateLiteral\") {\n return node.expressions.some((e) => isTainted(e as Node));\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n return isTainted(node.left) || isTainted(node.right);\n }\n // Logical expressions (next || \"/dashboard\", value ?? fallback) —\n // if either operand is tainted, the result can carry that value.\n if (\n node.type === \"LogicalExpression\" &&\n (node.operator === \"||\" || node.operator === \"??\" || node.operator === \"&&\")\n ) {\n return isTainted(node.left) || isTainted(node.right);\n }\n // Ternary: condition ? tainted : safe → could still return tainted.\n if (node.type === \"ConditionalExpression\") {\n return isTainted(node.consequent) || isTainted(node.alternate);\n }\n // Unwrap await expressions transparently.\n if (node.type === \"AwaitExpression\") {\n return isTainted(node.argument);\n }\n // Nested member access on a tainted root.\n if (node.type === \"MemberExpression\") {\n return isTainted(node.object);\n }\n // Call on a tainted receiver (e.g., `tainted.toString()` is still tainted).\n if (node.type === \"CallExpression\") {\n if (node.callee.type === \"MemberExpression\") {\n if (isTainted(node.callee.object)) return true;\n // Known passthrough calls: path.join/resolve/normalize/format etc.\n // propagate taint from any argument to the return value. Without\n // this, `path.join(BASE, tainted)` silently cleans the taint and\n // path-traversal rules don't fire on the classic `fs.readFile(\n // path.join(dir, req.query.file)` shape.\n const obj = node.callee.object;\n const prop = node.callee.property;\n if (\n prop.type === \"Identifier\" &&\n obj.type === \"Identifier\" &&\n obj.name === \"path\" &&\n [\"join\", \"resolve\", \"normalize\", \"format\", \"relative\", \"parse\"].includes(prop.name)\n ) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && isTainted(a as Node));\n }\n // Buffer.from(x) / Buffer.concat([...]) — member-expression wrappers\n // that propagate taint from any argument. Without this, wrapping a\n // tainted string in Buffer.from silently cleans it.\n if (\n prop.type === \"Identifier\" &&\n obj.type === \"Identifier\" &&\n obj.name === \"Buffer\" &&\n [\"from\", \"concat\", \"alloc\", \"allocUnsafe\"].includes(prop.name)\n ) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && isTainted(a as Node));\n }\n }\n // Coercion-style wrappers called as bare identifiers: String(x),\n // Number(x), URL(x), etc.\n if (node.callee.type === \"Identifier\") {\n if ([\"String\", \"Number\", \"Boolean\", \"URL\", \"URLSearchParams\"].includes(node.callee.name)) {\n return node.arguments.some((a) => a.type !== \"SpreadElement\" && isTainted(a as Node));\n }\n }\n }\n return false;\n };\n\n return {\n isTainted,\n isTaintedIdent: (name: string) => tainted.has(name),\n taintedNames: () => Array.from(tainted),\n };\n}\n","/**\n * Visitor helpers for rule authors. Each helper takes a parsed file and a\n * callback, and walks the AST in the most common pattern (find call\n * expressions to a particular function, find member expressions of a\n * particular shape, etc.).\n *\n * The rule code stays short and declarative; this file carries the\n * @babel/traverse boilerplate and default-export interop.\n */\nimport type { BinaryExpression, CallExpression, Node, ObjectExpression, SpreadElement } from \"@babel/types\";\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nimport _traverse from \"@babel/traverse\";\nimport type { ParsedFile } from \"./parse.js\";\n\nconst traverse = (\n typeof _traverse === \"function\" ? _traverse : (_traverse as { default: typeof _traverse }).default\n) as typeof _traverse;\n\n/**\n * Visit every BinaryExpression in the parsed file. Rule authors use this to\n * catch `a === b` / `a !== b` comparisons where one side is a secret identifier.\n */\nexport function visitBinary(\n parsed: ParsedFile,\n visit: (node: BinaryExpression, line: number) => void,\n): void {\n traverse(parsed.ast, {\n BinaryExpression(path) {\n const line = path.node.loc?.start.line ?? 1;\n visit(path.node, line);\n },\n });\n}\n\n/**\n * Visit every CallExpression. `matchCallee` decides whether to surface the\n * call; if it returns true, `visit` is invoked with the node and its\n * 1-indexed source line.\n */\nexport function visitCalls(\n parsed: ParsedFile,\n matchCallee: (callee: Node) => boolean,\n visit: (call: CallExpression, line: number) => void,\n): void {\n traverse(parsed.ast, {\n CallExpression(path) {\n const node = path.node;\n if (!matchCallee(node.callee)) return;\n const line = node.loc?.start.line ?? 1;\n visit(node, line);\n },\n });\n}\n\n/**\n * \"Does this callee resolve to a member/global named <name>?\" — handles\n * foo(x)\n * obj.foo(x)\n * a.b.foo(x)\n * foo?.(x)\n * Matches the terminal identifier only; doesn't bind to a specific receiver.\n */\nexport function isCalleeNamed(callee: Node, name: string): boolean {\n if (callee.type === \"Identifier\") return callee.name === name;\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n return callee.property.name === name;\n }\n if (callee.type === \"OptionalMemberExpression\" && callee.property.type === \"Identifier\") {\n return callee.property.name === name;\n }\n return false;\n}\n\n/**\n * Matches a call to `objName.methodName(...)` — useful for things like\n * `_.merge`, `Object.assign`, `Handlebars.compile`, `libxml.parseXml`.\n */\nexport function isMethodCall(callee: Node, objName: string, methodName: string): boolean {\n if (callee.type !== \"MemberExpression\" && callee.type !== \"OptionalMemberExpression\") {\n return false;\n }\n if (callee.property.type !== \"Identifier\") return false;\n if (callee.property.name !== methodName) return false;\n if (callee.object.type !== \"Identifier\") return false;\n return callee.object.name === objName;\n}\n\n/** Looks up an ObjectExpression property by key name. */\nexport function getObjectProperty(\n node: ObjectExpression,\n key: string,\n): { value: Node } | null {\n for (const prop of node.properties) {\n if (prop.type === \"ObjectProperty\") {\n if (prop.key.type === \"Identifier\" && prop.key.name === key) {\n return { value: prop.value as Node };\n }\n if (prop.key.type === \"StringLiteral\" && prop.key.value === key) {\n return { value: prop.value as Node };\n }\n }\n }\n return null;\n}\n\n/** Does this CallExpression spread an expression `matcher` returns true for? */\nexport function callSpreads(\n call: CallExpression,\n matcher: (node: Node) => boolean,\n): boolean {\n for (const arg of call.arguments) {\n if (arg.type === \"SpreadElement\") {\n if (matcher((arg as SpreadElement).argument as Node)) return true;\n }\n // Direct Object spread inside an object-literal argument:\n // fn({ ...req.body })\n if (arg.type === \"ObjectExpression\") {\n for (const prop of arg.properties) {\n if (prop.type === \"SpreadElement\") {\n if (matcher((prop as SpreadElement).argument as Node)) return true;\n }\n }\n }\n }\n return false;\n}\n","import type { CustomRule, Finding, RuleMatch } from \"./types.js\";\nimport { getSnippet } from \"./snippet.js\";\nimport {\n parseFile,\n buildTaintMap,\n visitCalls,\n visitBinary,\n isCalleeNamed,\n isMethodCall,\n getObjectProperty,\n callSpreads,\n type ParsedFile,\n type TaintMap,\n} from \"./ast/index.js\";\nimport type { Node } from \"@babel/types\";\n\n// ────────────────────────────────────────────\n// GLOBAL PRE-FILTERS\n// Reduces false positives before any rule runs\n// ────────────────────────────────────────────\n\n// Broad test/mock/fixture file detection\nconst TEST_FILE_PATTERN = /(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|\\.stories\\.|\\.story\\.|\\/test\\/|\\/tests\\/|\\/fixtures?\\/|\\/mocks?\\/|\\.mock\\.|test-utils|testing|\\.cy\\.|\\.e2e\\.)/i;\n\nfunction isTestFile(filePath: string): boolean {\n return TEST_FILE_PATTERN.test(filePath);\n}\n\n// Centralized \"this file looks like server-side code\" heuristic. Used by\n// every rule that gates on \"is this an API/route/handler/middleware file?\"\n// Two shapes:\n// - directory-style: /api/, /routes/, /controllers/, /endpoints/, /handlers/,\n// /middleware/, /webhooks/, /services/, /lambda/, /functions/, /pages/api/,\n// /app/.../route.{js,ts}\n// - bare-filename: routes.js, handler.js, middleware.js, server.js, app.js,\n// index.js, main.js, controller.js, api.js, webhook.js, login.js, auth.js,\n// admin.js, signup.js, register.js, callback.js, oauth.js, ingest.js,\n// create-*.js, update-*.js, delete-*.js, audit.js, etc.\n//\n// The bare-filename branch matters because many small projects organize code\n// flat at the package root rather than under nested route directories. Without\n// this our path filters fired only on conventional layouts and silently dropped\n// findings on minimalist apps and serverless function packages.\nconst SERVER_SIDE_PATH_RE = new RegExp(\n [\n // Directory-style anchors — match with OR without leading slash so\n // relative paths (`routes/users.js`) work as well as absolute.\n \"(?:^|/)api/\",\n \"(?:^|/)routes?/\",\n \"(?:^|/)controllers?/\",\n \"(?:^|/)endpoints?/\",\n \"(?:^|/)handlers?/\",\n \"(?:^|/)middleware/\",\n \"(?:^|/)webhooks?/\",\n \"(?:^|/)services?/\",\n \"(?:^|/)lambda/\",\n \"(?:^|/)functions?/\",\n \"(?:^|/)pages/api/\",\n \"(?:^|/)app/.*/route\\\\.(?:m?[jt]sx?|cjs)$\",\n // Bare-filename anchors (with or without leading directory).\n // Allows compound names like webhook-handler.js, cron-runner.js,\n // user-service.ts via the optional (?:[-_]\\w+)* suffix group.\n \"(?:^|/)(?:api|routes?|controllers?|endpoints?|handlers?|middleware|webhooks?|services?|server|app|index|main|lambda|function|ingest|runner|worker|resolvers?)(?:[-_]\\\\w+)*\\\\.(?:m?[jt]sx?|cjs|py|rb|go)$\",\n // Common functional names that imply request handling.\n \"(?:^|/)(?:auth|login|logout|signup|signin|register|password|reset|callback|oauth|admin|dashboard|profile|account|checkout|payment|webhook|audit|cron|search|upload|download|errors?|create-?\\\\w*|update-?\\\\w*|delete-?\\\\w*)(?:[-_]\\\\w+)*\\\\.(?:m?[jt]sx?|cjs|py|rb|go)$\",\n ].join(\"|\"),\n \"i\",\n);\n\nfunction isServerSideFile(filePath: string): boolean {\n if (isTestFile(filePath)) return false;\n return SERVER_SIDE_PATH_RE.test(filePath);\n}\n\n// \"This file looks like a config file.\" Different shape from server-side\n// code: filenames like db.config.js, knexfile.js, drizzle.config.ts,\n// next.config.js, vite.config.ts, *.conf, .env*, settings.py, etc.\nconst CONFIG_FILE_PATTERN = new RegExp(\n [\n \"\\\\.config\\\\.(?:m?[jt]sx?|cjs)$\",\n \"(?:^|/)config\\\\.(?:m?[jt]sx?|cjs|py|rb|json|ya?ml)$\",\n \"(?:^|/)settings\\\\.(?:m?[jt]sx?|cjs|py)$\",\n \"(?:^|/)\\\\.env(?:\\\\.|$)\",\n \"(?:^|/)(?:knex|drizzle|next|vite|rollup|webpack|tailwind|postcss|jest|vitest|tsup|babel)\\\\.config\\\\.(?:m?[jt]sx?|cjs)$\",\n \"(?:^|/)(?:db|database|connection|pool)\\\\.(?:config\\\\.)?(?:m?[jt]sx?|cjs|py|rb)$\",\n ].join(\"|\"),\n \"i\",\n);\n\nfunction isConfigFile(filePath: string): boolean {\n return CONFIG_FILE_PATTERN.test(filePath);\n}\n\n// Check if a match falls on a comment line (JS/TS/Python/Ruby/YAML/HTML)\nfunction isCommentLine(content: string, matchIndex: number): boolean {\n const lineStart = content.lastIndexOf(\"\\n\", matchIndex - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", matchIndex)).trimStart();\n return (\n lineText.startsWith(\"//\") ||\n lineText.startsWith(\"#\") ||\n lineText.startsWith(\"*\") ||\n lineText.startsWith(\"/*\") ||\n lineText.startsWith(\"<!--\") ||\n lineText.startsWith(\"'\") && lineText.length > 1 && /\\.(vb|bas)$/i.test(\"\") // VB comments\n );\n}\n\n// Check if a match is inside a string literal that's a fix/description message\n// (e.g., inside a findMatches callback or rule description)\nfunction isInsideFixMessage(content: string, matchIndex: number): boolean {\n const lineStart = content.lastIndexOf(\"\\n\", matchIndex - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", matchIndex));\n // Skip if the line looks like a fix suggestion string or rule description.\n // Word-boundaries on the imperative verbs are critical — without them,\n // words like \"reset\" (contains \"Set\") or \"added\" (contains \"Add\") trip\n // the filter and silently drop real findings.\n return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\\s*[:=(]/i.test(lineText) ||\n /return\\s*[\"'`].*\\b(?:Use|Replace|Add|Move|Set|Enable|Disable|Never|Don't|Do not|Instead)\\b/i.test(lineText);\n}\n\n// Context-aware matching: checks if mitigation exists within N lines after the match\nfunction hasMitigationNearby(content: string, matchIndex: number, mitigationPattern: RegExp, linesAhead: number = 5): boolean {\n const lines = content.split(\"\\n\");\n const matchLine = content.substring(0, matchIndex).split(\"\\n\").length - 1;\n const endLine = Math.min(matchLine + linesAhead, lines.length - 1);\n const nearbyContent = lines.slice(matchLine, endLine + 1).join(\"\\n\");\n return mitigationPattern.test(nearbyContent);\n}\n\n// Helper to find all regex matches with line numbers\n// Automatically skips matches on comment lines and fix messages\nfunction findMatches(\n content: string,\n pattern: RegExp,\n rule: Omit<CustomRule, \"check\">,\n filePath: string,\n fixTemplate?: (match: RegExpExecArray) => string,\n): RuleMatch[] {\n const matches: RuleMatch[] = [];\n const lines = content.split(\"\\n\");\n let m: RegExpExecArray | null;\n const re = new RegExp(pattern.source, pattern.flags.includes(\"g\") ? pattern.flags : `${pattern.flags}g`);\n\n while ((m = re.exec(content)) !== null) {\n // Skip matches on comment lines\n if (isCommentLine(content, m.index)) continue;\n // Skip matches inside fix/description strings\n if (isInsideFixMessage(content, m.index)) continue;\n\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: rule.id,\n title: rule.title,\n severity: rule.severity,\n category: rule.category,\n file: filePath,\n line: lineNum,\n snippet: getSnippet(content, lineNum),\n fix: fixTemplate?.(m),\n });\n }\n\n return matches;\n}\n\n/**\n * Build a RuleMatch from an AST-derived location. Mirrors findMatches' output\n * shape so AST-based rules can be mixed into the same Finding stream. `line`\n * comes from node.loc; we still call getSnippet for consistent UI.\n */\nfunction astMatch(\n content: string,\n filePath: string,\n line: number,\n rule: Omit<CustomRule, \"check\">,\n fix?: string,\n): RuleMatch {\n return {\n rule: rule.id,\n title: rule.title,\n severity: rule.severity,\n category: rule.category,\n file: filePath,\n line,\n snippet: getSnippet(content, line),\n fix,\n };\n}\n\n/**\n * Parse once per (file, rule) call, cached. Returns null if the file isn't\n * JS/TS, parsing failed, or the file is empty — callers fall back to the\n * regex-only path so malformed code can't break a scan.\n */\nfunction tryParse(content: string, filePath: string): { parsed: ParsedFile; taint: TaintMap } | null {\n const parsed = parseFile(content, filePath);\n if (!parsed) return null;\n return { parsed, taint: buildTaintMap(parsed) };\n}\n\n// ────────────────────────────────────────────\n// RULE DEFINITIONS\n// ────────────────────────────────────────────\n\nexport const hardcodedSecrets: CustomRule = {\n id: \"VC001\",\n title: \"Hardcoded API Key or Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"API keys, tokens, or secrets hardcoded in source code can be extracted by anyone with access to the code.\",\n check(content, filePath) {\n // Skip .env.example, template, test, and documentation files\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (isTestFile(filePath)) return [];\n if (filePath.match(/\\.(md|txt|rst|adoc)$/)) return [];\n\n const patterns = [\n // Generic API key patterns — require actual value assignment, not variable declarations\n /(?:api[_-]?key|apikey|api[_-]?secret)\\s*[:=]\\s*[\"'`]([a-zA-Z0-9_\\-]{20,})[\"'`]/gi,\n // AWS keys\n /(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/g,\n // Stripe keys\n /(?:sk_live|pk_live|sk_test|pk_test)_[a-zA-Z0-9]{20,}/g,\n // Supabase anon/service keys (JWT format)\n /(?:supabase[_-]?(?:anon|service)[_-]?key|SUPABASE_(?:ANON|SERVICE_ROLE)_KEY)\\s*[:=]\\s*[\"'`](eyJ[a-zA-Z0-9_-]{50,})[\"'`]/gi,\n // OpenAI keys\n /sk-[a-zA-Z0-9]{20,}T3BlbkFJ[a-zA-Z0-9]{20,}/g,\n // Generic tokens in assignments — require standalone word and longer min length\n /(?:^|[\\s,({])(?:token|secret|password|passwd|pwd)\\s*[:=]\\s*[\"'`]([a-zA-Z0-9_\\-!@#$%^&*]{20,})[\"'`]/gim,\n // SCREAMING_CASE identifiers ending in SECRET/TOKEN/KEY/PASSWORD\n // (OAUTH_CLIENT_SECRET, JWT_PRIVATE_KEY, STRIPE_WEBHOOK_SECRET, etc.)\n // that are assigned a string literal, not an env var or function call.\n /[A-Z][A-Z0-9_]*_(?:SECRET|TOKEN|KEY|PASSWORD|PASSWD)\\s*[:=]\\s*[\"'`]([a-zA-Z0-9_\\-!@#$%^&*.\\/]{12,})[\"'`]/g,\n // camelCase / snake_case object properties that look like credentials:\n // apiKey: \"...\", webhookUrl: \"https://...\", accountSid: \"...\", authToken: \"...\"\n // password: \"...\" caught here separately with a lower threshold (many\n // real passwords are 8-16 chars, below the 12-char floor of the\n // generic `secret = \"...\"` pattern).\n /\\b(?:apiKey|api_key|authToken|auth_token|webhookUrl|webhook_url|accountSid|account_sid|clientSecret|client_secret|refreshToken|refresh_token)\\s*:\\s*[\"'`]([a-zA-Z0-9_\\-!@#$%^&*.\\/:\\-]{12,})[\"'`]/gi,\n /\\b(?:password|passwd|pwd)\\s*:\\s*[\"'`]([a-zA-Z0-9_\\-!@#$%^&*.\\/:\\-]{8,})[\"'`]/gi,\n // Private keys\n /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g,\n // Database URLs with credentials\n /(?:postgres|mysql|mongodb(?:\\+srv)?):\\/\\/[^:]+:[^@]+@[^/\\s\"'`]+/gi,\n ];\n\n // Pattern-specific fix messages with risk context\n const fixMessages: string[] = [\n \"Move this API key to an environment variable. If this key has been committed, rotate it immediately — it may have already been scraped by bots.\",\n \"AWS access key detected — may grant full account access (EC2, S3, IAM, billing). Rotate immediately in AWS Console → IAM → Security Credentials. Use IAM roles or environment variables instead.\",\n \"Stripe key detected. sk_live_ keys can process real charges, issue refunds, and access customer payment data. sk_test_ keys are lower risk but should still not be committed. Rotate in Stripe Dashboard → Developers → API Keys.\",\n \"Supabase key detected. Service role keys bypass Row Level Security and grant full database read/write access. Move to a server-side environment variable immediately.\",\n \"OpenAI API key detected — grants full API access and can incur charges. Rotate at platform.openai.com → API Keys.\",\n \"Hardcoded token or password detected. Move to an environment variable and rotate the credential if it has been committed to version control.\",\n \"Named secret constant detected (XXX_SECRET / XXX_TOKEN / XXX_KEY). Move to an environment variable: `const NAME = process.env.NAME;`. Rotate the credential if this file has been committed to version control.\",\n \"Credential assigned to a config object property. Move to an environment variable — `apiKey: process.env.API_KEY` — and rotate the value if this file has been committed.\",\n \"Hardcoded password detected in a config object. Move to an environment variable (`password: process.env.DB_PASSWORD`) and rotate the password if this file has been committed.\",\n \"Private key found in source code. If this has been committed to version control, consider the key compromised — generate a new key pair and revoke the old one.\",\n \"Database credentials in connection string. An attacker with this URL has full database access. Move to an environment variable, restrict network access, and rotate the password.\",\n ];\n\n // Per-pattern minimum-value-length floor. Most secret shapes require 12+\n // characters (below that it's often an enum value, version, or similar);\n // the password-specific pattern allows 8+ (real passwords legitimately\n // start at 8). Index into this lines up with `patterns` above.\n const minLen: number[] = [\n 12, // [0] generic API key\n 16, // [1] AWS — already rigid in the regex, length floor is defensive\n 20, // [2] Stripe\n 50, // [3] Supabase JWT\n 40, // [4] OpenAI\n 20, // [5] generic tokens with {20,} in the regex\n 12, // [6] SCREAMING_CASE named constants\n 12, // [7] camelCase config props (apiKey/webhookUrl/etc.)\n 8, // [8] password: \"...\" — lower floor per the pattern comment above\n 0, // [9] private keys — no length check\n 0, // [10] DB URLs — no length check\n ];\n\n const matches: RuleMatch[] = [];\n for (let pi = 0; pi < patterns.length; pi++) {\n const pattern = patterns[pi];\n const rawMatches = findMatches(content, pattern, hardcodedSecrets, filePath, () => fixMessages[pi]);\n const floor = minLen[pi] ?? 12;\n for (const rm of rawMatches) {\n const lineText = content.split(\"\\n\")[rm.line - 1] || \"\";\n // Skip comment lines (additional guard beyond findMatches)\n const trimmed = lineText.trimStart();\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"#\")) continue;\n // Enforce per-pattern minimum value length\n if (floor > 0) {\n const secretMatch = lineText.match(/[:=]\\s*[\"'`]([^\"'`]*)[\"'`]/);\n if (secretMatch && secretMatch[1].length < floor) continue;\n }\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\nexport const exposedEnvFile: CustomRule = {\n id: \"VC002\",\n title: \"Environment File May Be Committed\",\n severity: \"high\",\n category: \"Secrets\",\n description: \".env files containing secrets may be committed to version control.\",\n check(content, filePath) {\n // Only applies to .env files (not .env.example)\n if (!filePath.match(/\\.env(?:\\.[a-z]+)?$/) || filePath.includes(\"example\")) return [];\n\n const hasSecrets = /(?:KEY|SECRET|TOKEN|PASSWORD|PRIVATE|DATABASE_URL)\\s*=/i.test(content);\n if (!hasSecrets) return [];\n\n return [{\n rule: \"VC002\",\n title: exposedEnvFile.title,\n severity: \"high\",\n category: \"Secrets\",\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: 'Add \".env*\" to your .gitignore file and remove this file from git history with: git rm --cached ' + filePath,\n }];\n },\n};\n\nexport const missingAuthMiddleware: CustomRule = {\n id: \"VC003\",\n title: \"API Route Missing Authentication\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"API routes without authentication checks allow unauthorized access.\",\n check(content, filePath) {\n // Only check files that look like server-side request handlers.\n if (!isServerSideFile(filePath)) return [];\n\n // Look for route handlers without auth checks\n const routePatterns = [\n // Express/Hono style\n /\\.(get|post|put|patch|delete)\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*(?:async\\s+)?\\(?(?:req|c|ctx)/gi,\n // Next.js API routes\n /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|PATCH|DELETE)\\s*\\(/gi,\n ];\n\n // Skip test files\n if (isTestFile(filePath)) return [];\n // Skip non-code files\n if (filePath.match(/\\.(md|txt|rst|html|css|json|yaml|yml)$/)) return [];\n\n // Skip routes that are intentionally unauthenticated\n const isAuthRoute = /\\/auth\\/|\\/login|\\/signup|\\/register|\\/logout|\\/password\\/|\\/forgot|\\/reset/i.test(filePath);\n if (isAuthRoute) return [];\n\n // Skip webhook receivers (they use their own auth: HMAC, shared secrets, etc.)\n const isWebhookRoute = /\\/webhook/i.test(filePath);\n if (isWebhookRoute) return [];\n\n // Strip line + block comments before the auth check — otherwise a\n // comment like \"// no auth check\" suppresses the rule on the very\n // file the rule was supposed to flag.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\")\n .replace(/^\\s*#.*$/gm, \"\");\n\n const authPatterns = [\n /\\bgetUser\\b/i, /\\bcurrentUser\\b/i, /\\bisAuthenticated\\b/i,\n /\\brequireAuth\\b/i, /\\brequireUser\\b/i, /\\brequireUserForApi\\b/i,\n /\\bwithAuth\\b/i, /\\bgetServerSession\\b/i, /\\bgetToken\\b/i,\n /\\bverifyToken\\b/i, /\\bvalidateToken\\b/i, /\\bcheckApiKey\\b/i,\n /\\bverifyCronSecret\\b/i, /\\bverifySecret\\b/i,\n /supabase\\.auth/i, /firebase\\.auth/i,\n /\\bclerk\\b/i, /\\bpassport\\b/i, /\\bcognito\\b/i,\n /\\bauth\\(\\)/i, /req\\.user\\b/i, /req\\.session\\.\\w+/i,\n /\\bjwt\\.verify\\b/i, /\\bbearer\\s/i,\n /\\b(?:protect|guard)\\(/i,\n /\\.use\\([^)]*(?:auth|session|protect|requireAuth)/i,\n ];\n\n const hasAuth = authPatterns.some((p) => p.test(codeOnly));\n if (hasAuth) return [];\n\n const matches: RuleMatch[] = [];\n for (const pattern of routePatterns) {\n matches.push(\n ...findMatches(content, pattern, missingAuthMiddleware, filePath, () =>\n \"Add authentication middleware to protect this route. Check the user's session/token before processing the request.\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const supabaseNoRLS: CustomRule = {\n id: \"VC004\",\n title: \"Supabase Client Without Row Level Security\",\n severity: \"critical\",\n category: \"Authorization\",\n description: \"Using Supabase with the service role key or bypassing RLS exposes all database rows to any user.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n\n // Service role key used in client-side code\n if (\n /supabase_service_role|service_role_key/i.test(content) &&\n (/[\"']use client[\"']/.test(content) || filePath.match(/\\.(jsx|tsx|vue|svelte)$/))\n ) {\n matches.push(\n ...findMatches(\n content,\n /service_role/gi,\n supabaseNoRLS,\n filePath,\n () => \"Never expose the service_role key in client-side code. Use the anon key with RLS policies instead.\",\n ),\n );\n }\n\n // .rpc() or direct table access without .auth\n if (/createClient/i.test(content) && /\\.from\\(/.test(content)) {\n const hasRLSBypass = /\\.rpc\\(|auth\\.admin|service_role/i.test(content);\n if (hasRLSBypass) {\n matches.push(\n ...findMatches(\n content,\n /\\.rpc\\(|auth\\.admin/gi,\n { ...supabaseNoRLS, title: \"Supabase RLS Bypass Detected\" },\n filePath,\n () => \"Ensure RLS policies are enabled on all tables and avoid bypassing them with service_role or admin methods in user-facing code.\",\n ),\n );\n }\n }\n\n return matches;\n },\n};\n\nexport const stripeWebhookUnprotected: CustomRule = {\n id: \"VC005\",\n title: \"Unprotected Stripe Webhook Endpoint\",\n severity: \"critical\",\n category: \"Payment Security\",\n description: \"Stripe webhook endpoints without signature verification allow attackers to fake payment events.\",\n check(content, filePath) {\n // Only scan code files, never docs/README/markdown/lock files\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n\n // Must actually reference Stripe (not just generic \"webhook\")\n if (!/stripe/i.test(content)) return [];\n\n // Must have a webhook handler pattern (route definition or event handling)\n const hasStripeWebhookHandler =\n /(?:stripe.*webhook|webhook.*stripe)/i.test(content) ||\n /(?:checkout\\.session\\.completed|invoice\\.paid|payment_intent|customer\\.subscription)/i.test(content);\n if (!hasStripeWebhookHandler) return [];\n\n // Already has signature verification — safe\n if (/constructEvent|verifyHeader|stripe[_-]?signature|webhook[_-]?secret|STRIPE_WEBHOOK_SECRET/i.test(content)) return [];\n\n // Find the actual route handler, not every mention of \"webhook\"\n const handlerPatterns = [\n // POST handler that processes Stripe events\n /export\\s+(?:async\\s+)?function\\s+POST\\s*\\(/g,\n // Express-style Stripe webhook route\n /\\.(post|all)\\s*\\(\\s*[\"'`][^\"'`]*(?:stripe|webhook)[^\"'`]*[\"'`]/gi,\n // Event type checking without prior verification\n /(?:event\\.type|req\\.body\\.type)\\s*===?\\s*[\"'`](?:checkout|invoice|payment|customer)\\./g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of handlerPatterns) {\n matches.push(\n ...findMatches(content, pattern, stripeWebhookUnprotected, filePath, () =>\n \"Verify the Stripe webhook signature using stripe.webhooks.constructEvent(body, sig, webhookSecret) to prevent forged payment events.\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const sqlInjection: CustomRule = {\n id: \"VC006\",\n title: \"Potential SQL Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"String concatenation or template literals in SQL queries allow attackers to execute arbitrary database commands.\",\n check(content, filePath) {\n const patterns = [\n // Template literal passed as first arg to query/execute/raw/sql/\n // queryRaw/queryRawUnsafe/execute. Examples that SHOULD fire:\n // db.query(`SELECT ... ${x}`)\n // prisma.$queryRawUnsafe(`... ${x}`)\n // knex.raw(`... ${x}`)\n /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\\s*\\(\\s*`[^`]*\\$\\{/gi,\n // String concatenation in SQL — now includes raw() and sql() and\n // Drizzle's sql.raw() / Prisma's $queryRawUnsafe() variants. Previous\n // version only covered query()/execute() and missed knex.raw(\"...\" + x).\n //\n // The string literal part uses a proper \"quoted string\" regex that\n // allows the opposite quote type inside (common in SQL — \"WHERE x = 'a'\").\n // The older `[^\"']*` class bailed out at the first inner quote and\n // missed every realistic SQL-containing concat.\n /(?:query|execute|raw|sql|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\\s*\\(\\s*(?:\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*')\\s*\\+/gi,\n // Direct variable interpolation in a SQL verb context\n /(?:SELECT|INSERT|UPDATE|DELETE|WHERE)\\s+.*\\$\\{(?!.*parameterized)/gi,\n ];\n\n const matches: RuleMatch[] = [];\n\n // Skip if using parameterized queries / prepared statements\n const usesParams = /\\?\\s*,|\\$\\d+|:[\\w]+|\\bprepare\\b|\\bplaceholder\\b/i.test(content);\n if (usesParams) return [];\n\n for (const pattern of patterns) {\n const raw = findMatches(content, pattern, sqlInjection, filePath, () =>\n \"Use parameterized queries or prepared statements instead of string interpolation. Example: db.query('SELECT * FROM users WHERE id = ?', [userId])\"\n );\n\n // Filter out safe tagged-template forms. These look like `Prisma.sql`...``\n // or Drizzle's `sql\\`...\\`` — both bind ${...} as query parameters\n // rather than concatenating. Previously the VC006 regex fired a FP\n // when these were nested inside `prisma.$queryRaw(Prisma.sql\\`...\\`)`\n // because the outer call's backtick/template looked like a dynamic SQL\n // string.\n //\n // Heuristic: if the line contains Prisma.sql`` or Drizzle's sql`` OR\n // the enclosing call is a KNOWN SAFE wrapper (e.g. $queryRaw WITHOUT\n // \"Unsafe\" suffix), skip the finding.\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n // Prisma.sql tagged template — safe\n if (/\\bPrisma\\.sql\\s*`/.test(lineText)) continue;\n // Drizzle's sql`...` tagged template — safe (NOT sql.raw(), which is\n // unsafe and flagged by a different pattern above)\n if (/\\bsql\\s*`/.test(lineText) && !/\\bsql\\.raw\\s*\\(/.test(lineText)) continue;\n // Safe Prisma helpers — $queryRaw/$executeRaw (without Unsafe suffix)\n // always take a Prisma.sql tagged template; if the dev used one of\n // the unsafe variants the regex above will still flag them.\n if (/\\$queryRaw\\s*\\(\\s*Prisma\\.sql|\\$executeRaw\\s*\\(\\s*Prisma\\.sql/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\nexport const xssVulnerability: CustomRule = {\n id: \"VC007\",\n title: \"Potential Cross-Site Scripting (XSS)\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Rendering user input without sanitization allows attackers to inject malicious scripts.\",\n check(content, filePath) {\n // Skip if file is a sanitizer utility or already uses DOMPurify\n if (/(?:sanitize|purify|escape|xss)/i.test(filePath)) return [];\n if (/DOMPurify\\.sanitize|sanitizeHtml|xss\\(|escapeHtml/i.test(content)) return [];\n // Skip if the file imports or requires DOMPurify or a sanitize library anywhere\n if (/(?:import|require)\\s*\\(?.*(?:DOMPurify|dompurify|sanitize|sanitizer)/i.test(content)) return [];\n const patterns = [\n // React dangerouslySetInnerHTML\n /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html\\s*:/g,\n // Direct innerHTML assignment\n /\\.innerHTML\\s*=\\s*(?![\"'`]\\s*$)/gm,\n // document.write\n /document\\.write\\s*\\(/g,\n // v-html in Vue\n /v-html\\s*=/g,\n // {@html} in Svelte\n /\\{@html\\s/g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of patterns) {\n const raw = findMatches(content, pattern, xssVulnerability, filePath, () =>\n \"Sanitize user input before rendering as HTML. Use a library like DOMPurify: DOMPurify.sanitize(userInput)\"\n );\n // Filter out innerHTML assignments that use only static strings (no user input)\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n // Skip if innerHTML is assigned a pure string literal with no interpolation\n if (/\\.innerHTML\\s*=\\s*['\"]/.test(lineText) && !/\\$\\{/.test(lineText)) continue;\n // Skip if innerHTML is assigned a string concatenation of only literals (no variables from user input)\n if (/\\.innerHTML\\s*=\\s*['\"][^'\"]*['\"]\\s*$/.test(lineText)) continue;\n matches.push(m);\n }\n }\n\n // --- AST layer: res.send(<tainted template literal containing HTML>) ---\n // Node/Express handlers that echo user-supplied HTML back to the browser.\n // `res.send(\\`<div>${userHtml}</div>\\`)` is the canonical reflected-XSS\n // shape the AST layer can catch cleanly.\n if (!/\\.(?:send|end|write)\\s*\\(/.test(content) || !/\\$\\{/.test(content)) {\n return matches;\n }\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n visitCalls(\n parsed,\n (callee: Node) => {\n if (callee.type !== \"MemberExpression\") return false;\n if (callee.property.type !== \"Identifier\") return false;\n return [\"send\", \"end\", \"write\"].includes(callee.property.name);\n },\n (call, line) => {\n const first = call.arguments[0];\n if (!first || first.type !== \"TemplateLiteral\") return;\n // Only flag when the template literal contains HTML-looking syntax\n // AND at least one expression is tainted.\n const literalParts = first.quasis.map((q) => q.value.raw).join(\"\");\n if (!/<\\/?\\w+/.test(literalParts)) return;\n if (!taint.isTainted(first as Node)) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n xssVulnerability,\n \"Never echo user-supplied HTML back to the browser unescaped. Sanitize with DOMPurify, use a templating engine that auto-escapes, or set `res.type('text/plain')` if you never intended HTML.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\nexport const noRateLimiting: CustomRule = {\n id: \"VC008\",\n title: \"API Endpoint Without Rate Limiting\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"API endpoints without rate limiting are vulnerable to abuse and denial-of-service attacks.\",\n check(content, filePath) {\n // Only check main server/app entry files\n const isEntryFile = /(?:server|app|index|main)\\.[jt]sx?$/.test(filePath) ||\n filePath.includes(\"middleware\");\n if (!isEntryFile) return [];\n\n // Check if this is a server file\n const isServer = /(?:express|hono|fastify|koa|next|createServer|listen\\()/i.test(content);\n if (!isServer) return [];\n\n // Check for rate limiting\n const hasRateLimit = /rate.?limit|throttle|express-rate-limit|@elysiajs\\/rate-limit|hono.*limiter/i.test(content);\n if (hasRateLimit) return [];\n\n return [{\n rule: \"VC008\",\n title: noRateLimiting.title,\n severity: \"medium\",\n category: \"Availability\",\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: \"Add rate limiting middleware to your server. For Express: npm install express-rate-limit. For other frameworks, check their rate limiting plugins.\",\n }];\n },\n};\n\nexport const corsWildcard: CustomRule = {\n id: \"VC009\",\n title: \"CORS Allows All Origins\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Wildcard CORS (*) allows any website to make requests to your API, potentially exposing user data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n const patterns = [\n /cors\\(\\s*\\)/g, // cors() with no options = allow all\n /origin\\s*:\\s*[\"'`]\\*[\"'`]/g,\n /[\"'`]Access-Control-Allow-Origin[\"'`]\\s*,\\s*[\"'`]\\*[\"'`]/g,\n /origin\\s*:\\s*true/g,\n ];\n\n const matches: RuleMatch[] = [];\n for (const pattern of patterns) {\n matches.push(\n ...findMatches(content, pattern, corsWildcard, filePath, () =>\n \"Restrict CORS to your specific frontend domain(s): cors({ origin: 'https://yourdomain.com' })\"\n ),\n );\n }\n return matches;\n },\n};\n\nexport const clientSideAuth: CustomRule = {\n id: \"VC010\",\n title: \"Client-Side Only Authorization\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Hiding UI elements based on roles without server-side checks lets attackers bypass restrictions using DevTools.\",\n check(content, filePath) {\n // Only check frontend component files\n if (!filePath.match(/\\.(jsx|tsx|vue|svelte)$/)) return [];\n\n const matches: RuleMatch[] = [];\n\n // Pattern: conditional rendering based on role/admin without server check\n const rolePatterns = [\n /\\{.*(?:isAdmin|role\\s*===?\\s*[\"'`]admin[\"'`]|user\\.role).*&&/gi,\n /v-if\\s*=\\s*[\"'`].*(?:isAdmin|role\\s*===?\\s*'admin')/gi,\n ];\n\n for (const pattern of rolePatterns) {\n // Only flag if the file has no server-side fetch for auth verification\n const hasServerCheck = /getServerSession|getUser|server|api\\/auth|middleware/i.test(content);\n if (hasServerCheck) continue;\n\n matches.push(\n ...findMatches(content, pattern, clientSideAuth, filePath, () =>\n \"Client-side role checks only hide UI — they don't prevent access. Always verify permissions on the server/API side too.\"\n ),\n );\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC011 – Secret in NEXT_PUBLIC_ env var\n// ────────────────────────────────────────────\n\nexport const nextPublicSecret: CustomRule = {\n id: \"VC011\",\n title: \"Secret in NEXT_PUBLIC_ Environment Variable\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"NEXT_PUBLIC_ variables are exposed to the browser. Secrets placed here are visible to anyone.\",\n check(content, filePath) {\n if (!filePath.match(/\\.env/) && !filePath.match(/next\\.config/)) return [];\n const patterns = [\n /NEXT_PUBLIC_[A-Z_]*(?:SECRET|KEY|TOKEN|PASSWORD|PRIVATE)[A-Z_]*\\s*=\\s*.+/gi,\n /NEXT_PUBLIC_[A-Z_]*(?:SUPABASE_SERVICE|CLERK_SECRET|STRIPE_SECRET)[A-Z_]*\\s*=\\s*.+/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const raw = findMatches(content, p, nextPublicSecret, filePath, () =>\n \"Remove the NEXT_PUBLIC_ prefix. Only use NEXT_PUBLIC_ for values safe to expose in the browser.\"\n );\n // Filter out publishable/public keys that are DESIGNED to be client-side\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/PUBLISHABLE|ANON_KEY|PUBLIC_KEY/i.test(lineText)) continue;\n // Skip Clerk publishable keys (pk_test_, pk_live_)\n if (/CLERK_PUBLISHABLE/i.test(lineText)) continue;\n // Skip Stripe publishable keys\n if (/STRIPE_PUBLISHABLE/i.test(lineText)) continue;\n // Skip if the value is a placeholder (empty, pk_test_, etc.)\n if (/=\\s*[\"']?\\s*$|=\\s*[\"']?pk_(?:test|live)_[\"']?\\s*$/.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC012 – Firebase config in client code\n// ────────────────────────────────────────────\n\nexport const firebaseClientConfig: CustomRule = {\n id: \"VC012\",\n title: \"Firebase Config with API Key in Client Code\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Firebase config objects in client code expose your API key. While Firebase API keys aren't secret, they should be restricted in the Firebase console.\",\n check(content, filePath) {\n if (!/firebase/i.test(content)) return [];\n const patterns = [\n /firebaseConfig\\s*=\\s*\\{[^}]*apiKey\\s*:/gi,\n /initializeApp\\s*\\(\\s*\\{[^}]*apiKey\\s*:/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, firebaseClientConfig, filePath, () =>\n \"Move Firebase config to environment variables. Restrict the API key in Firebase Console > Project Settings > API restrictions.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC013 – Supabase anon key for admin ops\n// ────────────────────────────────────────────\n\nexport const supabaseAnonAdmin: CustomRule = {\n id: \"VC013\",\n title: \"Supabase Anon Key Used for Admin Operations\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Using the Supabase anon key for operations that require elevated privileges is insecure.\",\n check(content, filePath) {\n if (!/supabase/i.test(content)) return [];\n if (!/anon/i.test(content)) return [];\n if (/service_role/i.test(content)) return [];\n const patterns = [\n /supabase[^.]*\\.auth\\.admin/gi,\n /supabase[^.]*\\.rpc\\s*\\(/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, supabaseAnonAdmin, filePath, () =>\n \"Use the service_role key on the server side for admin operations. Never expose it to the client.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC014 – .env not in .gitignore\n// ────────────────────────────────────────────\n\nexport const envNotGitignored: CustomRule = {\n id: \"VC014\",\n title: \".env File Not in .gitignore\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"If .env is not listed in .gitignore, secrets will be committed to version control.\",\n check(content, filePath) {\n if (!filePath.endsWith(\".gitignore\")) return [];\n if (/\\.env/i.test(content)) return [];\n return [{\n rule: \"VC014\", title: envNotGitignored.title, severity: \"high\" as const, category: \"Secrets\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add \".env*\" to your .gitignore file to prevent committing secrets.',\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC015 – eval() / new Function()\n// ────────────────────────────────────────────\n\nexport const evalUsage: CustomRule = {\n id: \"VC015\",\n title: \"Use of eval() or Function Constructor\",\n severity: \"high\",\n category: \"Injection\",\n description: \"eval() and new Function() execute arbitrary code, creating severe injection risks. Common in AI-generated code.\",\n check(content, filePath) {\n if (filePath.includes(\"node_modules\") || filePath.includes(\".min.\")) return [];\n if (filePath.match(/(?:webpack|rollup|vite|jest|babel|tsup|esbuild)\\.config/i)) return [];\n if (isTestFile(filePath)) return [];\n // Skip files that are linters/scanners/rule engines (they reference eval in detection patterns)\n if (filePath.match(/(?:rules?|scanner|lint|check|detect|analyz)/i) && /\\.check\\s*\\(|findMatches/i.test(content)) return [];\n const patterns = [\n // Actual eval() calls — not in strings or regex patterns\n /\\beval\\s*\\(\\s*(?![\"'`](?:eval|source))/g,\n /new\\s+Function\\s*\\(\\s*(?![\"'`])/g,\n ];\n // Skip if eval is only in a string (e.g., devtool: 'eval-source-map')\n const hasEvalInString = /[\"'`]eval(?:-source-map|[\"'`])/i.test(content);\n if (hasEvalInString && !/\\beval\\s*\\([^)]*(?:req\\.|body\\.|input|params|user|data)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const rawMatches = findMatches(content, p, evalUsage, filePath, () =>\n \"Replace eval() with JSON.parse() for data, or a proper parser for expressions. Never pass user input to eval().\"\n );\n // Skip matches on lines containing devtool or source-map config\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const lineText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/devtool|source-map/i.test(lineText)) continue;\n // Skip if eval appears on a line with description/title/message keys (string literal context)\n if (/(?:description|title|message)\\s*[:=]/i.test(lineText)) continue;\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC016 – Unvalidated redirect\n// ────────────────────────────────────────────\n\nexport const unvalidatedRedirect: CustomRule = {\n id: \"VC016\",\n title: \"Unvalidated Redirect\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Redirecting users to URLs from untrusted input enables phishing attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip if the file already validates redirect URLs\n if (/isAllowedRedirect|validateRedirect|isSafeRedirect|allowedDomains|trustedDomains|whitelist.*url|allowlist.*url/i.test(content)) return [];\n const patterns = [\n /window\\.location\\s*=\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.href\\s*=\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.assign\\s*\\(\\s*(?![\"'`]https?:\\/\\/)/g,\n /window\\.location\\.replace\\s*\\(\\s*(?![\"'`]https?:\\/\\/)/g,\n /res\\.redirect\\s*\\(\\s*(?:req\\.|params\\.|query\\.)/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unvalidatedRedirect, filePath, () =>\n \"Validate redirect URLs against an allowlist of trusted domains. Never redirect to user-supplied URLs directly.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC017 – Insecure cookie settings\n// ────────────────────────────────────────────\n\nexport const insecureCookies: CustomRule = {\n id: \"VC017\",\n title: \"Insecure Cookie Settings\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Cookies without httpOnly, secure, or sameSite flags are vulnerable to theft and CSRF attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/cookie/i.test(content)) return [];\n const setCookiePattern = /(?:set-cookie|setCookie|cookie\\s*=|res\\.cookie\\s*\\()/gi;\n if (!setCookiePattern.test(content)) return [];\n const hasHttpOnly = /httpOnly\\s*:\\s*true|httponly/i.test(content);\n const hasSecure = /secure\\s*:\\s*true|;\\s*secure/i.test(content);\n const hasSameSite = /sameSite\\s*:|samesite/i.test(content);\n const matches: RuleMatch[] = [];\n if (!hasHttpOnly || !hasSecure || !hasSameSite) {\n const missing: string[] = [];\n if (!hasHttpOnly) missing.push(\"httpOnly\");\n if (!hasSecure) missing.push(\"secure\");\n if (!hasSameSite) missing.push(\"sameSite\");\n matches.push(...findMatches(content, /(?:set-cookie|setCookie|cookie\\s*=|res\\.cookie\\s*\\()/gi, insecureCookies, filePath, () =>\n `Add missing cookie flags: ${missing.join(\", \")}. Example: { httpOnly: true, secure: true, sameSite: 'lax' }`\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC018 – Exposed auth provider secret key\n// ────────────────────────────────────────────\n\nexport const exposedAuthSecret: CustomRule = {\n id: \"VC018\",\n title: \"Exposed Clerk/Auth Secret Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Auth provider secret keys (Clerk, Auth0, NextAuth) must never be in client-side code or NEXT_PUBLIC_ variables.\",\n check(content, filePath) {\n const isClientFile = filePath.match(/\\.(jsx|tsx|vue|svelte)$/) || /[\"']use client[\"']/.test(content);\n const isEnvFile = filePath.match(/\\.env/);\n if (!isClientFile && !isEnvFile) return [];\n const patterns: RegExp[] = [];\n if (isClientFile) {\n patterns.push(\n /CLERK_SECRET_KEY/g,\n /AUTH0_CLIENT_SECRET/g,\n /NEXTAUTH_SECRET/g,\n );\n }\n if (isEnvFile) {\n patterns.push(\n /NEXT_PUBLIC_CLERK_SECRET/gi,\n /NEXT_PUBLIC_AUTH0_SECRET/gi,\n /NEXT_PUBLIC_NEXTAUTH_SECRET/gi,\n );\n }\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedAuthSecret, filePath, () =>\n \"Move this secret to a server-side environment variable (without the NEXT_PUBLIC_ prefix). Never expose auth secrets to the browser.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC019 – Insecure Electron BrowserWindow\n// ────────────────────────────────────────────\n\nexport const insecureElectronWindow: CustomRule = {\n id: \"VC019\",\n title: \"Insecure Electron BrowserWindow Configuration\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Electron BrowserWindow with nodeIntegration enabled, contextIsolation disabled, or sandbox disabled allows renderer processes to access Node.js APIs, enabling remote code execution.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/BrowserWindow/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /nodeIntegration\\s*:\\s*true/g,\n /contextIsolation\\s*:\\s*false/g,\n /sandbox\\s*:\\s*false/g,\n /webSecurity\\s*:\\s*false/g,\n /allowRunningInsecureContent\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureElectronWindow, filePath, (m) =>\n `Set ${m[0].split(\":\")[0].trim()}: ${m[0].includes(\"true\") ? \"false\" : \"true\"}. Enable contextIsolation, sandbox, and webSecurity; disable nodeIntegration and allowRunningInsecureContent.`\n ));\n }\n // Check for BrowserWindow without sandbox/webSecurity set at all\n if (/new\\s+BrowserWindow\\s*\\(/g.test(content)) {\n if (!/sandbox\\s*:/i.test(content)) {\n matches.push(...findMatches(content, /new\\s+BrowserWindow\\s*\\(/g, { ...insecureElectronWindow, title: \"Electron BrowserWindow Missing sandbox:true\" }, filePath, () =>\n \"Add sandbox: true to BrowserWindow webPreferences for defense in depth.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC020 – Missing Content Security Policy\n// ────────────────────────────────────────────\n\nexport const missingCSP: CustomRule = {\n id: \"VC020\",\n title: \"Missing Content Security Policy (CSP)\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Without a Content-Security-Policy header or meta tag, your app is vulnerable to XSS and data injection attacks.\",\n check(content, filePath) {\n // Check HTML files for missing CSP meta tag\n if (filePath.match(/\\.(html|htm)$/)) {\n if (!/Content-Security-Policy/i.test(content)) {\n return [{\n rule: \"VC020\", title: missingCSP.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add a CSP meta tag: <meta http-equiv=\"Content-Security-Policy\" content=\"default-src \\'self\\'; script-src \\'self\\'\">'\n }];\n }\n }\n // Skip Electron main process — CSP is typically set in the HTML file (already checked above)\n // Flagging main.ts creates false positives since CSP belongs in index.html for Electron apps\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC021 – IPC Handler Without Path Validation\n// ────────────────────────────────────────────\n\nexport const ipcPathTraversal: CustomRule = {\n id: \"VC021\",\n title: \"IPC/File Handler Without Path Validation\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"IPC handlers that read or write files based on renderer-supplied paths without validation allow path traversal attacks, potentially exposing sensitive files like .ssh keys or .env files.\",\n check(content, filePath) {\n if (!/ipcMain\\.handle|ipcMain\\.on/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Look for file read/write in IPC handlers without path validation\n const hasFileOps = /readFile|writeFile|readFileSync|writeFileSync|createReadStream|createWriteStream/i.test(content);\n if (!hasFileOps) return [];\n const hasPathValidation = /(?:path\\.resolve|path\\.normalize|startsWith|isAbsolute|\\.includes\\s*\\(\\s*[\"'`]\\.\\.[\"'`]\\s*\\)|allowedPaths|safePath|validatePath|sanitizePath)/i.test(content);\n if (!hasPathValidation) {\n matches.push(...findMatches(content, /ipcMain\\.(?:handle|on)\\s*\\(\\s*[\"'`][^\"'`]*(?:read|write|file|save|load|open|export)[^\"'`]*[\"'`]/gi, ipcPathTraversal, filePath, () =>\n \"Validate file paths in IPC handlers: ensure paths are within an allowed directory (e.g., app.getPath('userData')), reject paths containing '..', and block access to sensitive directories (.ssh, .env, etc).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC022 – HTML Export Without Sanitization\n// ────────────────────────────────────────────\n\nexport const unsanitizedHTMLExport: CustomRule = {\n id: \"VC022\",\n title: \"HTML Export/Render Without Sanitization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Generating HTML from user content without sanitization (e.g., DOMPurify) allows stored XSS attacks. Malicious content saved in documents could execute scripts when exported or previewed.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip if DOMPurify is imported or used anywhere in the file\n if (/DOMPurify|dompurify/i.test(content)) return [];\n // Skip React/Vue component files — JSX is not unsafe HTML concatenation\n if (filePath.match(/\\.(jsx|tsx|vue|svelte)$/) && !/innerHTML|document\\.write|\\.html\\s*=/i.test(content)) return [];\n // Skip files that have any sanitizer\n const hasSanitizer = /sanitize|escapeHtml|escapeHTML|xss|htmlEncode|purify/i.test(content);\n if (hasSanitizer) return [];\n // Only flag files that actually export/write HTML (not just template rendering)\n if (!/(?:export|download|save|write|send).*(?:html|HTML)|\\.innerHTML\\s*=|document\\.write|res\\.send\\s*\\(/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const htmlBuildPatterns = [\n /`<[^`]*\\$\\{[^}]*(?:content|title|body|text|name|message|description|input|value|data)[^}]*\\}[^`]*>`/gi,\n /[\"']<[^\"']*['\"]\\s*\\+\\s*(?:content|title|body|text|message|data|doc\\.|post\\.|article\\.)/gi,\n ];\n for (const p of htmlBuildPatterns) {\n matches.push(...findMatches(content, p, unsanitizedHTMLExport, filePath, () =>\n \"Sanitize user content before embedding in HTML. Use DOMPurify: DOMPurify.sanitize(content). For plain text, use a function to escape HTML entities (<, >, &, quotes).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC023 – Prototype Pollution via Storage\n// ────────────────────────────────────────────\n\nexport const prototypePollution: CustomRule = {\n id: \"VC023\",\n title: \"Prototype Pollution Risk\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Deep-merging or Object.assign-ing attacker-controlled data (request bodies, parsed localStorage, query params) into a host object can pollute Object.prototype via keys like __proto__ and constructor.prototype.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n\n // --- Regex layer (unchanged): localStorage JSON.parse + unsafe merges ---\n const storageParsePatterns = [\n /JSON\\.parse\\s*\\(\\s*(?:localStorage|sessionStorage)\\.getItem/g,\n /JSON\\.parse\\s*\\(\\s*window\\.localStorage/g,\n ];\n const hasValidation = /schema|validate|sanitize|whitelist|allowedKeys|pick\\(|Object\\.freeze|zod|yup|joi|ajv/i.test(content);\n if (!hasValidation) {\n const hasUnsafeMerge = /Object\\.assign\\s*\\([^)]*JSON\\.parse|\\.\\.\\.JSON\\.parse|\\{.*\\.\\.\\.(?:stored|saved|cached|parsed|data)/i.test(content);\n if (hasUnsafeMerge) {\n matches.push(...findMatches(content, /Object\\.assign\\s*\\([^)]*JSON\\.parse|\\.\\.\\.JSON\\.parse/g, prototypePollution, filePath, () =>\n \"Validate parsed data against an expected schema before merging into objects. Use Object.freeze(), a validation library (Zod, Yup), or manually check for __proto__ and constructor keys.\"\n ));\n }\n for (const p of storageParsePatterns) {\n matches.push(...findMatches(content, p, prototypePollution, filePath, () =>\n \"Validate localStorage data against an expected schema before using it. Malicious extensions or XSS can modify localStorage values.\"\n ));\n }\n }\n\n // --- AST layer: dangerous merge/assign sinks with tainted source ---\n // Cheap regex pre-filter so we don't parse files that clearly can't have\n // a dangerous merge in them.\n if (!/\\b(?:merge|assign|extend|defaults)\\s*\\(/.test(content)) return matches;\n\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n // Merge-family sinks we flag when a non-first argument is tainted.\n // We deliberately skip `mergeWith` when the call has 4+ args — its 4th\n // arg is a customizer and the common FP-preventing pattern drops\n // __proto__ / constructor / prototype keys there.\n const ALL_ARG_SINKS: Array<{ obj?: string; method: string }> = [\n { obj: \"_\", method: \"merge\" },\n { obj: \"lodash\", method: \"merge\" },\n { method: \"merge\" }, // bare `merge(target, src)` from ESM import\n { obj: \"_\", method: \"defaultsDeep\" },\n { obj: \"lodash\", method: \"defaultsDeep\" },\n { obj: \"Object\", method: \"assign\" },\n { method: \"extend\" }, // jquery/underscore style\n { obj: \"$\", method: \"extend\" },\n ];\n\n visitCalls(\n parsed,\n (callee: Node) => {\n for (const sink of ALL_ARG_SINKS) {\n if (sink.obj && isMethodCall(callee, sink.obj, sink.method)) return true;\n if (!sink.obj && isCalleeNamed(callee, sink.method)) return true;\n }\n return false;\n },\n (call, line) => {\n // mergeWith(target, src, customizer) — skip if customizer present.\n // (Not in ALL_ARG_SINKS, but kept as a comment so we remember why.)\n\n // Skip first arg (the target) — we care if the SOURCE is tainted.\n const sources = call.arguments.slice(1);\n const tainted = sources.some((arg) => {\n if (arg.type === \"SpreadElement\") return taint.isTainted(arg.argument as Node);\n return taint.isTainted(arg as Node);\n });\n if (!tainted) return;\n\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n prototypePollution,\n \"Filter __proto__, constructor, and prototype keys before merging user-supplied data into an object. Prefer lodash.mergeWith() with a customizer that returns undefined for those keys, or validate the source with a schema library (Zod, Yup, Joi, Ajv) first.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC024 – Missing File Size Limits\n// ────────────────────────────────────────────\n\nexport const missingFileSizeLimits: CustomRule = {\n id: \"VC024\",\n title: \"File Write/Save Without Size Limit\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"File save or upload handlers without size validation can lead to denial-of-service via disk exhaustion or memory exhaustion.\",\n check(content, filePath) {\n if (!/(?:writeFile|save|upload|export)/i.test(filePath) && !/(?:writeFile|writeFileSync|createWriteStream)/i.test(content)) return [];\n // Look for file write operations in handlers\n const hasWriteOps = /(?:ipcMain|app\\.(?:post|put)|router\\.(?:post|put)).*(?:writeFile|save|export)/is.test(content) ||\n /(?:writeFile|writeFileSync)\\s*\\(/g.test(content);\n if (!hasWriteOps) return [];\n const hasSizeCheck = /(?:size|length|byteLength|bytes)\\s*(?:>|>=|<|<=|===)\\s*\\d|maxSize|MAX_SIZE|sizeLimit|content-length/i.test(content);\n if (hasSizeCheck) return [];\n return findMatches(content, /(?:writeFile|writeFileSync)\\s*\\(/g, missingFileSizeLimits, filePath, () =>\n \"Add file size validation before writing. Check content.length or Buffer.byteLength() against a maximum (e.g., 10MB) to prevent disk exhaustion.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC025 – Unsanitized Export Filenames\n// ────────────────────────────────────────────\n\nexport const unsanitizedFilenames: CustomRule = {\n id: \"VC025\",\n title: \"Unsanitized Filename in File Operations\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Using user-supplied filenames without sanitization in file operations can enable path traversal, overwriting system files, or executing commands via special characters.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const hasSanitization = /sanitize|cleanFilename|safeFilename|replace\\s*\\(\\s*\\/\\[.*\\]\\/|\\.startsWith\\s*\\(\\s*(?:UPLOADS_DIR|\\w+_DIR)/i.test(content);\n if (hasSanitization) return [];\n\n // --- Regex layer: existing inline patterns ---\n const patterns = [\n /(?:writeFile|writeFileSync|createWriteStream|rename|copyFile)\\s*\\(\\s*(?:`[^`]*\\$\\{|[^\"'`\\s,]+\\s*\\+)/g,\n /(?:dialog\\.showSaveDialog|saveDialog).*(?:defaultPath|fileName)\\s*:\\s*(?![\"'`])/g,\n /\\.download\\s*=\\s*(?![\"'`])/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unsanitizedFilenames, filePath, () =>\n \"Sanitize filenames before use: strip path separators (/ \\\\), special chars, and '..' sequences. Example: name.replace(/[^a-zA-Z0-9._-]/g, '_')\"\n ));\n }\n\n // --- AST layer: fs.* sinks whose path argument is tainted ---\n if (!/\\b(?:fs|fsPromises)\\.(?:readFile|writeFile|appendFile|readFileSync|writeFileSync|appendFileSync|createReadStream|createWriteStream|unlink|unlinkSync|stat|statSync|rm|rmSync|mkdir|mkdirSync)\\s*\\(/.test(content)) {\n return matches;\n }\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n const FS_SINKS = new Set([\n \"readFile\", \"writeFile\", \"appendFile\",\n \"readFileSync\", \"writeFileSync\", \"appendFileSync\",\n \"createReadStream\", \"createWriteStream\",\n \"unlink\", \"unlinkSync\", \"rm\", \"rmSync\",\n \"stat\", \"statSync\", \"mkdir\", \"mkdirSync\",\n ]);\n\n // Receivers we consider \"the node fs module\" — direct `fs`, the promises\n // submodule `fsPromises`, or a `fs.promises` member expression. If the\n // receiver isn't one of these, the call might be on a custom library\n // (`myStorageLib.readFile(...)`) that happens to share a method name —\n // those shouldn't trigger this rule.\n const isFsReceiver = (obj: Node): boolean => {\n if (obj.type === \"Identifier\") return obj.name === \"fs\" || obj.name === \"fsPromises\";\n if (obj.type === \"MemberExpression\") {\n return (\n obj.object.type === \"Identifier\" &&\n obj.object.name === \"fs\" &&\n obj.property.type === \"Identifier\" &&\n obj.property.name === \"promises\"\n );\n }\n return false;\n };\n\n visitCalls(\n parsed,\n (callee: Node) => {\n if (callee.type !== \"MemberExpression\") return false;\n if (callee.property.type !== \"Identifier\") return false;\n if (!FS_SINKS.has(callee.property.name)) return false;\n return isFsReceiver(callee.object as Node);\n },\n (call, line) => {\n const first = call.arguments[0];\n if (!first || first.type === \"SpreadElement\") return;\n if (!taint.isTainted(first as Node)) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n unsanitizedFilenames,\n \"Resolve the path against a fixed base directory and verify it stays within that base before the fs call: `const target = path.resolve(BASE, req.body.name); if (!target.startsWith(BASE + path.sep)) throw new Error('path traversal');`\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC026 – Electron Navigation Not Restricted\n// ────────────────────────────────────────────\n\nexport const electronNavigationUnrestricted: CustomRule = {\n id: \"VC026\",\n title: \"Electron: External Navigation Not Blocked\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Electron apps that don't block navigation to external URLs or new window creation are vulnerable to phishing and drive-by downloads. Malicious links in app content can redirect the entire app to an attacker's site.\",\n check(content, filePath) {\n if (!/BrowserWindow|electron/i.test(content)) return [];\n if (!/main|index/i.test(filePath)) return [];\n const hasNavBlock = /will-navigate|new-window|setWindowOpenHandler|webContents\\.on.*navigate/i.test(content);\n if (hasNavBlock) return [];\n if (/new\\s+BrowserWindow/i.test(content)) {\n return findMatches(content, /new\\s+BrowserWindow\\s*\\(/g, electronNavigationUnrestricted, filePath, () =>\n \"Block external navigation: win.webContents.on('will-navigate', (e, url) => { if (!url.startsWith('file://')) e.preventDefault(); }); and use setWindowOpenHandler to block new windows.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC027 – Missing Security Meta Tags\n// ────────────────────────────────────────────\n\nexport const missingSecurityMeta: CustomRule = {\n id: \"VC027\",\n title: \"Missing Security Meta Tags / Headers\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"HTML pages without X-Content-Type-Options, referrer policy, or other security meta tags are more susceptible to MIME-sniffing attacks and information leakage.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(html|htm)$/)) return [];\n const matches: RuleMatch[] = [];\n if (!/X-Content-Type-Options/i.test(content) && !/<meta[^>]*nosniff/i.test(content)) {\n matches.push({\n rule: \"VC027\", title: \"Missing X-Content-Type-Options Header\", severity: \"medium\" as const,\n category: \"Configuration\", file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta http-equiv=\"X-Content-Type-Options\" content=\"nosniff\"> to prevent MIME-type sniffing.'\n });\n }\n if (!/referrer/i.test(content)) {\n matches.push({\n rule: \"VC027\", title: \"Missing Referrer Policy\", severity: \"medium\" as const,\n category: \"Configuration\", file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta name=\"referrer\" content=\"no-referrer\"> or \"strict-origin-when-cross-origin\" to limit referrer leakage.'\n });\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC028 – Unvalidated API Parameters\n// ────────────────────────────────────────────\n\nexport const unvalidatedAPIParams: CustomRule = {\n id: \"VC028\",\n title: \"Unvalidated API Request Parameters\",\n severity: \"high\",\n category: \"Injection\",\n description: \"API requests constructed with unvalidated user input (API keys, model names, URLs) can be exploited for injection attacks or unauthorized access to different API models/endpoints.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // API key passed without format validation\n const apiKeyPatterns = [\n /(?:apiKey|api_key|authorization)\\s*[:=]\\s*(?:req\\.body|req\\.query|params|input|formData|body)\\./gi,\n /headers\\s*:\\s*\\{[^}]*Authorization\\s*:\\s*(?![\"'`]Bearer\\s)/gi,\n ];\n const hasValidation = /validate|sanitize|regex|test\\(|match\\(|pattern|allowList|whitelist|enum|includes\\(/i.test(content);\n if (hasValidation) return [];\n // Model selection without allowlist\n if (/model\\s*[:=]\\s*(?:req\\.body|params|input|body)\\./i.test(content) || /model\\s*[:=]\\s*(?![\"'`])[a-z]/i.test(content)) {\n const hasModelValidation = /allowedModels|validModels|models\\s*\\.\\s*includes|model.*(?:===|!==|includes)/i.test(content);\n if (!hasModelValidation && /(?:openai|anthropic|claude|gpt|llm)/i.test(content)) {\n matches.push(...findMatches(content, /model\\s*[:=]\\s*(?:req\\.body|params|input|body)\\./gi, unvalidatedAPIParams, filePath, () =>\n \"Validate model selection against an allowlist of approved models. Example: const ALLOWED_MODELS = ['gpt-4', 'claude-3']; if (!ALLOWED_MODELS.includes(model)) throw new Error('Invalid model');\"\n ));\n }\n }\n for (const p of apiKeyPatterns) {\n matches.push(...findMatches(content, p, unvalidatedAPIParams, filePath, () =>\n \"Validate API key format before using it (e.g., check prefix and length). Never pass user-supplied API keys directly to third-party services without validation.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC029 – Unvalidated Event/Message Data\n// ────────────────────────────────────────────\n\nexport const unvalidatedEventData: CustomRule = {\n id: \"VC029\",\n title: \"Unvalidated Event or PostMessage Data\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Custom events, postMessage, or IPC message data used without type-checking can lead to injection attacks or unexpected behavior when malicious data is sent through event channels.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // addEventListener('message') without origin check\n if (/addEventListener\\s*\\(\\s*[\"'`]message[\"'`]/i.test(content)) {\n if (!/event\\.origin|e\\.origin|message\\.origin/i.test(content)) {\n matches.push(...findMatches(content, /addEventListener\\s*\\(\\s*[\"'`]message[\"'`]/g, unvalidatedEventData, filePath, () =>\n \"Always verify event.origin in message event handlers to prevent cross-origin attacks. Example: if (event.origin !== 'https://trusted.com') return;\"\n ));\n }\n }\n // dispatchEvent with custom data inserted without validation\n if (/new\\s+CustomEvent\\s*\\(/i.test(content) || /ipcRenderer\\.send/i.test(content)) {\n const hasTypeCheck = /typeof\\s|instanceof|z\\.|schema|validate|Number\\.isFinite|parseInt|parseFloat/i.test(content);\n if (!hasTypeCheck) {\n matches.push(...findMatches(content, /new\\s+CustomEvent\\s*\\(/g, unvalidatedEventData, filePath, () =>\n \"Type-check custom event data before using it. Validate that data.detail contains expected types to prevent injection.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC030 – Insecure Deserialization\n// ────────────────────────────────────────────\n\nexport const insecureDeserialization: CustomRule = {\n id: \"VC030\",\n title: \"Insecure Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Deserializing untrusted data (pickle, unserialize, yaml.load) can execute arbitrary code. Attackers craft malicious payloads to gain remote code execution.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const patterns = [\n // Python pickle\n /pickle\\.loads?\\s*\\(/g,\n /cPickle\\.loads?\\s*\\(/g,\n // PHP unserialize\n /unserialize\\s*\\(/g,\n // Ruby Marshal\n /Marshal\\.load\\s*\\(/g,\n // YAML unsafe load (Python + js-yaml). Filtered below so safe schema\n // arguments (SAFE_SCHEMA, FAILSAFE_SCHEMA, yaml.SafeLoader) don't FP.\n /yaml\\.load\\s*\\(/g,\n /yaml\\.unsafe_load\\s*\\(/g,\n // Java ObjectInputStream\n /ObjectInputStream\\s*\\(/g,\n // Node.js node-serialize\n /serialize\\.unserialize\\s*\\(/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureDeserialization, filePath, () =>\n \"Never deserialize untrusted data. Use JSON instead of pickle/Marshal/unserialize. For YAML, use yaml.safe_load(). Validate and sanitize all input before deserialization.\"\n ));\n }\n // Post-filter: drop yaml.load() matches that pass an explicit safe schema\n // or loader argument (covers both js-yaml options and PyYAML's Loader=).\n return matches.filter((m) => {\n if (!/yaml\\.load\\s*\\(/.test(m.snippet ?? \"\")) return true;\n const lineText = (m.snippet ?? \"\").toLowerCase();\n if (/safe_schema|failsafe_schema|safe_load|safeloader/.test(lineText)) {\n return false;\n }\n // Also check the wider context — safe schema args often wrap to the\n // next line in real code.\n const lineIdx = m.line - 1;\n const lines = content.split(\"\\n\");\n const ctx = lines.slice(lineIdx, lineIdx + 3).join(\"\\n\").toLowerCase();\n if (/safe_schema|failsafe_schema|safe_load|safeloader/.test(ctx)) {\n return false;\n }\n return true;\n });\n },\n};\n\n// ────────────────────────────────────────────\n// VC031 – Hardcoded JWT Secret\n// ────────────────────────────────────────────\n\nexport const hardcodedJWTSecret: CustomRule = {\n id: \"VC031\",\n title: \"Hardcoded JWT Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"JWT tokens signed with a hardcoded string secret can be forged by anyone who reads the source code.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\") || filePath.includes(\"test\")) return [];\n const patterns = [\n /jwt\\.sign\\s*\\([^,]+,\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /jwt\\.verify\\s*\\([^,]+,\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /jsonwebtoken.*secret\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/gi,\n /JWT_SECRET\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, hardcodedJWTSecret, filePath, () =>\n \"Move JWT secret to an environment variable: jwt.sign(payload, process.env.JWT_SECRET). Use a strong, random secret (256+ bits).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC032 – Missing HTTPS Enforcement\n// ────────────────────────────────────────────\n\nexport const missingHTTPS: CustomRule = {\n id: \"VC032\",\n title: \"Missing HTTPS Enforcement\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"HTTP URLs in production code, missing HSTS headers, or insecure redirect configurations expose data to man-in-the-middle attacks.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.includes(\"test\") || filePath.includes(\"README\")) return [];\n if (filePath.match(/\\.(md|txt)$/)) return [];\n const matches: RuleMatch[] = [];\n // Skip standard XML/HTML doctypes, DTDs, schema URIs, namespace URLs\n if (/<!DOCTYPE|xmlns|\\.dtd|\\.xsd/i.test(content) && !/fetch|axios|request|http\\.get/i.test(content)) return [];\n // Hardcoded http:// URLs to non-local hosts (excluding standard schema/namespace URIs)\n const httpPattern = /[\"'`]http:\\/\\/(?!localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|192\\.168\\.|10\\.|172\\.(?:1[6-9]|2\\d|3[01])\\.|www\\.w3\\.org|www\\.apple\\.com\\/DTDs|schemas?\\.|xml\\.org|purl\\.org|ns\\.adobe|xmlpull\\.org|java\\.sun\\.com)[^\"'`\\s]+[\"'`]/g;\n const rawMatches = findMatches(content, httpPattern, missingHTTPS, filePath, () =>\n \"Use https:// instead of http:// for all production URLs. Add HSTS header: Strict-Transport-Security: max-age=31536000; includeSubDomains\"\n );\n // Skip DOCTYPE/DTD URLs\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const matchText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/DTD|DOCTYPE|w3\\.org|apple\\.com\\/DTDs/i.test(matchText)) continue;\n matches.push(rm);\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC033 – Exposed Debug/Dev Mode\n// ────────────────────────────────────────────\n\nexport const exposedDebugMode: CustomRule = {\n id: \"VC033\",\n title: \"Debug/Development Mode Exposed\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Debug mode, verbose logging, or development configuration left in production code exposes internal details and may enable debug endpoints.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.endsWith(\".example\") || filePath.includes(\"node_modules\")) return [];\n if (filePath.match(/\\.env\\.development$/)) return []; // Expected in dev env files\n const matches: RuleMatch[] = [];\n const patterns = [\n // Debug flags set to true\n /DEBUG\\s*[:=]\\s*(?:true|1|[\"'`]true[\"'`]|[\"'`]\\*[\"'`])/g,\n // Django DEBUG\n /DEBUG\\s*=\\s*True/g,\n // Flask/Express debug mode\n /app\\.debug\\s*=\\s*True/g,\n /app\\.run\\s*\\([^)]*debug\\s*=\\s*True/g,\n // Source maps in production\n /devtool\\s*:\\s*[\"'`](?:eval|cheap|source-map|inline-source-map)[\"'`]/g,\n // Debug endpoints that leak environment / process internals in the\n // response body. Very high-signal — a production service shouldn't\n // serialize process.env or process.version to callers.\n /res\\.(?:json|send)\\s*\\([^)]*process\\.(?:env|version|pid|arch|platform)/gi,\n /(?:env|environment)\\s*:\\s*process\\.env\\b/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedDebugMode, filePath, () =>\n \"Disable debug mode in production. Use environment variables: DEBUG = process.env.NODE_ENV !== 'production'. Remove source maps from production builds.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC034 – Insecure Randomness\n// ────────────────────────────────────────────\n\nexport const insecureRandomness: CustomRule = {\n id: \"VC034\",\n title: \"Insecure Randomness for Security-Sensitive Values\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"Math.random() is not cryptographically secure. Using it for tokens, session IDs, passwords, or OTPs makes them predictable.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/Math\\.random\\s*\\(\\s*\\)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n\n // --- Same-line inline pattern: const token = Math.random() ---\n const securityContext = /(?:token|secret|session|password|otp|nonce|salt|csrf|auth)\\s*[:=]\\s*.*Math\\.random/gi;\n const rawMatches = findMatches(content, securityContext, insecureRandomness, filePath, () =>\n \"Use crypto.randomUUID() or crypto.getRandomValues() for security-sensitive values. Math.random() is predictable.\"\n );\n const nonSecurityVarNames = /(?:id|key|color|index|delay|position|size|width|height|offset|opacity|rotation|animation|random(?!.*(?:token|secret|key|password)))\\s*[:=]\\s*.*Math\\.random/i;\n for (const rm of rawMatches) {\n const lineText = content.split(\"\\n\")[rm.line - 1] || \"\";\n if (nonSecurityVarNames.test(lineText)) continue;\n matches.push(rm);\n }\n\n // --- File-level context: function/variable NAMES that declare security\n // intent. If present anywhere in the file, treat every Math.random() call\n // as suspicious (the security-critical value is built across lines). ---\n const SECURITY_FN_CONTEXT =\n /\\b(?:function|def|async|const|let|var)\\s+\\w*(?:generate|create|make|new|mint|issue)[A-Z_]\\w*(?:Token|Session|Nonce|Salt|Secret|Password|Otp|Csrf|Key|Code|Auth)\\w*/i;\n const SECURITY_IDENT_CONTEXT =\n /\\b(?:session[_-]?token|csrf[_-]?token|reset[_-]?token|verify[_-]?code|otp[_-]?code|refresh[_-]?token|api[_-]?key)\\b/i;\n\n if (SECURITY_FN_CONTEXT.test(content) || SECURITY_IDENT_CONTEXT.test(content)) {\n const raw = findMatches(content, /Math\\.random\\s*\\(\\s*\\)/g, insecureRandomness, filePath, () =>\n \"Math.random() is not cryptographically secure. Use crypto.randomBytes() (Node), crypto.getRandomValues() (browser), or crypto.randomUUID() for tokens, session IDs, salts, and similar values.\",\n );\n for (const m of raw) {\n if (matches.some((existing) => existing.line === m.line)) continue;\n matches.push(m);\n }\n }\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC035 – Open Redirect via URL Params\n// ────────────────────────────────────────────\n\nexport const openRedirectParams: CustomRule = {\n id: \"VC035\",\n title: \"Open Redirect via URL Parameters\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Redirect parameters like ?redirect_url=, ?return_to=, ?next= passed directly to redirects enable phishing attacks.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n // Strip comments before validating-presence check — otherwise comments\n // like \"// no allowlist here\" trigger the check and suppress the rule.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\");\n const hasValidation = /allowed[_-]?(?:urls?|domains?|hosts?|paths?|routes?|list)|allow[_-]?list|valid[_-]?url|safe[_-]?domain|whitelist|startsWith.*https|new URL.*hostname/i.test(codeOnly);\n if (hasValidation) return [];\n\n // --- Regex layer (unchanged): inline patterns ---\n const patterns = [\n /(?:redirect_url|redirect_uri|return_to|return_url|next|callback_url|continue|goto|target|dest|destination|forward|redir)\\s*(?:=|:)\\s*(?:req\\.query|req\\.params|searchParams|query|params)\\./gi,\n /redirect\\s*\\(\\s*(?:req\\.query|req\\.params|searchParams\\.get)\\s*\\(\\s*[\"'`](?:redirect|return|next|callback|url|goto)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, openRedirectParams, filePath, () =>\n \"Validate redirect URLs against an allowlist of trusted domains. Use: const url = new URL(input); if (!ALLOWED_HOSTS.includes(url.hostname)) reject.\"\n ));\n }\n\n // --- AST layer: res.redirect(<tainted>) where the arg traces to user input ---\n if (!/\\.redirect\\s*\\(/.test(content)) return matches;\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n visitCalls(\n parsed,\n (callee: Node) => isCalleeNamed(callee, \"redirect\"),\n (call, line) => {\n const first = call.arguments[0];\n if (!first || first.type === \"SpreadElement\") return;\n if (!taint.isTainted(first as Node)) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n openRedirectParams,\n \"Validate redirect targets against an allowlist before calling res.redirect(). `const ALLOWED = new Set(['/dashboard', '/settings']); if (!ALLOWED.has(next)) return res.redirect('/dashboard');`\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC036 – Missing Error Boundary (React)\n// ────────────────────────────────────────────\n\nexport const missingErrorBoundary: CustomRule = {\n id: \"VC036\",\n title: \"React App Missing Error Boundary\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"React apps without error boundaries display raw stack traces and component tree info to users when crashes occur, leaking internal details.\",\n check(content, filePath) {\n // Only check App.tsx, App.jsx, app.tsx, app.jsx files\n const basename = filePath.split(\"/\").pop() || \"\";\n if (!/^[Aa]pp\\.[jt]sx$/i.test(basename)) return [];\n // Skip .ts files (non-JSX)\n if (filePath.match(/\\.ts$/)) return [];\n // Only trigger if file contains JSX (< followed by an uppercase letter)\n if (!/<[A-Z]/.test(content)) return [];\n // Skip Electron main process files\n if (/(?:BrowserWindow|electron|ipcMain|app\\.on\\s*\\(\\s*[\"']ready)/i.test(content)) return [];\n if (/\\/main\\//.test(filePath) && !/react-dom|createRoot/i.test(content)) return [];\n // Must have React imports and render calls\n if (!/(?:import.*react|from\\s+['\"]react|require.*react)/i.test(content)) return [];\n if (!/(?:createRoot|ReactDOM\\.render)/i.test(content)) return [];\n const hasErrorBoundary = /ErrorBoundary|componentDidCatch|getDerivedStateFromError|error-boundary/i.test(content);\n if (hasErrorBoundary) return [];\n if (/createRoot|ReactDOM\\.render/i.test(content)) {\n return [{\n rule: \"VC036\", title: missingErrorBoundary.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Wrap your app in an ErrorBoundary component to catch rendering errors gracefully. Use react-error-boundary or create a class component with componentDidCatch.\"\n }];\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC037 – Exposed Stack Traces in API\n// ────────────────────────────────────────────\n\nexport const exposedStackTraces: CustomRule = {\n id: \"VC037\",\n title: \"Stack Traces Exposed in API Responses\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"Returning error.stack or detailed error messages in API responses reveals internal code paths, file structure, and dependencies to attackers.\",\n check(content, filePath) {\n if (!isServerSideFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Sending stack trace in response\n /(?:res\\.(?:json|send|status)|c\\.json|return.*json)\\s*\\([^)]*(?:err\\.stack|error\\.stack|e\\.stack)/gi,\n /(?:res\\.(?:json|send|status)|c\\.json)\\s*\\([^)]*(?:err\\.message|error\\.message|e\\.message)/gi,\n // Express-style error with stack\n /(?:message|error)\\s*:\\s*(?:err|error|e)\\.(?:stack|message)/gi,\n ];\n const hasEnvCheck = /process\\.env\\.NODE_ENV\\s*(?:===|!==)\\s*[\"'`]production[\"'`]|NODE_ENV/i.test(content);\n if (hasEnvCheck) return []; // They're conditionally showing errors\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedStackTraces, filePath, () =>\n \"Never expose error.stack or error.message to clients in production. Return generic error messages: { error: 'Something went wrong' }. Log details server-side only.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC038 – Insecure File Upload Type\n// ────────────────────────────────────────────\n\nexport const insecureFileUpload: CustomRule = {\n id: \"VC038\",\n title: \"Insecure File Upload Validation\",\n severity: \"high\",\n category: \"Injection\",\n description: \"File uploads validated only by extension (not MIME type or content) allow attackers to upload executable files disguised as images or documents.\",\n check(content, filePath) {\n if (!/upload|multer|formidable|busboy|multipart/i.test(content)) return [];\n // Strip comments before the mitigation-presence checks. Fixture / real\n // code comments that say \"we should check MIME\" match the mime regex\n // and silently suppress the rule.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\")\n .replace(/^\\s*#.*$/gm, \"\");\n const matches: RuleMatch[] = [];\n // Extension-only validation takes three common shapes:\n // file.originalname.endsWith(\".jpg\")\n // allowed.some(ext => file.originalname.endsWith(ext))\n // /\\.(jpg|png|pdf)$/.test(file.originalname) or .match(/\\.(jpg|png)/)\n // The regex-literal pattern anchors on `/\\.(` followed by 2+ short\n // lowercase tokens separated by `|`, so we don't fire on any bare\n // mention of \"png\" or \"pdf\" in unrelated code. `(?:` (non-capture\n // group) is recognized alongside the simpler `(` shape.\n const hasExtCheck =\n /\\.(?:endsWith|match|test)\\s*\\([^)]*(?:\\.jpg|\\.png|\\.pdf|\\.doc|ext)/i.test(codeOnly) ||\n /\\/\\\\\\.\\((?:\\?:)?[a-z]{2,5}(?:\\|[a-z]{2,5})+\\)/i.test(codeOnly);\n const hasMimeCheck = /\\bmimetype\\b|\\bcontent-type\\b|\\bfile\\.type\\b|\\bmime\\.\\w|\\bmagic\\.detect\\b|\\bfile-type\\b|fileTypeFromBuffer/i.test(codeOnly);\n if (hasExtCheck && !hasMimeCheck) {\n matches.push(...findMatches(content, /upload|multer|formidable|busboy/gi, insecureFileUpload, filePath, () =>\n \"Validate file uploads by MIME type AND magic bytes, not just extension. Use the 'file-type' package to detect actual file type from content. Also enforce size limits.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC039 – Missing Dependency Lock File\n// ────────────────────────────────────────────\n\nexport const missingLockFile: CustomRule = {\n id: \"VC039\",\n title: \"Missing Dependency Lock File\",\n severity: \"medium\",\n category: \"Supply Chain\",\n description: \"Without a lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock), dependency versions are unpinned and vulnerable to supply chain attacks via version substitution.\",\n check(content, filePath) {\n // Only check .gitignore for lock files being ignored\n if (!filePath.endsWith(\".gitignore\")) return [];\n const ignoresLock = /package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock/i.test(content);\n if (ignoresLock) {\n return findMatches(content, /(?:package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock)/gi, missingLockFile, filePath, () =>\n \"Remove the lockfile from .gitignore. Lockfiles should be committed to prevent supply chain attacks. They ensure exact versions are installed across all environments.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC040 – Exposed .git Directory\n// ────────────────────────────────────────────\n\nexport const exposedGitDir: CustomRule = {\n id: \"VC040\",\n title: \"Exposed .git Directory via Web Server\",\n severity: \"critical\",\n category: \"Information Leakage\",\n description: \"Web server configs that don't block access to .git directories expose your entire source code, commit history, secrets, and credentials.\",\n check(content, filePath) {\n // Check web server configs\n if (!filePath.match(/(?:nginx|apache|httpd|caddy|\\.htaccess|vercel\\.json|netlify\\.toml|server\\.[jt]s)/i)) return [];\n // For static file servers, check they block .git\n if (/(?:static|serve|express\\.static|serveStatic|public)/i.test(content)) {\n const blocksGit = /\\.git|dotfiles|hidden/i.test(content);\n if (!blocksGit) {\n return findMatches(content, /(?:static|serve|express\\.static|serveStatic)\\s*\\(/g, exposedGitDir, filePath, () =>\n \"Block access to .git and other dotfiles in your static file server config. For Express: app.use('/.git', (req, res) => res.status(403).end()). For Nginx: location ~ /\\\\.git { deny all; }\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC041 – Server-Side Request Forgery (SSRF)\n// ────────────────────────────────────────────\n\nexport const ssrfVulnerability: CustomRule = {\n id: \"VC041\",\n title: \"Potential Server-Side Request Forgery (SSRF)\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Fetching URLs from user input without validation allows attackers to access internal services, cloud metadata endpoints (169.254.169.254), and private networks.\",\n check(content, filePath) {\n if (!isServerSideFile(filePath)) return [];\n\n const matches: RuleMatch[] = [];\n const hasValidation = /allowedHosts|allowedDomains|allowedUrls|safeDomain|whitelist|urlValidator|new URL.*hostname.*includes|isAllowedUrl|validateUrl|isValidUrl/i.test(content);\n if (hasValidation) return [];\n\n // --- Regex layer: inline req.body/query/params passed straight to fetch/axios ---\n const inlinePattern = /(?:fetch|axios\\.get|axios\\.post|axios|got|request|http\\.get|https\\.get)\\s*\\(\\s*(?:req\\.(?:body|query|params))\\./gi;\n matches.push(...findMatches(content, inlinePattern, ssrfVulnerability, filePath, () =>\n \"Validate URLs against an allowlist before fetching. Block internal IPs: 127.0.0.1, 10.x, 172.16-31.x, 192.168.x, 169.254.169.254 (cloud metadata). Use: const url = new URL(input); if (!ALLOWED_HOSTS.includes(url.hostname)) throw new Error('Blocked');\"\n ));\n\n // --- AST layer: fetch-like calls whose first arg traces back to user input ---\n if (!/\\b(?:fetch|axios|got|request|http\\.get|https\\.get)\\b/.test(content)) return matches;\n\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n const FETCH_CALLEES = new Set([\"fetch\", \"axios\", \"got\", \"request\"]);\n const FETCH_METHODS = new Set([\"get\", \"post\", \"put\", \"patch\", \"delete\", \"request\"]);\n\n visitCalls(\n parsed,\n (callee: Node) => {\n // fetch(...), got(...), axios(...)\n if (callee.type === \"Identifier\" && FETCH_CALLEES.has(callee.name)) return true;\n // axios.get(...), http.get(...), https.get(...), got.post(...)\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n if (!FETCH_METHODS.has(callee.property.name)) return false;\n const obj = callee.object;\n if (obj.type === \"Identifier\") {\n return obj.name === \"axios\" || obj.name === \"got\" || obj.name === \"http\" || obj.name === \"https\";\n }\n }\n return false;\n },\n (call, line) => {\n const first = call.arguments[0];\n if (!first || first.type === \"SpreadElement\") return;\n if (!taint.isTainted(first as Node)) return;\n // De-dupe against the regex layer: if we already recorded this line\n // from the inline-pattern pass, skip.\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n ssrfVulnerability,\n \"Validate URLs against an allowlist before fetching. Parse the URL, check the hostname against a static set, and block internal IPs (127.0.0.1, 10.x, 172.16-31.x, 192.168.x, 169.254.169.254).\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC042 – Mass Assignment\n// ────────────────────────────────────────────\n\nexport const massAssignment: CustomRule = {\n id: \"VC042\",\n title: \"Mass Assignment Vulnerability\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Spreading or assigning request body directly into database models allows attackers to set fields they shouldn't (e.g., isAdmin, role, verified).\",\n check(content, filePath) {\n if (!isServerSideFile(filePath)) return [];\n const hasSanitization = /pick\\(|omit\\(|allowedFields|sanitize|whitelist|permit|strong_params/i.test(content);\n if (hasSanitization) return [];\n\n const matches: RuleMatch[] = [];\n\n // --- Regex layer: narrow, inline-only patterns (kept for speed/coverage) ---\n const patterns = [\n /Object\\.assign\\s*\\(\\s*(?:user|account|profile|record|doc|model|entity)[^,]*,\\s*(?:req\\.body|body|input|data)\\s*\\)/gi,\n /(?:create|update|insert|save|findOneAndUpdate|updateOne|upsert)\\s*\\(\\s*\\{[^}]*\\.\\.\\.(?:req\\.body|body|input|data)/gi,\n /(?:create|insert|save)\\s*\\(\\s*(?:req\\.body|body)\\s*\\)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, massAssignment, filePath, () =>\n \"Never pass req.body directly to database operations. Explicitly pick allowed fields: const { name, email } = req.body; await db.create({ name, email });\"\n ));\n }\n\n // --- AST layer: ORM calls whose argument spreads a tainted object ---\n // Both exact-name matches and prefix matches (upsertFlag, createUser,\n // saveOrder, findByUserIdAndUpdate etc.) count. A lot of real code uses\n // purpose-specific method names on top of an ORM — naming them all\n // individually is hopeless, so we match by prefix instead.\n const ORM_METHOD_PREFIXES = [\n \"create\", \"insert\", \"save\", \"update\", \"upsert\", \"patch\",\n \"bulkCreate\", \"bulkInsert\", \"bulkUpsert\",\n \"findOneAndUpdate\", \"findByIdAndUpdate\", \"findAndUpdate\",\n \"build\", \"merge\", \"replace\",\n ];\n const isOrmMethod = (name: string): boolean =>\n ORM_METHOD_PREFIXES.some((p) => name === p || name.startsWith(p));\n\n if (!/\\b(?:create|insert|save|update|upsert|patch|bulk|findOneAndUpdate|findByIdAndUpdate|findAndUpdate|build|merge|replace)\\w*\\s*\\(/.test(content)) {\n return matches;\n }\n\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n visitCalls(\n parsed,\n (callee: Node) => {\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n return isOrmMethod(callee.property.name);\n }\n return false;\n },\n (call, line) => {\n // Flag on: ORM.create({ ...tainted }) or ORM.create(tainted)\n const tainted = call.arguments.some((arg) => {\n if (arg.type === \"SpreadElement\") return taint.isTainted(arg.argument as Node);\n if (arg.type === \"ObjectExpression\") {\n return arg.properties.some(\n (p) => p.type === \"SpreadElement\" && taint.isTainted((p.argument as Node)),\n );\n }\n return taint.isTainted(arg as Node);\n });\n if (!tainted) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n massAssignment,\n \"Pick allowed fields explicitly instead of spreading the request body. `User.create({ email: req.body.email, name: req.body.name })` rather than `User.create({ ...req.body })`.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC043 – Timing Attack on Comparison\n// ────────────────────────────────────────────\n\nexport const timingAttack: CustomRule = {\n id: \"VC043\",\n title: \"Timing-Unsafe Secret Comparison\",\n severity: \"medium\",\n category: \"Cryptography\",\n description: \"Using === to compare secrets, tokens, or hashes leaks information via timing side-channels. Attackers can determine the correct value one character at a time.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n const hasTimingSafe = /timingSafeEqual|constantTimeEqual|safeCompare|secureCompare/i.test(content);\n if (hasTimingSafe) return [];\n\n // --- Regex layer (unchanged): direct inline secret-vs-request comparisons ---\n const patterns = [\n /(?:token|secret|hash|digest|signature|hmac|apiKey|api_key)\\s*(?:===|!==)\\s*(?:req\\.|body\\.|params\\.|query\\.|input)/gi,\n /(?:^|[^.\\w])(?:req\\.|body\\.|params\\.|query\\.|input)[\\w.]*(?:token|secret|hash|digest|signature|hmac)\\s*(?:===|!==)/gim,\n ];\n for (const p of patterns) {\n const raw = findMatches(content, p, timingAttack, filePath, () =>\n \"Use crypto.timingSafeEqual() for comparing secrets: crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)). This prevents timing-based side-channel attacks.\"\n );\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/typeof\\s/.test(lineText)) continue;\n matches.push(m);\n }\n }\n\n // --- AST layer: `provided === EXPECTED_KEY` where either side names a secret ---\n // Catches const EXPECTED = process.env.INTERNAL_API_KEY; if (x === EXPECTED) ...\n // that the inline regex misses because the secret is named, not inlined.\n if (!/===|!==/.test(content)) return matches;\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n\n // Substring match (not \\b-anchored): \"EXPECTED_API_KEY\" has no word\n // boundary between the underscores, so \\b rejects it. Keep the list\n // tight enough that incidental matches are unlikely.\n const SECRET_NAME_RE = /(?:secret|token|api[_-]?key|auth[_-]?key|jwt[_-]?secret|signature|hmac|password|passwd|pwd_hash|pwd_digest|digest)/i;\n\n /** Does the expression reference something that looks like a secret? */\n function looksLikeSecret(node: Node): boolean {\n if (node.type === \"Identifier\") return SECRET_NAME_RE.test(node.name);\n if (node.type === \"MemberExpression\") {\n // process.env.INTERNAL_API_KEY / process.env.JWT_SECRET / headers[\"x-api-key\"]\n if (node.property.type === \"Identifier\" && SECRET_NAME_RE.test(node.property.name)) return true;\n if (node.property.type === \"StringLiteral\" && SECRET_NAME_RE.test(node.property.value)) return true;\n return looksLikeSecret(node.object as Node);\n }\n return false;\n }\n\n visitBinary(ctx.parsed, (n, line) => {\n if (n.operator !== \"===\" && n.operator !== \"!==\" && n.operator !== \"==\" && n.operator !== \"!=\") {\n return;\n }\n // Skip typeof comparisons (common type guards)\n if (n.left.type === \"UnaryExpression\" && n.left.operator === \"typeof\") return;\n if (n.right.type === \"UnaryExpression\" && n.right.operator === \"typeof\") return;\n // One side must look like a secret; other side must be non-literal\n // (literal-to-literal isn't a secret check).\n const leftSecret = looksLikeSecret(n.left as Node);\n const rightSecret = looksLikeSecret(n.right as Node);\n if (!leftSecret && !rightSecret) return;\n const otherSide = leftSecret ? n.right : n.left;\n if (\n otherSide.type === \"StringLiteral\" ||\n otherSide.type === \"NumericLiteral\" ||\n otherSide.type === \"NullLiteral\" ||\n otherSide.type === \"BooleanLiteral\"\n ) {\n return;\n }\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n timingAttack,\n \"Compare secrets with crypto.timingSafeEqual() after a length check. === short-circuits on the first differing byte and leaks information via timing.\",\n ),\n );\n });\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC044 – Log Injection\n// ────────────────────────────────────────────\n\nexport const logInjection: CustomRule = {\n id: \"VC044\",\n title: \"Potential Log Injection\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Logging unsanitized user input allows attackers to forge log entries, inject malicious content, or exploit log aggregation systems via newlines and special characters.\",\n check(content, filePath) {\n if (!isServerSideFile(filePath)) return [];\n // Only short-circuit when the file genuinely strips newlines / control\n // characters before logging. JSON.stringify was previously on this\n // list but it doesn't strip newlines — a JSON-encoded string can\n // still embed \"\\n\" that ends up in the log line. Treat it as no-op.\n const hasSanitization = /replace\\s*\\(\\s*\\/\\[?\\\\r\\\\n\\]|sanitizeLog|stripNewlines|sanitizeForLog/i.test(content);\n if (hasSanitization) return [];\n\n const matches: RuleMatch[] = [];\n\n // --- Regex layer: inline console.log(req.*) patterns ---\n const patterns = [\n /console\\.(?:log|warn|error|info)\\s*\\([^)]*(?:req\\.body|req\\.query|req\\.params|req\\.headers)\\s*\\)/gi,\n /(?:logger|log)\\.(?:info|warn|error|debug)\\s*\\([^)]*(?:req\\.body|req\\.query|req\\.params)\\s*\\)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, logInjection, filePath, () =>\n \"Sanitize user input before logging: strip newlines and control characters. Use JSON.stringify() or a structured logger (e.g., pino, winston) that escapes values automatically.\"\n ));\n }\n\n // --- AST layer: console.log / logger.* called with a tainted template literal ---\n if (!/(?:console\\.|logger\\.|log\\.)\\s*\\w+\\s*\\(/.test(content)) return matches;\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n const LOG_METHODS = new Set([\"log\", \"warn\", \"error\", \"info\", \"debug\", \"trace\"]);\n\n visitCalls(\n parsed,\n (callee: Node) => {\n if (callee.type !== \"MemberExpression\") return false;\n if (callee.property.type !== \"Identifier\") return false;\n if (!LOG_METHODS.has(callee.property.name)) return false;\n const obj = callee.object;\n if (obj.type === \"Identifier\") {\n return obj.name === \"console\" || obj.name === \"logger\" || obj.name === \"log\";\n }\n return false;\n },\n (call, line) => {\n const tainted = call.arguments.some((arg) => {\n if (arg.type === \"SpreadElement\") return taint.isTainted(arg.argument as Node);\n return taint.isTainted(arg as Node);\n });\n if (!tainted) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n logInjection,\n \"Strip newlines/control chars before embedding user input in a log line. `value.replace(/[\\\\r\\\\n]/g, ' ')` for quick fix; a structured logger escapes values automatically.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC045 – Weak Password Requirements\n// ────────────────────────────────────────────\n\nexport const weakPasswordRequirements: CustomRule = {\n id: \"VC045\",\n title: \"Weak Password Requirements\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Registration or password-change endpoints without minimum length or complexity validation allow weak passwords that are easily brute-forced.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!/(?:password|passwd|pwd)/i.test(content)) return [];\n const isPasswordContext =\n /(?:register|signup|sign.up|createUser|create.user|changePassword|resetPassword|set.password|validatePassword|validate.password|passwordPolicy|password.policy)/i.test(\n content,\n ) || isServerSideFile(filePath);\n\n // First: explicit weak-threshold detection — fires even if some\n // validation exists, because the validation itself is the bug.\n // `password.length < N` for N in 1..7, or `password.length >= N` for\n // N in 1..7 (means a password of length N is accepted).\n const matches: RuleMatch[] = [];\n if (isPasswordContext) {\n const weakThresholdPatterns = [\n // password.length < 4, password.length < 6, etc.\n /(?:password|pwd|passwd)\\s*\\.length\\s*<\\s*[1-7]\\b/gi,\n /(?:password|pwd|passwd)\\s*\\.length\\s*<=\\s*[0-6]\\b/gi,\n // password.length >= 4, password.length >= 6 (min length set too low)\n /(?:password|pwd|passwd)\\s*\\.length\\s*>=\\s*[1-7]\\b/gi,\n /(?:password|pwd|passwd)\\s*\\.length\\s*>\\s*[0-6]\\b/gi,\n // Python: len(password) < 8 with a low threshold\n /len\\s*\\(\\s*(?:password|pwd|passwd)\\s*\\)\\s*<\\s*[1-7]\\b/gi,\n ];\n for (const p of weakThresholdPatterns) {\n matches.push(...findMatches(content, p, weakPasswordRequirements, filePath, () =>\n \"Minimum password length is too low. Require at least 8 characters with complexity (upper+lower+digit+symbol) or 12+ characters without complexity. OWASP ASVS L1 requires 8+; NIST 800-63B recommends 12+.\",\n ));\n }\n if (matches.length > 0) return matches;\n }\n\n // Fallback: original detection of password assignment without any validation.\n if (!isPasswordContext) return [];\n const hasValidation = /(?:password|pwd).*(?:\\.length|minLength|minlength|min_length)\\s*(?:>=?|<|>)\\s*\\d|(?:password|pwd).*(?:match|test|regex|pattern)|zxcvbn|password-validator|passwordStrength|isStrongPassword|joi\\.|yup\\.|zod\\.|validate|schema/i.test(content);\n if (hasValidation) return [];\n const hasPasswordHandling = /(?:password|pwd)\\s*[:=]\\s*(?:req\\.body|body|input|params|args)\\./i.test(content);\n if (!hasPasswordHandling) return [];\n const rawMatches = findMatches(content, /(?:password|pwd)\\s*[:=]\\s*(?:req\\.body|body|input|params|args)\\./gi, weakPasswordRequirements, filePath, () =>\n \"Enforce minimum password requirements: at least 8 characters, mix of letters/numbers/symbols. Use a library like zxcvbn for strength estimation.\"\n );\n // Skip if validation logic exists within 10 lines of the password assignment\n const lines = content.split(\"\\n\");\n const validationPattern = /\\.length|minLength|minlength|min_length|match|test|regex|pattern|validate|schema|zxcvbn|isStrongPassword/i;\n return rawMatches.filter((rm) => {\n const start = Math.max(0, rm.line - 1 - 10);\n const end = Math.min(lines.length, rm.line - 1 + 10);\n const nearby = lines.slice(start, end).join(\"\\n\");\n return !validationPattern.test(nearby);\n });\n },\n};\n\n// ────────────────────────────────────────────\n// VC046 – Session Fixation\n// ────────────────────────────────────────────\n\nexport const sessionFixation: CustomRule = {\n id: \"VC046\",\n title: \"Session Fixation Risk\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Not regenerating session IDs after login allows attackers to pre-set a session ID and hijack the authenticated session.\",\n check(content, filePath) {\n // Only applies to server-side code files, not iOS/Swift, docs, or tests\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/(?:login|signin|sign.in|authenticate)/i.test(content)) return [];\n if (!/session/i.test(content)) return [];\n // Strip comments — otherwise a comment like \"// should regenerate here\"\n // incorrectly suppresses the rule.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\");\n const hasRegenerate = /regenerate|destroy.*create|req\\.session\\.id\\s*=|session\\.regenerateId|rotateSession|clearCookies/i.test(codeOnly);\n if (hasRegenerate) return [];\n // Must have a login function definition, not just a reference or import\n const hasLogin = /(?:function\\s+(?:login|signin|authenticate)|(?:login|signin|authenticate)\\s*(?:=\\s*(?:async\\s*)?\\(|:\\s*(?:async\\s*)?\\())/i.test(content);\n if (!hasLogin) return [];\n return findMatches(content, /(?:function\\s+(?:login|signin|authenticate)|(?:login|signin|authenticate)\\s*(?:=\\s*(?:async\\s*)?\\())/gi, sessionFixation, filePath, () =>\n \"Regenerate the session ID after successful login: req.session.regenerate() (Express) or equivalent. This prevents session fixation attacks.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC047 – Missing Brute Force Protection\n// ────────────────────────────────────────────\n\nexport const missingBruteForce: CustomRule = {\n id: \"VC047\",\n title: \"Login Without Brute Force Protection\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Login endpoints without rate limiting, account lockout, or progressive delays are vulnerable to credential stuffing and brute force attacks.\",\n check(content, filePath) {\n const isLoginFile = /(?:login|signin|sign.in|auth)/i.test(filePath) || /(?:login|signin|authenticate).*(?:post|handler|route)/i.test(content);\n if (!isLoginFile) return [];\n if (!/(?:password|credential)/i.test(content)) return [];\n // Strip comments before the mitigation check — comments like\n // \"// brute force is a risk here\" or \"// rate limiting is missing\"\n // should not look to the scanner like brute-force protection exists.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\")\n .replace(/^\\s*#.*$/gm, \"\");\n const hasBruteForce = /rate.?limit|throttle|lockout|maxAttempts|max_attempts|failedAttempts|loginAttempts|express-brute|express-rate-limit|slowDown/i.test(codeOnly);\n if (hasBruteForce) return [];\n return findMatches(content, /\\.(post|handler)\\s*\\([^)]*(?:login|signin|auth)/gi, missingBruteForce, filePath, () =>\n \"Add brute force protection to login endpoints: rate limiting (5 attempts/minute), progressive delays, or account lockout after N failures. Use express-rate-limit or similar.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC048 – NoSQL Injection\n// ────────────────────────────────────────────\n\nexport const nosqlInjection: CustomRule = {\n id: \"VC048\",\n title: \"Potential NoSQL Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Passing unsanitized user input directly into MongoDB/NoSQL queries allows attackers to bypass authentication, extract data, or modify queries using operators like $gt, $ne, $regex.\",\n check(content, filePath) {\n if (!/(?:mongo|mongoose|findOne|findById|find\\(|collection|aggregate)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Direct req.body in MongoDB queries\n /\\.find(?:One)?\\s*\\(\\s*(?:req\\.body|body|input|params)\\s*\\)/gi,\n /\\.find(?:One)?\\s*\\(\\s*\\{[^}]*:\\s*(?:req\\.body|body|input|params)\\./gi,\n // $where with user input\n /\\$where\\s*:\\s*(?![\"'`])/g,\n // Direct variable in query without sanitization\n /\\.(?:findOne|findById|deleteOne|updateOne|findOneAndUpdate)\\s*\\(\\s*\\{[^}]*:\\s*(?:req\\.(?:body|query|params))\\./gi,\n // findOneAndUpdate / updateOne / deleteOne / find with req.body as\n // the entire filter (attacker controls the filter shape).\n /\\.(?:findOne|findById|findOneAndUpdate|updateOne|deleteOne|deleteMany|updateMany|replaceOne|find)\\s*\\(\\s*req\\.body\\s*[,)]/gi,\n ];\n const hasSanitization = /sanitize|escape|mongo-sanitize|express-mongo-sanitize|validator|typeof.*===.*string/i.test(content);\n if (hasSanitization) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, nosqlInjection, filePath, () =>\n \"Sanitize MongoDB query inputs: use express-mongo-sanitize, validate types (ensure strings aren't objects), and avoid $where. Example: if (typeof input !== 'string') throw new Error('Invalid input');\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC049 – Exposed DB Credentials in Config\n// ────────────────────────────────────────────\n\nexport const exposedDBCredentials: CustomRule = {\n id: \"VC049\",\n title: \"Database Credentials in Config File\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Database connection strings with embedded usernames and passwords in committed config files expose credentials to anyone with repo access.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (!filePath.match(/(?:config|setting|database|db|knexfile|sequelize|drizzle|prisma)/i) && !filePath.match(/\\.(json|yaml|yml|toml|js|ts)$/)) return [];\n if (filePath.match(/\\.env/)) return []; // Handled by VC002\n const patterns = [\n // Connection strings with credentials\n /(?:host|server|database|db).*(?:password|passwd|pwd)\\s*[:=]\\s*[\"'`][^\"'`]{3,}[\"'`]/gi,\n // Inline connection URLs with credentials\n /(?:connection|database|db).*(?:postgres|mysql|mongodb|redis):\\/\\/[^:]+:[^@]+@/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedDBCredentials, filePath, () =>\n \"Move database credentials to environment variables. Use: process.env.DATABASE_URL instead of hardcoding connection strings in config files.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC050 – Missing DB Connection Encryption\n// ────────────────────────────────────────────\n\nexport const missingDBEncryption: CustomRule = {\n id: \"VC050\",\n title: \"Database Connection Without SSL/TLS\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Database connections without SSL/TLS encryption transmit credentials and data in plaintext, allowing eavesdropping on the network.\",\n check(content, filePath) {\n if (!/(?:createConnection|createPool|createClient|connect|new.*Client|knex|sequelize|drizzle)/i.test(content)) return [];\n if (!/(?:postgres|mysql|mariadb|pg|mongo)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // SSL explicitly disabled\n const sslDisabled = [\n /ssl\\s*:\\s*false/gi,\n /sslmode\\s*[:=]\\s*[\"'`]?disable[\"'`]?/gi,\n /rejectUnauthorized\\s*:\\s*false/gi,\n ];\n for (const p of sslDisabled) {\n matches.push(...findMatches(content, p, missingDBEncryption, filePath, () =>\n \"Enable SSL/TLS for database connections: { ssl: { rejectUnauthorized: true } }. In production, always verify server certificates.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC051 – GraphQL Introspection Enabled\n// ────────────────────────────────────────────\n\nexport const graphqlIntrospection: CustomRule = {\n id: \"VC051\",\n title: \"GraphQL Introspection Enabled in Production\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"GraphQL introspection exposes your entire API schema, types, queries, and mutations to attackers, making it easy to find attack vectors.\",\n check(content, filePath) {\n if (\n !/graphql|apollo|ApolloServer|GraphQLServer|createYoga|buildSchema|makeExecutableSchema/i.test(content) &&\n !/graphql|apollo/i.test(filePath)\n ) return [];\n const matches: RuleMatch[] = [];\n // Introspection explicitly enabled or not disabled\n if (/introspection\\s*:\\s*true/i.test(content)) {\n matches.push(...findMatches(content, /introspection\\s*:\\s*true/gi, graphqlIntrospection, filePath, () =>\n \"Disable GraphQL introspection in production: introspection: process.env.NODE_ENV !== 'production'. This prevents schema exposure.\"\n ));\n }\n // GraphQL server setup without introspection config\n if (/(?:ApolloServer|GraphQLServer|createYoga|buildSchema|makeExecutableSchema)\\s*\\(/i.test(content)) {\n if (!/introspection/i.test(content)) {\n matches.push(...findMatches(content, /(?:ApolloServer|GraphQLServer|createYoga)\\s*\\(/gi, graphqlIntrospection, filePath, () =>\n \"Explicitly disable introspection in production: new ApolloServer({ introspection: process.env.NODE_ENV !== 'production' })\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC052 – Missing Request Size Limit\n// ────────────────────────────────────────────\n\nexport const missingRequestSizeLimit: CustomRule = {\n id: \"VC052\",\n title: \"Missing Request Body Size Limit\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"Express/Hono/Fastify servers without request body size limits are vulnerable to denial-of-service via oversized payloads that exhaust memory.\",\n check(content, filePath) {\n if (!/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) return [];\n if (!/(?:express|hono|fastify|koa)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // express.json() without limit\n if (/express\\.json\\s*\\(\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /express\\.json\\s*\\(\\s*\\)/g, missingRequestSizeLimit, filePath, () =>\n \"Set a body size limit: express.json({ limit: '1mb' }). Without this, attackers can send huge payloads to crash your server.\"\n ));\n }\n // bodyParser without limit\n if (/bodyParser\\.json\\s*\\(\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /bodyParser\\.json\\s*\\(\\s*\\)/g, missingRequestSizeLimit, filePath, () =>\n \"Set a body size limit: bodyParser.json({ limit: '1mb' }).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC053 – Hardcoded IP/Host Allowlist\n// ────────────────────────────────────────────\n\nexport const hardcodedIPAllowlist: CustomRule = {\n id: \"VC053\",\n title: \"Hardcoded IP or Host Allowlist\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Hardcoded IP addresses or hostnames in allowlists are brittle and hard to update. They should be in environment variables or configuration files.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\") || filePath.match(/\\.(md|txt)$/)) return [];\n const matches: RuleMatch[] = [];\n // Arrays of IPs used in access control\n const patterns = [\n /(?:allowedIPs|allowed_ips|whitelist|allowlist|trustedHosts)\\s*[:=]\\s*\\[\\s*[\"'`]\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/gi,\n /(?:allowedIPs|allowed_ips|whitelist|allowlist|trustedHosts)\\s*[:=]\\s*\\[\\s*[\"'`][\\w.-]+\\.(?:com|net|org|io)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, hardcodedIPAllowlist, filePath, () =>\n \"Move IP/host allowlists to environment variables or a config file: const allowed = process.env.ALLOWED_IPS?.split(',') || [];\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC054 – Sensitive Data in localStorage\n// ────────────────────────────────────────────\n\nexport const sensitiveLocalStorage: CustomRule = {\n id: \"VC054\",\n title: \"Sensitive Data in localStorage\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Storing tokens, passwords, or secrets in localStorage is insecure — it's accessible to any JavaScript on the page (XSS) and persists indefinitely. Use httpOnly cookies instead.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?|vue|svelte)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Skip test-related files (mock, spec in the path)\n if (/mock|spec/i.test(filePath)) return [];\n // Skip if localStorage.removeItem is used (cleanup code, not storage)\n if (!/localStorage\\.setItem|localStorage\\[/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /localStorage\\.setItem\\s*\\(\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret)/gi,\n /localStorage\\s*\\[\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveLocalStorage, filePath, () =>\n \"Don't store tokens/secrets in localStorage — use httpOnly cookies instead. localStorage is accessible to any XSS attack. For session tokens, set them as httpOnly, secure, sameSite cookies.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC055 – Exposed Source Maps in Production\n// ────────────────────────────────────────────\n\nexport const exposedSourceMaps: CustomRule = {\n id: \"VC055\",\n title: \"Source Maps Exposed in Production\",\n severity: \"medium\",\n category: \"Information Leakage\",\n description: \"Source map files (.map) in production expose your original source code, comments, and internal logic to anyone who downloads them.\",\n check(content, filePath) {\n // Check build configs for source maps in production\n if (!filePath.match(/(?:webpack|vite|rollup|next)\\.config|tsconfig/i)) return [];\n const matches: RuleMatch[] = [];\n // Source maps enabled without environment check\n if (/(?:sourceMap|source-map|sourcemap)\\s*[:=]\\s*true/i.test(content)) {\n const hasEnvCheck = /process\\.env\\.NODE_ENV|NODE_ENV|production/i.test(content);\n if (!hasEnvCheck) {\n matches.push(...findMatches(content, /(?:sourceMap|source-map|sourcemap)\\s*[:=]\\s*true/gi, exposedSourceMaps, filePath, () =>\n \"Disable source maps in production builds: sourceMap: process.env.NODE_ENV !== 'production'. Or use 'hidden-source-map' to generate maps without exposing them.\"\n ));\n }\n }\n // productionSourceMap in Vue\n if (/productionSourceMap\\s*:\\s*true/i.test(content)) {\n matches.push(...findMatches(content, /productionSourceMap\\s*:\\s*true/gi, exposedSourceMaps, filePath, () =>\n \"Set productionSourceMap: false to avoid exposing source code in production.\"\n ));\n }\n // productionBrowserSourceMaps in Next.js — this literally enables prod maps,\n // no conditional to check. Fires unconditionally when set to `true`.\n if (/productionBrowserSourceMaps\\s*:\\s*true/i.test(content)) {\n matches.push(...findMatches(content, /productionBrowserSourceMaps\\s*:\\s*true/gi, exposedSourceMaps, filePath, () =>\n \"Set productionBrowserSourceMaps: false in next.config.js. If you need source maps for error tracking, upload them to your error tracker instead (e.g. Sentry upload-sourcemaps) rather than serving them publicly.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC056 – Clickjacking / Missing X-Frame-Options\n// ────────────────────────────────────────────\n\nexport const clickjacking: CustomRule = {\n id: \"VC056\",\n title: \"Clickjacking — Missing X-Frame-Options\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Without X-Frame-Options or frame-ancestors CSP directive, your page can be embedded in an attacker's iframe for UI redress (clickjacking) attacks.\",\n check(content, filePath) {\n // Check HTML files\n if (filePath.match(/\\.(html|htm)$/)) {\n if (!/X-Frame-Options|frame-ancestors/i.test(content)) {\n return [{\n rule: \"VC056\", title: clickjacking.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: 'Add <meta http-equiv=\"X-Frame-Options\" content=\"DENY\"> or set frame-ancestors in CSP to prevent clickjacking.'\n }];\n }\n }\n // Check server configs\n if (/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) {\n if (/(?:express|hono|fastify|koa)/i.test(content)) {\n if (!/X-Frame-Options|frame-ancestors|helmet/i.test(content)) {\n return findMatches(content, /(?:express|hono|fastify|koa)\\s*\\(/gi, clickjacking, filePath, () =>\n \"Add X-Frame-Options header: res.setHeader('X-Frame-Options', 'DENY'). Or use helmet: app.use(helmet()) which sets this and other security headers.\"\n );\n }\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC057 – Overly Permissive IAM/Cloud Roles\n// ────────────────────────────────────────────\n\nexport const overlyPermissiveIAM: CustomRule = {\n id: \"VC057\",\n title: \"Overly Permissive IAM/Cloud Permissions\",\n severity: \"critical\",\n category: \"Authorization\",\n description: \"Wildcard (*) permissions in AWS IAM, GCP, or Terraform configs grant unrestricted access, violating the principle of least privilege.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(tf|hcl|json|yaml|yml)$/) && !filePath.match(/(?:iam|policy|role|permission)/i)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // AWS IAM wildcard\n /[\"'`]Action[\"'`]\\s*:\\s*[\"'`]\\*[\"'`]/g,\n /[\"'`]Resource[\"'`]\\s*:\\s*[\"'`]\\*[\"'`]/g,\n // Terraform aws_iam\n /actions\\s*=\\s*\\[\\s*[\"'`]\\*[\"'`]\\s*\\]/g,\n /resources\\s*=\\s*\\[\\s*[\"'`]\\*[\"'`]\\s*\\]/g,\n // GCP bindings\n /role\\s*[:=]\\s*[\"'`]roles\\/(?:owner|editor)[\"'`]/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, overlyPermissiveIAM, filePath, () =>\n \"Follow the principle of least privilege: replace wildcard (*) with specific actions and resources. Example: 'Action': 's3:GetObject' instead of '*'.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC058 – Docker Running as Root\n// ────────────────────────────────────────────\n\nexport const dockerRunAsRoot: CustomRule = {\n id: \"VC058\",\n title: \"Docker Container Running as Root\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Containers running as root give attackers full system access if they escape the container. Always run as a non-root user.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const hasUser = /^\\s*USER\\s+/m.test(content);\n if (hasUser) return [];\n return [{\n rule: \"VC058\", title: dockerRunAsRoot.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Add a USER directive: RUN addgroup -S app && adduser -S app -G app\\\\nUSER app. Place it after installing dependencies but before COPY/CMD.\"\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC059 – Exposed Ports in Docker Compose\n// ────────────────────────────────────────────\n\nexport const exposedDockerPorts: CustomRule = {\n id: \"VC059\",\n title: \"Docker Compose Binding to All Interfaces\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Binding ports to 0.0.0.0 (default) in Docker Compose exposes services to the entire network. Bind to 127.0.0.1 for local-only access.\",\n check(content, filePath) {\n if (!filePath.match(/docker-compose|compose\\.(yaml|yml)$/i)) return [];\n const matches: RuleMatch[] = [];\n // ports: \"3000:3000\" or \"8080:80\" without binding to 127.0.0.1\n const portPattern = /ports:\\s*\\n(?:\\s*-\\s*[\"'`]?\\d+:\\d+[\"'`]?\\s*\\n?)+/g;\n if (portPattern.test(content) && !/127\\.0\\.0\\.1:/i.test(content)) {\n matches.push(...findMatches(content, /^\\s*-\\s*[\"'`]?\\d+:\\d+[\"'`]?/gm, exposedDockerPorts, filePath, () =>\n \"Bind to localhost only: '127.0.0.1:3000:3000' instead of '3000:3000'. This prevents external network access to the service.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC060 – Weak Hashing Algorithm\n// ────────────────────────────────────────────\n\nexport const weakHashing: CustomRule = {\n id: \"VC060\",\n title: \"Weak Hashing Algorithm for Passwords\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"MD5 and SHA1/SHA256 are too fast for password hashing — they can be brute-forced at billions of attempts per second. Use bcrypt, scrypt, or argon2 instead.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n // Skip if file also uses strong hashing (indicates migration / comparison code).\n if (/bcrypt|scrypt|argon2/i.test(content)) return [];\n\n const matches: RuleMatch[] = [];\n\n // --- Inline patterns (password on the same line as the hash call) ---\n const inlinePatterns = [\n /(?:md5|sha1|sha256|sha512)\\s*\\([^)]*(?:password|passwd|pwd)/gi,\n /createHash\\s*\\(\\s*[\"'`](?:md5|sha1|sha256)[\"'`]\\).*(?:password|passwd|pwd)/gi,\n /(?:password|passwd|pwd).*createHash\\s*\\(\\s*[\"'`](?:md5|sha1|sha256)[\"'`]\\)/gi,\n /hashlib\\.(?:md5|sha1|sha256)\\s*\\([^)]*(?:password|passwd|pwd)/gi,\n /Digest::(?:MD5|SHA1|SHA256).*(?:password|passwd|pwd)/gi,\n ];\n for (const p of inlinePatterns) {\n matches.push(...findMatches(content, p, weakHashing, filePath, () =>\n \"Use bcrypt, scrypt, or argon2 for password hashing — they're intentionally slow. Example: const hash = await bcrypt.hash(password, 12);\"\n ));\n }\n\n // --- File-level context: the password identifier and the weak hash call\n // live in the same file but on different lines. Flag every weak hash call\n // when password-hashing context exists somewhere in the file. ---\n const hasPasswordContext =\n /\\b(?:hashPassword|hash_password|verify_password|verifyPassword|password_hash|password_hash_verify|password_hashing)\\b/i.test(content) ||\n /\\b(?:function|def|async)\\s+\\w*(?:password|pwd)\\w*/i.test(content) ||\n /\\bpassword\\s*[:=]/i.test(content);\n\n if (hasPasswordContext) {\n const weakCallPatterns = [\n /createHash\\s*\\(\\s*[\"'`](?:md5|sha1|sha256)[\"'`]\\s*\\)/gi,\n /hashlib\\.(?:md5|sha1|sha256)\\s*\\(/gi,\n /Digest::(?:MD5|SHA1|SHA256)\\./gi,\n ];\n for (const p of weakCallPatterns) {\n const raw = findMatches(content, p, weakHashing, filePath, () =>\n \"Use bcrypt, scrypt, or argon2 for password hashing. These algorithms are intentionally slow so brute-force attacks are infeasible.\",\n );\n for (const m of raw) {\n if (matches.some((existing) => existing.line === m.line)) continue;\n matches.push(m);\n }\n }\n }\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC061 – Disabled TLS Certificate Verification\n// ────────────────────────────────────────────\n\nexport const disabledTLSVerification: CustomRule = {\n id: \"VC061\",\n title: \"Disabled TLS Certificate Verification\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"Disabling TLS certificate verification (NODE_TLS_REJECT_UNAUTHORIZED=0 or rejectUnauthorized:false) makes all HTTPS connections vulnerable to man-in-the-middle attacks.\",\n check(content, filePath) {\n const matches: RuleMatch[] = [];\n const patterns = [\n /NODE_TLS_REJECT_UNAUTHORIZED\\s*[:=]\\s*[\"'`]?0[\"'`]?/g,\n /rejectUnauthorized\\s*:\\s*false/g,\n /verify\\s*[:=]\\s*false.*(?:ssl|tls|cert|https)/gi,\n /PYTHONHTTPSVERIFY\\s*[:=]\\s*[\"'`]?0[\"'`]?/g,\n /ssl_verify\\s*[:=]\\s*false/gi,\n /InsecureSkipVerify\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, disabledTLSVerification, filePath, () =>\n \"Never disable TLS certificate verification in production. Fix the root cause: install the correct CA certificate, or use NODE_EXTRA_CA_CERTS for custom CAs.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC062 – Hardcoded Encryption Key/IV\n// ────────────────────────────────────────────\n\nexport const hardcodedEncryptionKey: CustomRule = {\n id: \"VC062\",\n title: \"Hardcoded Encryption Key or IV\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"Hardcoded encryption keys and initialization vectors (IVs) in source code can be extracted to decrypt all data. IVs must be random per encryption operation.\",\n check(content, filePath) {\n if (filePath.endsWith(\".example\") || filePath.endsWith(\".template\")) return [];\n if (isTestFile(filePath)) return [];\n // Skip HTML/XML/config files — meta tags and config values are not encryption keys\n if (filePath.match(/\\.(html|htm|xml|plist|svg|xhtml)$/)) return [];\n // Skip files without any crypto-related code\n if (!/(?:cipher|encrypt|decrypt|crypto|aes|createCipher)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Encryption key as string literal\n /(?:encryption_key|encryptionKey|cipher_key|cipherKey|aes_key|AES_KEY|ENCRYPTION_KEY)\\s*[:=]\\s*[\"'`][^\"'`]{8,}[\"'`]/g,\n // createCipheriv with hardcoded key\n /createCipher(?:iv)?\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*[\"'`][^\"'`]+[\"'`]/g,\n // Buffer.from with hardcoded key near cipher context\n /(?:key|iv|nonce)\\s*[:=]\\s*Buffer\\.from\\s*\\(\\s*[\"'`][^\"'`]{8,}[\"'`]/gi,\n // Static IV (should be random) — only in crypto context\n /(?:^|[\\s,({])(?:iv|nonce|initialVector)\\s*[:=]\\s*[\"'`][0-9a-fA-F]{16,}[\"'`]/gi,\n ];\n for (const p of patterns) {\n const rawMatches = findMatches(content, p, hardcodedEncryptionKey, filePath, () =>\n \"Move encryption keys to environment variables. Generate IVs randomly per operation: crypto.randomBytes(16). Never reuse IVs.\"\n );\n // Skip matches on lines containing CSP meta tags or security headers\n for (const rm of rawMatches) {\n const lineStart = content.lastIndexOf(\"\\n\", content.split(\"\\n\").slice(0, rm.line - 1).join(\"\\n\").length) + 1;\n const lineEnd = content.indexOf(\"\\n\", lineStart + 1);\n const lineText = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);\n if (/meta\\s+http-equiv|Content-Security-Policy|X-Frame-Options|X-Content-Type/i.test(lineText)) continue;\n matches.push(rm);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC063 – dangerouslySetInnerHTML\n// ────────────────────────────────────────────\n\nexport const dangerousInnerHTML: CustomRule = {\n id: \"VC063\",\n title: \"Unsanitized dangerouslySetInnerHTML\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Using dangerouslySetInnerHTML without sanitization (DOMPurify) enables XSS attacks. User-controlled content injected as raw HTML can execute arbitrary JavaScript.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/dangerouslySetInnerHTML/i.test(content)) return [];\n // Strip comments before the sanitize-presence check. Otherwise a\n // fixture comment like \"// unsanitized dangerouslySetInnerHTML\"\n // substring-matches \"sanitize\" and suppresses the rule.\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/(^|[^:])\\/\\/.*$/gm, \"$1\")\n .replace(/\\{\\s*\\/\\*[\\s\\S]*?\\*\\/\\s*\\}/g, \"\");\n const hasSanitize = /\\b(?:DOMPurify|sanitizeHtml|sanitize-html|isomorphic-dompurify)\\b|\\b(?:sanitize|purify|xss)\\s*\\(/i.test(codeOnly);\n if (hasSanitize) return [];\n // Find all dangerouslySetInnerHTML usages, but skip if the value is a\n // static constant (all-caps like THEME_INIT_SCRIPT), a string literal,\n // or a marked safe value — these are not user-controlled input\n const findings: RuleMatch[] = [];\n const re = /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html\\s*:\\s*([^}]+)\\}/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const value = m[1].trim();\n // Skip static constants (UPPER_CASE), string literals, or template strings without interpolation\n if (/^[A-Z][A-Z0-9_]+$/.test(value)) continue;\n if (/^[\"'`][^$]*[\"'`]$/.test(value)) continue;\n // Skip JSON-LD structured data (standard SEO practice, developer-controlled)\n if (/JSON\\.stringify/i.test(value)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC063\", title: dangerousInnerHTML.title,\n severity: \"critical\" as const, category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Sanitize HTML before using dangerouslySetInnerHTML: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}. Install: npm install dompurify\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC064 – Exposed Next.js Server Actions\n// ────────────────────────────────────────────\n\nexport const exposedServerActions: CustomRule = {\n id: \"VC064\",\n title: \"Next.js Server Action Without Auth Check\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Next.js Server Actions ('use server') are publicly callable endpoints. Without authentication checks, anyone can invoke them directly.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n if (!/[\"']use server[\"']/i.test(content)) return [];\n const hasAuth = /getServerSession|auth\\(\\)|currentUser|getUser|requireAuth|requireUser|requireUserForApi|session|clerk|getAuth|verifyCronSecret|checkApiKey/i.test(content);\n if (hasAuth) return [];\n // Check if there are exported async functions (server actions)\n if (/export\\s+async\\s+function/i.test(content)) {\n return findMatches(content, /export\\s+async\\s+function\\s+\\w+/g, exposedServerActions, filePath, () =>\n \"Add authentication to Server Actions: const session = await getServerSession(); if (!session) throw new Error('Unauthorized');\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC065 – Unprotected Next.js API Routes\n// ────────────────────────────────────────────\n\nexport const unprotectedAPIRoutes: CustomRule = {\n id: \"VC065\",\n title: \"Unprotected Next.js API Route\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Next.js API routes under /api/ without authentication middleware can be called by anyone, exposing data or mutations.\",\n check(content, filePath) {\n if (!filePath.match(/\\/api\\/.*\\.(jsx?|tsx?)$/) && !filePath.match(/\\/app\\/api\\/.*route\\.(jsx?|tsx?)$/)) return [];\n // Skip health/public endpoints\n if (/health|status|public|webhook/i.test(filePath)) return [];\n const hasAuth = /getServerSession|auth\\(\\)|currentUser|getUser|requireAuth|session|clerk|getAuth|verifyToken|authenticate|middleware/i.test(content);\n if (hasAuth) return [];\n const hasHandler = /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)|export\\s+default/i.test(content);\n if (hasHandler) {\n return findMatches(content, /export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/g, unprotectedAPIRoutes, filePath, () =>\n \"Add authentication to API routes: const session = await getServerSession(authOptions); if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC066 – Client Component Using Secrets\n// ────────────────────────────────────────────\n\nexport const clientComponentSecret: CustomRule = {\n id: \"VC066\",\n title: \"Secret Used in Client Component\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Using server-side secrets (process.env without NEXT_PUBLIC_) in 'use client' components exposes them in the browser bundle.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n if (!/[\"']use client[\"']/i.test(content)) return [];\n // process.env without NEXT_PUBLIC_ prefix in a client component\n const pattern = /process\\.env\\.(?!NEXT_PUBLIC_)[A-Z_]{3,}/g;\n if (pattern.test(content)) {\n return findMatches(content, /process\\.env\\.(?!NEXT_PUBLIC_)[A-Z_]{3,}/g, clientComponentSecret, filePath, () =>\n \"Server-side env vars are not available in client components and may leak in builds. Move this logic to a Server Component or API route.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC067 – Insecure Deep Link Handling\n// ────────────────────────────────────────────\n\nexport const insecureDeepLink: CustomRule = {\n id: \"VC067\",\n title: \"Insecure Deep Link Handling\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Deep links that navigate or execute actions without validating the URL scheme, host, or parameters can be exploited for phishing or unauthorized actions.\",\n check(content, filePath) {\n if (!/(?:Linking|DeepLinking|deep.?link|handleURL|openURL|url.?scheme)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n // React Native Linking without validation\n const patterns = [\n /Linking\\.addEventListener\\s*\\([^)]*(?:url|link)/gi,\n /Linking\\.getInitialURL\\s*\\(\\)/g,\n /handleOpenURL|handleDeepLink|onDeepLink/gi,\n ];\n const hasValidation = /allowedSchemes|allowedHosts|validateURL|isAllowedURL|whitelist|URL.*hostname.*includes/i.test(content);\n if (hasValidation) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, insecureDeepLink, filePath, () =>\n \"Validate deep link URLs: check the scheme and host against an allowlist before navigating or executing actions.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC068 – Sensitive Data in AsyncStorage\n// ────────────────────────────────────────────\n\nexport const sensitiveAsyncStorage: CustomRule = {\n id: \"VC068\",\n title: \"Sensitive Data in AsyncStorage\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"React Native AsyncStorage is unencrypted. Storing tokens, passwords, or secrets there makes them readable by other apps or anyone with device access.\",\n check(content, filePath) {\n if (!/AsyncStorage/i.test(content)) return [];\n const patterns = [\n /AsyncStorage\\.setItem\\s*\\(\\s*[\"'`](?:token|access_token|auth_token|jwt|session|refresh_token|api_key|password|secret|private_key)/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveAsyncStorage, filePath, () =>\n \"Use react-native-keychain or expo-secure-store instead of AsyncStorage for sensitive data. These use the OS keychain (encrypted).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC069 – Missing Certificate Pinning\n// ────────────────────────────────────────────\n\nexport const missingCertPinning: CustomRule = {\n id: \"VC069\",\n title: \"Missing Certificate Pinning in Mobile App\",\n severity: \"medium\",\n category: \"Cryptography\",\n description: \"Mobile apps without SSL certificate pinning are vulnerable to MITM attacks via compromised or rogue CAs. Pin your API server's certificate.\",\n check(content, filePath) {\n // Only for mobile project files\n if (!/(?:react.native|expo|android|ios|mobile)/i.test(filePath) && !/(?:React.*Native|expo)/i.test(content)) return [];\n if (!/(?:fetch|axios|http|api|request)/i.test(content)) return [];\n // Check for HTTP client setup without pinning\n if (/axios\\.create|new\\s+(?:HttpClient|ApiClient)/i.test(content)) {\n const hasPinning = /pinning|certificate|cert|ssl|TrustKit|react-native-ssl-pinning|cert-pinner/i.test(content);\n if (!hasPinning) {\n return findMatches(content, /axios\\.create|new\\s+(?:HttpClient|ApiClient)/gi, missingCertPinning, filePath, () =>\n \"Add SSL certificate pinning: use react-native-ssl-pinning or TrustKit. This prevents MITM attacks via rogue certificates.\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC070 – Android Debuggable Flag\n// ────────────────────────────────────────────\n\nexport const androidDebuggable: CustomRule = {\n id: \"VC070\",\n title: \"Android App Debuggable in Production\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"android:debuggable='true' in AndroidManifest.xml allows attackers to attach debuggers, inspect memory, and bypass security controls.\",\n check(content, filePath) {\n if (!filePath.match(/AndroidManifest\\.xml$/i)) return [];\n if (/android:debuggable\\s*=\\s*[\"']true[\"']/i.test(content)) {\n return findMatches(content, /android:debuggable\\s*=\\s*[\"']true[\"']/gi, androidDebuggable, filePath, () =>\n \"Remove android:debuggable='true' or set it to false. Debug builds should use build variants, not manifest flags.\"\n );\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC071 – Django DEBUG=True\n// ────────────────────────────────────────────\n\nexport const djangoDebug: CustomRule = {\n id: \"VC071\",\n title: \"Django DEBUG Mode Enabled\",\n severity: \"critical\",\n category: \"Configuration\",\n description: \"Django with DEBUG=True exposes detailed error pages with source code, database queries, environment variables, and installed apps to anyone.\",\n check(content, filePath) {\n if (!filePath.match(/settings\\.py$/i) && !filePath.match(/config.*\\.py$/i)) return [];\n if (/^\\s*DEBUG\\s*=\\s*True\\s*$/m.test(content)) {\n const hasEnvCheck = /os\\.environ|env\\(|config\\(|getenv/i.test(content);\n if (!hasEnvCheck) {\n return findMatches(content, /^\\s*DEBUG\\s*=\\s*True/gm, djangoDebug, filePath, () =>\n \"Use environment variable: DEBUG = os.environ.get('DEBUG', 'False') == 'True'. Never hardcode DEBUG=True.\"\n );\n }\n }\n return [];\n },\n};\n\n// ────────────────────────────────────────────\n// VC072 – Flask Hardcoded Secret Key\n// ────────────────────────────────────────────\n\nexport const flaskSecretKey: CustomRule = {\n id: \"VC072\",\n title: \"Flask Secret Key Hardcoded\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Hardcoded Flask secret_key allows attackers to forge sessions, CSRF tokens, and signed cookies. Must be a random value from environment variables.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const patterns = [\n /(?:app\\.)?secret_key\\s*=\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n /(?:app\\.config)\\s*\\[\\s*[\"']SECRET_KEY[\"']\\s*\\]\\s*=\\s*[\"'`][^\"'`]{3,}[\"'`]/g,\n ];\n const hasEnv = /os\\.environ|env\\(|config\\(|getenv/i.test(content);\n if (hasEnv) return [];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, flaskSecretKey, filePath, () =>\n \"Use environment variable: app.secret_key = os.environ['SECRET_KEY']. Generate with: python -c \\\"import secrets; print(secrets.token_hex(32))\\\"\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC073 – Pickle Deserialization\n// ────────────────────────────────────────────\n\nexport const pickleDeserialization: CustomRule = {\n id: \"VC073\",\n title: \"Unsafe Pickle Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"pickle.loads() on untrusted data allows arbitrary code execution. An attacker can craft a pickle payload that runs system commands on your server.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /pickle\\.loads?\\s*\\(/g,\n /cPickle\\.loads?\\s*\\(/g,\n /shelve\\.open\\s*\\(/g,\n /yaml\\.load\\s*\\([^)]*(?!Loader\\s*=\\s*yaml\\.SafeLoader)/g,\n ];\n const hasSafe = /restricted_loads|SafeUnpickler|safe_load|yaml\\.safe_load/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, pickleDeserialization, filePath, () =>\n \"Never unpickle untrusted data — it allows arbitrary code execution. Use JSON for data exchange, or yaml.safe_load() for YAML.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC074 – Missing CSRF Protection (Django)\n// ────────────────────────────────────────────\n\nexport const missingCSRF: CustomRule = {\n id: \"VC074\",\n title: \"CSRF Protection Disabled\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Using @csrf_exempt on state-changing views (POST/PUT/DELETE) allows attackers to forge requests from other sites, performing actions as authenticated users.\",\n check(content, filePath) {\n if (!filePath.match(/\\.py$/)) return [];\n const matches: RuleMatch[] = [];\n if (/csrf_exempt/i.test(content)) {\n matches.push(...findMatches(content, /@csrf_exempt/g, missingCSRF, filePath, () =>\n \"Remove @csrf_exempt and use proper CSRF tokens. For APIs, use token-based auth (JWT) instead of session cookies.\"\n ));\n }\n // Also check for CsrfViewMiddleware removal. Strip Python comments first\n // so a commented-out line like `# \"...CsrfViewMiddleware\",` doesn't look\n // like the middleware is still active.\n const pythonCodeOnly = content.replace(/^\\s*#.*$/gm, \"\");\n if (/MIDDLEWARE.*=.*\\[/s.test(pythonCodeOnly) && !/CsrfViewMiddleware/i.test(pythonCodeOnly) && /django/i.test(content)) {\n matches.push(...findMatches(content, /MIDDLEWARE\\s*=/g, missingCSRF, filePath, () =>\n \"Re-add 'django.middleware.csrf.CsrfViewMiddleware' to MIDDLEWARE. CSRF protection is essential for session-based auth.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC075 – GitHub Actions Script Injection\n// ────────────────────────────────────────────\n\nexport const githubActionsInjection: CustomRule = {\n id: \"VC075\",\n title: \"GitHub Actions Script Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Using ${{ github.event.* }} directly in 'run:' steps allows attackers to inject shell commands via PR titles, issue bodies, or branch names.\",\n check(content, filePath) {\n // Accept the conventional .github/workflows/*.yml path OR any .yml/.yaml\n // whose content looks like a GitHub Actions workflow. Many projects put\n // reusable workflows in repositories where the containing path doesn't\n // include `.github/workflows/` (e.g. fixture/example workflows, or they\n // get split out).\n const isWorkflowPath = /\\.github\\/workflows\\/.*\\.(ya?ml)$/i.test(filePath);\n const looksLikeWorkflow =\n /\\.(ya?ml)$/i.test(filePath) &&\n /^\\s*on\\s*:/m.test(content) &&\n /^\\s*jobs\\s*:/m.test(content);\n if (!isWorkflowPath && !looksLikeWorkflow) return [];\n\n const matches: RuleMatch[] = [];\n // Direct interpolation in run steps.\n const patterns = [\n /run:.*\\$\\{\\{\\s*github\\.event\\.(?:issue|pull_request|comment|review|head_commit)\\.(?:title|body|message)/gi,\n /run:.*\\$\\{\\{\\s*github\\.event\\.(?:inputs|head_ref|base_ref)/gi,\n // Literal-block `run: |` with user-controllable interpolation on the\n // SAME line or a following indented line. Anchored to `run:` (either\n // on the same line or within the prior few lines before the\n // interpolation) so `env: TITLE: ${{ github.event.issue.title }}` —\n // the recommended-fix shape — doesn't false-positive.\n /(?:^\\s*(?:-\\s+)?run:\\s*\\|?\\s*\\n(?:(?!^\\s*(?:[-]\\s*)?(?:env|with|id|name|if|timeout-minutes|continue-on-error|shell|working-directory|uses):).+\\n){0,20}\\s*.*)?\\$\\{\\{\\s*github\\.event\\.(?:issue|pull_request|comment|review|head_commit)\\.(?:title|body|message|name)/gmi,\n ];\n // Also pre-compute which lines look like `env:` mappings so we can drop\n // matches on those lines — the rule itself recommends `env: TITLE: ...`\n // as the safe fix, and that line must not fire.\n const lines = content.split(\"\\n\");\n const isEnvMappingLine = (lineNum: number): boolean => {\n const lineText = lines[lineNum - 1] ?? \"\";\n // \"env:\" (block start) — safe, skip\n // \"KEY: ${{ ... }}\" inside an `env:` block — safe, skip\n // Walk up to the nearest non-indented key to check containment.\n if (/^\\s*env\\s*:/.test(lineText)) return true;\n const indent = (lineText.match(/^(\\s*)/)?.[1]?.length) ?? 0;\n if (indent === 0) return false;\n for (let i = lineNum - 2; i >= 0; i--) {\n const prev = lines[i] ?? \"\";\n if (!prev.trim()) continue;\n const prevIndent = (prev.match(/^(\\s*)/)?.[1]?.length) ?? 0;\n if (prevIndent < indent) {\n return /^\\s*env\\s*:/.test(prev);\n }\n }\n return false;\n };\n\n for (const p of patterns) {\n for (const m of findMatches(content, p, githubActionsInjection, filePath, () =>\n \"Never use ${{ github.event.* }} directly in 'run:'. Pass it as an environment variable: env: TITLE: ${{ github.event.issue.title }} then use $TITLE in the script.\"\n )) {\n if (isEnvMappingLine(m.line)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC076 – Secrets in CI Config\n// ────────────────────────────────────────────\n\nexport const secretsInCI: CustomRule = {\n id: \"VC076\",\n title: \"Hardcoded Secrets in CI/CD Config\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Hardcoded tokens, passwords, or API keys in CI/CD workflow files are visible to anyone with repo access. Use encrypted secrets instead.\",\n check(content, filePath) {\n if (!filePath.match(/\\.github\\/workflows\\/|\\.gitlab-ci|Jenkinsfile|\\.circleci|bitbucket-pipelines/i)) return [];\n const matches: RuleMatch[] = [];\n // Hardcoded values that look like secrets (not using ${{ secrets.* }})\n const patterns = [\n /(?:password|token|key|secret|api_key|apikey)\\s*[:=]\\s*[\"'`][A-Za-z0-9+/=_-]{20,}[\"'`]/gi,\n /(?:DOCKER_PASSWORD|NPM_TOKEN|AWS_SECRET_ACCESS_KEY|GH_TOKEN|GITHUB_TOKEN)\\s*[:=]\\s*[\"'`][^\"'`$]+[\"'`]/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, secretsInCI, filePath, () =>\n \"Use repository secrets: ${{ secrets.MY_TOKEN }} (GitHub) or CI/CD variable settings. Never hardcode credentials in workflow files.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC077 – CORS Wildcard in Serverless Config\n// ────────────────────────────────────────────\n\nexport const corsServerless: CustomRule = {\n id: \"VC077\",\n title: \"CORS Wildcard in Serverless Config\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Setting Access-Control-Allow-Origin: * in serverless.yml, vercel.json, or similar configs allows any website to make authenticated requests to your API.\",\n check(content, filePath) {\n if (!filePath.match(/serverless\\.(yml|yaml)|vercel\\.json|netlify\\.toml|amplify\\.yml/i)) return [];\n const matches: RuleMatch[] = [];\n // Inline single-line: access-control-allow-origin: \"*\"\n if (/(?:Access-Control-Allow-Origin|allowOrigin|cors).*['\"]\\*['\"]/i.test(content)) {\n matches.push(...findMatches(content, /(?:Access-Control-Allow-Origin|allowOrigin|cors).*['\"]\\*['\"]/gi, corsServerless, filePath, () =>\n \"Replace wildcard CORS with specific origins: allowOrigin: ['https://yourdomain.com']. Wildcard allows any site to call your API.\"\n ));\n }\n // YAML nested-map shape:\n // cors:\n // origin: \"*\"\n // The .*['\"]\\*['\"] pattern above can't span the newline. Catch it here\n // with an origin-key match instead; serverless.yml / vercel.json / etc.\n // are the only files this rule runs on, so the false-positive surface is small.\n if (/^\\s*origin\\s*:\\s*['\"]\\*['\"]/m.test(content)) {\n matches.push(...findMatches(content, /^\\s*origin\\s*:\\s*['\"]\\*['\"]/gim, corsServerless, filePath, () =>\n \"Replace the wildcard origin with a concrete allowlist: `origin: https://yourdomain.com` (or an array). Combined with `allowCredentials: true`, wildcard CORS is rejected by the browser; combined with auth headers, it's a data-exfiltration surface.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC078 – Kubernetes Privileged Container\n// ────────────────────────────────────────────\n\nexport const k8sPrivileged: CustomRule = {\n id: \"VC078\",\n title: \"Kubernetes Privileged Container\",\n severity: \"critical\",\n category: \"Configuration\",\n description: \"Running containers with privileged: true or as root in Kubernetes gives full host access, making container escapes trivial.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/) || !/(?:kind|apiVersion|container)/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /privileged\\s*:\\s*true/g,\n /runAsUser\\s*:\\s*0\\b/g,\n /runAsNonRoot\\s*:\\s*false/g,\n /allowPrivilegeEscalation\\s*:\\s*true/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, k8sPrivileged, filePath, () =>\n \"Set securityContext: { privileged: false, runAsNonRoot: true, allowPrivilegeEscalation: false }. Never run containers as root in production.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// FRAMEWORK DETECTION\n// ────────────────────────────────────────────\n\nexport type DetectedFramework = \"next.js\" | \"react\" | \"react-native\" | \"express\" | \"hono\" | \"fastify\" | \"django\" | \"flask\" | \"electron\" | \"vue\" | \"svelte\" | \"unknown\";\n\nexport function detectFramework(files: { path: string; content: string }[]): DetectedFramework[] {\n const frameworks: Set<DetectedFramework> = new Set();\n for (const { path, content } of files) {\n if (path.match(/next\\.config/i) || /from\\s+[\"']next/i.test(content)) frameworks.add(\"next.js\");\n if (/from\\s+[\"']react-native/i.test(content) || path.match(/react-native\\.config/i)) frameworks.add(\"react-native\");\n else if (/from\\s+[\"']react/i.test(content) || /import\\s+React/i.test(content)) frameworks.add(\"react\");\n if (/from\\s+[\"']express/i.test(content) || /require\\s*\\(\\s*[\"']express/i.test(content)) frameworks.add(\"express\");\n if (/from\\s+[\"']hono/i.test(content)) frameworks.add(\"hono\");\n if (/from\\s+[\"']fastify/i.test(content)) frameworks.add(\"fastify\");\n if (/from\\s+[\"']electron/i.test(content) || path.match(/electron/i)) frameworks.add(\"electron\");\n if (path.match(/settings\\.py$/) || /from\\s+django/i.test(content)) frameworks.add(\"django\");\n if (/from\\s+flask/i.test(content) || /Flask\\s*\\(/i.test(content)) frameworks.add(\"flask\");\n if (/from\\s+[\"']vue/i.test(content) || path.match(/vue\\.config/i)) frameworks.add(\"vue\");\n if (/from\\s+[\"']svelte/i.test(content) || path.match(/svelte\\.config/i)) frameworks.add(\"svelte\");\n }\n if (frameworks.size === 0) frameworks.add(\"unknown\");\n return [...frameworks];\n}\n\n// ────────────────────────────────────────────\n// SEVERITY GRADING\n// ────────────────────────────────────────────\n\nexport type SecurityGrade = \"A+\" | \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n\nexport interface GradeResult {\n grade: SecurityGrade;\n score: number;\n summary: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function calculateGrade(findings: Finding[], _totalFiles: number): GradeResult {\n if (findings.length === 0) {\n return { grade: \"A+\", score: 100, summary: \"No security issues detected. Excellent.\" };\n }\n\n // Single source of truth for severity weights. Matches the dashboard\n // scoring modal and the GitHub Action action.yml so all three surfaces\n // produce the same grade for the same input.\n let critical = 0, high = 0, medium = 0, low = 0;\n for (const f of findings) {\n if (f.severity === \"critical\") critical++;\n else if (f.severity === \"high\") high++;\n else if (f.severity === \"medium\") medium++;\n else if (f.severity === \"low\") low++;\n }\n\n const deductions = critical * 15 + high * 7 + medium * 3 + low * 1;\n const rawScore = Math.max(0, 100 - deductions);\n\n // Score-based grade thresholds\n let grade: SecurityGrade;\n if (rawScore >= 97) grade = \"A+\";\n else if (rawScore >= 90) grade = \"A\";\n else if (rawScore >= 80) grade = \"B\";\n else if (rawScore >= 70) grade = \"C\";\n else if (rawScore >= 60) grade = \"D\";\n else grade = \"F\";\n\n // Severity caps — a scan with serious findings cannot earn an A regardless\n // of what the score math says. Prevents the 'strong security with minor\n // concerns' bug where 2 High findings were rated A.\n const capGrade = (cap: SecurityGrade): SecurityGrade => {\n const order: SecurityGrade[] = [\"A+\", \"A\", \"B\", \"C\", \"D\", \"F\"];\n return order.indexOf(grade) < order.indexOf(cap) ? cap : grade;\n };\n if (critical >= 1) grade = capGrade(\"D\"); // any critical → max D\n else if (high >= 3) grade = capGrade(\"D\"); // 3+ high → max D\n else if (high >= 1) grade = capGrade(\"B\"); // 1–2 high → max B\n else if (medium >= 5) grade = capGrade(\"B\"); // 5+ medium → max B\n else if (medium >= 1) grade = capGrade(\"A\"); // any medium → max A (not A+)\n\n // Summary references what's actually wrong, not just the score bucket.\n let summary: string;\n if (critical > 0) {\n summary = `${critical} critical ${critical === 1 ? \"vulnerability requires\" : \"vulnerabilities require\"} immediate attention.`;\n } else if (high >= 3) {\n summary = `${high} high-severity issues require urgent attention.`;\n } else if (high > 0) {\n summary = `${high} high-severity ${high === 1 ? \"issue needs\" : \"issues need\"} attention.`;\n } else if (medium >= 5) {\n summary = `${medium} medium-severity issues to address.`;\n } else if (medium > 0) {\n summary = `Clean of critical and high issues. ${medium} medium-severity ${medium === 1 ? \"issue\" : \"issues\"} to review.`;\n } else if (low > 0) {\n summary = `Clean of critical, high, and medium issues. ${low} low-severity best-practice ${low === 1 ? \"note\" : \"notes\"}.`;\n } else {\n summary = \"No security issues detected.\";\n }\n\n return { grade, score: rawScore, summary };\n}\n\n// ────────────────────────────────────────────\n// VC079 – JWT Algorithm Confusion\n// ────────────────────────────────────────────\n\nexport const jwtAlgConfusion: CustomRule = {\n id: \"VC079\",\n title: \"JWT Algorithm Confusion (alg:none)\",\n severity: \"critical\",\n category: \"Authentication\",\n description: \"Accepting 'none' as a JWT algorithm allows attackers to forge tokens by removing the signature entirely.\",\n check(content, filePath) {\n if (!/jwt|jsonwebtoken|jose/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /algorithms\\s*:\\s*\\[.*[\"']none[\"']/gi,\n /algorithm\\s*[:=]\\s*[\"']none[\"']/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, jwtAlgConfusion, filePath, () =>\n \"Never allow algorithm 'none'. Explicitly specify: algorithms: ['RS256'] or algorithms: ['HS256']. Reject tokens with alg:none.\"\n ));\n }\n // Also check for missing algorithm restriction\n if (/jwt\\.verify\\s*\\([^)]*\\)\\s*(?!.*algorithms)/i.test(content) && !/algorithms/i.test(content)) {\n matches.push(...findMatches(content, /jwt\\.verify\\s*\\(/g, jwtAlgConfusion, filePath, () =>\n \"Specify allowed algorithms in jwt.verify: jwt.verify(token, secret, { algorithms: ['HS256'] }).\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC080 – Regex DoS (ReDoS)\n// ────────────────────────────────────────────\n\nexport const regexDos: CustomRule = {\n id: \"VC080\",\n title: \"Potential Regular Expression DoS (ReDoS)\",\n severity: \"high\",\n category: \"Availability\",\n description: \"Nested quantifiers like (a+)+ or (a*){2,} cause catastrophic backtracking, allowing attackers to freeze your server with crafted input.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n // Detect nested quantifiers in regex\n const patterns = [\n /new\\s+RegExp\\s*\\(\\s*[\"'`].*\\([^)]*[+*]\\)[+*{]/g,\n /\\/.*\\([^)]*[+*]\\)[+*{].*\\//g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, regexDos, filePath, () =>\n \"Avoid nested quantifiers in regex. Use atomic groups, possessive quantifiers, or the 're2' library for safe regex execution.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC081 – XML External Entity (XXE)\n// ────────────────────────────────────────────\n\nexport const xxeVulnerability: CustomRule = {\n id: \"VC081\",\n title: \"XML External Entity (XXE) Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"XML parsers that process external entities allow attackers to read files, perform SSRF, or cause DoS via billion-laughs attacks.\",\n check(content, filePath) {\n if (!/xml|parseXml|parseXML|DOMParser|SAXParser|etree|lxml|libxml/i.test(content)) return [];\n const matches: RuleMatch[] = [];\n\n // --- Regex layer: broad XML parser patterns, skipped if explicit protection present ---\n const patterns = [\n /\\.parseXm?l\\s*\\(/gi, // catches parseXml (libxmljs) AND parseXML\n /new\\s+DOMParser\\s*\\(\\)/g,\n /etree\\.parse\\s*\\(/g,\n /lxml\\.etree/g,\n /SAXParserFactory/g,\n /XMLReaderFactory/g,\n ];\n const hasProtection = /noent\\s*:\\s*false|resolveExternals\\s*:\\s*false|FEATURE_EXTERNAL.*false|defusedxml|disallow-doctype-decl|nonet\\s*:\\s*true/i.test(content);\n if (!hasProtection) {\n for (const p of patterns) {\n matches.push(...findMatches(content, p, xxeVulnerability, filePath, () =>\n \"Disable external entities: set noent: false + dtdload: false + nonet: true (libxmljs), or use defusedxml (Python), or factory.setFeature('http://apache.org/xml/features/disallow-doctype-decl', true) (Java).\"\n ));\n }\n }\n\n // --- AST layer: explicit dangerous options on libxmljs parseXml ---\n // libxml.parseXml(xml, { noent: true, dtdload: true }) — flag even if the\n // file also has safe-looking patterns nearby (e.g., imports).\n if (!/parseXml\\s*\\(/.test(content)) return matches;\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n\n visitCalls(\n ctx.parsed,\n (callee: Node) => isCalleeNamed(callee, \"parseXml\") || isCalleeNamed(callee, \"parseXML\"),\n (call, line) => {\n const opts = call.arguments[1];\n if (!opts || opts.type !== \"ObjectExpression\") return;\n const noent = getObjectProperty(opts, \"noent\");\n const dtdload = getObjectProperty(opts, \"dtdload\");\n const external = getObjectProperty(opts, \"resolveExternals\");\n const dangerous =\n (noent?.value.type === \"BooleanLiteral\" && noent.value.value === true) ||\n (dtdload?.value.type === \"BooleanLiteral\" && dtdload.value.value === true) ||\n (external?.value.type === \"BooleanLiteral\" && external.value.value === true);\n if (!dangerous) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n xxeVulnerability,\n \"Set noent: false, dtdload: false, and nonet: true on the parser options to disable external-entity resolution.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC082 – Server-Side Template Injection\n// ────────────────────────────────────────────\n\nexport const ssti: CustomRule = {\n id: \"VC082\",\n title: \"Server-Side Template Injection (SSTI)\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Rendering templates from user-controlled strings allows attackers to execute arbitrary code on the server.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n\n // --- Regex layer: inline user input to template render functions ---\n const patterns = [\n /render_template_string\\s*\\(\\s*(?![\"'`])/g,\n /Template\\s*\\(\\s*(?:req\\.|body\\.|input|params|args|user)/gi,\n /engine\\.render\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n /nunjucks\\.renderString\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n /ejs\\.render\\s*\\(\\s*(?:req\\.|body\\.|input)/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, ssti, filePath, () =>\n \"Never render templates from user input. Use pre-defined templates and pass data as context variables: render_template('template.html', data=user_data).\"\n ));\n }\n\n // --- AST layer: template compile/render with a tainted template string ---\n if (!/(?:\\.compile|\\.render|renderString|render_template_string)\\s*\\(/.test(content)) {\n return matches;\n }\n const ctx = tryParse(content, filePath);\n if (!ctx) return matches;\n const { parsed, taint } = ctx;\n\n const SSTI_METHODS = new Set([\n \"compile\", // Handlebars.compile, pug.compile, _.template (returns a function)\n \"render\", // ejs.render, engine.render, mustache.render\n \"renderString\", // nunjucks.renderString\n \"render_template_string\",\n ]);\n\n visitCalls(\n parsed,\n (callee: Node) => {\n if (callee.type === \"Identifier\" && SSTI_METHODS.has(callee.name)) return true;\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n return SSTI_METHODS.has(callee.property.name);\n }\n return false;\n },\n (call, line) => {\n const first = call.arguments[0];\n if (!first || first.type === \"SpreadElement\") return;\n if (!taint.isTainted(first as Node)) return;\n if (matches.some((m) => m.line === line)) return;\n matches.push(\n astMatch(\n content,\n filePath,\n line,\n ssti,\n \"Compile templates from a trusted, static source (a file path or a constant string). Pass user data only as context values.\",\n ),\n );\n },\n );\n\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC083 – Insecure Java Deserialization\n// ────────────────────────────────────────────\n\nexport const javaDeserialization: CustomRule = {\n id: \"VC083\",\n title: \"Insecure Java Deserialization\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"ObjectInputStream.readObject() on untrusted data allows arbitrary code execution via gadget chains in the classpath.\",\n check(content, filePath) {\n if (!filePath.match(/\\.java$|\\.kt$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /ObjectInputStream\\s*\\(/g,\n /\\.readObject\\s*\\(\\)/g,\n /XMLDecoder\\s*\\(/g,\n /XStream\\s*\\(\\)/g,\n ];\n const hasSafe = /ValidatingObjectInputStream|ObjectInputFilter|SerialKiller|NotSerializableException/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, javaDeserialization, filePath, () =>\n \"Use ValidatingObjectInputStream with an allowlist of classes, or avoid Java serialization entirely. Use JSON instead.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC084 – Missing Subresource Integrity (SRI)\n// ────────────────────────────────────────────\n\nexport const missingSRI: CustomRule = {\n id: \"VC084\",\n title: \"Missing Subresource Integrity (SRI)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"External scripts and stylesheets loaded without integrity= attributes can be tampered with if the CDN is compromised.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(html|htm|jsx|tsx|ejs|hbs)$/)) return [];\n const matches: RuleMatch[] = [];\n // Script tags with external src but no integrity\n const scriptPattern = /<script\\s+[^>]*src\\s*=\\s*[\"']https?:\\/\\/[^\"']+[\"'][^>]*>/gi;\n let m: RegExpExecArray | null;\n const re = new RegExp(scriptPattern.source, scriptPattern.flags);\n while ((m = re.exec(content)) !== null) {\n if (!m[0].includes(\"integrity\")) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC084\", title: missingSRI.title, severity: \"medium\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: 'Add integrity and crossorigin attributes: <script src=\"...\" integrity=\"sha384-...\" crossorigin=\"anonymous\">'\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC085 – Exposed Admin/Debug Routes\n// ────────────────────────────────────────────\n\nexport const exposedAdminRoutes: CustomRule = {\n id: \"VC085\",\n title: \"Exposed Admin or Debug Route\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Routes like /admin, /debug, /phpinfo, or /actuator without authentication expose sensitive controls and information to attackers.\",\n check(content, filePath) {\n if (!isServerSideFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /[.'\"]\\s*(?:get|use|all)\\s*\\(\\s*[\"'`]\\/(?:admin|debug|_debug|__debug__|phpinfo|actuator|graphiql|playground|swagger|reset|seed|test|dev|mock)[\"'`]/gi,\n ];\n const hasAuth = /auth|requireAuth|isAdmin|requireAdmin|authenticate|middleware.*admin|requireUser/i.test(content);\n if (hasAuth) return [];\n // Skip if there's a dev-only guard\n if (/NODE_ENV\\s*!==\\s*[\"']development[\"']|NODE_ENV\\s*===\\s*[\"']production[\"']/i.test(content)) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, exposedAdminRoutes, filePath, () =>\n \"Protect admin/debug routes with authentication middleware. In production, disable debug endpoints entirely.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC086 – Insecure WebSocket\n// ────────────────────────────────────────────\n\nexport const insecureWebSocket: CustomRule = {\n id: \"VC086\",\n title: \"Insecure WebSocket Connection (ws://)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Using ws:// instead of wss:// transmits data in plaintext, vulnerable to eavesdropping and man-in-the-middle attacks.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n return findMatches(content, /new\\s+WebSocket\\s*\\(\\s*[\"'`]ws:\\/\\//g, insecureWebSocket, filePath, () =>\n \"Use wss:// (WebSocket Secure) instead of ws:// for encrypted connections: new WebSocket('wss://...').\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC087 – Missing HSTS\n// ────────────────────────────────────────────\n\nexport const missingHSTS: CustomRule = {\n id: \"VC087\",\n title: \"Missing HTTP Strict Transport Security (HSTS)\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Without HSTS headers, browsers allow downgrade attacks from HTTPS to HTTP, exposing traffic to interception.\",\n check(content, filePath) {\n if (!/(?:server|app|index|main)\\.[jt]sx?$/.test(filePath)) return [];\n if (!/(?:express|hono|fastify|koa)/i.test(content)) return [];\n if (/Strict-Transport-Security|hsts|helmet/i.test(content)) return [];\n return findMatches(content, /(?:express|hono|fastify|koa)\\s*\\(/gi, missingHSTS, filePath, () =>\n \"Add HSTS header: res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'). Or use helmet().\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC088 – Sensitive Data in URL Parameters\n// ────────────────────────────────────────────\n\nexport const sensitiveURLParams: CustomRule = {\n id: \"VC088\",\n title: \"Sensitive Data in URL Parameters\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Passing passwords, tokens, or API keys in URL query parameters exposes them in server logs, browser history, and referrer headers.\",\n check(content, filePath) {\n // Only scan code files, never docs/README/examples/configs\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (isTestFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Template literals embedding sensitive values in URL query strings\n /[?&](?:password|passwd|pwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|session(?:[_-]?id)?|token|jwt|ssn|credit[_-]?card)\\s*=\\s*\\$\\{/gi,\n // String concat building the same\n /[?&](?:password|passwd|pwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|session(?:[_-]?id)?|token|jwt|ssn|credit[_-]?card)\\s*=[\"']\\s*\\+/gi,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, sensitiveURLParams, filePath, () =>\n \"Never pass sensitive data in URL parameters. Use request headers (Authorization: Bearer ...) or POST body instead.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC089 – Missing Content-Disposition\n// ────────────────────────────────────────────\n\nexport const missingContentDisposition: CustomRule = {\n id: \"VC089\",\n title: \"File Download Missing Content-Disposition\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"File download endpoints without Content-Disposition headers may render files inline, leading to XSS if the file contains HTML/JS.\",\n check(content, filePath) {\n if (!/(?:download|sendFile|send_file|pipe|createReadStream)/i.test(content)) return [];\n if (!isServerSideFile(filePath)) return [];\n if (/Content-Disposition|attachment|download/i.test(content)) return [];\n return findMatches(content, /(?:sendFile|send_file|createReadStream|\\.pipe)\\s*\\(/gi, missingContentDisposition, filePath, () =>\n \"Set Content-Disposition: attachment header on file downloads: res.setHeader('Content-Disposition', 'attachment; filename=\\\"file.pdf\\\"').\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC090 – Open Redirect via Host Header\n// ────────────────────────────────────────────\n\nexport const hostHeaderRedirect: CustomRule = {\n id: \"VC090\",\n title: \"Open Redirect via Host Header\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Using req.headers.host to construct redirect URLs allows attackers to inject a malicious host header, redirecting users to phishing sites.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n if (/req\\.headers\\.host|req\\.get\\s*\\(\\s*[\"']host[\"']\\)/i.test(content) && /redirect|location/i.test(content)) {\n matches.push(...findMatches(content, /req\\.headers\\.host|req\\.get\\s*\\(\\s*[\"']host[\"']\\)/gi, hostHeaderRedirect, filePath, () =>\n \"Don't use req.headers.host for redirects — it's attacker-controlled. Use a hardcoded domain or environment variable.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC091 – Race Condition / TOCTOU\n// ────────────────────────────────────────────\n\nexport const raceCondition: CustomRule = {\n id: \"VC091\",\n title: \"Potential Race Condition (TOCTOU)\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Check-then-act patterns (e.g., checking if a file exists then writing to it) are vulnerable to race conditions where state changes between the check and the action.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Check-then-act with writes / deletes / renames\n /(?:existsSync|exists)\\s*\\([^)]+\\)[\\s\\S]{0,80}(?:writeFileSync|writeFile|unlinkSync|unlink|renameSync|rename)\\s*\\(/g,\n // Check-then-read — also vulnerable: an attacker can swap the symlink\n // between the exists/stat call and the read.\n /(?:existsSync|statSync)\\s*\\([^)]+\\)[\\s\\S]{0,80}(?:readFileSync|readFile|createReadStream)\\s*\\(/g,\n /os\\.path\\.exists\\s*\\([^)]+\\)[\\s\\S]{0,80}open\\s*\\(/g,\n /File\\.exists\\?\\s*\\([^)]+\\)[\\s\\S]{0,80}File\\.(?:write|delete|read)/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, raceCondition, filePath, () =>\n \"Use atomic operations instead of check-then-act. For files: use fs.open with 'wx' flag (exclusive create), or use file locks.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC092 – Unsafe Object.assign from User Input\n// ────────────────────────────────────────────\n\nexport const unsafeObjectAssign: CustomRule = {\n id: \"VC092\",\n title: \"Unsafe Object Spread from User Input\",\n severity: \"medium\",\n category: \"Injection\",\n description: \"Spreading request body into a new object can copy __proto__, constructor, or other dangerous properties, enabling prototype pollution.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\")) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /Object\\.assign\\s*\\(\\s*\\{\\s*\\}\\s*,\\s*(?:req\\.(?:body|query|params)|body|input)\\s*\\)/gi,\n /\\{\\s*\\.\\.\\.(?:req\\.(?:body|query|params)|body|input)\\s*\\}/gi,\n ];\n const hasSafe = /omit.*__proto__|sanitize|pick\\(|lodash\\.pick|stripProto/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, unsafeObjectAssign, filePath, () =>\n \"Explicitly pick allowed properties instead of spreading: const { name, email } = req.body; const safe = { name, email };\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC093 – Unprotected File Download Endpoint\n// ────────────────────────────────────────────\n\nexport const unprotectedDownload: CustomRule = {\n id: \"VC093\",\n title: \"File Download Without Path Validation\",\n severity: \"medium\",\n category: \"Authorization\",\n description: \"File download endpoints that accept user-controlled filenames without path validation allow directory traversal to read arbitrary files.\",\n check(content, filePath) {\n if (!/(?:download|sendFile|send_file)/i.test(content)) return [];\n if (!isServerSideFile(filePath)) return [];\n const matches: RuleMatch[] = [];\n // sendFile/download with user input\n if (/(?:sendFile|download|send_file)\\s*\\([^)]*(?:req\\.|params\\.|query\\.|body\\.)/i.test(content)) {\n const hasValidation = /path\\.resolve|path\\.normalize|path\\.join.*__dirname|realpath|includes\\s*\\(\\s*[\"']\\.\\./i.test(content);\n if (!hasValidation) {\n matches.push(...findMatches(content, /(?:sendFile|download|send_file)\\s*\\(/gi, unprotectedDownload, filePath, () =>\n \"Validate file paths: const safePath = path.resolve(DOWNLOAD_DIR, filename); if (!safePath.startsWith(DOWNLOAD_DIR)) throw new Error('Invalid path');\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC094 – Command Injection\n// ────────────────────────────────────────────\n\nexport const commandInjection: CustomRule = {\n id: \"VC094\",\n title: \"Potential Command Injection\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"Passing user input to shell commands (exec, system, child_process) allows attackers to execute arbitrary system commands.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb)$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n // Node.js — require standalone exec/execSync, not db.exec() or conn.exec()\n /(?<![.\\w])(?:exec|execSync)\\s*\\(\\s*(?:`[^`]*\\$\\{|[\"'][^\"']*\\+\\s*(?:req\\.|body\\.|input|params|args|user))/gi,\n /child_process.*exec\\s*\\(\\s*(?![\"'`])/g,\n // spawn / execFile / exec with shell: true AND a template literal\n // first arg. `shell: true` converts the first argument into a string\n // passed to `/bin/sh -c`, so any ${} interpolation is a shell injection\n // opportunity. Without shell: true, spawn/execFile are safe because\n // the command and args are kept separate. Previously this class of\n // bug was missed — the \"hasSafe\" skip below assumed any spawn in the\n // file was fine, which is the opposite of true with shell: true.\n /(?<![.\\w])(?:spawn|spawnSync|execFile|execFileSync|exec|execSync)\\s*\\(\\s*`[^`]*\\$\\{[\\s\\S]*?shell\\s*:\\s*(?:true|[\"'][^\"']+[\"'])/gi,\n // Python\n /os\\.system\\s*\\(\\s*(?![\"'`].*[\"'`]\\s*\\))/g,\n /subprocess\\.(?:call|run|Popen)\\s*\\([^)]*shell\\s*=\\s*True/gi,\n // Ruby\n /system\\s*\\(\\s*[\"'].*#\\{/g,\n ];\n // Honor known-safe helpers only when shell: true is NOT present. Once\n // shell:true is in the file the spawn \"safety\" is gone, so we can't\n // early-exit on its presence.\n const hasShellTrue = /shell\\s*:\\s*(?:true|[\"'][^\"']+[\"'])/i.test(content);\n const hasSafe = !hasShellTrue && /execFile|spawn|escapeshellarg|shlex\\.quote|shellEscape/i.test(content);\n if (hasSafe) return [];\n for (const p of patterns) {\n const raw = findMatches(content, p, commandInjection, filePath, () =>\n \"Use execFile/spawn instead of exec (avoids shell). Never concatenate user input into shell commands. Use parameterized arguments.\"\n );\n // Filter out database .exec() calls (better-sqlite3, knex, sequelize, etc.)\n for (const m of raw) {\n const lineText = content.split(\"\\n\")[m.line - 1] || \"\";\n if (/(?:conn|db|database|knex|sequelize|prisma|sqlite|pool|client)\\s*\\.exec/i.test(lineText)) continue;\n if (/ALTER\\s+TABLE|CREATE\\s+TABLE|DROP\\s+TABLE|INSERT\\s+INTO/i.test(lineText)) continue;\n matches.push(m);\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC095 – Hardcoded CORS Origin Localhost\n// ────────────────────────────────────────────\n\nexport const corsLocalhost: CustomRule = {\n id: \"VC095\",\n title: \"Hardcoded Localhost CORS Origin\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Hardcoded localhost CORS origins in production code allow any local process to make authenticated requests to your API.\",\n check(content, filePath) {\n if (filePath.includes(\"test\") || filePath.includes(\"mock\") || filePath.includes(\".env\")) return [];\n if (!/origin/i.test(content)) return [];\n return findMatches(content, /origin\\s*[:=]\\s*[\"'`]http:\\/\\/localhost/gi, corsLocalhost, filePath, () =>\n \"Use environment variables for CORS origins: origin: process.env.ALLOWED_ORIGIN. Remove localhost from production configs.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC096 – Unencrypted gRPC\n// ────────────────────────────────────────────\n\nexport const insecureGRPC: CustomRule = {\n id: \"VC096\",\n title: \"Unencrypted gRPC Channel\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Using insecure gRPC channels transmits data including credentials in plaintext.\",\n check(content, filePath) {\n if (!/grpc/i.test(content)) return [];\n return findMatches(content, /(?:insecure_channel|createInsecure|grpc\\.Insecure)/gi, insecureGRPC, filePath, () =>\n \"Use encrypted gRPC channels: grpc.ssl_channel_credentials() or grpc.credentials.createSsl().\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// COMPLIANCE MAPPING (OWASP Top 10 + CWE)\n// ────────────────────────────────────────────\n\nexport const complianceMap: Record<string, { owasp: string; cwe: string }> = {\n VC001: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC002: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC003: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC004: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC005: { owasp: \"A08:2021\", cwe: \"CWE-345\" },\n VC006: { owasp: \"A03:2021\", cwe: \"CWE-89\" },\n VC007: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC008: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC009: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC010: { owasp: \"A01:2021\", cwe: \"CWE-602\" },\n VC011: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC012: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC013: { owasp: \"A01:2021\", cwe: \"CWE-269\" },\n VC014: { owasp: \"A05:2021\", cwe: \"CWE-538\" },\n VC015: { owasp: \"A03:2021\", cwe: \"CWE-95\" },\n VC016: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC017: { owasp: \"A05:2021\", cwe: \"CWE-614\" },\n VC018: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC019: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC020: { owasp: \"A05:2021\", cwe: \"CWE-1021\" },\n VC021: { owasp: \"A01:2021\", cwe: \"CWE-22\" },\n VC022: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC023: { owasp: \"A08:2021\", cwe: \"CWE-1321\" },\n VC024: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC025: { owasp: \"A03:2021\", cwe: \"CWE-22\" },\n VC026: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC027: { owasp: \"A05:2021\", cwe: \"CWE-693\" },\n VC028: { owasp: \"A07:2021\", cwe: \"CWE-20\" },\n VC029: { owasp: \"A08:2021\", cwe: \"CWE-20\" },\n VC030: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC031: { owasp: \"A02:2021\", cwe: \"CWE-321\" },\n VC032: { owasp: \"A05:2021\", cwe: \"CWE-319\" },\n VC033: { owasp: \"A05:2021\", cwe: \"CWE-215\" },\n VC034: { owasp: \"A02:2021\", cwe: \"CWE-338\" },\n VC035: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC036: { owasp: \"A04:2021\", cwe: \"CWE-755\" },\n VC037: { owasp: \"A09:2021\", cwe: \"CWE-209\" },\n VC038: { owasp: \"A04:2021\", cwe: \"CWE-434\" },\n VC039: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC040: { owasp: \"A05:2021\", cwe: \"CWE-538\" },\n VC041: { owasp: \"A10:2021\", cwe: \"CWE-918\" },\n VC042: { owasp: \"A01:2021\", cwe: \"CWE-915\" },\n VC043: { owasp: \"A02:2021\", cwe: \"CWE-208\" },\n VC044: { owasp: \"A09:2021\", cwe: \"CWE-117\" },\n VC045: { owasp: \"A07:2021\", cwe: \"CWE-521\" },\n VC046: { owasp: \"A07:2021\", cwe: \"CWE-384\" },\n VC047: { owasp: \"A07:2021\", cwe: \"CWE-307\" },\n VC048: { owasp: \"A03:2021\", cwe: \"CWE-943\" },\n VC049: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC050: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC051: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC052: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC053: { owasp: \"A05:2021\", cwe: \"CWE-798\" },\n VC054: { owasp: \"A07:2021\", cwe: \"CWE-922\" },\n VC055: { owasp: \"A05:2021\", cwe: \"CWE-540\" },\n VC056: { owasp: \"A05:2021\", cwe: \"CWE-1021\" },\n VC057: { owasp: \"A01:2021\", cwe: \"CWE-269\" },\n VC058: { owasp: \"A05:2021\", cwe: \"CWE-250\" },\n VC059: { owasp: \"A05:2021\", cwe: \"CWE-284\" },\n VC060: { owasp: \"A02:2021\", cwe: \"CWE-328\" },\n VC061: { owasp: \"A02:2021\", cwe: \"CWE-295\" },\n VC062: { owasp: \"A02:2021\", cwe: \"CWE-321\" },\n VC063: { owasp: \"A03:2021\", cwe: \"CWE-79\" },\n VC064: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC065: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC066: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC067: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC068: { owasp: \"A07:2021\", cwe: \"CWE-922\" },\n VC069: { owasp: \"A02:2021\", cwe: \"CWE-295\" },\n VC070: { owasp: \"A05:2021\", cwe: \"CWE-489\" },\n VC071: { owasp: \"A05:2021\", cwe: \"CWE-215\" },\n VC072: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC073: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC074: { owasp: \"A01:2021\", cwe: \"CWE-352\" },\n VC075: { owasp: \"A03:2021\", cwe: \"CWE-78\" },\n VC076: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC077: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC078: { owasp: \"A05:2021\", cwe: \"CWE-250\" },\n VC079: { owasp: \"A02:2021\", cwe: \"CWE-327\" },\n VC080: { owasp: \"A04:2021\", cwe: \"CWE-1333\" },\n VC081: { owasp: \"A03:2021\", cwe: \"CWE-611\" },\n VC082: { owasp: \"A03:2021\", cwe: \"CWE-94\" },\n VC083: { owasp: \"A08:2021\", cwe: \"CWE-502\" },\n VC084: { owasp: \"A06:2021\", cwe: \"CWE-353\" },\n VC085: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC086: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC087: { owasp: \"A05:2021\", cwe: \"CWE-311\" },\n VC088: { owasp: \"A07:2021\", cwe: \"CWE-598\" },\n VC089: { owasp: \"A05:2021\", cwe: \"CWE-430\" },\n VC090: { owasp: \"A01:2021\", cwe: \"CWE-601\" },\n VC091: { owasp: \"A04:2021\", cwe: \"CWE-367\" },\n VC092: { owasp: \"A08:2021\", cwe: \"CWE-1321\" },\n VC093: { owasp: \"A01:2021\", cwe: \"CWE-22\" },\n VC094: { owasp: \"A03:2021\", cwe: \"CWE-78\" },\n VC095: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC096: { owasp: \"A02:2021\", cwe: \"CWE-319\" },\n VC097: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC098: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC099: { owasp: \"A04:2021\", cwe: \"CWE-401\" },\n VC100: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC101: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC102: { owasp: \"A04:2021\", cwe: \"CWE-400\" },\n VC103: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC104: { owasp: \"A04:2021\", cwe: \"CWE-390\" },\n VC105: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC106: { owasp: \"A04:2021\", cwe: \"CWE-710\" },\n VC107: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC108: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC109: { owasp: \"A01:2021\", cwe: \"CWE-284\" },\n VC110: { owasp: \"A09:2021\", cwe: \"CWE-778\" },\n VC111: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC112: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC113: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC114: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC115: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC116: { owasp: \"A05:2021\", cwe: \"CWE-770\" },\n VC117: { owasp: \"A03:2021\", cwe: \"CWE-22\" },\n VC118: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC119: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC120: { owasp: \"A07:2021\", cwe: \"CWE-352\" },\n VC121: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n VC122: { owasp: \"A02:2021\", cwe: \"CWE-326\" },\n VC123: { owasp: \"A02:2021\", cwe: \"CWE-326\" },\n VC124: { owasp: \"A02:2021\", cwe: \"CWE-327\" },\n VC125: { owasp: \"A07:2021\", cwe: \"CWE-640\" },\n VC126: { owasp: \"A05:2021\", cwe: \"CWE-200\" },\n VC127: { owasp: \"A01:2021\", cwe: \"CWE-862\" },\n VC128: { owasp: \"A05:2021\", cwe: \"CWE-444\" },\n VC129: { owasp: \"A02:2021\", cwe: \"CWE-311\" },\n VC130: { owasp: \"A07:2021\", cwe: \"CWE-307\" },\n VC131: { owasp: \"A06:2021\", cwe: \"CWE-1104\" },\n // VC132–VC145: Service-specific API key detection\n VC132: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC133: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC134: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC135: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC136: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC137: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC138: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC139: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC140: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC141: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC142: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC143: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC144: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC145: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n // VC146–VC151: Secret exposure path analysis\n VC146: { owasp: \"A07:2021\", cwe: \"CWE-598\" },\n VC147: { owasp: \"A09:2021\", cwe: \"CWE-532\" },\n VC148: { owasp: \"A09:2021\", cwe: \"CWE-209\" },\n VC149: { owasp: \"A07:2021\", cwe: \"CWE-798\" },\n VC150: { owasp: \"A07:2021\", cwe: \"CWE-615\" },\n VC151: { owasp: \"A07:2021\", cwe: \"CWE-214\" },\n // VC152–VC158: New rules\n VC152: { owasp: \"A08:2021\", cwe: \"CWE-345\" },\n VC153: { owasp: \"A05:2021\", cwe: \"CWE-942\" },\n VC154: { owasp: \"A03:2021\", cwe: \"CWE-20\" },\n VC155: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC156: { owasp: \"A04:2021\", cwe: \"CWE-770\" },\n VC157: { owasp: \"A05:2021\", cwe: \"CWE-16\" },\n VC158: { owasp: \"A01:2021\", cwe: \"CWE-639\" },\n // VC159–VC183: Additional service-specific API key detection.\n // All map to A07:2021 (Identification & Auth Failures) / CWE-798 (Hardcoded\n // Credentials), matching the existing VC132–VC145 cluster.\n VC159: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Cohere\n VC160: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Replicate\n VC161: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Mistral\n VC162: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Together AI\n VC163: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Groq\n VC164: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Fireworks AI\n VC165: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Postmark\n VC166: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Resend\n VC167: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Loops\n VC168: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Cloudflare\n VC169: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Fastly\n VC170: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Netlify\n VC171: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Railway\n VC172: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Fly.io\n VC173: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Algolia admin key\n VC174: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Qdrant\n VC175: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Weaviate\n VC176: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Linear\n VC177: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Notion\n VC178: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Discord bot token\n VC179: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Intercom\n VC180: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Sentry auth token\n VC181: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Logtail / Better Stack\n VC182: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Highlight\n VC183: { owasp: \"A07:2021\", cwe: \"CWE-798\" }, // Plivo\n // VC184–VC187: GitHub Actions workflow security\n VC184: { owasp: \"A08:2021\", cwe: \"CWE-829\" }, // pull_request_target + checkout PR head\n VC185: { owasp: \"A05:2021\", cwe: \"CWE-732\" }, // permissions: write-all\n VC186: { owasp: \"A03:2021\", cwe: \"CWE-78\" }, // expression injection in run:\n VC187: { owasp: \"A08:2021\", cwe: \"CWE-829\" }, // third-party action receiving secrets\n // VC188–VC190: Dockerfile hardening\n VC188: { owasp: \"A05:2021\", cwe: \"CWE-1357\" }, // ADD instead of COPY\n VC189: { owasp: \"A08:2021\", cwe: \"CWE-494\" }, // RUN curl|sh\n VC190: { owasp: \"A04:2021\", cwe: \"CWE-754\" }, // missing HEALTHCHECK\n // VC191–VC197: Python-specific security gaps\n VC191: { owasp: \"A02:2021\", cwe: \"CWE-295\" }, // requests verify=False\n VC192: { owasp: \"A03:2021\", cwe: \"CWE-79\" }, // Jinja2 autoescape=False\n VC193: { owasp: \"A04:2021\", cwe: \"CWE-377\" }, // tempfile.mktemp TOCTOU\n VC194: { owasp: \"A03:2021\", cwe: \"CWE-79\" }, // Django mark_safe\n VC195: { owasp: \"A02:2021\", cwe: \"CWE-295\" }, // paramiko AutoAddPolicy\n VC196: { owasp: \"A05:2021\", cwe: \"CWE-20\" }, // ALLOWED_HOSTS wildcard\n VC197: { owasp: \"A02:2021\", cwe: \"CWE-347\" }, // PyJWT no algorithm allowlist\n // VC198–VC203: AI / LLM-specific security\n VC198: { owasp: \"A03:2021\", cwe: \"CWE-94\" }, // prompt injection via user input\n VC199: { owasp: \"A03:2021\", cwe: \"CWE-94\" }, // system-prompt injection\n VC200: { owasp: \"A03:2021\", cwe: \"CWE-79\" }, // LLM output as raw HTML (XSS)\n VC201: { owasp: \"A01:2021\", cwe: \"CWE-639\" }, // vector-store query without user filter\n VC202: { owasp: \"A01:2021\", cwe: \"CWE-639\" }, // vector-store upsert without user metadata\n VC203: { owasp: \"A04:2021\", cwe: \"CWE-770\" }, // LLM call without max_tokens\n // VC204–VC206: GraphQL server hardening\n VC204: { owasp: \"A04:2021\", cwe: \"CWE-770\" }, // no query depth limit\n VC205: { owasp: \"A04:2021\", cwe: \"CWE-770\" }, // no query complexity limit\n VC206: { owasp: \"A01:2021\", cwe: \"CWE-352\" }, // Apollo csrfPrevention: false\n};\n\n// ────────────────────────────────────────────\n// VC097 – Console.log in Production\n// ────────────────────────────────────────────\n\nexport const consoleLogProduction: CustomRule = {\n id: \"VC097\",\n title: \"Console.log Left in Production Code\",\n severity: \"low\",\n category: \"Performance\",\n description: \"console.log statements left in production code can leak sensitive data, slow down rendering, and clutter browser consoles.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n // Skip server-side scripts, migration files, CLI tools, and seed files\n if (/(?:migrate|seed|script|cli|setup|dev)\\./i.test(filePath)) return [];\n if (!/console\\.log\\s*\\(/g.test(content)) return [];\n const lines = content.split(\"\\n\");\n const logCount = lines.filter(l => /console\\.log\\s*\\(/.test(l.trim()) && !l.trim().startsWith(\"//\")).length;\n // If a file has many console.logs, it's intentional logging, not forgotten debug statements\n if (logCount > 5) return [];\n const matches: RuleMatch[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (/console\\.log\\s*\\(/.test(line) && !line.startsWith(\"//\") && !line.startsWith(\"*\") && !/if\\s*\\(\\s*(?:debug|process\\.env)/i.test(lines[Math.max(0, i-1)] + line)) {\n matches.push({ rule: \"VC097\", title: consoleLogProduction.title, severity: \"low\" as const, category: \"Performance\", file: filePath, line: i + 1, snippet: getSnippet(content, i + 1), fix: \"Remove console.log or use a structured logger that can be disabled in production.\" });\n }\n }\n return matches.slice(0, 1); // Max 1 per file to avoid noise\n },\n};\n\n// ────────────────────────────────────────────\n// VC098 – Synchronous File Operations\n// ────────────────────────────────────────────\n\nexport const syncFileOps: CustomRule = {\n id: \"VC098\",\n title: \"Synchronous File Operations\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Synchronous file operations (readFileSync, writeFileSync) block the event loop, causing all other requests to wait.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|__tests__|fixture|config|\\.config\\./i)) return [];\n return findMatches(content, /(?:readFileSync|writeFileSync|appendFileSync|mkdirSync|rmdirSync|statSync)\\s*\\(/g, syncFileOps, filePath, () =>\n \"Use async file operations (readFile, writeFile) to avoid blocking the event loop.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC099 – Memory Leak: Event Listener\n// ────────────────────────────────────────────\n\nexport const eventListenerLeak: CustomRule = {\n id: \"VC099\",\n title: \"Memory Leak: Event Listener Not Cleaned Up\",\n severity: \"high\",\n category: \"Performance\",\n description: \"Adding event listeners in React useEffect without a cleanup function causes memory leaks as listeners accumulate on re-renders.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx|tsx)$/)) return [];\n if (!/addEventListener/i.test(content)) return [];\n if (/removeEventListener/i.test(content)) return [];\n if (!/useEffect/i.test(content)) return [];\n return findMatches(content, /addEventListener\\s*\\(/g, eventListenerLeak, filePath, () =>\n \"Return a cleanup function from useEffect: useEffect(() => { window.addEventListener('resize', fn); return () => window.removeEventListener('resize', fn); }, []);\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC100 – N+1 Query Pattern\n// ────────────────────────────────────────────\n\nexport const nPlusOneQuery: CustomRule = {\n id: \"VC100\",\n title: \"N+1 Query Pattern Detected\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Database queries inside loops cause N+1 performance problems — one query per iteration instead of a single batch query.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n const hasLoopWithQuery = /(?:for\\s*\\(|\\.forEach\\s*\\(|\\.map\\s*\\(|while\\s*\\()[^}]*(?:\\.find\\(|\\.findOne\\(|\\.findById\\(|\\.query\\(|\\.execute\\(|SELECT\\s)/is.test(content);\n if (!hasLoopWithQuery) return [];\n return findMatches(content, /(?:for\\s*\\(|\\.forEach\\s*\\(|\\.map\\s*\\(|while\\s*\\()/g, nPlusOneQuery, filePath, () =>\n \"Fetch all data in a single query using WHERE IN, JOIN, or batch operations instead of querying per item in a loop.\"\n ).slice(0, 2);\n },\n};\n\n// ────────────────────────────────────────────\n// VC101 – Large Bundle Import\n// ────────────────────────────────────────────\n\nexport const largeBundleImport: CustomRule = {\n id: \"VC101\",\n title: \"Importing Entire Library (Large Bundle)\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Importing entire libraries like lodash or moment.js adds hundreds of KB to your bundle. Import only the functions you need.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n const matches: RuleMatch[] = [];\n const patterns = [\n /import\\s+_\\s+from\\s+['\"]lodash['\"]/g,\n /import\\s+\\*\\s+as\\s+_\\s+from\\s+['\"]lodash['\"]/g,\n /import\\s+moment\\s+from\\s+['\"]moment['\"]/g,\n /const\\s+_\\s*=\\s*require\\s*\\(\\s*['\"]lodash['\"]\\s*\\)/g,\n /const\\s+moment\\s*=\\s*require\\s*\\(\\s*['\"]moment['\"]\\s*\\)/g,\n ];\n for (const p of patterns) {\n matches.push(...findMatches(content, p, largeBundleImport, filePath, () =>\n \"Import only what you need: import { debounce } from 'lodash/debounce'. Or switch to lighter alternatives like date-fns instead of moment.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC102 – Blocking Main Thread\n// ────────────────────────────────────────────\n\nexport const blockingMainThread: CustomRule = {\n id: \"VC102\",\n title: \"Blocking Main Thread with Heavy Computation\",\n severity: \"medium\",\n category: \"Performance\",\n description: \"Infinite loops or deeply nested iterations on the main thread freeze the UI and cause unresponsiveness.\",\n check(content, filePath) {\n if (filePath.match(/worker|test|spec|mock/i)) return [];\n const matches: RuleMatch[] = [];\n if (/while\\s*\\(\\s*true\\s*\\)/g.test(content)) {\n matches.push(...findMatches(content, /while\\s*\\(\\s*true\\s*\\)/g, blockingMainThread, filePath, () =>\n \"Avoid while(true) on the main thread. Use Web Workers for heavy computation or requestIdleCallback for non-urgent work.\"\n ));\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC103 – TODO/FIXME Left in Code\n// ────────────────────────────────────────────\n\nexport const todoLeftInCode: CustomRule = {\n id: \"VC103\",\n title: \"TODO/FIXME Left in Code\",\n severity: \"low\",\n category: \"Code Quality\",\n description: \"TODO, FIXME, HACK, and XXX comments indicate unfinished work that should be resolved before shipping to production.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|__tests__|fixture|node_modules/i)) return [];\n return findMatches(content, /\\/\\/\\s*(?:TODO|FIXME|HACK|XXX)\\b/gi, todoLeftInCode, filePath, () =>\n \"Resolve TODO/FIXME comments before shipping. If it's intentional tech debt, track it in your issue tracker instead.\"\n ).slice(0, 5);\n },\n};\n\n// ────────────────────────────────────────────\n// VC104 – Empty Catch Block\n// ────────────────────────────────────────────\n\nexport const emptyCatchBlock: CustomRule = {\n id: \"VC104\",\n title: \"Empty Catch Block\",\n severity: \"medium\",\n category: \"Code Quality\",\n description: \"Empty catch blocks silently swallow errors, making bugs impossible to diagnose. At minimum, log the error.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n return findMatches(content, /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*\\}/g, emptyCatchBlock, filePath, () =>\n \"Handle errors in catch blocks: catch(err) { console.error('Operation failed:', err); } or re-throw if appropriate.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC105 – Callback Hell\n// ────────────────────────────────────────────\n\nexport const callbackHell: CustomRule = {\n id: \"VC105\",\n title: \"Deeply Nested Callbacks (Promise Chain)\",\n severity: \"medium\",\n category: \"Code Quality\",\n description: \"Long .then() chains or deeply nested callbacks are hard to read, debug, and maintain. Refactor to async/await.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock/i)) return [];\n return findMatches(content, /\\.then\\s*\\([^)]*\\)\\s*\\.then\\s*\\([^)]*\\)\\s*\\.then/g, callbackHell, filePath, () =>\n \"Refactor .then() chains to async/await for cleaner, more readable code.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC106 – Magic Numbers\n// ────────────────────────────────────────────\n\nexport const magicNumbers: CustomRule = {\n id: \"VC106\",\n title: \"Magic Numbers in Code\",\n severity: \"low\",\n category: \"Code Quality\",\n description: \"Unnamed numeric constants in conditions or calculations make code hard to understand. Extract them into named constants.\",\n check(content, filePath) {\n if (filePath.match(/test|spec|mock|config|\\.config\\.|constant|enum|migration/i)) return [];\n if (!filePath.match(/\\.(jsx?|tsx?)$/)) return [];\n const matches: RuleMatch[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (line.startsWith(\"//\") || line.startsWith(\"*\")) continue;\n // Look for conditions with magic numbers > 1 (skip 0, 1, -1, 2)\n if (/(?:===|!==|>=?|<=?)\\s*\\d{3,}/.test(line) || /(?:setTimeout|setInterval)\\s*\\([^,]+,\\s*\\d{4,}/.test(line)) {\n matches.push({ rule: \"VC106\", title: magicNumbers.title, severity: \"low\" as const, category: \"Code Quality\", file: filePath, line: i + 1, snippet: getSnippet(content, i + 1), fix: \"Extract magic numbers into named constants: const MAX_RETRIES = 3; const TIMEOUT_MS = 5000;\" });\n }\n }\n return matches.slice(0, 3);\n },\n};\n\n// ────────────────────────────────────────────\n// VC107 – S3 Bucket Without Encryption\n// ────────────────────────────────────────────\n\nexport const s3BucketNoEncryption: CustomRule = {\n id: \"VC107\",\n title: \"S3 Bucket Without Encryption\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"AWS S3 buckets without server-side encryption leave data at rest unprotected. Enable encryption to protect sensitive data.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_s3_bucket\"/.test(content)) return [];\n const matches: RuleMatch[] = [];\n // Find S3 bucket resources without encryption configuration nearby\n const bucketPattern = /resource\\s+\"aws_s3_bucket\"\\s+\"(\\w+)\"/g;\n let m: RegExpExecArray | null;\n while ((m = bucketPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check if there's a server_side_encryption_configuration within a reasonable range\n const blockEnd = Math.min(m.index + 2000, content.length);\n const blockContent = content.substring(m.index, blockEnd);\n if (!/server_side_encryption/.test(blockContent)) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC107\", title: s3BucketNoEncryption.title, severity: \"high\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Enable S3 bucket encryption: server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { sse_algorithm = 'aws:kms' } } }\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC108 – Security Group Allows All Inbound\n// ────────────────────────────────────────────\n\nexport const securityGroupAllInbound: CustomRule = {\n id: \"VC108\",\n title: \"Security Group Allows All Inbound\",\n severity: \"critical\",\n category: \"Infrastructure\",\n description: \"Security groups allowing all inbound traffic (0.0.0.0/0 on all ports) expose resources to the entire internet.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(tf|json|yaml|yml)$/)) return [];\n const matches: RuleMatch[] = [];\n // Terraform: ingress with 0.0.0.0/0 and port 0 or no port restriction\n if (filePath.match(/\\.tf$/)) {\n const ingressPattern = /ingress\\s*\\{[^}]*cidr_blocks\\s*=\\s*\\[\\s*\"0\\.0\\.0\\.0\\/0\"\\s*\\][^}]*\\}/gs;\n let m: RegExpExecArray | null;\n while ((m = ingressPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check for unrestricted ports (from_port = 0 or no port spec)\n if (/from_port\\s*=\\s*0/.test(m[0]) || !/from_port/.test(m[0])) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC108\", title: securityGroupAllInbound.title, severity: \"critical\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Restrict security group ingress to specific IP ranges and ports.\",\n });\n }\n }\n }\n // CloudFormation: AWS::EC2::SecurityGroup with CidrIp: 0.0.0.0/0\n if (filePath.match(/\\.(json|yaml|yml)$/)) {\n const cfnPattern = /AWS::EC2::SecurityGroup/g;\n if (cfnPattern.test(content) && /CidrIp\\s*:\\s*[\"']?0\\.0\\.0\\.0\\/0/.test(content)) {\n matches.push(...findMatches(content, /CidrIp\\s*:\\s*[\"']?0\\.0\\.0\\.0\\/0/g, securityGroupAllInbound, filePath, () =>\n \"Restrict security group ingress to specific IP ranges and ports.\"\n ));\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC109 – RDS Instance Publicly Accessible\n// ────────────────────────────────────────────\n\nexport const rdsPubliclyAccessible: CustomRule = {\n id: \"VC109\",\n title: \"RDS Instance Publicly Accessible\",\n severity: \"critical\",\n category: \"Infrastructure\",\n description: \"RDS instances with publicly_accessible = true are exposed to the internet, risking unauthorized database access.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_db_instance\"/.test(content)) return [];\n return findMatches(content, /publicly_accessible\\s*=\\s*true/g, rdsPubliclyAccessible, filePath, () =>\n \"Set publicly_accessible = false. Access RDS through VPC private subnets.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC110 – Missing CloudTrail Logging\n// ────────────────────────────────────────────\n\nexport const missingCloudTrail: CustomRule = {\n id: \"VC110\",\n title: \"Missing CloudTrail Logging\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"AWS environments without CloudTrail lack audit logging of API calls, making it difficult to detect unauthorized activity.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n // Only flag if the file uses AWS provider but has no cloudtrail resource\n if (!/provider\\s+\"aws\"/.test(content)) return [];\n if (/aws_cloudtrail/.test(content)) return [];\n const lineNum = content.substring(0, content.search(/provider\\s+\"aws\"/)).split(\"\\n\").length;\n return [{\n rule: \"VC110\", title: missingCloudTrail.title, severity: \"high\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Enable CloudTrail for audit logging of all AWS API calls.\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC111 – Lambda Without VPC\n// ────────────────────────────────────────────\n\nexport const lambdaWithoutVPC: CustomRule = {\n id: \"VC111\",\n title: \"Lambda Without VPC\",\n severity: \"medium\",\n category: \"Infrastructure\",\n description: \"Lambda functions not placed in a VPC lack network isolation and cannot access VPC-only resources securely.\",\n check(content, filePath) {\n if (!filePath.match(/\\.tf$/)) return [];\n if (!/resource\\s+\"aws_lambda_function\"/.test(content)) return [];\n const matches: RuleMatch[] = [];\n const lambdaPattern = /resource\\s+\"aws_lambda_function\"\\s+\"(\\w+)\"/g;\n let m: RegExpExecArray | null;\n while ((m = lambdaPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const blockEnd = Math.min(m.index + 2000, content.length);\n const blockContent = content.substring(m.index, blockEnd);\n if (!/vpc_config\\s*\\{/.test(blockContent)) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC111\", title: lambdaWithoutVPC.title, severity: \"medium\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Place Lambda functions in a VPC for network isolation.\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC112 – Docker Image Using Latest Tag\n// ────────────────────────────────────────────\n\nexport const dockerLatestTag: CustomRule = {\n id: \"VC112\",\n title: \"Docker Image Using Latest Tag\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Using :latest or no tag in Docker FROM directives leads to non-reproducible builds and potential security regressions.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const matches: RuleMatch[] = [];\n // Match FROM image:latest or FROM image (no tag, no AS, no scratch)\n const fromPattern = /^FROM\\s+(?!scratch)(\\S+?)(?:\\s+AS\\s+\\S+)?\\s*$/gm;\n let m: RegExpExecArray | null;\n while ((m = fromPattern.exec(content)) !== null) {\n const image = m[1];\n // Flag if using :latest or no tag at all (no colon)\n if (image.endsWith(\":latest\") || (!image.includes(\":\") && !image.startsWith(\"$\"))) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n matches.push({\n rule: \"VC112\", title: dockerLatestTag.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pin Docker image versions: FROM node:20-alpine instead of FROM node:latest\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC113 – Docker COPY With Sensitive Files\n// ────────────────────────────────────────────\n\nexport const dockerCopySensitive: CustomRule = {\n id: \"VC113\",\n title: \"Docker COPY With Sensitive Files\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Using COPY . . or ADD . . without a .dockerignore can leak .env files, .git history, and other sensitive data into the Docker image.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n // Check for COPY . . or ADD . .\n if (!/(?:COPY|ADD)\\s+\\.\\s+\\./.test(content)) return [];\n return findMatches(content, /(?:COPY|ADD)\\s+\\.\\s+\\./g, dockerCopySensitive, filePath, () =>\n \"Use .dockerignore to exclude .env, .git, node_modules, and sensitive files from the Docker build context.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC114 – Docker Exposing Too Many Ports\n// ────────────────────────────────────────────\n\nexport const dockerTooManyPorts: CustomRule = {\n id: \"VC114\",\n title: \"Docker Exposing Too Many Ports\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"Exposing many ports or wide port ranges increases the attack surface of a container.\",\n check(content, filePath) {\n if (!filePath.match(/Dockerfile$/i)) return [];\n const matches: RuleMatch[] = [];\n // Detect port ranges like EXPOSE 3000-9000\n const rangePattern = /^EXPOSE\\s+\\d+-\\d+/gm;\n matches.push(...findMatches(content, rangePattern, dockerTooManyPorts, filePath, () =>\n \"Only expose necessary ports. Each EXPOSE should have a clear purpose.\"\n ));\n // Detect multiple EXPOSE directives (more than 3)\n const exposeLines = content.split(\"\\n\").filter(l => /^\\s*EXPOSE\\s+/.test(l));\n if (exposeLines.length > 3) {\n const firstExpose = content.search(/^\\s*EXPOSE\\s+/m);\n if (firstExpose >= 0) {\n const lineNum = content.substring(0, firstExpose).split(\"\\n\").length;\n matches.push({\n rule: \"VC114\", title: dockerTooManyPorts.title, severity: \"medium\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Only expose necessary ports. Each EXPOSE should have a clear purpose.\",\n });\n }\n }\n return matches;\n },\n};\n\n// ────────────────────────────────────────────\n// VC115 – Kubernetes Secret Not Encrypted\n// ────────────────────────────────────────────\n\nexport const k8sSecretNotEncrypted: CustomRule = {\n id: \"VC115\",\n title: \"Kubernetes Secret Not Encrypted\",\n severity: \"high\",\n category: \"Infrastructure\",\n description: \"Kubernetes Secrets stored as plain base64 in manifests are not encrypted and can be trivially decoded. Use sealed-secrets or external-secrets.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/)) return [];\n if (!/kind\\s*:\\s*Secret/i.test(content)) return [];\n // Skip if using sealed-secrets or external-secrets annotations\n if (/sealedsecrets\\.bitnami\\.com|external-secrets\\.io/i.test(content)) return [];\n return findMatches(content, /kind\\s*:\\s*Secret/g, k8sSecretNotEncrypted, filePath, () =>\n \"Use sealed-secrets or external-secrets-operator. Never store plain secrets in manifests.\"\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC116 – Kubernetes Pod Without Resource Limits\n// ────────────────────────────────────────────\n\nexport const k8sNoResourceLimits: CustomRule = {\n id: \"VC116\",\n title: \"Kubernetes Pod Without Resource Limits\",\n severity: \"medium\",\n category: \"Infrastructure\",\n description: \"Pods or deployments without resource limits can consume excessive CPU/memory, causing cluster instability.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(yaml|yml)$/)) return [];\n if (!/kind\\s*:\\s*(?:Pod|Deployment|StatefulSet|DaemonSet|Job|CronJob)/i.test(content)) return [];\n // Check if any container spec has resources.limits\n if (/resources\\s*:\\s*\\n\\s+limits\\s*:/m.test(content)) return [];\n // Also accept inline resources: { limits: ... }\n if (/resources\\s*:.*limits/i.test(content)) return [];\n const kindMatch = content.match(/kind\\s*:\\s*(?:Pod|Deployment|StatefulSet|DaemonSet|Job|CronJob)/i);\n if (!kindMatch) return [];\n const lineNum = content.substring(0, kindMatch.index).split(\"\\n\").length;\n return [{\n rule: \"VC116\", title: k8sNoResourceLimits.title, severity: \"medium\" as const, category: \"Infrastructure\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Set resource limits to prevent pods from consuming excessive CPU/memory.\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC117 – Path Traversal Vulnerability\n// ────────────────────────────────────────────\n\nexport const pathTraversal: CustomRule = {\n id: \"VC117\",\n title: \"Path Traversal Vulnerability\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"User input is used to construct file paths without sanitization, allowing attackers to read/write arbitrary files (e.g., ../../etc/passwd).\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|php|java)$/)) return [];\n const findings: RuleMatch[] = [];\n // Pattern: file operations using user input (req.params, req.query, req.body) in path\n const patterns = [\n /(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync|appendFile|unlink|unlinkSync|access|stat|statSync|existsSync)\\s*\\(\\s*(?:req\\.|request\\.|ctx\\.|params\\.|query\\.)/gi,\n /(?:path\\.join|path\\.resolve)\\s*\\([^)]*(?:req\\.|request\\.|ctx\\.|params\\.|query\\.)[^)]*\\)/gi,\n /(?:res\\.sendFile|res\\.download)\\s*\\([^)]*(?:req\\.|request\\.)/gi,\n /open\\s*\\(\\s*(?:request\\.|params\\[|args\\.)/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Skip if path.normalize or sanitization is near\n const surrounding = content.substring(Math.max(0, m.index - 200), m.index + 200);\n if (/path\\.normalize|sanitize|\\.replace\\(\\s*['\"]\\.\\./i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC117\", title: pathTraversal.title, severity: \"critical\" as const, category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Sanitize file paths: use path.normalize(), reject paths containing '..', and validate against an allowed base directory.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC118 – PII in Log Output\n// ────────────────────────────────────────────\n\nexport const piiInLogs: CustomRule = {\n id: \"VC118\",\n title: \"Personally Identifiable Information in Logs\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Logging statements that include email addresses, passwords, SSNs, credit card numbers, or other PII can leak sensitive data to log aggregation systems.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match console.log/logger.info etc. that reference sensitive fields\n const logPattern = /(?:console\\.(?:log|info|warn|error|debug)|logger\\.(?:info|warn|error|debug|log)|log\\.(?:info|warn|error|debug)|logging\\.(?:info|warn|error|debug))\\s*\\([^)]*(?:password|passwd|secret|ssn|social.?security|credit.?card|cardNumber|cvv|token|bearer|authorization)\\b/gi;\n let m;\n while ((m = logPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC118\", title: piiInLogs.title, severity: \"high\" as const, category: \"Information Leakage\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never log sensitive data. Redact or mask PII before logging: log('user login', { email: maskEmail(email) }).\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC119 – Hardcoded OAuth Client Secret\n// ────────────────────────────────────────────\n\nexport const hardcodedOAuthSecret: CustomRule = {\n id: \"VC119\",\n title: \"Hardcoded OAuth Client Secret\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"OAuth client secrets hardcoded in source code can be extracted and used to impersonate your application.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php|env|json|yaml|yml)$/)) return [];\n // Skip lockfiles and generated files\n if (/package-lock|yarn\\.lock|pnpm-lock|composer\\.lock/i.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /client[_-]?secret\\s*[:=]\\s*[\"'][a-zA-Z0-9_\\-]{20,}[\"']/gi,\n /GOOGLE_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /GITHUB_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /FACEBOOK_APP_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n /AUTH0_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']{10,}[\"']/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Skip if value is a placeholder or env ref\n if (/process\\.env|os\\.environ|ENV\\[|getenv|\\$\\{|<your|CHANGE_ME|xxx|placeholder/i.test(m[0])) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC119\", title: hardcodedOAuthSecret.title, severity: \"critical\" as const, category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move OAuth client secrets to environment variables. Never commit secrets to source control.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC120 – Missing OAuth State Parameter\n// ────────────────────────────────────────────\n\nexport const missingOAuthState: CustomRule = {\n id: \"VC120\",\n title: \"OAuth Flow Missing State Parameter\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"OAuth authorization requests without a state parameter are vulnerable to CSRF attacks, allowing attackers to link their account to a victim's session.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // OAuth authorization URL construction without state param\n const oauthUrlPattern = /(?:authorize\\?|\\/oauth\\/authorize|\\/auth\\?|authorization_endpoint)[^}]*(?:client_id|response_type)/gi;\n let m;\n while ((m = oauthUrlPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const surrounding = content.substring(m.index, Math.min(content.length, m.index + 500));\n if (/state\\s*[=:]/i.test(surrounding)) continue; // Has state param\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC120\", title: missingOAuthState.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Always include a cryptographically random 'state' parameter in OAuth authorization requests and validate it on callback.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC121 – Unpinned GitHub Actions Version\n// ────────────────────────────────────────────\n\nexport const unpinnedGitHubAction: CustomRule = {\n id: \"VC121\",\n title: \"Unpinned GitHub Actions Version\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"GitHub Actions using branch references (@main, @master) instead of commit SHAs can be compromised via supply-chain attacks.\",\n check(content, filePath) {\n if (!filePath.match(/\\.github\\/workflows\\/.*\\.(yml|yaml)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match uses: org/action@branch (not @sha or @v1.2.3)\n const usesPattern = /uses\\s*:\\s*[\\w\\-]+\\/[\\w\\-]+@(main|master|dev|develop|latest)\\b/gi;\n let m;\n while ((m = usesPattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC121\", title: unpinnedGitHubAction.title, severity: \"high\" as const, category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pin GitHub Actions to a specific commit SHA instead of a branch name: uses: owner/action@<full-sha>\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC122 – Deprecated TLS Version\n// ────────────────────────────────────────────\n\nexport const deprecatedTLS: CustomRule = {\n id: \"VC122\",\n title: \"Deprecated TLS Version Configured\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"TLS 1.0 and 1.1 are deprecated and have known vulnerabilities. Only TLS 1.2+ should be used.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|yaml|yml|json|conf|cfg|xml)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /(?:TLSv1_METHOD|TLSv1\\.0|TLSv1\\.1|ssl\\.PROTOCOL_TLSv1|SSLv3|SSLv2|TLS_1_0|TLS_1_1|minVersion\\s*[:=]\\s*[\"']TLSv1(?:\\.1)?[\"'])/gi,\n /(?:tls\\.DEFAULT_MIN_VERSION|secureProtocol)\\s*[:=]\\s*[\"'](?:TLSv1_method|TLSv1_1_method|SSLv3_method)[\"']/gi,\n /ssl_protocols\\s+.*(?:TLSv1(?:\\.1)?(?:\\s|;))/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC122\", title: deprecatedTLS.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use TLS 1.2 or higher. Set minVersion to 'TLSv1.2' and remove support for TLS 1.0/1.1.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC123 – Weak RSA Key Size\n// ────────────────────────────────────────────\n\nexport const weakRSAKeySize: CustomRule = {\n id: \"VC123\",\n title: \"Weak RSA Key Size\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"RSA keys smaller than 2048 bits are considered insecure and can be factored with modern hardware.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Match RSA key generation with explicit small sizes\n const patterns = [\n /generateKeyPair\\s*\\(\\s*[\"']rsa[\"']\\s*,\\s*\\{[^}]*modulusLength\\s*:\\s*(512|768|1024)\\b/gi,\n /RSA\\.generate\\s*\\(\\s*(512|768|1024)\\b/gi,\n /rsa\\.GenerateKey\\s*\\([^,]*,\\s*(512|768|1024)\\b/gi,\n /KeyPairGenerator.*initialize\\s*\\(\\s*(512|768|1024)\\b/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC123\", title: weakRSAKeySize.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use RSA key sizes of at least 2048 bits. 4096 bits is recommended for long-term security.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC124 – ECB Mode Encryption\n// ────────────────────────────────────────────\n\nexport const ecbModeEncryption: CustomRule = {\n id: \"VC124\",\n title: \"Insecure ECB Mode Encryption\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"ECB (Electronic Codebook) mode encrypts identical plaintext blocks to identical ciphertext, leaking patterns in the data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /createCipher(?:iv)?\\s*\\(\\s*[\"'](?:aes-(?:128|192|256)-ecb|des-ecb)[\"']/gi,\n /AES\\.(?:new|MODE_ECB)|MODE_ECB/gi,\n /Cipher\\.getInstance\\s*\\(\\s*[\"']AES\\/ECB/gi,\n /aes\\.NewCipher|cipher\\.NewECBEncrypter/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC124\", title: ecbModeEncryption.title, severity: \"high\" as const, category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use AES-GCM or AES-CBC with HMAC instead of ECB mode. GCM provides both encryption and authentication.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC125 – Insecure Password Reset Flow\n// ────────────────────────────────────────────\n\nexport const insecurePasswordReset: CustomRule = {\n id: \"VC125\",\n title: \"Insecure Password Reset Implementation\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Password reset using predictable tokens, no expiration, or user-enumeration leaks can be exploited to take over accounts.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n if (!/(?:reset|forgot).*(?:password|passwd)/i.test(content)) return [];\n const findings: RuleMatch[] = [];\n // Predictable reset tokens (using Date.now, Math.random, uuid without crypto)\n const weakTokens = /(?:reset|forgot).*(?:token|code)\\s*[:=].*(?:Date\\.now|Math\\.random|uuid\\(\\)|nanoid\\(\\))/gi;\n let m;\n while ((m = weakTokens.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC125\", title: insecurePasswordReset.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Use crypto.randomBytes(32).toString('hex') for reset tokens. Set expiration (15-60 minutes) and single-use enforcement.\",\n });\n }\n // User enumeration: different responses for valid vs invalid emails\n const enumeration = /(?:user|email|account)\\s*(?:not\\s*found|does\\s*not\\s*exist|invalid)/gi;\n while ((m = enumeration.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Only flag if in a password reset context\n const surrounding = content.substring(Math.max(0, m.index - 500), m.index);\n if (!/(?:reset|forgot).*password/i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC125\", title: insecurePasswordReset.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Always return the same response regardless of whether the email exists. Say 'If an account exists, a reset link was sent.'\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC126 – Terraform State File Exposed\n// ────────────────────────────────────────────\n\nexport const terraformStateExposed: CustomRule = {\n id: \"VC126\",\n title: \"Terraform State File Committed\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Terraform state files contain sensitive infrastructure details, secrets, and access credentials in plaintext. They must never be committed to version control.\",\n check(content, filePath) {\n const findings: RuleMatch[] = [];\n // Direct detection of terraform state files\n if (/terraform\\.tfstate(\\.backup)?$/.test(filePath)) {\n findings.push({\n rule: \"VC126\", title: terraformStateExposed.title, severity: \"critical\" as const, category: \"Secrets\",\n file: filePath, line: 1, snippet: getSnippet(content, 1),\n fix: \"Add '*.tfstate' and '*.tfstate.backup' to .gitignore. Use remote state backends (S3, GCS, Terraform Cloud) instead.\",\n });\n }\n // Check .gitignore for missing tfstate\n if (filePath.endsWith(\".gitignore\") && !content.includes(\"tfstate\")) {\n // Only flag if the repo has terraform files\n if (/\\.tf$/.test(filePath) || content.includes(\".tf\")) {\n // This is a weak signal, skip\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC127 – Insecure HTTP Method Handling\n// ────────────────────────────────────────────\n\nexport const insecureHTTPMethods: CustomRule = {\n id: \"VC127\",\n title: \"Dangerous HTTP Methods Without Auth\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"DELETE, PUT, and PATCH endpoints without authentication checks allow unauthorized data modification or deletion.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Express/Hono/Fastify route handlers for dangerous methods\n const routePatterns = [\n /(?:app|router|server)\\.(delete|put|patch)\\s*\\(\\s*[\"']/gi,\n ];\n for (const pat of routePatterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check the handler for auth middleware or auth checks\n const handlerBlock = content.substring(m.index, Math.min(content.length, m.index + 500));\n if (/auth|authenticate|authorize|requireAuth|isAuthenticated|protect|guard|middleware|session|jwt|verify|clerk|getAuth/i.test(handlerBlock)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC127\", title: insecureHTTPMethods.title, severity: \"high\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add authentication middleware to DELETE, PUT, and PATCH routes. Verify the user has permission to modify the resource.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC128 – HTTP Request Smuggling Risk\n// ────────────────────────────────────────────\n\nexport const httpRequestSmuggling: CustomRule = {\n id: \"VC128\",\n title: \"HTTP Request Smuggling Risk\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Manually parsing Content-Length or Transfer-Encoding headers can lead to request smuggling when behind a reverse proxy.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /(?:headers?\\[?|getHeader\\s*\\(\\s*)[\"'](?:content-length|transfer-encoding)[\"']\\s*\\]?\\s*\\)/gi,\n /parseInt\\s*\\(\\s*(?:req|request)\\.headers?\\[?\\s*[\"']content-length[\"']/gi,\n ];\n for (const pat of patterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC128\", title: httpRequestSmuggling.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Let your framework handle Content-Length and Transfer-Encoding headers. Do not manually parse or trust these values.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC129 – Unencrypted PII in Database Schema\n// ────────────────────────────────────────────\n\nexport const unencryptedPII: CustomRule = {\n id: \"VC129\",\n title: \"Sensitive Data Stored Without Encryption\",\n severity: \"high\",\n category: \"Information Leakage\",\n description: \"Database schemas storing PII (SSN, credit cards, health records) in plaintext violate compliance requirements and expose data in breaches.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(sql|prisma|py|ts|js|rb)$/)) return [];\n const findings: RuleMatch[] = [];\n // Schema definitions with sensitive field names stored as plain text/varchar\n const schemaPatterns = [\n /(?:column|field|Column|Field)\\s*\\(\\s*[\"'](?:ssn|social_security|tax_id|credit_card|card_number|cvv|passport|driver_license|bank_account|routing_number)[\"']\\s*,\\s*(?:String|TEXT|VARCHAR|Text|text)/gi,\n /(?:ssn|social_security|tax_id|credit_card|card_number|cvv|passport_number|driver_license|bank_account|routing_number)\\s+(?:TEXT|VARCHAR|String|text|character varying)/gi,\n ];\n for (const pat of schemaPatterns) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC129\", title: unencryptedPII.title, severity: \"high\" as const, category: \"Information Leakage\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Encrypt sensitive fields at the application level before storing. Use field-level encryption with a KMS for SSN, credit cards, and health data.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC130 – Missing Rate Limit on Auth Endpoints\n// ────────────────────────────────────────────\n\nexport const missingAuthRateLimit: CustomRule = {\n id: \"VC130\",\n title: \"Authentication Endpoint Without Rate Limiting\",\n severity: \"high\",\n category: \"Authentication\",\n description: \"Login, registration, and password reset endpoints without rate limiting are vulnerable to credential stuffing and brute-force attacks.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n // Routes that handle auth operations\n const authRoutePattern = /(?:app|router|server)\\.(?:post|put)\\s*\\(\\s*[\"'](?:\\/(?:api\\/)?(?:auth\\/)?(?:login|signin|sign-in|register|signup|sign-up|forgot-password|reset-password))[\"']/gi;\n let m;\n while ((m = authRoutePattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Check surrounding code for rate limiting\n const surrounding = content.substring(Math.max(0, m.index - 300), Math.min(content.length, m.index + 300));\n if (/rateLimit|rateLimiter|throttle|slowDown|express-rate-limit|rate_limit|RateLimiter|limiter/i.test(surrounding)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC130\", title: missingAuthRateLimit.title, severity: \"high\" as const, category: \"Authentication\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add rate limiting to authentication endpoints. Limit to 5-10 attempts per minute per IP. Use express-rate-limit or similar.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC131 – Known Vulnerable Dependency Pattern\n// ────────────────────────────────────────────\n\nexport const vulnerableDependencies: CustomRule = {\n id: \"VC131\",\n title: \"Potentially Vulnerable Dependency\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"Dependencies with known security issues that are commonly found in AI-generated codebases.\",\n check(content, filePath) {\n if (!filePath.endsWith(\"package.json\")) return [];\n // Skip nested package.json (node_modules, etc)\n if (/node_modules|\\.next|dist|build/.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Known problematic packages and version ranges\n const vulnerablePackages: [RegExp, string][] = [\n [/\"jsonwebtoken\"\\s*:\\s*\"[\\^~]?[0-7]\\./g, \"jsonwebtoken < 8.x has signature bypass vulnerabilities\"],\n [/\"lodash\"\\s*:\\s*\"[\\^~]?[0-3]\\./g, \"lodash < 4.x has prototype pollution vulnerabilities\"],\n [/\"minimist\"\\s*:\\s*\"[\\^~]?[01]\\.[01]\\./g, \"minimist < 1.2.6 has prototype pollution\"],\n [/\"node-fetch\"\\s*:\\s*\"[\\^~]?[12]\\./g, \"node-fetch < 3.x has data exposure issues\"],\n [/\"express\"\\s*:\\s*\"[\\^~]?[0-3]\\./g, \"express < 4.x has multiple known vulnerabilities\"],\n [/\"axios\"\\s*:\\s*\"[\\^~]?0\\.[0-9]\\./g, \"axios < 0.21 has SSRF vulnerabilities\"],\n [/\"tar\"\\s*:\\s*\"[\\^~]?[0-5]\\./g, \"tar < 6.x has path traversal vulnerabilities\"],\n [/\"glob-parent\"\\s*:\\s*\"[\\^~]?[0-4]\\./g, \"glob-parent < 5.1.2 has ReDoS vulnerability\"],\n ];\n for (const [pat, message] of vulnerablePackages) {\n let m;\n while ((m = pat.exec(content)) !== null) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC131\", title: vulnerableDependencies.title, severity: \"high\" as const, category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: `${message}. Update to the latest version and run 'npm audit' regularly.`,\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC132–VC145: SERVICE-SPECIFIC API KEY DETECTION\n// Each rule targets a service with a known key prefix/format.\n// Descriptions include permission context (what the key can do).\n// ────────────────────────────────────────────\n\nconst SECRET_FILE_EXT = /\\.(js|ts|jsx|tsx|py|rb|go|java|php|env|json|yaml|yml|toml|cfg|conf|ini|sh|bash|zsh)$/;\nconst PLACEHOLDER_RE = /process\\.env|os\\.environ|ENV\\[|getenv|System\\.getenv|env\\(|CHANGE_ME|YOUR_|xxx|placeholder|example|TODO|FIXME|<your|dummy|fake_?key|test_?key/i;\nconst LOCK_FILE_RE = /package-lock|yarn\\.lock|pnpm-lock|Gemfile\\.lock|Pipfile\\.lock|composer\\.lock/i;\n\nfunction secretRuleCheck(\n content: string,\n filePath: string,\n pattern: RegExp,\n ruleId: string,\n title: string,\n severity: \"critical\" | \"high\",\n fix: string,\n): RuleMatch[] {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const re = new RegExp(pattern.source, pattern.flags.includes(\"g\") ? pattern.flags : `${pattern.flags}g`);\n let m: RegExpExecArray | null;\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n // Skip if this looks like an env var reference, not a literal\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: ruleId, title, severity, category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum), fix,\n });\n }\n return findings;\n}\n\nexport const hardcodedAnthropicKey: CustomRule = {\n id: \"VC132\",\n title: \"Hardcoded Anthropic API Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Anthropic API keys (sk-ant-*) grant full API access: running models, incurring charges, and accessing usage history. A leaked key can be used to make unlimited API calls at your expense.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /sk-ant-api03-[A-Za-z0-9_\\-]{80,}/g, \"VC132\", this.title, \"critical\",\n \"Move the Anthropic API key to an environment variable (ANTHROPIC_API_KEY). Rotate the exposed key immediately at console.anthropic.com → API Keys.\");\n },\n};\n\nexport const hardcodedGitHubPAT: CustomRule = {\n id: \"VC133\",\n title: \"Hardcoded GitHub Personal Access Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_) grant repository access: reading/writing code, managing issues, triggering Actions, and accessing private repos depending on scopes.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}/g, \"VC133\", this.title, \"critical\",\n \"Move the GitHub token to an environment variable (GITHUB_TOKEN). Rotate immediately at github.com → Settings → Developer settings → Personal access tokens. Consider using fine-grained tokens with minimal permissions.\");\n },\n};\n\nexport const hardcodedSendGridKey: CustomRule = {\n id: \"VC134\",\n title: \"Hardcoded SendGrid API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"SendGrid API keys (SG.*) can send emails as your domain, access contact lists, view email activity, and modify sender reputation. A leaked key enables phishing from your verified domain.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /SG\\.[A-Za-z0-9_\\-]{22}\\.[A-Za-z0-9_\\-]{43}/g, \"VC134\", this.title, \"high\",\n \"Move the SendGrid API key to an environment variable (SENDGRID_API_KEY). Rotate at app.sendgrid.com → Settings → API Keys.\");\n },\n};\n\nexport const hardcodedSlackToken: CustomRule = {\n id: \"VC135\",\n title: \"Hardcoded Slack Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Slack tokens (xoxb-, xoxp-, xapp-) can read/send messages, access channels, list users, and manage workspace settings depending on scopes. Bot tokens (xoxb) have app-level access; user tokens (xoxp) act as the installing user.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /xox[bpas]-[0-9A-Za-z\\-]{10,}/g, \"VC135\", this.title, \"high\",\n \"Move the Slack token to an environment variable (SLACK_BOT_TOKEN). Rotate at api.slack.com → Your Apps → OAuth & Permissions → Reinstall.\");\n },\n};\n\nexport const hardcodedGCPServiceAccount: CustomRule = {\n id: \"VC136\",\n title: \"Hardcoded GCP Service Account Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GCP service account JSON keys grant project-level access: compute, storage, databases, IAM, and billing depending on assigned roles. A leaked key can compromise your entire Google Cloud project.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(json|js|ts|py|yaml|yml)$/)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for the combination of service_account type + private key in the same file\n if (!/\"type\"\\s*:\\s*\"service_account\"/.test(content)) return [];\n if (!/-----BEGIN (?:RSA )?PRIVATE KEY-----/.test(content)) return [];\n const findings: RuleMatch[] = [];\n const m = content.match(/\"type\"\\s*:\\s*\"service_account\"/);\n if (m && m.index !== undefined) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC136\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"GCP service account keys should never be committed. Use Workload Identity Federation or store the key in a secrets manager. Delete this key in Google Cloud Console → IAM → Service Accounts and generate a new one.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedShopifyToken: CustomRule = {\n id: \"VC137\",\n title: \"Hardcoded Shopify Access Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Shopify access tokens (shpat_, shpca_, shppa_) grant full store access: orders, customers, products, and payment information. A leaked token can expose customer PII and modify store data.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /shp(?:at|ca|pa)_[a-fA-F0-9]{32,}/g, \"VC137\", this.title, \"critical\",\n \"Move the Shopify token to an environment variable. Rotate in Shopify Admin → Apps → Develop apps → API credentials.\");\n },\n};\n\nexport const hardcodedGitLabToken: CustomRule = {\n id: \"VC138\",\n title: \"Hardcoded GitLab Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"GitLab personal/project tokens (glpat-) grant repository and CI/CD access: reading/writing code, triggering pipelines, and managing project settings.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /glpat-[A-Za-z0-9_\\-]{20,}/g, \"VC138\", this.title, \"critical\",\n \"Move the GitLab token to an environment variable. Revoke and regenerate at gitlab.com → Preferences → Access Tokens.\");\n },\n};\n\nexport const hardcodedTwilioKey: CustomRule = {\n id: \"VC139\",\n title: \"Hardcoded Twilio API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Twilio API key SIDs (SK*) can send SMS/calls, access call recordings and logs, and modify account configuration. A leaked key enables toll fraud and unauthorized communications.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /SK[0-9a-fA-F]{32}/g, \"VC139\", this.title, \"high\",\n \"Move the Twilio API key to an environment variable (TWILIO_API_KEY). Rotate at twilio.com → Console → API Keys.\");\n },\n};\n\nexport const hardcodedMailgunKey: CustomRule = {\n id: \"VC140\",\n title: \"Hardcoded Mailgun API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Mailgun API keys (key-*) can send emails as your domain, access email logs, and manage routes. A leaked key enables phishing from your verified sending domain.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /key-[0-9a-zA-Z]{32}/g, \"VC140\", this.title, \"high\",\n \"Move the Mailgun key to an environment variable (MAILGUN_API_KEY). Rotate at app.mailgun.com → Settings → API Security.\");\n },\n};\n\nexport const hardcodedDatadogKey: CustomRule = {\n id: \"VC141\",\n title: \"Hardcoded Datadog API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Datadog API/app keys grant access to all monitoring data and can modify dashboards, alerts, and integrations. A leaked key exposes your entire infrastructure topology.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for DD_API_KEY or DD_APP_KEY assignments with hex values\n const pattern = /(?:DD_API_KEY|DD_APP_KEY|datadog[_-]?(?:api|app)[_-]?key)\\s*[:=]\\s*[\"'`]([a-f0-9]{32})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC141\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Datadog key to an environment variable (DD_API_KEY). Rotate at app.datadoghq.com → Organization Settings → API Keys.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedVercelToken: CustomRule = {\n id: \"VC142\",\n title: \"Hardcoded Vercel Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Vercel tokens can deploy projects, manage environment variables (which may contain other secrets), and access project settings. A leaked token can modify your production deployments.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n const pattern = /(?:VERCEL_TOKEN|vercel[_-]?token|vercel[_-]?api[_-]?token)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{24,})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC142\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Vercel token to an environment variable (VERCEL_TOKEN). Rotate at vercel.com → Settings → Tokens.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedSupabaseServiceRole: CustomRule = {\n id: \"VC143\",\n title: \"Hardcoded Supabase Service Role Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Supabase service role keys bypass Row Level Security and grant full database read/write access, auth admin, and storage admin. A leaked service role key exposes all user data.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n // Look for service_role key assignments (JWT format near service_role context).\n // Supabase service role keys are JWTs — header.payload.signature. The `.`\n // between the three base64url segments MUST be in the character class, or\n // we miss every real token. Previously this regex excluded the dots and\n // silently passed right over real service-role keys.\n const pattern = /(?:service[_-]?role[_-]?key|SUPABASE_SERVICE_ROLE_KEY|supabaseServiceRole)\\s*[:=]\\s*[\"'`](eyJ[A-Za-z0-9_\\-.]{50,})[\"'`]/gi;\n const findings: RuleMatch[] = [];\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC143\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move the Supabase service role key to a server-side environment variable. This key bypasses RLS — it must NEVER be exposed to the client. Rotate at supabase.com → Project Settings → API.\",\n });\n }\n return findings;\n },\n};\n\nexport const hardcodedVaultToken: CustomRule = {\n id: \"VC144\",\n title: \"Hardcoded HashiCorp Vault Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Vault tokens (hvs.*, s.*) grant access to secrets stored in HashiCorp Vault, potentially exposing database credentials, API keys, and certificates across your entire infrastructure.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /(?:hvs\\.[A-Za-z0-9_\\-]{24,}|(?:VAULT_TOKEN|vault[_-]?token)\\s*[:=]\\s*[\"'`]s\\.[A-Za-z0-9]{24,}[\"'`])/g, \"VC144\", this.title, \"critical\",\n \"Move the Vault token to an environment variable (VAULT_TOKEN). Revoke the exposed token immediately with `vault token revoke`. Use short-lived tokens with minimal policies.\");\n },\n};\n\nexport const hardcodedPineconeKey: CustomRule = {\n id: \"VC145\",\n title: \"Hardcoded Pinecone API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Pinecone API keys can read, write, and delete all vector data and manage indexes. A leaked key exposes your entire vector database and the embeddings stored in it.\",\n check(content, filePath) {\n return secretRuleCheck(content, filePath, /pcsk_[A-Za-z0-9_\\-]{50,}/g, \"VC145\", this.title, \"high\",\n \"Move the Pinecone API key to an environment variable (PINECONE_API_KEY). Rotate at app.pinecone.io → API Keys.\");\n },\n};\n\n// ────────────────────────────────────────────\n// VC159–VC183: ADDITIONAL SERVICE-SPECIFIC API KEY DETECTION\n//\n// Same pattern as VC132–VC145 — each rule targets a service with a distinctive\n// key prefix or a context-anchored variable assignment. Severity follows the\n// same convention: \"critical\" for keys that grant infrastructure / data\n// access (cloud, auth, storage), \"high\" for keys that grant scoped service\n// access (email, chat, vector DBs, observability).\n//\n// Detection strategy:\n// - PREFIX-based: When a vendor uses a distinctive token prefix (e.g.\n// Resend's `re_`, Groq's `gsk_`), match the prefix +\n// expected suffix length. Lowest false-positive rate.\n// - CONTEXT-based: When a vendor's tokens are just hex/alphanumeric\n// without a prefix, anchor on the variable name (e.g.\n// `COHERE_API_KEY: \"...\"`). Higher precision than naked\n// entropy, lower coverage than a prefix match.\n//\n// All rules go through `secretRuleCheck` (prefix-based) or\n// `contextSecretRuleCheck` (context-based) which apply the same skip rules:\n// test files, lock files, comment lines, fix-message embedding, and\n// placeholder strings (process.env, CHANGE_ME, YOUR_, etc).\n// ────────────────────────────────────────────\n\n/**\n * Helper for context-based secret rules — vendors whose tokens are plain\n * hex/alphanumeric with no distinctive prefix. Anchors detection on a\n * variable-name pattern + value capture so naked random strings elsewhere\n * in the file don't fire.\n *\n * Mirrors the inline pattern already used by VC141 (Datadog), VC142 (Vercel),\n * VC143 (Supabase service role). Extracted here because 9 of the 25 new\n * rules need the same shape and inlining 9 copies is a maintenance trap.\n */\nfunction contextSecretRuleCheck(\n content: string,\n filePath: string,\n pattern: RegExp,\n ruleId: string,\n title: string,\n severity: \"critical\" | \"high\",\n fix: string,\n): RuleMatch[] {\n if (isTestFile(filePath)) return [];\n if (!SECRET_FILE_EXT.test(filePath)) return [];\n if (LOCK_FILE_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const re = new RegExp(pattern.source, pattern.flags.includes(\"g\") ? pattern.flags : `${pattern.flags}g`);\n let m: RegExpExecArray | null;\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineStart = content.lastIndexOf(\"\\n\", m.index - 1) + 1;\n const lineText = content.substring(lineStart, content.indexOf(\"\\n\", m.index));\n if (PLACEHOLDER_RE.test(lineText)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: ruleId, title, severity, category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum), fix,\n });\n }\n return findings;\n}\n\n// ─── AI providers ───────────────────────────────────────────────────────\n\nexport const hardcodedCohereKey: CustomRule = {\n id: \"VC159\",\n title: \"Hardcoded Cohere API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Cohere API keys grant access to all model endpoints (generation, embeddings, classification) and incur charges per token. A leaked key can be used to run unlimited inference at your expense.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:COHERE_API_KEY|cohere[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{40})[\"'`]/gi,\n \"VC159\",\n this.title,\n \"high\",\n \"Move the Cohere API key to an environment variable (COHERE_API_KEY). Rotate at dashboard.cohere.com → API keys.\",\n );\n },\n};\n\nexport const hardcodedReplicateKey: CustomRule = {\n id: \"VC160\",\n title: \"Hardcoded Replicate API Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Replicate API tokens (r8_*) grant access to run any model in the Replicate catalog and incur charges per second of GPU time. A leaked token can run expensive image/video models at your expense.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /r8_[A-Za-z0-9]{40,}/g,\n \"VC160\",\n this.title,\n \"high\",\n \"Move the Replicate token to an environment variable (REPLICATE_API_TOKEN). Rotate at replicate.com → Account → API tokens.\",\n );\n },\n};\n\nexport const hardcodedMistralKey: CustomRule = {\n id: \"VC161\",\n title: \"Hardcoded Mistral API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Mistral API keys grant access to all chat and embedding models and incur charges per token. A leaked key can be used to run unlimited inference at your expense.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:MISTRAL_API_KEY|mistral[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{32})[\"'`]/gi,\n \"VC161\",\n this.title,\n \"high\",\n \"Move the Mistral key to an environment variable (MISTRAL_API_KEY). Rotate at console.mistral.ai → API Keys.\",\n );\n },\n};\n\nexport const hardcodedTogetherKey: CustomRule = {\n id: \"VC162\",\n title: \"Hardcoded Together AI API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Together AI API keys grant access to all open-source models hosted on the platform (Llama, Mixtral, Stable Diffusion, etc.) and incur charges per token. A leaked key can run unlimited inference at your expense.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:TOGETHER_API_KEY|together[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([a-f0-9]{64})[\"'`]/gi,\n \"VC162\",\n this.title,\n \"high\",\n \"Move the Together AI key to an environment variable (TOGETHER_API_KEY). Rotate at api.together.ai → Settings → API Keys.\",\n );\n },\n};\n\nexport const hardcodedGroqKey: CustomRule = {\n id: \"VC163\",\n title: \"Hardcoded Groq API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Groq API keys (gsk_*) grant access to all hosted models on Groq's LPU inference platform and incur per-token charges. A leaked key can be used to run unlimited inference at your expense.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /gsk_[A-Za-z0-9]{52,}/g,\n \"VC163\",\n this.title,\n \"high\",\n \"Move the Groq key to an environment variable (GROQ_API_KEY). Rotate at console.groq.com → API Keys.\",\n );\n },\n};\n\nexport const hardcodedFireworksKey: CustomRule = {\n id: \"VC164\",\n title: \"Hardcoded Fireworks AI API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Fireworks AI API keys (fw_*) grant access to hosted open-source models and incur per-token charges. A leaked key can be used to run unlimited inference at your expense.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /fw_[A-Za-z0-9]{20,}/g,\n \"VC164\",\n this.title,\n \"high\",\n \"Move the Fireworks key to an environment variable (FIREWORKS_API_KEY). Rotate at fireworks.ai → API Keys.\",\n );\n },\n};\n\n// ─── Email providers ────────────────────────────────────────────────────\n\nexport const hardcodedPostmarkKey: CustomRule = {\n id: \"VC165\",\n title: \"Hardcoded Postmark Server Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Postmark server tokens grant the ability to send emails as your domain, view delivery logs, and manage templates. A leaked token enables phishing from your verified sending domain.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:POSTMARK_(?:API|SERVER)_TOKEN|postmark[_-]?(?:server|api)[_-]?token)\\s*[:=]\\s*[\"'`]([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})[\"'`]/gi,\n \"VC165\",\n this.title,\n \"high\",\n \"Move the Postmark token to an environment variable (POSTMARK_SERVER_TOKEN). Rotate at account.postmarkapp.com → Servers → API Tokens.\",\n );\n },\n};\n\nexport const hardcodedResendKey: CustomRule = {\n id: \"VC166\",\n title: \"Hardcoded Resend API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Resend API keys (re_*) can send emails as your domain, access email logs, and manage domains. A leaked key enables phishing from your verified sending domain.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /re_[A-Za-z0-9_]{20,}/g,\n \"VC166\",\n this.title,\n \"high\",\n \"Move the Resend API key to an environment variable (RESEND_API_KEY). Rotate at resend.com → API Keys.\",\n );\n },\n};\n\nexport const hardcodedLoopsKey: CustomRule = {\n id: \"VC167\",\n title: \"Hardcoded Loops API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Loops API keys grant access to your contact list, transactional email sending, and audience segmentation. A leaked key can exfiltrate your customer list and send unauthorized emails.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:LOOPS_API_KEY|loops[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{32,})[\"'`]/gi,\n \"VC167\",\n this.title,\n \"high\",\n \"Move the Loops API key to an environment variable (LOOPS_API_KEY). Rotate at app.loops.so → Settings → API.\",\n );\n },\n};\n\n// ─── Hosting / infra ────────────────────────────────────────────────────\n\nexport const hardcodedCloudflareToken: CustomRule = {\n id: \"VC168\",\n title: \"Hardcoded Cloudflare API Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Cloudflare API tokens grant access to DNS records, SSL configuration, Workers, R2 storage, and account settings depending on token scope. A leaked token can hijack DNS, intercept traffic, or wipe storage.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:CLOUDFLARE_API_TOKEN|CF_API_TOKEN|cloudflare[_-]?api[_-]?token)\\s*[:=]\\s*[\"'`]([A-Za-z0-9_\\-]{40})[\"'`]/gi,\n \"VC168\",\n this.title,\n \"critical\",\n \"Move the Cloudflare token to an environment variable (CLOUDFLARE_API_TOKEN). Rotate at dash.cloudflare.com → My Profile → API Tokens.\",\n );\n },\n};\n\nexport const hardcodedFastlyToken: CustomRule = {\n id: \"VC169\",\n title: \"Hardcoded Fastly API Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Fastly API tokens can purge cache, modify VCL configuration, and access service settings. A leaked token can disrupt CDN caching or redirect traffic via VCL changes.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:FASTLY_API_(?:KEY|TOKEN)|fastly[_-]?api[_-]?(?:key|token))\\s*[:=]\\s*[\"'`]([A-Za-z0-9_\\-]{32,})[\"'`]/gi,\n \"VC169\",\n this.title,\n \"high\",\n \"Move the Fastly token to an environment variable (FASTLY_API_TOKEN). Rotate at manage.fastly.com → Account → API tokens.\",\n );\n },\n};\n\nexport const hardcodedNetlifyToken: CustomRule = {\n id: \"VC170\",\n title: \"Hardcoded Netlify Personal Access Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Netlify access tokens (nfp_*) grant access to deploy any site, modify environment variables (which may contain other secrets), and manage team settings. A leaked token can compromise production deployments.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /nfp_[A-Za-z0-9_\\-]{20,}/g,\n \"VC170\",\n this.title,\n \"critical\",\n \"Move the Netlify token to an environment variable (NETLIFY_AUTH_TOKEN). Rotate at app.netlify.com → User Settings → Applications → Personal access tokens.\",\n );\n },\n};\n\nexport const hardcodedRailwayToken: CustomRule = {\n id: \"VC171\",\n title: \"Hardcoded Railway API Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Railway API tokens grant project deployment, environment variable management, and database access. A leaked token can read all your project secrets and modify production services.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:RAILWAY_API_TOKEN|RAILWAY_TOKEN|railway[_-]?api[_-]?token)\\s*[:=]\\s*[\"'`]([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})[\"'`]/gi,\n \"VC171\",\n this.title,\n \"critical\",\n \"Move the Railway token to an environment variable (RAILWAY_TOKEN). Rotate at railway.app → Account Settings → Tokens.\",\n );\n },\n};\n\nexport const hardcodedFlyToken: CustomRule = {\n id: \"VC172\",\n title: \"Hardcoded Fly.io Auth Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Fly.io tokens (FlyV1 fm2_*) grant access to deploy and configure any app in your organization, including modifying secrets and machines. A leaked token can compromise production infrastructure.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /FlyV1\\s+fm2_[A-Za-z0-9+/=_\\-]{40,}/g,\n \"VC172\",\n this.title,\n \"critical\",\n \"Move the Fly.io token to an environment variable (FLY_API_TOKEN). Rotate via `fly tokens revoke` and `fly tokens create deploy`.\",\n );\n },\n};\n\n// ─── Search / vector DBs ────────────────────────────────────────────────\n\nexport const hardcodedAlgoliaAdminKey: CustomRule = {\n id: \"VC173\",\n title: \"Hardcoded Algolia Admin API Key\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Algolia admin API keys grant full access to manage indices, modify records, and create/delete API keys. A leaked admin key exposes all search data and lets attackers replace indexed content.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:ALGOLIA_ADMIN_(?:API_)?KEY|algolia[_-]?admin[_-]?(?:api[_-]?)?key)\\s*[:=]\\s*[\"'`]([a-f0-9]{32})[\"'`]/gi,\n \"VC173\",\n this.title,\n \"critical\",\n \"Move the Algolia admin key to a server-side environment variable (ALGOLIA_ADMIN_KEY). NEVER expose admin keys to the client — use search-only API keys for browser code. Rotate at dashboard.algolia.com → API Keys.\",\n );\n },\n};\n\nexport const hardcodedQdrantKey: CustomRule = {\n id: \"VC174\",\n title: \"Hardcoded Qdrant API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Qdrant API keys grant read/write access to all vector collections in your cluster. A leaked key exposes your vector database and the embeddings stored in it.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:QDRANT_API_KEY|qdrant[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9_\\-\\.]{30,})[\"'`]/gi,\n \"VC174\",\n this.title,\n \"high\",\n \"Move the Qdrant API key to an environment variable (QDRANT_API_KEY). Rotate in your Qdrant Cloud dashboard → API Keys.\",\n );\n },\n};\n\nexport const hardcodedWeaviateKey: CustomRule = {\n id: \"VC175\",\n title: \"Hardcoded Weaviate API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Weaviate API keys grant read/write access to all classes and objects in your vector database. A leaked key exposes embeddings and metadata for every document indexed.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:WEAVIATE_API_KEY|weaviate[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9_\\-]{20,})[\"'`]/gi,\n \"VC175\",\n this.title,\n \"high\",\n \"Move the Weaviate API key to an environment variable (WEAVIATE_API_KEY). Rotate in your Weaviate Cloud Services console → Cluster details.\",\n );\n },\n};\n\n// ─── Productivity / chat ────────────────────────────────────────────────\n\nexport const hardcodedLinearKey: CustomRule = {\n id: \"VC176\",\n title: \"Hardcoded Linear API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Linear API keys (lin_api_*) grant read/write access to all issues, projects, and team data. A leaked key exposes private roadmap, customer-reported bugs, and internal discussion.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /lin_(?:api|oauth)_[A-Za-z0-9]{40,}/g,\n \"VC176\",\n this.title,\n \"high\",\n \"Move the Linear key to an environment variable (LINEAR_API_KEY). Rotate at linear.app → Settings → API → Personal API keys.\",\n );\n },\n};\n\nexport const hardcodedNotionKey: CustomRule = {\n id: \"VC177\",\n title: \"Hardcoded Notion Integration Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Notion integration tokens (secret_*) grant access to every page and database the integration is connected to, including content, comments, and member info. A leaked token exposes private workspace data.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /secret_[A-Za-z0-9]{43}/g,\n \"VC177\",\n this.title,\n \"high\",\n \"Move the Notion token to an environment variable (NOTION_TOKEN). Rotate at notion.so/profile/integrations.\",\n );\n },\n};\n\nexport const hardcodedDiscordToken: CustomRule = {\n id: \"VC178\",\n title: \"Hardcoded Discord Bot Token\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Discord bot tokens grant full control over the bot's actions in every guild it has joined: reading messages, sending messages, managing channels, and accessing member data per intent scopes. A leaked token can be used to spam, exfiltrate messages, or take over channels.\",\n check(content, filePath) {\n // Discord bot token format: <user_id_b64>.<timestamp_b64>.<hmac> — the\n // user ID prefix is base64-encoded and starts with M, N, O (Discord\n // snowflake IDs in the bot range).\n return secretRuleCheck(\n content,\n filePath,\n /[MNO][A-Za-z\\d_\\-]{23,28}\\.[\\w\\-]{6,7}\\.[\\w\\-]{27,38}/g,\n \"VC178\",\n this.title,\n \"critical\",\n \"Move the Discord token to an environment variable (DISCORD_TOKEN). Reset immediately at discord.com/developers/applications → your bot → Bot → Reset Token.\",\n );\n },\n};\n\nexport const hardcodedIntercomToken: CustomRule = {\n id: \"VC179\",\n title: \"Hardcoded Intercom Access Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Intercom access tokens grant access to all customer conversations, contact records, and company data. A leaked token exposes customer PII and support history.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:INTERCOM_ACCESS_TOKEN|intercom[_-]?access[_-]?token|intercom[_-]?api[_-]?key)\\s*[:=]\\s*[\"'`]([A-Za-z0-9_=+/\\-]{50,})[\"'`]/gi,\n \"VC179\",\n this.title,\n \"high\",\n \"Move the Intercom token to an environment variable (INTERCOM_ACCESS_TOKEN). Rotate at app.intercom.com → Settings → Developers → Apps.\",\n );\n },\n};\n\n// ─── Observability ──────────────────────────────────────────────────────\n\nexport const hardcodedSentryAuthToken: CustomRule = {\n id: \"VC180\",\n title: \"Hardcoded Sentry Auth Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Sentry auth tokens (sntrys_*) grant access to error data across every project the token is scoped to, plus the ability to manage releases, projects, and source maps. A leaked token exposes stack traces, PII captured in errors, and lets attackers tamper with release artifacts.\",\n check(content, filePath) {\n return secretRuleCheck(\n content,\n filePath,\n /sntrys_[A-Za-z0-9_]{40,}/g,\n \"VC180\",\n this.title,\n \"high\",\n \"Move the Sentry auth token to an environment variable (SENTRY_AUTH_TOKEN). Rotate at sentry.io → User Settings → Auth Tokens.\",\n );\n },\n};\n\nexport const hardcodedLogtailToken: CustomRule = {\n id: \"VC181\",\n title: \"Hardcoded Better Stack (Logtail) Source Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Better Stack / Logtail source tokens grant the ability to write logs to your account. A leaked token lets attackers fill your log retention with junk data, mask their own activity in noise, or rack up your ingestion bill.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:LOGTAIL_(?:SOURCE_)?TOKEN|BETTERSTACK_(?:SOURCE_)?TOKEN|logtail[_-]?(?:source[_-]?)?token)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{20,})[\"'`]/gi,\n \"VC181\",\n this.title,\n \"high\",\n \"Move the Logtail token to an environment variable (LOGTAIL_SOURCE_TOKEN). Rotate in betterstack.com → Sources → Edit.\",\n );\n },\n};\n\nexport const hardcodedHighlightKey: CustomRule = {\n id: \"VC182\",\n title: \"Hardcoded Highlight.io Project ID / API Key\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Highlight.io API keys grant access to recorded session replays, error data, and console logs from your users' browsers. A leaked key exposes user behavior data and any sensitive content captured in replays.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:HIGHLIGHT_API_KEY|HIGHLIGHT_PROJECT_ID|highlight[_-]?(?:api[_-]?key|project[_-]?id))\\s*[:=]\\s*[\"'`]([A-Za-z0-9_\\-]{20,})[\"'`]/gi,\n \"VC182\",\n this.title,\n \"high\",\n \"Move the Highlight key to an environment variable (HIGHLIGHT_API_KEY). Rotate at app.highlight.io → Project Settings → API Keys.\",\n );\n },\n};\n\n// ─── Telecom ────────────────────────────────────────────────────────────\n\nexport const hardcodedPlivoToken: CustomRule = {\n id: \"VC183\",\n title: \"Hardcoded Plivo Auth Token\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Plivo auth tokens grant the ability to send SMS, place calls, and access call/message logs. A leaked token enables toll fraud and unauthorized communications billed to your account.\",\n check(content, filePath) {\n return contextSecretRuleCheck(\n content,\n filePath,\n /(?:PLIVO_AUTH_TOKEN|plivo[_-]?auth[_-]?token)\\s*[:=]\\s*[\"'`]([A-Za-z0-9]{40,})[\"'`]/gi,\n \"VC183\",\n this.title,\n \"high\",\n \"Move the Plivo token to an environment variable (PLIVO_AUTH_TOKEN). Rotate at console.plivo.com → Account → API Keys & Credentials.\",\n );\n },\n};\n\n// ────────────────────────────────────────────\n// VC184–VC187: GITHUB ACTIONS WORKFLOW SECURITY\n//\n// Catches the most common attack patterns in `.github/workflows/*.yml`\n// files. AI tools generate workflows constantly and rarely understand the\n// security model — the same `pull_request_target` mistake that compromised\n// hundreds of repos in the 2024–2025 supply-chain incidents.\n//\n// All rules scope to workflow files only:\n// `.github/workflows/<name>.yml` or `.yaml`\n// ────────────────────────────────────────────\n\nconst GHA_WORKFLOW_RE = /\\.github\\/workflows\\/.*\\.(yml|yaml)$/;\n\nexport const ghaPullRequestTargetCheckout: CustomRule = {\n id: \"VC184\",\n title: \"GitHub Actions: pull_request_target with checkout of PR head\",\n severity: \"critical\",\n category: \"Supply Chain\",\n description: \"Workflows triggered by `pull_request_target` run with the base repository's secrets and write permissions. Checking out the PR's head ref (or sha) and then executing any code from that checkout — install scripts, build steps, lint hooks — gives attackers in any forked PR full code execution with your repository's secrets. This is the canonical \\\"pwn request\\\" attack pattern.\",\n check(content, filePath) {\n if (!GHA_WORKFLOW_RE.test(filePath)) return [];\n // Both conditions must be true: the workflow uses pull_request_target AND\n // a checkout step references the PR's head. Either alone is fine.\n if (!/(^|\\n)\\s*(?:on\\s*:\\s*\\[?[^\\]]*pull_request_target|pull_request_target\\s*:)/.test(content)) {\n return [];\n }\n const findings: RuleMatch[] = [];\n const checkoutPattern =\n /uses\\s*:\\s*actions\\/checkout@[^\\n]*[\\s\\S]{0,400}?ref\\s*:\\s*\\$\\{\\{\\s*github\\.event\\.pull_request\\.head\\.(?:ref|sha)\\s*\\}\\}/g;\n let m: RegExpExecArray | null;\n while ((m = checkoutPattern.exec(content)) !== null) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC184\", title: ghaPullRequestTargetCheckout.title, severity: \"critical\", category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Either (a) switch the trigger to `pull_request` (no secrets, but safe), or (b) keep `pull_request_target` but check out only the BASE ref (don't pass `ref: ${{ github.event.pull_request.head.* }}`), or (c) split into two workflows: a `pull_request` workflow that does the build/test, and a `pull_request_target` workflow that only does the privileged step (commenting, labeling) without executing PR code.\",\n });\n }\n return findings;\n },\n};\n\nexport const ghaPermissionsWriteAll: CustomRule = {\n id: \"VC185\",\n title: \"GitHub Actions: permissions set to write-all\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"`permissions: write-all` (or omitting `permissions` entirely on a public repo with default-permissive settings) gives every step in the workflow write access to the repo, packages, deployments, and more. If any action in the workflow is compromised — directly or via a transitive dependency — it can push commits, modify releases, and exfiltrate secrets. Default-deny + grant only what each job needs.\",\n check(content, filePath) {\n if (!GHA_WORKFLOW_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const pattern = /(^|\\n)(\\s*)permissions\\s*:\\s*write-all\\s*(?:#[^\\n]*)?$/gm;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n const lineNum = content.substring(0, m.index + m[1].length).split(\"\\n\").length;\n findings.push({\n rule: \"VC185\", title: ghaPermissionsWriteAll.title, severity: \"high\", category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Replace `permissions: write-all` with an explicit allowlist: `permissions:\\\\n contents: read\\\\n pull-requests: write` (or whichever scopes the workflow actually needs). See https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs.\",\n });\n }\n return findings;\n },\n};\n\nexport const ghaExpressionInjection: CustomRule = {\n id: \"VC186\",\n title: \"GitHub Actions: expression injection in run block\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Interpolating `${{ github.event.* }}` (issue title, PR body, commit message, branch name, etc.) directly into a shell script in a `run:` block lets attackers inject arbitrary shell commands by crafting the trigger payload. Same class of bug as SQL injection — the value is untrusted and gets evaluated by the shell.\",\n check(content, filePath) {\n if (!GHA_WORKFLOW_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Find run: blocks that interpolate untrusted github.event.* / github.head_ref values.\n // The block-scalar form `run: |` is the most common; we also catch single-line.\n // Untrusted contexts (per GitHub's own security guidance):\n // github.event.issue.title / .body\n // github.event.pull_request.title / .body / .head.ref\n // github.event.comment.body / .review.body\n // github.head_ref (alias for github.event.pull_request.head.ref)\n // Match both block-scalar `run: |` / `run: >` (multi-line) and single-line\n // `run: echo ${{ github.head_ref }}` forms. The original pattern required\n // `[|>]` after `run:`, which silently missed every single-line injection\n // — caught by Macroscope on PR #306 review (false negative).\n const pattern =\n /run\\s*:\\s*(?:[\\|>][^\\n]*\\n[\\s\\S]*?|.*?)\\$\\{\\{\\s*github\\.(?:event\\.(?:issue|pull_request|comment|review|discussion)\\.[^}]*|head_ref)\\s*\\}\\}/g;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n // Find the line of the interpolation, not the run: opener\n const interpStart = m[0].lastIndexOf(\"${{\");\n const absIdx = m.index + interpStart;\n const lineNum = content.substring(0, absIdx).split(\"\\n\").length;\n findings.push({\n rule: \"VC186\", title: ghaExpressionInjection.title, severity: \"high\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Don't interpolate untrusted `github.event.*` values directly into shell scripts. Instead, pass them via env vars: `env:\\\\n TITLE: ${{ github.event.issue.title }}\\\\nrun: echo \\\"$TITLE\\\"`. Env var expansion in the shell is safe; expression interpolation is not.\",\n });\n // Avoid re-matching the same run: block multiple times\n pattern.lastIndex = m.index + m[0].length;\n }\n return findings;\n },\n};\n\nexport const ghaThirdPartyActionWithSecrets: CustomRule = {\n id: \"VC187\",\n title: \"GitHub Actions: secrets passed to third-party action\",\n severity: \"medium\",\n category: \"Supply Chain\",\n description: \"Passing repository secrets (`${{ secrets.* }}`) via the `with:` block to a third-party action means the action's source code — and any transitive dependencies it pulls — has access to those secrets. The 2025 tj-actions/changed-files supply-chain attack stole secrets exactly this way. Common when intentional (AWS, Cloudflare, GCP credential actions) but worth flagging for review.\",\n check(content, filePath) {\n if (!GHA_WORKFLOW_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Match `uses: <owner>/<action>` where owner is NOT `actions` (official),\n // `github` (first-party), or `aws-actions/azure/gcp/google-github-actions`\n // (cloud vendors — passing secrets to these is the documented usage),\n // followed within ~30 lines by a `with:` block containing `secrets.`.\n const pattern =\n /uses\\s*:\\s*(?!actions\\/|github\\/|aws-actions\\/|azure\\/|google-github-actions\\/|hashicorp\\/)([\\w\\-]+\\/[\\w\\-./]+)@[^\\n]*\\n[\\s\\S]{0,800}?with\\s*:[\\s\\S]{0,800}?\\$\\{\\{\\s*secrets\\./g;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC187\", title: ghaThirdPartyActionWithSecrets.title, severity: \"medium\", category: \"Supply Chain\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: `Audit the third-party action source (${m[1]}) before passing secrets. Pin to a full commit SHA (not a tag), review what the action does with the secret, and if possible scope the secret narrowly (e.g. a deploy-only token, not your full GitHub PAT). Consider replacing with an official action where one exists.`,\n });\n pattern.lastIndex = m.index + m[0].length;\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC188–VC190: DOCKERFILE SECURITY HARDENING\n//\n// Catches the most common Dockerfile mistakes AI tools generate. Engine\n// already scans `Dockerfile`, `Dockerfile.*`, and `*.dockerfile` per the\n// SOURCE_EXTENSIONS list — these rules just add specific patterns on top.\n// ────────────────────────────────────────────\n\nconst DOCKERFILE_RE = /(?:^|\\/)Dockerfile(\\..+)?$|\\.dockerfile$/i;\n\nexport const dockerfileADDInsteadOfCOPY: CustomRule = {\n id: \"VC188\",\n title: \"Dockerfile: ADD used for local files instead of COPY\",\n severity: \"low\",\n category: \"Configuration\",\n description: \"`ADD` has two features `COPY` doesn't: it auto-extracts tar archives and can fetch URLs. Both are footguns for local-file copies — auto-extraction can introduce zip-slip or symlink-traversal bugs, and URL fetches break reproducibility and have no integrity check. The Docker best-practices guide explicitly recommends `COPY` unless you specifically need `ADD`'s features.\",\n check(content, filePath) {\n if (!DOCKERFILE_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Find ADD lines that aren't fetching a URL and aren't extracting a known\n // archive format. Anything else should be COPY.\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const m = /^\\s*ADD\\s+(?:--[\\w-]+(?:=\\S+)?\\s+)*(\\S+)/i.exec(line);\n if (!m) continue;\n if (line.trim().startsWith(\"#\")) continue;\n const src = m[1];\n // Skip URL fetches (one of ADD's legitimate uses)\n if (/^https?:\\/\\//.test(src)) continue;\n // Skip tar/archive extraction (the other legitimate use)\n if (/\\.(tar(\\.(gz|bz2|xz|zst))?|tgz|tbz|txz)\\b/i.test(src)) continue;\n findings.push({\n rule: \"VC188\", title: dockerfileADDInsteadOfCOPY.title, severity: \"low\", category: \"Configuration\",\n file: filePath, line: i + 1, snippet: getSnippet(content, i + 1),\n fix: \"Replace `ADD` with `COPY` for local-file copies. `ADD` should only be used when you specifically need URL-fetch or tar auto-extraction behavior — otherwise `COPY` is safer and faster.\",\n });\n }\n return findings;\n },\n};\n\nexport const dockerfileUnverifiedShellPipe: CustomRule = {\n id: \"VC189\",\n title: \"Dockerfile: RUN with unverified shell pipe (curl|sh, wget|bash)\",\n severity: \"high\",\n category: \"Supply Chain\",\n description: \"`RUN curl https://example.com/install.sh | sh` (or `wget ... | bash`) downloads and executes a remote script with no integrity check. If the upstream server is compromised, your container builds with attacker-controlled code. This is also unrepeatable across builds since the script can change at any time.\",\n check(content, filePath) {\n if (!DOCKERFILE_RE.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n const lines = content.split(\"\\n\");\n // Multiline RUN blocks need to be concatenated; track line continuations.\n let inRun = false;\n let runStart = -1;\n let buffer = \"\";\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n if (raw.trim().startsWith(\"#\")) continue;\n const startMatch = /^\\s*RUN\\s+(.*)$/i.exec(raw);\n if (startMatch) {\n if (inRun && buffer && /(?:curl|wget)\\b[^|]*\\|\\s*(?:bash|sh|zsh)\\b/i.test(buffer)) {\n findings.push({\n rule: \"VC189\", title: dockerfileUnverifiedShellPipe.title, severity: \"high\", category: \"Supply Chain\",\n file: filePath, line: runStart + 1, snippet: getSnippet(content, runStart + 1),\n fix: \"Don't pipe untrusted remote scripts into a shell. Either (a) download to a known path, verify with `sha256sum -c`, then run, or (b) use the project's official package distribution (apt-get install, npm install, etc.) which is signed and pinned.\",\n });\n }\n inRun = true;\n runStart = i;\n buffer = startMatch[1];\n if (!buffer.endsWith(\"\\\\\")) {\n // Single-line RUN\n if (/(?:curl|wget)\\b[^|]*\\|\\s*(?:bash|sh|zsh)\\b/i.test(buffer)) {\n findings.push({\n rule: \"VC189\", title: dockerfileUnverifiedShellPipe.title, severity: \"high\", category: \"Supply Chain\",\n file: filePath, line: i + 1, snippet: getSnippet(content, i + 1),\n fix: \"Don't pipe untrusted remote scripts into a shell. Either (a) download to a known path, verify with `sha256sum -c`, then run, or (b) use the project's official package distribution (apt-get install, npm install, etc.) which is signed and pinned.\",\n });\n }\n inRun = false;\n buffer = \"\";\n }\n } else if (inRun) {\n buffer += \" \" + raw.trim().replace(/\\\\$/, \"\");\n if (!raw.trim().endsWith(\"\\\\\")) {\n if (/(?:curl|wget)\\b[^|]*\\|\\s*(?:bash|sh|zsh)\\b/i.test(buffer)) {\n findings.push({\n rule: \"VC189\", title: dockerfileUnverifiedShellPipe.title, severity: \"high\", category: \"Supply Chain\",\n file: filePath, line: runStart + 1, snippet: getSnippet(content, runStart + 1),\n fix: \"Don't pipe untrusted remote scripts into a shell. Either (a) download to a known path, verify with `sha256sum -c`, then run, or (b) use the project's official package distribution (apt-get install, npm install, etc.) which is signed and pinned.\",\n });\n }\n inRun = false;\n buffer = \"\";\n }\n }\n }\n return findings;\n },\n};\n\nexport const dockerfileMissingHealthcheck: CustomRule = {\n id: \"VC190\",\n title: \"Dockerfile: missing HEALTHCHECK directive\",\n severity: \"low\",\n category: \"Configuration\",\n description: \"`HEALTHCHECK` lets Docker (and orchestrators like Kubernetes via probes) detect when a container is alive but unable to serve traffic — e.g. a web server in a deadlock or a DB connection pool that's exhausted. Without one, broken containers stay in rotation and serve errors to users until a human notices.\",\n check(content, filePath) {\n if (!DOCKERFILE_RE.test(filePath)) return [];\n // Skip tiny / library / build-stage Dockerfiles — only flag final\n // application images. Heuristic: file has at least one FROM and\n // a CMD or ENTRYPOINT (i.e. it produces a runnable image).\n if (!/^\\s*FROM\\s+/im.test(content)) return [];\n if (!/^\\s*(CMD|ENTRYPOINT)\\s+/im.test(content)) return [];\n if (/^\\s*HEALTHCHECK\\s+/im.test(content)) return [];\n // Skip multi-stage builders that aren't the final stage. Crude heuristic:\n // if the LAST FROM is `as <name>` with name implying intermediate\n // (builder, build, deps, etc.), skip.\n const fromLines = [...content.matchAll(/^\\s*FROM\\s+\\S+(?:\\s+as\\s+(\\w+))?/gim)];\n const lastFrom = fromLines[fromLines.length - 1];\n if (lastFrom && lastFrom[1] && /^(builder|build|deps|prep|base)$/i.test(lastFrom[1])) return [];\n // Find the last CMD or ENTRYPOINT line for reporting\n const cmdMatch = [...content.matchAll(/^\\s*(CMD|ENTRYPOINT)\\s+/gim)].pop();\n if (!cmdMatch || cmdMatch.index === undefined) return [];\n const lineNum = content.substring(0, cmdMatch.index).split(\"\\n\").length;\n return [{\n rule: \"VC190\", title: dockerfileMissingHealthcheck.title, severity: \"low\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add a HEALTHCHECK directive before the final CMD/ENTRYPOINT. Example for a web app: `HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -fsS http://localhost:3000/health || exit 1`.\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// VC191–VC197: PYTHON-SPECIFIC SECURITY GAPS\n//\n// AI tools generate a lot of Python (FastAPI, Flask, Django, ML scripts)\n// and the existing rules covered the headline cases (VC015 eval, VC071\n// Django DEBUG, VC072 Flask SECRET_KEY, VC073 pickle/yaml.load, VC081\n// XXE, VC094 subprocess shell=True / os.system). These 7 fill the\n// remaining high-value gaps from the Tier 1 audit.\n//\n// All rules scope to `.py` files only.\n// ────────────────────────────────────────────\n\nconst PY_FILE_RE = /\\.py$/;\n\nexport const pyRequestsVerifyFalse: CustomRule = {\n id: \"VC191\",\n title: \"Python: requests called with verify=False (TLS verification disabled)\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"`requests.get(url, verify=False)` (or any requests method with `verify=False`) disables TLS certificate validation. The HTTPS connection becomes susceptible to man-in-the-middle attacks — anyone on the network path can intercept and modify the response. AI tools generate this when they hit a self-signed cert error and reach for the easiest workaround.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n const pattern = /requests\\.(?:get|post|put|delete|patch|head|options|request|Session\\(\\)\\.[a-z]+)\\s*\\([^)]*verify\\s*=\\s*False/gi;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC191\", title: pyRequestsVerifyFalse.title, severity: \"high\", category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Remove `verify=False`. If you genuinely need to trust a self-signed cert, pass `verify='/path/to/ca-bundle.pem'` so only that specific CA is trusted. Don't disable validation globally.\",\n });\n }\n return findings;\n },\n};\n\nexport const pyJinja2AutoescapeOff: CustomRule = {\n id: \"VC192\",\n title: \"Python: Jinja2 Environment with autoescape=False\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Jinja2 doesn't auto-escape by default — you have to opt in. Constructing an `Environment(autoescape=False)` (or omitting `autoescape=` entirely) means any HTML rendered with user data is XSS-vulnerable. The Flask integration sets autoescape correctly for `.html` templates, but standalone Jinja2 use has the unsafe default.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n // Two-stage detection: only fire when Jinja2 is actually being used in\n // this file (via import or an `Environment(`/`Template(` constructor\n // call) AND `autoescape=False` is set somewhere. A single regex spanning\n // `Environment(...autoescape=False...)` breaks on nested parens like\n // `FileSystemLoader(\"templates\")` — the negated char class can't see\n // past the inner `)`. Splitting the check sidesteps that entirely.\n const usesJinja2 =\n /\\bjinja2\\b/.test(content) ||\n /\\b(?:Environment|Template)\\s*\\(/.test(content);\n if (!usesJinja2) return [];\n const findings: RuleMatch[] = [];\n const pattern = /\\bautoescape\\s*=\\s*False\\b/g;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC192\", title: pyJinja2AutoescapeOff.title, severity: \"high\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Set `autoescape=True` (or use `select_autoescape(['html', 'htm', 'xml'])` for finer control). XSS in Jinja2 templates is the same class of bug as React's dangerouslySetInnerHTML — turn escaping on by default.\",\n });\n }\n return findings;\n },\n};\n\nexport const pyTempfileMktemp: CustomRule = {\n id: \"VC193\",\n title: \"Python: tempfile.mktemp() — TOCTOU race\",\n severity: \"medium\",\n category: \"Configuration\",\n description: \"`tempfile.mktemp()` is documented as deprecated and unsafe — it returns a path string but doesn't create the file. Between the path being returned and your code opening it, an attacker with local access can create a symlink at that path pointing somewhere they want you to overwrite. Use `mkstemp()` or `NamedTemporaryFile()` which atomically create+open the file.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n const pattern = /\\btempfile\\.mktemp\\s*\\(/g;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC193\", title: pyTempfileMktemp.title, severity: \"medium\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Replace `tempfile.mktemp()` with `tempfile.mkstemp()` (returns an open fd + path) or `tempfile.NamedTemporaryFile()` (context manager). Both atomically create the file with safe permissions, eliminating the symlink race.\",\n });\n }\n return findings;\n },\n};\n\nexport const pyDjangoMarkSafe: CustomRule = {\n id: \"VC194\",\n title: \"Python: Django mark_safe() / format_html with non-literal input\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Django's `mark_safe()` tells the template engine \\\"this string is already safe HTML, don't escape it.\\\" When the argument is anything other than a hardcoded literal — a user-controlled value, a variable from a model, or a formatted string — you've created an XSS sink. AI tools call `mark_safe(user.bio)` to render rich text and forget that the bio is unsanitized.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Match mark_safe() with non-literal argument: f-string, .format(),\n // %-formatting, variable name, or .field access.\n const patterns = [\n // f-string: mark_safe(f\"...\")\n /\\bmark_safe\\s*\\(\\s*f[\"']/g,\n // .format() in argument\n /\\bmark_safe\\s*\\([^)]*\\.format\\s*\\(/g,\n // %-formatting\n /\\bmark_safe\\s*\\([^)]*%\\s*[\\w(]/g,\n // Variable / attribute access (not a string literal).\n // Match mark_safe(foo) or mark_safe(obj.attr) — anything starting with\n // a letter that isn't a quote.\n /\\bmark_safe\\s*\\(\\s*[a-zA-Z_]\\w*(?:\\.\\w+)*\\s*[),]/g,\n ];\n const seenLines = new Set<number>();\n for (const pattern of patterns) {\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n if (seenLines.has(lineNum)) continue;\n seenLines.add(lineNum);\n findings.push({\n rule: \"VC194\", title: pyDjangoMarkSafe.title, severity: \"high\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Don't bypass auto-escaping with `mark_safe()` on user-controlled data. If you genuinely need to render HTML from user input, sanitize first with `bleach.clean(value, tags=ALLOWED_TAGS)`. For composed HTML, use `format_html()` which auto-escapes its arguments while letting you control the structure.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const pyParamikoAutoAdd: CustomRule = {\n id: \"VC195\",\n title: \"Python: paramiko AutoAddPolicy (accepts unknown SSH host keys)\",\n severity: \"high\",\n category: \"Cryptography\",\n description: \"`AutoAddPolicy` tells paramiko to silently trust any host key it hasn't seen before. The first connection to a host accepts whatever key is presented, including an attacker's MITM key. Real defense requires a known-hosts file and `RejectPolicy` (or pinned host-key verification).\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n /set_missing_host_key_policy\\s*\\(\\s*(?:paramiko\\.)?AutoAddPolicy\\s*\\(\\s*\\)\\s*\\)/g,\n /paramiko\\.AutoAddPolicy\\s*\\(\\s*\\)/g,\n ];\n const seenLines = new Set<number>();\n for (const pattern of patterns) {\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n if (seenLines.has(lineNum)) continue;\n seenLines.add(lineNum);\n findings.push({\n rule: \"VC195\", title: pyParamikoAutoAdd.title, severity: \"high\", category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Replace `AutoAddPolicy` with `RejectPolicy` and pre-load known host keys: `client.load_system_host_keys()` or `client.load_host_keys('/path/to/known_hosts')`. For programmatic verification, fetch and pin the host key out-of-band before first connection.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const pyDjangoAllowedHostsWildcard: CustomRule = {\n id: \"VC196\",\n title: \"Python: Django ALLOWED_HOSTS contains wildcard\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"`ALLOWED_HOSTS = ['*']` (or any list containing `'*'`) disables host header validation entirely. Attackers can craft requests with a malicious Host header — used for cache poisoning, password-reset link poisoning, and SSRF in webhook callbacks that include the host. Pin ALLOWED_HOSTS to your real domains.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n // Only fire on settings-like files to reduce noise.\n if (!/settings|config/i.test(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Match ALLOWED_HOSTS = ['*'] or ALLOWED_HOSTS = [\"*\"]; tolerate spacing\n // and trailing comments.\n const pattern = /^\\s*ALLOWED_HOSTS\\s*=\\s*\\[[^\\]]*[\"']\\*[\"'][^\\]]*\\]/gm;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC196\", title: pyDjangoAllowedHostsWildcard.title, severity: \"high\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Replace `ALLOWED_HOSTS = ['*']` with your real production domains: `ALLOWED_HOSTS = ['app.example.com', 'example.com']`. For local development, use `['localhost', '127.0.0.1']`. Read from environment so prod and dev configs differ.\",\n });\n }\n return findings;\n },\n};\n\nexport const pyJWTDecodeWeakConfig: CustomRule = {\n id: \"VC197\",\n title: \"Python: PyJWT decode without algorithm allowlist (alg:none risk)\",\n severity: \"critical\",\n category: \"Cryptography\",\n description: \"`jwt.decode(token, key, algorithms=...)` requires the `algorithms=` parameter in modern PyJWT — older code that uses positional args or omits it is vulnerable to the `alg: none` attack and key-confusion attacks. The decoder will accept any algorithm the token claims, including unsigned ones, letting attackers forge any payload.\",\n check(content, filePath) {\n if (!PY_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n // Two patterns to cover both PyJWT call styles. Macroscope's review on\n // PR #308 flagged that the original `\\bjwt\\.decode\\s*\\(` only matched\n // `jwt.decode(...)` and missed `from jwt import decode` followed by a\n // bare `decode(token, key)` call.\n //\n // The literal suggested diff — `/\\b(?:jwt\\.)?decode\\s*\\(/` — would FP\n // hard on `bytes.decode(...)`, `payload.decode(\"utf-8\")`, base64 helpers,\n // JSON decoders, etc. So instead of dropping the prefix unconditionally,\n // we add a second pattern that fires on bare `decode(` ONLY when the\n // file has a `from jwt import ...decode...` statement. That preserves\n // detection of the `from jwt import decode` shape without firing on\n // every bytes-decode in the codebase.\n const patterns: RegExp[] = [\n /\\bjwt\\.decode\\s*\\(([^()]*(?:\\([^()]*\\)[^()]*)*)\\)/g,\n ];\n if (/\\bfrom\\s+jwt\\s+import\\s+[^\\n]*\\bdecode\\b/.test(content)) {\n patterns.push(/(?<![.\\w])decode\\s*\\(([^()]*(?:\\([^()]*\\)[^()]*)*)\\)/g);\n }\n const seenLines = new Set<number>();\n for (const callRe of patterns) {\n let m: RegExpExecArray | null;\n while ((m = callRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const args = m[1];\n if (/\\balgorithms\\s*=/.test(args)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n if (seenLines.has(lineNum)) continue;\n seenLines.add(lineNum);\n findings.push({\n rule: \"VC197\", title: pyJWTDecodeWeakConfig.title, severity: \"critical\", category: \"Cryptography\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Always pass an explicit algorithm allowlist: `jwt.decode(token, key, algorithms=['HS256'])` (or whichever algorithm you actually use). Without it, PyJWT will accept whatever algorithm the token's header claims — including `none`, which lets attackers forge any payload.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC198–VC203: AI / LLM-SPECIFIC SECURITY\n//\n// New attack surface that traditional SAST tools don't have rules for:\n// prompt injection, RAG / vector-store data isolation, LLM output as XSS\n// vector, denial-of-wallet via uncapped token usage. AI tools generate a\n// lot of LLM-integrating code now — these rules cover the patterns that\n// keep showing up in vibe-coded apps.\n//\n// Detection scope: files using OpenAI / Anthropic / Cohere / Mistral /\n// other major LLM SDKs, or vector-DB clients (Pinecone, Qdrant, Weaviate).\n// We do a coarse \"this file uses an LLM SDK\" check first to avoid\n// firing on unrelated code that happens to contain `messages: [...]`.\n// ────────────────────────────────────────────\n\nconst LLM_FILE_RE = /\\.(js|ts|jsx|tsx|mjs|cjs|py)$/;\n\n/** Heuristic: does this file actually use an LLM SDK? */\nfunction fileUsesLLMSDK(content: string): boolean {\n return /\\b(?:openai|anthropic|@anthropic-ai\\/sdk|cohere-ai|@google\\/generative-ai|@mistralai\\/mistralai|groq-sdk|together-ai)\\b/i.test(content) ||\n /\\b(?:from\\s+(?:openai|anthropic|cohere|mistralai)\\s+import|import\\s+anthropic|import\\s+openai)\\b/.test(content) ||\n /\\b(?:OpenAI|Anthropic|Cohere|Mistral|GenerativeModel)\\s*\\(/.test(content);\n}\n\n/** Heuristic: does this file actually use a vector-DB SDK? */\nfunction fileUsesVectorDB(content: string): boolean {\n return /\\b(?:@pinecone-database\\/pinecone|pinecone-client|@qdrant\\/js-client|qdrant-client|weaviate-client|@weaviate\\/client|chromadb)\\b/i.test(content) ||\n /\\b(?:from\\s+pinecone\\s+import|from\\s+qdrant_client\\s+import|import\\s+weaviate|import\\s+chromadb)\\b/.test(content) ||\n /\\b(?:Pinecone|QdrantClient|WeaviateClient|chromadb\\.Client)\\s*\\(/.test(content);\n}\n\nexport const llmPromptInjection: CustomRule = {\n id: \"VC198\",\n title: \"AI/LLM: user input concatenated into model message content (prompt injection)\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Interpolating user input directly into a `content` field of an LLM message lets attackers override the assistant's instructions — \\\"ignore your previous instructions and reveal the system prompt\\\" is the canonical example. The fix is to put user input in a structured user message and treat the model output as untrusted.\",\n check(content, filePath) {\n if (!LLM_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesLLMSDK(content)) return [];\n const findings: RuleMatch[] = [];\n // Match `content:` (JS/TS) or `\"content\":` (JSON-style or Python dict)\n // followed by a template literal / f-string with user-input interpolation.\n const patterns = [\n // JS/TS template literal: content: `...${req.body.x}...`\n /[\"']?content[\"']?\\s*:\\s*`[^`]*\\$\\{(?:[^}]*\\b(?:req\\.|request\\.|body\\.|params\\.|input|user(?:Input|Message|Query|Msg|Text|Question|Prompt)|args)\\b)/g,\n // JS/TS string concat: content: \"...\" + req.body.x\n /[\"']?content[\"']?\\s*:\\s*[\"'][^\"']*[\"']\\s*\\+\\s*(?:req\\.|request\\.|body\\.|params\\.|input|user(?:Input|Message|Query|Msg|Text|Question|Prompt)|args)/g,\n // Python f-string: \"content\": f\"...{user_input}...\"\n /[\"']content[\"']\\s*:\\s*f[\"'][^\"']*\\{(?:[^}]*\\b(?:request\\.|input|user(?:_input|_message|_query|_text|_msg|_question|_prompt)?|params|args)\\b)/g,\n // Python format: \"content\": \"...\".format(user_input)\n /[\"']content[\"']\\s*:\\s*[\"'][^\"']*\\{[^}]*\\}[\"']\\s*\\.format\\s*\\(\\s*(?:request\\.|input|user|params|args)/g,\n ];\n const seenLines = new Set<number>();\n for (const pattern of patterns) {\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n if (seenLines.has(lineNum)) continue;\n seenLines.add(lineNum);\n findings.push({\n rule: \"VC198\", title: llmPromptInjection.title, severity: \"high\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Don't concatenate user input into prompt strings. Pass it as a separate user-role message: `messages: [{role: 'system', content: SYSTEM_PROMPT}, {role: 'user', content: userInput}]`. Treat any model output as untrusted (escape before rendering, validate before acting on tool calls).\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const llmSystemPromptInjection: CustomRule = {\n id: \"VC199\",\n title: \"AI/LLM: system prompt constructed with non-literal content\",\n severity: \"critical\",\n category: \"Injection\",\n description: \"System prompts are the highest-trust part of an LLM context — they define the assistant's identity, safety rules, and tool boundaries. Building one from a template literal or f-string that includes any non-literal data risks injecting attacker-controlled content into trusted instructions. Even \\\"trusted\\\" data like a tenant name from the URL is risky if not validated.\",\n check(content, filePath) {\n if (!LLM_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesLLMSDK(content)) return [];\n const findings: RuleMatch[] = [];\n // Match a system role followed (within ~200 chars in the same object)\n // by a template-literal or f-string content with interpolation.\n const patterns = [\n // JS/TS: { role: 'system', content: `...${anything}...` }\n /[\"']?role[\"']?\\s*:\\s*[\"']system[\"']\\s*,\\s*[\"']?content[\"']?\\s*:\\s*`[^`]*\\$\\{/g,\n // Python: {\"role\": \"system\", \"content\": f\"...{anything}...\"}\n /[\"']role[\"']\\s*:\\s*[\"']system[\"']\\s*,\\s*[\"']content[\"']\\s*:\\s*f[\"'][^\"']*\\{/g,\n ];\n for (const pattern of patterns) {\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC199\", title: llmSystemPromptInjection.title, severity: \"critical\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Keep the system prompt as a literal string. If you need to parameterize it (e.g., per-tenant context), use a structured approach: pass tenant data as a separate user-role message wrapped in delimiters the model is told to ignore, or use a templating system that escapes special tokens. Never let user-controlled input flow into the system role.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const llmOutputAsHTML: CustomRule = {\n id: \"VC200\",\n title: \"AI/LLM: model output rendered as raw HTML (XSS via model)\",\n severity: \"high\",\n category: \"Injection\",\n description: \"Rendering model output via `dangerouslySetInnerHTML` (React) or `innerHTML` (vanilla JS) treats the model's response as trusted HTML. Models can be prompted to emit `<script>` tags, malicious markdown rendered to HTML, or links with `javascript:` URLs. The XSS is the same as rendering any unsanitized user input — the only difference is the attacker's input arrived via a model call.\",\n check(content, filePath) {\n if (!/\\.(jsx|tsx|js|ts)$/.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesLLMSDK(content)) return [];\n const findings: RuleMatch[] = [];\n // Detect dangerouslySetInnerHTML / .innerHTML = with values that look\n // like model-output property accesses.\n const patterns = [\n // dangerouslySetInnerHTML with .choices[0].message.content / .text / etc.\n /dangerouslySetInnerHTML\\s*=\\s*\\{\\{\\s*__html\\s*:\\s*[^}]*\\b(?:choices\\[\\d*\\]?\\.message|completion|response|message\\.content|content_block|delta\\.text|generated_text|output_text|text)\\b/g,\n // .innerHTML = response.choices[0].message.content\n /\\.innerHTML\\s*=\\s*[^;]*\\b(?:choices\\[\\d*\\]?\\.message|completion|response\\.message|message\\.content|delta\\.text|generated_text|output_text)\\b/g,\n ];\n const seenLines = new Set<number>();\n for (const pattern of patterns) {\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n if (seenLines.has(lineNum)) continue;\n seenLines.add(lineNum);\n findings.push({\n rule: \"VC200\", title: llmOutputAsHTML.title, severity: \"high\", category: \"Injection\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Render model output as text, not HTML. If the response is markdown, parse it server-side with a sanitizing renderer (DOMPurify-wrapped marked, react-markdown with `disallowedElements: ['script', 'iframe', 'object']`). Models can be tricked into emitting active content — never trust the response shape.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const vectorStoreQueryNoUserFilter: CustomRule = {\n id: \"VC201\",\n title: \"AI/RAG: vector-store query without user/tenant filter\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"Querying a shared vector index without filtering by user/tenant ID returns results from every user's documents. In multi-tenant RAG apps this is a silent data leak — User A's question matches User B's embedded private documents, and the LLM cheerfully includes them in its answer. The fix is to scope every query to the requesting user's data.\",\n check(content, filePath) {\n if (!LLM_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesVectorDB(content)) return [];\n const findings: RuleMatch[] = [];\n // Find vector-search calls. Each call gets ~600 chars of forward\n // context to look for a user/tenant filter; absent → flag.\n const callRe = /\\b(?:index|client|collection|store|vectorstore)\\.(?:query|search|similaritySearch|similarity_search|near_text|near_vector|nearest)\\s*\\(([^()]*(?:\\([^()]*\\)[^()]*)*)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = callRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const args = m[1];\n // Three \"args are properly scoped\" heuristics. Macroscope flagged the\n // original two-stage `\\bword\\b` check on PR #309 — the trailing `\\b`\n // prevented `user` from matching `userId`, and `namespace: \"shared\"`\n // bypassed the rule even though it isn't user-scoped.\n //\n // 1. Args contain a user-scoped identifier (userId/tenantId/etc.) → ok\n if (/\\b(?:user[_-]?id|userId|tenant[_-]?id|tenantId|org[_-]?id|orgId|owner[_-]?id|ownerId|account[_-]?id|customer[_-]?id|workspace[_-]?id)\\b/i.test(args)) continue;\n // 2. Namespace is set to a user-scoped value (currentUser.id, `user-${id}`, etc.) → ok\n if (/\\bnamespace\\s*[:=]\\s*[`\"']?[^,)`\"']*(?:user|tenant|org|owner|account|customer|workspace)/i.test(args)) continue;\n // 3. Filter mentions a user/tenant/org token in its body → ok\n if (/\\bfilter\\s*[:=][\\s\\S]*?(?:\\buser|\\btenant|\\borg|\\bowner|\\baccount|\\bcustomer|\\bworkspace)/i.test(args)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC201\", title: vectorStoreQueryNoUserFilter.title, severity: \"high\", category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add a filter scoping the search to the requesting user's data. Pinecone: `index.query({ vector, topK, filter: { userId: { $eq: currentUser.id } } })`. Qdrant: `client.search({ ..., filter: { must: [{ key: 'userId', match: { value: currentUser.id } }] } })`. For namespace-based isolation, use `namespace: currentUser.id` (Pinecone) or per-tenant collections.\",\n });\n }\n return findings;\n },\n};\n\nexport const vectorStoreUpsertNoMetadata: CustomRule = {\n id: \"VC202\",\n title: \"AI/RAG: vector-store upsert without user/tenant metadata\",\n severity: \"medium\",\n category: \"Authorization\",\n description: \"Inserting embeddings without per-user metadata makes per-user filtering at query time impossible — even if you remember to filter at search, there's nothing to filter on. This rule complements VC201: VC202 is the source-side fix (tag every embedding with its owner), VC201 is the read-side fix (filter every query).\",\n check(content, filePath) {\n if (!LLM_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesVectorDB(content)) return [];\n const findings: RuleMatch[] = [];\n const callRe = /\\b(?:index|client|collection|store|vectorstore)\\.(?:upsert|insert|add|addDocuments|add_documents)\\s*\\(([^()]*(?:\\([^()]*\\)[^()]*)*)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = callRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const args = m[1];\n // Look for metadata containing a user/tenant identifier in the args.\n if (/\\bmetadata\\s*[:=]/i.test(args) && /\\b(?:user|tenant|org|owner|account|customer|workspace)/i.test(args)) {\n continue;\n }\n // Also accept namespace: 'something' as evidence of isolation\n if (/\\bnamespace\\s*[:=]\\s*[`\"']?[^,)`\"']*(?:user|tenant|org)/i.test(args)) {\n continue;\n }\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC202\", title: vectorStoreUpsertNoMetadata.title, severity: \"medium\", category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Tag every embedding with its owner: `metadata: { userId: currentUser.id, ... }` (Pinecone), `payload: { userId: currentUser.id, ... }` (Qdrant), or use a per-user namespace/collection. This is a prerequisite for query-time filtering (VC201).\",\n });\n }\n return findings;\n },\n};\n\nexport const llmCallNoMaxTokens: CustomRule = {\n id: \"VC203\",\n title: \"AI/LLM: model call without max_tokens / token budget cap (denial of wallet)\",\n severity: \"low\",\n category: \"Availability\",\n description: \"LLM API calls without a max_tokens cap let the model generate up to the model's full context-window response budget. When user input feeds into the prompt, attackers can craft inputs that maximize output tokens — generating a bill, exhausting your monthly quota, or triggering rate limits for legitimate users. Always pin a sensible upper bound.\",\n check(content, filePath) {\n if (!LLM_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileUsesLLMSDK(content)) return [];\n const findings: RuleMatch[] = [];\n // Find each LLM completion-style call by name, then walk forward through\n // the content to find the matching closing `)` (paren-balanced). Regex\n // alone can't handle arbitrarily-nested object/array literals in the\n // call args reliably — manual scanning is more robust.\n const callNameRe =\n /\\b(?:chat\\.completions\\.create|chat\\.create|messages\\.create|completions\\.create|generate_content|generateContent)\\s*\\(/g;\n const TOKEN_KW_RE = /\\b(?:max_tokens|max_output_tokens|maxTokens|maxOutputTokens|max_new_tokens|maxOutputTokenCount|max_completion_tokens|maxCompletionTokens)\\b/;\n let m: RegExpExecArray | null;\n while ((m = callNameRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n // Walk from the `(` to its matching `)`, counting depth and respecting\n // string literals so we don't trip on parens inside strings.\n const openIdx = m.index + m[0].length - 1; // points at `(`\n let depth = 1;\n let i = openIdx + 1;\n let inStr: '\"' | \"'\" | \"`\" | null = null;\n while (i < content.length && depth > 0) {\n const ch = content[i];\n if (inStr) {\n if (ch === \"\\\\\") { i += 2; continue; }\n if (ch === inStr) inStr = null;\n } else {\n if (ch === '\"' || ch === \"'\" || ch === \"`\") inStr = ch;\n else if (ch === \"(\") depth++;\n else if (ch === \")\") depth--;\n }\n if (depth === 0) break;\n i++;\n }\n if (depth !== 0) continue; // unbalanced — skip\n // Strip line and block comments before checking for the token-cap\n // keyword — otherwise a comment like `// max_tokens omitted` would\n // make the rule think a cap is set when it isn't.\n //\n // Macroscope flagged on PR #309 that the original (^|\\n)-anchored\n // line-comment regex only stripped comments at the START of a line\n // and missed inline comments like `model: \"gpt-4\", // max_tokens: 1000`.\n // Switched to anywhere-on-line stripping. Some tiny risk of stripping\n // `//` inside a string literal — but for our purpose (looking for a\n // `max_tokens:` kwarg in args), even if we shorten a string literal\n // the kwarg-name match on TOKEN_KW_RE is unaffected.\n const args = content\n .substring(openIdx + 1, i)\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/\\/\\/[^\\n]*/g, \"\")\n .replace(/#[^\\n]*/g, \"\");\n if (TOKEN_KW_RE.test(args)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC203\", title: llmCallNoMaxTokens.title, severity: \"low\", category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pin an explicit token cap. OpenAI: `max_tokens: 1024` (or `max_completion_tokens` for o1 models). Anthropic: `max_tokens: 1024`. Google: `maxOutputTokens: 1024`. Choose a value just larger than the longest legitimate response — this caps both runaway costs and adversarial DoS.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC204–VC206: GRAPHQL SERVER HARDENING\n//\n// VC051 already covers introspection-in-production. These three rules\n// fill the remaining \"AI tools generate a default GraphQL server, ship\n// it, get pwned\" patterns: missing query depth/complexity limits and\n// disabled CSRF protection.\n//\n// Detection scope: files that instantiate a GraphQL server. The big\n// libraries are Apollo Server (`new ApolloServer(`), graphql-yoga\n// (`createYoga(`), express-graphql (`graphqlHTTP(`), and Mercurius.\n// ────────────────────────────────────────────\n\nconst GQL_FILE_RE = /\\.(js|ts|jsx|tsx|mjs|cjs)$/;\n\n/** Heuristic: does this file actually instantiate a GraphQL server? */\nfunction fileInstantiatesGraphQLServer(content: string): boolean {\n return /\\bnew\\s+ApolloServer\\s*\\(/.test(content) ||\n /\\bcreateYoga\\s*\\(/.test(content) ||\n /\\bgraphqlHTTP\\s*\\(/.test(content) ||\n /\\bcreateHandler\\s*\\(\\s*\\{[\\s\\S]{0,200}?schema/.test(content) || // graphql-http\n /\\bmercurius\\s*\\(/.test(content);\n}\n\nexport const graphqlNoDepthLimit: CustomRule = {\n id: \"VC204\",\n title: \"GraphQL: server has no query depth limit\",\n severity: \"high\",\n category: \"Availability\",\n description: \"Without a query depth limit, attackers can submit deeply-nested queries that explode in execution cost — `user { friends { friends { friends { ... } } } }` repeated 100 levels deep. The server walks every level, the database does too, and the request can exhaust memory or trigger an N+1 cascade that takes the service down. Free, easy DoS.\",\n check(content, filePath) {\n if (!GQL_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileInstantiatesGraphQLServer(content)) return [];\n // Strip comments before the keyword check — otherwise a comment like\n // `// no depthLimit / graphql-armor here` would falsely satisfy the\n // \"depth limit is present\" check (same class of bug Macroscope flagged\n // for VC203 on PR #309).\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/\\/\\/[^\\n]*/g, \"\");\n const hasDepthLimit =\n /\\b(?:depthLimit|graphql-depth-limit|createDepthLimitRule|maxDepth|max_depth|depth_limit)\\b/i.test(codeOnly) ||\n // Envelop's `useDepthLimit` plugin\n /\\buseDepthLimit\\s*\\(/.test(codeOnly) ||\n // graphql-armor (covers depth + complexity + more)\n /\\b(?:graphql-armor|@escape\\.tech\\/graphql-armor)\\b/i.test(codeOnly);\n if (hasDepthLimit) return [];\n const findings: RuleMatch[] = [];\n // Anchor the finding on the server-instantiation line.\n const anchorRe = /\\b(?:new\\s+ApolloServer|createYoga|graphqlHTTP|createHandler|mercurius)\\s*\\(/g;\n let m: RegExpExecArray | null;\n while ((m = anchorRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC204\", title: graphqlNoDepthLimit.title, severity: \"high\", category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add a query depth limit. Apollo: `validationRules: [depthLimit(7)]` (from `graphql-depth-limit`). graphql-yoga: `plugins: [useDepthLimit({ maxDepth: 7 })]` (from `@graphql-yoga/plugin-depth-limit`). Or use `graphql-armor` for depth + complexity + more in one plugin.\",\n });\n break; // only flag the server instantiation once per file\n }\n return findings;\n },\n};\n\nexport const graphqlNoComplexityLimit: CustomRule = {\n id: \"VC205\",\n title: \"GraphQL: server has no query complexity limit\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"Even with a depth limit, attackers can build expensive queries that aren't deep but multiply at each level: `users(first: 1000) { posts(first: 1000) { comments(first: 1000) { ... } } }` is 3 levels deep but resolves a billion items. Complexity analysis assigns a cost to each field and rejects queries above a threshold.\",\n check(content, filePath) {\n if (!GQL_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n if (!fileInstantiatesGraphQLServer(content)) return [];\n // Strip comments before the keyword check (see VC204 comment for the\n // FN it prevents).\n const codeOnly = content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .replace(/\\/\\/[^\\n]*/g, \"\");\n const hasComplexityLimit =\n /\\b(?:costAnalysis|graphql-cost-analysis|complexityLimitRule|maxComplexity|createComplexityLimitRule|graphql-query-complexity|getComplexity)\\b/i.test(codeOnly) ||\n /\\buseDepthLimit\\s*\\(/.test(codeOnly) && /\\bmaxTokens\\s*:|maxAliases\\s*:/.test(codeOnly) ||\n /\\b(?:graphql-armor|@escape\\.tech\\/graphql-armor)\\b/i.test(codeOnly);\n if (hasComplexityLimit) return [];\n const findings: RuleMatch[] = [];\n const anchorRe = /\\b(?:new\\s+ApolloServer|createYoga|graphqlHTTP|createHandler|mercurius)\\s*\\(/g;\n let m: RegExpExecArray | null;\n while ((m = anchorRe.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC205\", title: graphqlNoComplexityLimit.title, severity: \"medium\", category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add a query complexity limit. Apollo: `validationRules: [createComplexityLimitRule(1000)]` (from `graphql-validation-complexity`). graphql-yoga: `plugins: [useDepthLimit({ maxTokens: 1000 })]`. Or use `graphql-armor` which bundles depth + complexity + cost in one plugin.\",\n });\n break;\n }\n return findings;\n },\n};\n\nexport const graphqlCSRFDisabled: CustomRule = {\n id: \"VC206\",\n title: \"GraphQL: Apollo Server with csrfPrevention: false\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Apollo Server v4+ enables `csrfPrevention` by default — it requires a non-simple Content-Type header on mutations to block CSRF attacks from <form>-style submissions. Setting `csrfPrevention: false` removes that protection, letting any website with a logged-in user trigger mutations on your GraphQL endpoint.\",\n check(content, filePath) {\n if (!GQL_FILE_RE.test(filePath)) return [];\n if (isTestFile(filePath)) return [];\n const findings: RuleMatch[] = [];\n const pattern = /\\bcsrfPrevention\\s*:\\s*false\\b/g;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC206\", title: graphqlCSRFDisabled.title, severity: \"high\", category: \"Configuration\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Remove `csrfPrevention: false` — it's the default in Apollo Server v4+. If a legitimate client is failing because of it, send a `Content-Type: application/json` header (or set `apollo-require-preflight: true`) instead of disabling the protection globally.\",\n });\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC146–VC151: SECRET EXPOSURE PATH ANALYSIS\n// Detects when secrets are used in ways that amplify exposure.\n// ────────────────────────────────────────────\n\nconst SECRET_VAR_RE = /(?:api[_-]?key|apikey|api[_-]?secret|secret[_-]?key|access[_-]?token|auth[_-]?token|private[_-]?key|password|passwd|pwd|credentials|client[_-]?secret|app[_-]?secret|master[_-]?key)/i;\n\nexport const secretInURLParam: CustomRule = {\n id: \"VC146\",\n title: \"Secret Passed in URL Query Parameter\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"API keys or tokens in URL query parameters are logged in server access logs, browser history, referrer headers, and proxy logs. Secrets in URLs are one of the most common causes of credential exposure.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb|go|java|php)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // Template literals: `?api_key=${secret}` or `&token=${token}`\n /[?&](?:api_key|apikey|api_secret|token|access_token|secret|key|password|auth|authorization)=\\$\\{/gi,\n // String concat: '?key=' + apiKey\n /[?&](?:api_key|apikey|api_secret|token|access_token|secret|key|password|auth)=[\"']\\s*\\+/gi,\n // Python f-strings: f\"?token={token}\"\n /[?&](?:api_key|apikey|token|access_token|secret|key|password)=\\{[a-zA-Z_]/g,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC146\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pass secrets in the Authorization header (Bearer token) or request body, never in URL parameters. URL parameters are logged in server access logs, browser history, and referrer headers.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretLoggedToConsole: CustomRule = {\n id: \"VC147\",\n title: \"Secret Logged to Console\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Logging variables that appear to contain secrets (key, token, password, secret) writes credentials to stdout, log files, and monitoring services where they can be harvested.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n const findings: RuleMatch[] = [];\n // console.log/debug/info/warn/error with a secret-named variable\n const pattern = /console\\.(?:log|debug|info|warn|error)\\s*\\([^)]*\\b(api[_-]?key|apikey|secret|token|password|passwd|credentials|private[_-]?key|auth[_-]?token|access[_-]?token)\\b/gi;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC147\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Remove this console statement or redact the secret before logging. Use structured logging with a secret-redaction filter in production.\",\n });\n }\n return findings;\n },\n};\n\nexport const secretInErrorResponse: CustomRule = {\n id: \"VC148\",\n title: \"Secret Leaked in Error Response\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Returning secrets or secret-named variables in API error responses exposes credentials to anyone who can trigger the error, including unauthenticated users.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n const findings: RuleMatch[] = [];\n // res.json/send/status().json containing secret-named variables in catch/error blocks\n const pattern = /(?:res\\.(?:json|send|status\\s*\\([^)]*\\)\\s*\\.json))\\s*\\(\\s*\\{[^}]*\\b(api[_-]?key|secret|token|password|credentials|private[_-]?key|process\\.env\\.[A-Z_]*(?:KEY|SECRET|TOKEN|PASSWORD))\\b/gi;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC148\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never include secrets in API responses. Return a generic error message and log the detailed error server-side only.\",\n });\n }\n return findings;\n },\n};\n\nexport const secretInBundleConfig: CustomRule = {\n id: \"VC149\",\n title: \"Secret in Client-Side Bundle Configuration\",\n severity: \"critical\",\n category: \"Secrets\",\n description: \"Secrets defined in Webpack DefinePlugin, Vite define, or Next.js publicRuntimeConfig are embedded into the client-side JavaScript bundle and visible to anyone viewing the page source.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/(?:webpack|vite|next)\\.config\\.|\\.config\\.(js|ts|mjs)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // Webpack DefinePlugin with secret-named key\n /DefinePlugin\\s*\\(\\s*\\{[^}]*\\b(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY|CLIENT_SECRET)\\b/gi,\n // Vite define with secret-named key\n /define\\s*:\\s*\\{[^}]*\\b(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY|CLIENT_SECRET)\\b/gi,\n // Next.js publicRuntimeConfig with secret-named key\n /publicRuntimeConfig\\s*:\\s*\\{[^}]*\\b(?:apiKey|secret|token|password|privateKey|clientSecret)\\b/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC149\", title: this.title, severity: \"critical\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Move secrets to server-side environment variables. Only expose public configuration (like API base URLs) in client-side bundle config. Use serverRuntimeConfig (Next.js) or server-only modules.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretInHTMLAttribute: CustomRule = {\n id: \"VC150\",\n title: \"Secret in HTML Meta Tag or Data Attribute\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"API keys or tokens embedded in HTML meta tags or data-* attributes are visible in the page source to anyone who visits the page, including search engine crawlers.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(html|htm|jsx|tsx|vue|svelte|ejs|hbs|pug)$/)) return [];\n const findings: RuleMatch[] = [];\n const patterns = [\n // <meta name=\"api-key\" content=\"...\">\n /<meta\\s[^>]*name\\s*=\\s*[\"'](?:api[_-]?key|secret|token|password)[^>]*>/gi,\n // data-api-key=\"actual-value\" (not a template/binding)\n /data-(?:api[_-]?key|secret|token|password|auth)\\s*=\\s*[\"'][a-zA-Z0-9_\\-]{12,}[\"']/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC150\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Never embed secrets in HTML attributes or meta tags. Load configuration from server-side APIs or use server-rendered environment injection with non-secret values only.\",\n });\n }\n }\n return findings;\n },\n};\n\nexport const secretInCLIArgument: CustomRule = {\n id: \"VC151\",\n title: \"Secret Passed as Command-Line Argument\",\n severity: \"high\",\n category: \"Secrets\",\n description: \"Secrets interpolated into exec/spawn/execSync command strings are visible in process listings (ps aux), shell history, and system audit logs to any user on the same machine.\",\n check(content, filePath) {\n if (isTestFile(filePath)) return [];\n if (!filePath.match(/\\.(js|ts|jsx|tsx|py|rb)$/)) return [];\n const findings: RuleMatch[] = [];\n // exec/spawn/execSync with secret-named variable interpolated\n const patterns = [\n /(?:exec|execSync|spawn|spawnSync|child_process)\\s*\\([^)]*\\$\\{[^}]*(?:api[_-]?key|secret|token|password|credentials)/gi,\n /(?:exec|execSync|spawn|spawnSync|child_process)\\s*\\([^)]*[\"']\\s*\\+\\s*(?:api[_-]?key|secret|token|password|credentials)/gi,\n /(?:subprocess|os\\.system|os\\.popen)\\s*\\([^)]*\\{[^}]*(?:api[_-]?key|secret|token|password|credentials)/gi,\n ];\n for (const p of patterns) {\n let m: RegExpExecArray | null;\n const re = new RegExp(p.source, p.flags.includes(\"g\") ? p.flags : `${p.flags}g`);\n while ((m = re.exec(content)) !== null) {\n if (isCommentLine(content, m.index)) continue;\n if (isInsideFixMessage(content, m.index)) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC151\", title: this.title, severity: \"high\", category: \"Secrets\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Pass secrets via environment variables (env option in spawn/exec) instead of command-line arguments. CLI arguments are visible to all users via `ps aux`.\",\n });\n }\n }\n return findings;\n },\n};\n\n// ────────────────────────────────────────────\n// VC152–VC158: NEW RULES (Webhook Verification, CORS, Validation, etc.)\n// ────────────────────────────────────────────\n\nexport const webhookSignatureVerification: CustomRule = {\n id: \"VC152\",\n title: \"Missing Webhook Signature Verification (Non-Stripe)\",\n severity: \"critical\",\n category: \"Payment Security\",\n description: \"Webhook endpoints for Clerk, GitHub, Resend, SendGrid, Slack, or Twilio that don't verify the request signature allow attackers to forge events.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\/|\\/webhook/i.test(filePath)) return [];\n // Must have a POST handler\n if (!/export\\s+(?:async\\s+)?function\\s+POST/i.test(content)) return [];\n\n const services = [\n { name: \"Clerk\", content: /clerk/i, events: /user\\.created|user\\.updated|user\\.deleted|session\\./i, verify: /svix|Webhook\\(\\)\\.verify|webhook-id|wh_secret/i },\n { name: \"GitHub\", content: /github/i, events: /push|pull_request|issues|installation/i, verify: /createHmac|x-hub-signature|verify.*signature|GITHUB_WEBHOOK_SECRET/i },\n { name: \"Resend\", content: /resend/i, events: /email\\.sent|email\\.delivered|email\\.bounced/i, verify: /svix|webhook.*verify|x-webhook-signature|RESEND_WEBHOOK_SECRET/i },\n { name: \"SendGrid\", content: /sendgrid/i, events: /inbound.*parse|event.*webhook/i, verify: /EventWebhook|x-twilio-email|verifySignature|SENDGRID_WEBHOOK_VERIFICATION/i },\n { name: \"Slack\", content: /slack/i, events: /event_callback|challenge|url_verification/i, verify: /signing_secret|createHmac|x-slack-signature|SLACK_SIGNING_SECRET/i },\n { name: \"Twilio\", content: /twilio/i, events: /sms|voice|StatusCallback|MessagingResponse/i, verify: /validateRequest|X-Twilio-Signature|authToken|TWILIO_AUTH_TOKEN/i },\n ];\n\n const findings: RuleMatch[] = [];\n for (const svc of services) {\n if (!svc.content.test(content)) continue;\n if (!svc.events.test(content)) continue;\n if (svc.verify.test(content)) continue;\n // Found a handler for this service without verification\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+POST/);\n if (!m || m.index === undefined) continue;\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n rule: \"VC152\", title: `${svc.name} Webhook Missing Signature Verification`,\n severity: \"critical\", category: \"Payment Security\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: `Verify the ${svc.name} webhook signature before processing events. Check the ${svc.name} documentation for their signature verification method.`,\n });\n break; // Max 1 finding per file\n }\n return findings;\n },\n};\n\nexport const reflectedCORSOrigin: CustomRule = {\n id: \"VC153\",\n title: \"Reflected CORS Origin with Credentials\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Echoing req.headers.origin into Access-Control-Allow-Origin with credentials enabled lets any website read authenticated API responses. This is worse than wildcard CORS because browsers actually send cookies.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Must have credentials enabled\n if (!/credentials\\s*[=:]\\s*true|Allow-Credentials.*true/i.test(content)) return [];\n // Must have origin reflection pattern\n if (!/req\\.headers\\.origin|callback\\s*\\(\\s*null\\s*,\\s*origin\\s*\\)|origin:\\s*true/i.test(content)) return [];\n // Skip if there's an allowlist check\n if (/ALLOWED_ORIGINS|allowedOrigins|whitelist|isAllowed|\\.includes\\s*\\(\\s*origin|\\.has\\s*\\(\\s*origin/i.test(content)) return [];\n\n const patterns = [\n /(?:Allow-Origin[\"'],\\s*req\\.headers\\.origin)/gi,\n /origin\\s*:\\s*\\(\\s*origin\\s*,\\s*callback\\s*\\)\\s*=>\\s*callback\\s*\\(\\s*null\\s*,\\s*origin\\s*\\)/gi,\n /origin\\s*:\\s*true/gi,\n ];\n const matches: RuleMatch[] = [];\n for (const p of patterns) {\n const raw = findMatches(content, p, reflectedCORSOrigin, filePath, () =>\n \"Use an explicit origin allowlist instead of reflecting the request origin. Only allow origins you control.\"\n );\n if (raw.length > 0) { matches.push(raw[0]); break; } // Max 1 per file\n }\n return matches;\n },\n};\n\nexport const missingRequestValidation: CustomRule = {\n id: \"VC154\",\n title: \"API Route Without Request Body Validation\",\n severity: \"medium\",\n category: \"Authorization\",\n description: \"API route reads the request body without any schema validation (zod, joi, yup, etc.). Unvalidated input can lead to unexpected behavior, injection, or data corruption.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n if (/\\/webhook/i.test(filePath)) return []; // Webhooks validate via signature\n // Must have a mutating handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:POST|PUT|PATCH)/i.test(content)) return [];\n // Must read the body\n if (!/req\\.body|request\\.json\\(\\)|c\\.req\\.json\\(\\)|await.*\\.json\\(\\)/i.test(content)) return [];\n // Skip if any validation library or manual checks exist\n if (/zod|joi|yup|ajv|class-validator|superstruct|valibot|\\.safeParse|\\.validate\\(|z\\.object|Joi\\.object|yup\\.object|schema\\.parse|zodResolver/i.test(content)) return [];\n if (/if\\s*\\(\\s*!(?:body|parsed|data|payload)\\.|throw.*(?:missing|invalid|required)/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+(?:POST|PUT|PATCH)/);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC154\", title: this.title, severity: \"medium\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Validate request bodies with a schema library like zod: const parsed = schema.safeParse(await request.json()); if (!parsed.success) return Response.json({ error: 'Invalid input' }, { status: 400 });\",\n }];\n },\n};\n\nexport const missingAIRateLimit: CustomRule = {\n id: \"VC155\",\n title: \"Missing Rate Limiting on AI/LLM API Call\",\n severity: \"high\",\n category: \"Availability\",\n description: \"API route calls an AI/LLM service (OpenAI, Anthropic, Cohere) without rate limiting. A single user can burn through your entire API budget in minutes.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n // Must have a route handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|PATCH|DELETE)/i.test(content) &&\n !/\\.(get|post|put|patch|delete)\\s*\\(/i.test(content)) return [];\n // Must reference an AI SDK\n if (!/openai|anthropic|@anthropic-ai|cohere|@google\\/generative-ai|chat\\.completions|messages\\.create/i.test(content)) return [];\n // Skip if rate limiting exists\n if (/rateLimit|rateLimiter|throttle|checkRateLimit|limiter|slowDown|express-rate-limit/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+(?:POST|GET)/i) || content.match(/\\.(post|get)\\s*\\(/i);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC155\", title: this.title, severity: \"high\" as const, category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add rate limiting to AI/LLM endpoints to prevent budget abuse. Use express-rate-limit or a custom limiter with per-user and per-IP limits.\",\n }];\n },\n};\n\nexport const missingPagination: CustomRule = {\n id: \"VC156\",\n title: \"Missing Pagination on List Endpoint\",\n severity: \"medium\",\n category: \"Availability\",\n description: \"API endpoint returns database records without a LIMIT or pagination. A single request can dump the entire table, crash the server, or cause an out-of-memory error.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n // Must have a GET handler (list endpoints)\n if (!/export\\s+(?:async\\s+)?function\\s+GET/i.test(content)) return [];\n // Must have a database query\n if (!/findMany|\\.find\\(\\s*\\{|\\.find\\(\\s*\\)|SELECT\\s.*FROM|\\.all\\(\\)|\\.query\\(/i.test(content)) return [];\n // Skip if any pagination exists\n if (/LIMIT|\\.take\\(|\\.limit\\(|\\.skip\\(|offset|cursor|page|perPage|pageSize|paginate|\\.slice\\(/i.test(content)) return [];\n // Skip if it filters by specific ID (not a list endpoint)\n if (/findUnique|findFirst|findById|\\.findOne|WHERE.*id\\s*=/i.test(content)) return [];\n const m = content.match(/export\\s+(?:async\\s+)?function\\s+GET/);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC156\", title: this.title, severity: \"medium\" as const, category: \"Availability\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add pagination to list endpoints: .findMany({ take: limit, skip: offset }) or SELECT ... LIMIT ? OFFSET ?\",\n }];\n },\n};\n\nexport const exposedDatabaseStudio: CustomRule = {\n id: \"VC157\",\n title: \"Database Studio Exposed in Production\",\n severity: \"high\",\n category: \"Configuration\",\n description: \"Prisma Studio or Drizzle Studio is configured in a production script or bound to 0.0.0.0, giving anyone with the URL full database admin access.\",\n check(content, filePath) {\n const basename = filePath.split(\"/\").pop() || \"\";\n if (basename !== \"package.json\" && !/docker-compose/i.test(basename)) return [];\n // Check for studio in production-facing scripts\n if (!/prisma\\s+studio|drizzle-kit\\s+studio/i.test(content)) return [];\n // Skip if only in dev/db:studio scripts (that's fine)\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (!/prisma\\s+studio|drizzle-kit\\s+studio/i.test(line)) continue;\n // Flag if in start/production script or bound to 0.0.0.0\n if (/\"start\"|\"production\"|\"build.*&&.*studio\"|0\\.0\\.0\\.0/i.test(line)) {\n return [{\n rule: \"VC157\", title: this.title, severity: \"high\" as const, category: \"Configuration\",\n file: filePath, line: i + 1, snippet: getSnippet(content, i + 1),\n fix: \"Only run database studio in development. Move the studio command to a dev-only script and never bind to 0.0.0.0 in production.\",\n }];\n }\n }\n return [];\n },\n};\n\nexport const insecureDirectObjectReference: CustomRule = {\n id: \"VC158\",\n title: \"Potential Insecure Direct Object Reference (IDOR)\",\n severity: \"high\",\n category: \"Authorization\",\n description: \"API route fetches a record by ID from URL parameters without checking if the requesting user owns that record. An attacker can access other users' data by guessing or enumerating IDs.\",\n check(content, filePath) {\n if (!filePath.match(/\\.(js|ts|jsx|tsx)$/)) return [];\n if (isTestFile(filePath)) return [];\n if (!/\\/api\\//i.test(filePath)) return [];\n // Must have a route handler\n if (!/export\\s+(?:async\\s+)?function\\s+(?:GET|PUT|PATCH|DELETE)/i.test(content)) return [];\n // Must read an ID from params\n if (!/params\\.id|params\\.(?:slug|uuid)|req\\.params\\./i.test(content)) return [];\n // Must query a database with that param\n if (!/findUnique|findFirst|findById|\\.findOne|WHERE.*=.*params|\\.get\\(.*params/i.test(content)) return [];\n // Skip if ownership check exists\n if (/userId|ownerId|createdBy|user_id|owner_id|AND.*user|where.*user|authorize|canAccess|checkPermission|isOwner|belongsTo/i.test(content)) return [];\n // Skip admin routes\n if (/admin/i.test(filePath)) return [];\n // Skip if auth is present (may be checking ownership via middleware)\n if (/requireUser|requireUserForApi|getServerSession|auth\\(\\)/i.test(content)) return [];\n const m = content.match(/params\\.id|params\\.(?:slug|uuid)|req\\.params\\./);\n if (!m || m.index === undefined) return [];\n const lineNum = content.substring(0, m.index).split(\"\\n\").length;\n return [{\n rule: \"VC158\", title: this.title, severity: \"high\" as const, category: \"Authorization\",\n file: filePath, line: lineNum, snippet: getSnippet(content, lineNum),\n fix: \"Add an ownership check: verify the requesting user owns the record before returning it. Example: WHERE id = params.id AND userId = session.userId\",\n }];\n },\n};\n\n// ────────────────────────────────────────────\n// RULE TIERS: FREE (30 rules) + PRO (all 206)\n// VC152-VC158 are Pro rules (not in freeRules)\n// ────────────────────────────────────────────\n\n// Free tier: core security rules available to everyone (30 of 151)\nexport const freeRules: CustomRule[] = [\n hardcodedSecrets, // VC001\n exposedEnvFile, // VC002\n missingAuthMiddleware, // VC003\n supabaseNoRLS, // VC004\n stripeWebhookUnprotected, // VC005\n sqlInjection, // VC006\n xssVulnerability, // VC007\n noRateLimiting, // VC008\n corsWildcard, // VC009\n clientSideAuth, // VC010\n nextPublicSecret, // VC011\n envNotGitignored, // VC014\n evalUsage, // VC015\n unvalidatedRedirect, // VC016\n insecureCookies, // VC017\n exposedAuthSecret, // VC018\n missingCSP, // VC020\n hardcodedJWTSecret, // VC031\n missingHTTPS, // VC032\n exposedDebugMode, // VC033\n insecureRandomness, // VC034\n missingErrorBoundary, // VC036\n exposedStackTraces, // VC037\n missingLockFile, // VC039\n dangerousInnerHTML, // VC063\n consoleLogProduction, // VC097\n emptyCatchBlock, // VC104\n todoLeftInCode, // VC103\n weakHashing, // VC060\n disabledTLSVerification, // VC061\n];\n\n// Pro rules are delivered server-side — not shipped in npm package\n// See: /api/cli/rules-bundle endpoint\nexport const allRules = freeRules;\n\n// Every rule defined in this file, in ID order. The web API runs the full set\n// on each scan; the CLI uses this as the catalog for `/rules` docs and admin\n// tooling. Keep this in sync when adding new rules (tests will catch gaps).\nexport const allCustomRules: CustomRule[] = [\n // Free tier (VC001–VC039 plus legacy free IDs)\n hardcodedSecrets,\n exposedEnvFile,\n missingAuthMiddleware,\n supabaseNoRLS,\n stripeWebhookUnprotected,\n sqlInjection,\n xssVulnerability,\n noRateLimiting,\n corsWildcard,\n clientSideAuth,\n nextPublicSecret,\n envNotGitignored,\n evalUsage,\n unvalidatedRedirect,\n insecureCookies,\n exposedAuthSecret,\n missingCSP,\n hardcodedJWTSecret,\n missingHTTPS,\n exposedDebugMode,\n insecureRandomness,\n missingErrorBoundary,\n exposedStackTraces,\n missingLockFile,\n dangerousInnerHTML,\n consoleLogProduction,\n emptyCatchBlock,\n todoLeftInCode,\n weakHashing,\n disabledTLSVerification,\n // Pro tier: infrastructure, cloud, deserialization, auth, mobile, etc.\n firebaseClientConfig,\n supabaseAnonAdmin,\n insecureElectronWindow,\n ipcPathTraversal,\n unsanitizedHTMLExport,\n prototypePollution,\n missingFileSizeLimits,\n unsanitizedFilenames,\n electronNavigationUnrestricted,\n missingSecurityMeta,\n unvalidatedAPIParams,\n unvalidatedEventData,\n insecureDeserialization,\n openRedirectParams,\n insecureFileUpload,\n exposedGitDir,\n ssrfVulnerability,\n massAssignment,\n timingAttack,\n logInjection,\n weakPasswordRequirements,\n sessionFixation,\n missingBruteForce,\n nosqlInjection,\n exposedDBCredentials,\n missingDBEncryption,\n graphqlIntrospection,\n missingRequestSizeLimit,\n hardcodedIPAllowlist,\n sensitiveLocalStorage,\n exposedSourceMaps,\n clickjacking,\n overlyPermissiveIAM,\n dockerRunAsRoot,\n exposedDockerPorts,\n hardcodedEncryptionKey,\n exposedServerActions,\n unprotectedAPIRoutes,\n clientComponentSecret,\n insecureDeepLink,\n sensitiveAsyncStorage,\n missingCertPinning,\n androidDebuggable,\n djangoDebug,\n flaskSecretKey,\n pickleDeserialization,\n missingCSRF,\n githubActionsInjection,\n secretsInCI,\n corsServerless,\n k8sPrivileged,\n jwtAlgConfusion,\n regexDos,\n xxeVulnerability,\n ssti,\n javaDeserialization,\n missingSRI,\n exposedAdminRoutes,\n insecureWebSocket,\n missingHSTS,\n sensitiveURLParams,\n missingContentDisposition,\n hostHeaderRedirect,\n raceCondition,\n unsafeObjectAssign,\n unprotectedDownload,\n commandInjection,\n corsLocalhost,\n insecureGRPC,\n syncFileOps,\n eventListenerLeak,\n nPlusOneQuery,\n largeBundleImport,\n blockingMainThread,\n callbackHell,\n magicNumbers,\n s3BucketNoEncryption,\n securityGroupAllInbound,\n rdsPubliclyAccessible,\n missingCloudTrail,\n lambdaWithoutVPC,\n dockerLatestTag,\n dockerCopySensitive,\n dockerTooManyPorts,\n k8sSecretNotEncrypted,\n k8sNoResourceLimits,\n pathTraversal,\n piiInLogs,\n hardcodedOAuthSecret,\n missingOAuthState,\n unpinnedGitHubAction,\n deprecatedTLS,\n weakRSAKeySize,\n ecbModeEncryption,\n insecurePasswordReset,\n terraformStateExposed,\n insecureHTTPMethods,\n httpRequestSmuggling,\n unencryptedPII,\n missingAuthRateLimit,\n vulnerableDependencies,\n // VC132–VC151: expanded secret detection\n hardcodedAnthropicKey,\n hardcodedGitHubPAT,\n hardcodedSendGridKey,\n hardcodedSlackToken,\n hardcodedGCPServiceAccount,\n hardcodedShopifyToken,\n hardcodedGitLabToken,\n hardcodedTwilioKey,\n hardcodedMailgunKey,\n hardcodedDatadogKey,\n hardcodedVercelToken,\n hardcodedSupabaseServiceRole,\n hardcodedVaultToken,\n hardcodedPineconeKey,\n secretInURLParam,\n secretLoggedToConsole,\n secretInErrorResponse,\n secretInBundleConfig,\n secretInHTMLAttribute,\n secretInCLIArgument,\n // VC152–VC158\n webhookSignatureVerification,\n reflectedCORSOrigin,\n missingRequestValidation,\n missingAIRateLimit,\n missingPagination,\n exposedDatabaseStudio,\n insecureDirectObjectReference,\n // VC159–VC183: additional service-specific API key detection\n hardcodedCohereKey,\n hardcodedReplicateKey,\n hardcodedMistralKey,\n hardcodedTogetherKey,\n hardcodedGroqKey,\n hardcodedFireworksKey,\n hardcodedPostmarkKey,\n hardcodedResendKey,\n hardcodedLoopsKey,\n hardcodedCloudflareToken,\n hardcodedFastlyToken,\n hardcodedNetlifyToken,\n hardcodedRailwayToken,\n hardcodedFlyToken,\n hardcodedAlgoliaAdminKey,\n hardcodedQdrantKey,\n hardcodedWeaviateKey,\n hardcodedLinearKey,\n hardcodedNotionKey,\n hardcodedDiscordToken,\n hardcodedIntercomToken,\n hardcodedSentryAuthToken,\n hardcodedLogtailToken,\n hardcodedHighlightKey,\n hardcodedPlivoToken,\n // VC184–VC187: GitHub Actions workflow security\n ghaPullRequestTargetCheckout,\n ghaPermissionsWriteAll,\n ghaExpressionInjection,\n ghaThirdPartyActionWithSecrets,\n // VC188–VC190: Dockerfile hardening\n dockerfileADDInsteadOfCOPY,\n dockerfileUnverifiedShellPipe,\n dockerfileMissingHealthcheck,\n // VC191–VC197: Python-specific security gaps\n pyRequestsVerifyFalse,\n pyJinja2AutoescapeOff,\n pyTempfileMktemp,\n pyDjangoMarkSafe,\n pyParamikoAutoAdd,\n pyDjangoAllowedHostsWildcard,\n pyJWTDecodeWeakConfig,\n // VC198–VC203: AI / LLM-specific security\n llmPromptInjection,\n llmSystemPromptInjection,\n llmOutputAsHTML,\n vectorStoreQueryNoUserFilter,\n vectorStoreUpsertNoMetadata,\n llmCallNoMaxTokens,\n // VC204–VC206: GraphQL server hardening\n graphqlNoDepthLimit,\n graphqlNoComplexityLimit,\n graphqlCSRFDisabled,\n];\n\nexport function runCustomRules(\n content: string,\n filePath: string,\n disabledRules: string[] = [],\n tier: \"free\" | \"pro\" = \"free\",\n extraRules: CustomRule[] = [],\n): Finding[] {\n const findings: Finding[] = [];\n\n // Skip files that ARE security scanners (avoid scanning ourselves)\n if (/function runScan\\(files\\)|export function runCustomRules/.test(content) && /const (?:rules|allRules)\\s*[:=]/.test(content) && /findMatches/.test(content)) {\n return findings;\n }\n\n // Skip compiled bundles (pro-rules-bundle.cjs, webpack output, etc.)\n if (/pro-rules-bundle|\\.bundle\\./i.test(filePath)) return findings;\n\n // Skip files that contain intentional example/demo vulnerable code\n // (onboarding tutorials, blog code samples, documentation examples)\n if (/onboarding|demo-data|example-vulnerable|code-sample/i.test(filePath)) return findings;\n\n // Free tier uses bundled rules; Pro tier uses free + server-delivered extra rules\n const ruleset = tier === \"pro\" && extraRules.length > 0\n ? [...freeRules, ...extraRules]\n : freeRules;\n for (const rule of ruleset) {\n if (disabledRules.includes(rule.id)) continue;\n\n const matches = rule.check(content, filePath);\n const compliance = complianceMap[rule.id];\n for (const match of matches) {\n findings.push({\n id: `${match.rule}-${match.file}:${match.line}`,\n rule: match.rule,\n severity: match.severity,\n title: match.title,\n description: rule.description,\n file: match.file,\n line: match.line,\n column: match.column,\n snippet: match.snippet,\n fix: match.fix,\n category: match.category,\n source: \"custom\",\n owasp: compliance?.owasp,\n cwe: compliance?.cwe,\n });\n }\n }\n\n return findings;\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Finding } from \"./types.js\";\n\n/**\n * AI-powered false positive filter.\n *\n * Takes findings from the regex/entropy scanners and asks Claude Haiku\n * to classify each one as \"real\" or \"false positive\" based on the\n * surrounding code context. Findings marked FP are removed before the\n * user sees them, but preserved in filteredFindings so users can\n * review the AI's decisions.\n */\n\nconst REVIEW_SYSTEM_PROMPT = `You are reviewing security scan findings for false positives. Your job is to look at each finding and the surrounding code to determine if it's a REAL security vulnerability or a FALSE POSITIVE.\n\nCommon false positive patterns you should catch:\n- Auth check exists inside the function body (requireUser, requireUserForApi, getSession, etc.) but the scanner only checked the function signature\n- The flagged pattern is in example/documentation/tutorial code, not production code\n- The variable is developer-controlled (constants, config values, static strings), not user input\n- The flagged function is a database method (conn.exec, db.exec, prisma.$executeRaw) not a shell command (child_process.exec)\n- The comparison is a type check (typeof x === \"string\") not a secret comparison\n- The file is a test, mock, or fixture file\n- The \"secret\" is a publishable/public key (pk_test_, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) designed to be client-side\n- The innerHTML/dangerouslySetInnerHTML uses a static constant or JSON.stringify, not user input\n- The redirect URL has already been validated (isAllowedRedirect, validateRedirect)\n- The webhook endpoint is for a non-Stripe service but flagged as \"Stripe webhook\"\n- The \"sensitive data in URL\" is in a comment or documentation, not actual code\n- Package-lock.json URLs flagged as secrets (they're npm registry URLs, not secrets)\n\nFor each finding, respond ONLY with a JSON array. No other text.\nEach element: {\"index\": <number>, \"verdict\": \"real\" or \"fp\", \"reason\": \"<1 sentence>\"}`;\n\nconst MAX_FINDINGS_PER_BATCH = 15;\nconst MAX_CONTEXT_LINES = 10;\nconst MAX_TOTAL_FINDINGS = 50;\n\ninterface ReviewResult {\n index: number;\n verdict: \"real\" | \"fp\";\n reason: string;\n}\n\nexport interface FilteredFinding {\n finding: Finding;\n reason: string;\n}\n\nexport interface AIFilterResult {\n findings: Finding[];\n filteredFindings: FilteredFinding[];\n aiReviewed: boolean;\n removedCount: number;\n totalBefore: number;\n}\n\nfunction getExpandedContext(content: string, line: number, contextLines: number = MAX_CONTEXT_LINES): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">>>\" : \" \";\n return `${marker} ${lineNum} | ${l}`;\n }).join(\"\\n\");\n}\n\nfunction buildReviewPrompt(findings: Finding[], fileContent: string): string {\n const parts: string[] = [];\n for (let i = 0; i < findings.length; i++) {\n const f = findings[i];\n const context = getExpandedContext(fileContent, f.line);\n parts.push(`--- Finding ${i} ---\nRule: ${f.rule} (${f.title})\nSeverity: ${f.severity}\nFile: ${f.file}\nLine: ${f.line}\nDescription: ${f.description}\nSuggested fix: ${f.fix || \"N/A\"}\n\nCode context:\n${context}\n`);\n }\n return `Review these ${findings.length} security scan findings. For each one, determine if it's a real vulnerability or a false positive based on the code context.\\n\\n${parts.join(\"\\n\")}`;\n}\n\nfunction parseReviewResponse(text: string): ReviewResult[] {\n try {\n const cleaned = text.replace(/```json\\n?/g, \"\").replace(/```\\n?/g, \"\").trim();\n const parsed = JSON.parse(cleaned);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (r: unknown): r is ReviewResult =>\n typeof r === \"object\" && r !== null &&\n \"index\" in r && \"verdict\" in r &&\n ((r as ReviewResult).verdict === \"real\" || (r as ReviewResult).verdict === \"fp\")\n );\n } catch {\n return [];\n }\n}\n\n/**\n * Filter false positives from findings using Claude Haiku.\n *\n * Returns filtered findings, the removed findings with AI reasons,\n * and metadata about what the AI did.\n */\nexport async function filterFalsePositives(\n findings: Finding[],\n fileContents: Map<string, string>,\n): Promise<AIFilterResult> {\n const empty: AIFilterResult = { findings, filteredFindings: [], aiReviewed: false, removedCount: 0, totalBefore: findings.length };\n if (!process.env.ANTHROPIC_API_KEY) return empty;\n if (findings.length === 0) return empty;\n\n const toReview = findings.slice(0, MAX_TOTAL_FINDINGS);\n const overflow = findings.slice(MAX_TOTAL_FINDINGS);\n const totalBefore = findings.length;\n\n const byFile = new Map<string, Finding[]>();\n for (const f of toReview) {\n const group = byFile.get(f.file) || [];\n group.push(f);\n byFile.set(f.file, group);\n }\n\n let client: Anthropic;\n try {\n client = new Anthropic();\n } catch {\n return empty;\n }\n\n // Track which findings are FP and why\n const fpMap = new Map<number, string>(); // globalIndex → reason\n\n for (const [file, fileFindings] of byFile) {\n const content = fileContents.get(file);\n if (!content) continue;\n\n for (let i = 0; i < fileFindings.length; i += MAX_FINDINGS_PER_BATCH) {\n const batch = fileFindings.slice(i, i + MAX_FINDINGS_PER_BATCH);\n const prompt = buildReviewPrompt(batch, content);\n\n try {\n const response = await client.messages.create({\n model: \"claude-haiku-4-5-20251001\",\n max_tokens: 1024,\n system: REVIEW_SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: prompt }],\n });\n\n const text = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\");\n\n const results = parseReviewResponse(text);\n\n for (const r of results) {\n if (r.verdict === \"fp\" && r.index >= 0 && r.index < batch.length) {\n const globalIndex = toReview.indexOf(batch[r.index]);\n if (globalIndex !== -1) {\n fpMap.set(globalIndex, r.reason);\n }\n }\n }\n } catch {\n continue;\n }\n }\n }\n\n const filtered = toReview.filter((_, i) => !fpMap.has(i));\n const filteredFindings: FilteredFinding[] = [];\n for (const [idx, reason] of fpMap) {\n filteredFindings.push({ finding: toReview[idx], reason });\n }\n\n return {\n findings: [...filtered, ...overflow],\n filteredFindings,\n aiReviewed: true,\n removedCount: fpMap.size,\n totalBefore,\n };\n}\n","/**\n * Shannon-entropy based secret detection.\n *\n * Catches high-entropy string literals that look like API keys / tokens /\n * credentials even when they don't match any of the service-specific\n * hardcoded-secret rules (VC001, VC132, etc). Fires a single `ENTROPY`\n * finding per suspicious literal.\n *\n * The trick to keeping this useful is keeping false positives down. Real\n * codebases are full of high-entropy strings that aren't secrets — SHA-256\n * hashes, UUIDs, base64 integrity hashes, Tailwind-generated class names,\n * SVG path data, Next.js content-addressed filenames, publishable\n * (intentionally-public) keys from Clerk/Stripe/etc. This scanner has three\n * layers of suppression before emitting a finding:\n *\n * 1. File-level: skip lockfiles, CSS/SVG/image/map files, node_modules,\n * minified/bundled output.\n * 2. Shape-level: if the string matches a known-safe shape (UUID, Git SHA,\n * integrity hash, publishable key prefix, Tailwind fingerprint, SVG\n * path data, Next.js content hash), skip.\n * 3. Context-level: inspect the assignment context — if the variable name\n * implies \"this holds a hash/digest/fingerprint\" (not a secret), skip.\n * If the variable name implies a secret (key/token/etc), lower the\n * entropy bar. Otherwise require high entropy AND no safe signals.\n *\n * When in doubt we bias toward suppressing — one FP every 100 scans is much\n * more costly to the product than one missed secret, because the expanded\n * VC132-VC151 service-specific rules already cover the most common key\n * formats.\n */\n\nimport type { Finding } from \"./types.js\";\n\nfunction shannonEntropy(str: string): number {\n const freq: Record<string, number> = {};\n for (const ch of str) {\n freq[ch] = (freq[ch] || 0) + 1;\n }\n const len = str.length;\n let entropy = 0;\n for (const count of Object.values(freq)) {\n const p = count / len;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\n// ────────────────────────────────────────────\n// Shape-based allowlist — non-secret strings that look high-entropy\n// ────────────────────────────────────────────\n\nconst SAFE_PATTERNS: RegExp[] = [\n // UUIDs (v1-v5)\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n // Git commit hashes (long and short)\n /^[0-9a-f]{40}$/i,\n /^[0-9a-f]{7,8}$/i,\n // Hex colors\n /^#?[0-9a-fA-F]{3,8}$/,\n // Small base64 (< 20 chars can't be a real key anyway)\n /^[A-Za-z0-9+/]{1,19}={0,2}$/,\n // Package versions\n /^\\d+\\.\\d+\\.\\d+/,\n // Integrity-hash prefix (sha256-, sha512-, ...)\n /^sha\\d+-/i,\n // URLs without credentials in them\n /^https?:\\/\\/[^:@]*$/,\n /^https?:\\/\\/registry\\.npmjs\\.org\\//,\n /^https?:\\/\\/registry\\.yarnpkg\\.com\\//,\n // Full package integrity hash (sha512-[base64]=)\n /^sha\\d+-[A-Za-z0-9+/=]+$/,\n // Package tarball URLs\n /\\.tgz$/,\n /registry.*\\/-\\/.*\\.tgz$/,\n // ISO dates / times\n /^\\d{4}-\\d{2}-\\d{2}/,\n // Locale tags\n /^[a-z]{2}-[A-Z]{2}$/,\n // Text encodings\n /^utf-?8|ascii|latin|iso-8859/i,\n // MIME types\n /^(?:application|text|image|audio|video)\\//,\n // CSS keyword values\n /^(?:inherit|none|auto|block|flex|grid|absolute|relative|fixed|px|em|rem|%)/,\n // Developer-placeholder tokens\n /^(?:test|example|sample|demo|placeholder|temp|tmp|foo|bar|baz|lorem|ipsum)/i,\n // XML / DTD markers\n /DTD|DOCTYPE|w3\\.org|apple\\.com\\/DTDs/i,\n /xmlns|schema|xsd|xsi/i,\n\n // NEW — added in Wave 3.2 for context-aware FP reduction\n // ──────────────────────────────────────────────────────\n\n // Tailwind JIT / CSS-in-JS class-name fingerprints, e.g. \"css-2kx3yr8\",\n // \"tw-abc12def\", \"jss-1a2b3c4d\". Typically prefix + 6-12 hex-ish chars.\n /^(?:css|tw|jss|emotion|styled|mui|chakra)-[a-z0-9]{4,14}$/i,\n\n // SVG path data — starts with a path command letter followed by coords.\n // These can get very long and high-entropy but are never secrets.\n /^[MmLlHhVvCcSsQqTtAaZz][\\d.,\\s\\-MmLlHhVvCcSsQqTtAaZz]{10,}$/,\n\n // Next.js / Vite / webpack content-addressed asset filenames, e.g.\n // \"main.4e5f6a78.js\", \"chunk-2a3b.js\", \"_next/static/chunks/pages-xyz\".\n /\\.[0-9a-f]{6,16}\\.(?:js|css|mjs|woff2?|ttf|png|jpg|svg)(?:\\?.*)?$/i,\n /^_next\\//,\n\n // Publishable / client-side keys from common vendors. Flagged by their\n // own service-specific rules if they look wrong, but entropy should NOT\n // double-flag these. They are designed to ship to the browser.\n /^pk_(?:live|test|[a-z0-9]+)_/, // Stripe / Clerk publishable\n /^NEXT_PUBLIC_|^VITE_|^REACT_APP_/, // Build-time public env vars\n /^pub_/, // Segment etc.\n /^ey[A-Za-z0-9_-]+\\.ey[A-Za-z0-9_-]+\\./, // JWT header.payload prefix — don't flag solely on entropy\n\n // Content-Security-Policy hashes / nonces in HTML/JSON\n /^'sha\\d+-/,\n /^nonce-/i,\n\n // Embedded data URIs\n /^data:[a-z]+\\//i,\n];\n\n// File types that shouldn't be scanned for entropy at all\nconst SKIP_FILES = /\\.(css|scss|less|svg|md|txt|html?|xml|yml|yaml|toml|lock|map|woff2?|ttf|eot|ico|png|jpg|gif|webp)$/i;\nconst SKIP_FILENAMES = /(?:package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock|composer\\.lock|Gemfile\\.lock|Cargo\\.lock|poetry\\.lock|Pipfile\\.lock|shrinkwrap\\.json)$/i;\n\n// Variable names whose contents are almost never secrets\nconst SAFE_VAR_NAMES = /(?:description|message|text|label|title|content|template|html|svg|css|style|class(?:Name)?|query|mutation|schema|regex|pattern|format|placeholder|comment|url|path|route|endpoint|href|src|alt|name|type|version|encoding|charset|locale|translation|copy|prose|markdown|slug|handle)/i;\n\n// Variable names that specifically imply hashing / fingerprinting rather than\n// secret storage. If a hex/base64 blob lands in one of these, suppress.\nconst HASH_LIKE_VAR_NAMES = /(?:^|[^a-z])(?:hash|digest|checksum|fingerprint|etag|crc|md5|sha1|sha256|sha512|contenthash|buildid|revision|commit|sri|integrity|cacheKey|fileHash|assetId|versionId)(?:$|[^a-z])/i;\n\n// Value prefixes / shapes that imply \"algorithm-name:hex-blob\" hash literals\n// — e.g. \"sha256:a94a8fe5ccb19...\", \"md5:5d41402abc4b2a76b9719d911017c592\"\nconst HASH_PREFIX_RE = /^(?:sha\\d+|md5|crc32|base64|bcrypt|argon2|pbkdf2|blake2b?)[:_]/i;\n\n// Variable names that IMPLY a credential. If one of these catches a\n// high-entropy literal, we emit the finding at a lower entropy bar and a\n// higher severity.\nconst SECRET_VAR_NAMES = /(?:^|[^a-z])(?:key|secret|token|password|passwd|pwd|api[_-]?key|auth|credential|private|signing|bearer|access[_-]?token|refresh[_-]?token|session[_-]?id)(?:$|[^a-z])/i;\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\n/**\n * Scan a batch of files for high-entropy string literals.\n *\n * Each element in `files` must be `{ path, content }`. Returns a list of\n * `Finding` objects for suspicious string literals. See the file header for\n * the suppression layers.\n */\nexport function scanEntropy(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n if (SKIP_FILES.test(filePath)) continue;\n if (SKIP_FILENAMES.test(filePath)) continue;\n const basename = filePath.split(\"/\").pop() || \"\";\n if (SKIP_FILENAMES.test(basename)) continue;\n if (filePath.includes(\"node_modules\")) continue;\n if (filePath.includes(\".min.\")) continue;\n if (/pro-rules-bundle|\\.bundle\\.|\\.chunk\\./i.test(filePath)) continue;\n if (/(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|fixtures?\\/)/i.test(filePath)) continue;\n\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Skip comment lines\n const trimmed = line.trimStart();\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"#\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\")) continue;\n\n // Find string assignments (quotes or backticks)\n const stringPattern = /(?:[:=]\\s*)([\"'`])([^\"'`\\n]{20,120})\\1/g;\n let match: RegExpExecArray | null;\n\n while ((match = stringPattern.exec(line)) !== null) {\n const value = match[2];\n\n // Layer 2 — shape-based allowlist\n if (SAFE_PATTERNS.some(p => p.test(value))) continue;\n if (HASH_PREFIX_RE.test(value)) continue;\n\n // Layer 3 — context\n const beforeAssign = line.substring(0, match.index);\n if (SAFE_VAR_NAMES.test(beforeAssign)) continue;\n\n // Skip URL-ish strings without credentials\n if (/^https?:\\/\\/[^:@]*$/.test(value)) continue;\n\n // Skip sentence-like strings (more than 2 spaces => likely prose)\n if ((value.match(/\\s/g) || []).length > 2) continue;\n\n // Adaptive entropy thresholds depending on the charset of the value\n const isHex = /^[0-9a-fA-F]+$/.test(value);\n const isBase64 = /^[A-Za-z0-9+/]+=*$/.test(value);\n\n let threshold = 4.0;\n if (isHex) threshold = 3.0;\n else if (isBase64) threshold = 4.5;\n\n if (value.length < 20) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy < threshold) continue;\n\n // Pull out the variable name for context checks\n const varName = beforeAssign.match(/(\\w+)\\s*[:=]\\s*$/)?.[1] || \"\";\n\n // Context suppression: if the variable name clearly describes a\n // hash/digest/fingerprint AND the value is hex-ish or base64-ish,\n // suppress. This catches things like:\n // const contentHash = \"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\";\n // const etag = \"W/\\\"686897696a7c876b7e\\\"\";\n // config.buildId = \"9a4f2c8b3d1e5f7a8c9b0d1e2f3a4b5c\";\n if (HASH_LIKE_VAR_NAMES.test(varName) && (isHex || isBase64)) continue;\n\n const isLikelySecret = SECRET_VAR_NAMES.test(varName);\n\n // Require EITHER very-high entropy OR a secret-suggesting var name\n // before actually emitting a finding. This is the final gate.\n if (entropy < 4.5 && !isLikelySecret) continue;\n\n const masked = value.substring(0, 6) + \"...\" + value.substring(value.length - 4);\n\n findings.push({\n id: `ENTROPY-${filePath}:${i + 1}`,\n rule: \"ENTROPY\",\n severity: isLikelySecret ? \"critical\" : \"high\",\n title: \"High-Entropy String Detected (Possible Secret)\",\n description: `Found a high-entropy string (${entropy.toFixed(1)} bits) that may be a hardcoded secret or API key: \"${masked}\"`,\n file: filePath,\n line: i + 1,\n snippet: getSnippet(content, i + 1),\n fix: \"If this is a secret, move it to an environment variable. If it's not a secret (e.g., hash, encoded data), add it to .xploitscanignore.\",\n category: \"Secrets\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-798\",\n });\n }\n }\n }\n\n return findings;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,SAAS,WACd,SACA,MACA,eAA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","names":["_traverse","isTainted","import_traverse","traverse","_traverse","Anthropic","getSnippet"]}