diff --git a/static_rules.go b/static_rules.go index 08728d5..ac42ae8 100644 --- a/static_rules.go +++ b/static_rules.go @@ -203,858 +203,4 @@ func parseHunkNewStart(header string) int { return n } -// defaultRules returns the built-in set of 30+ static analysis rules. -func defaultRules() []StaticRule { - return []StaticRule{ - // ===================================================================== - // SECURITY - Go - // ===================================================================== - { - ID: "SEC-GO-001", - Name: "SQL Injection", - Description: "String formatting used in SQL query construction; use parameterized queries instead", - Language: "go", - Pattern: regexp.MustCompile(`fmt\.Sprintf\s*\(\s*"[^"]*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"]*%[sv]`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-89", - Fix: "Use parameterized queries with ? or $1 placeholders instead of fmt.Sprintf for SQL", - }, - { - ID: "SEC-GO-002", - Name: "Command Injection", - Description: "Potentially unsanitized input passed to exec.Command", - Language: "go", - Pattern: regexp.MustCompile(`exec\.Command\s*\([^"` + "`" + `][^,)]*\)`), - Antipattern: regexp.MustCompile(`exec\.Command\s*\(\s*"[^"]+"\s*\)`), - Severity: "critical", - Category: "security", - CWE: "CWE-78", - Fix: "Validate and sanitize all input passed to exec.Command; use an allowlist of permitted commands", - }, - { - ID: "SEC-GO-003", - Name: "Path Traversal", - Description: "User-controlled path used in file operations without filepath.Clean or validation", - Language: "go", - Pattern: regexp.MustCompile(`(?:os\.(?:Open|ReadFile|Create|WriteFile)|filepath\.Join)\s*\(.*(?:req\.|r\.|input|param|query|user)`), - Antipattern: regexp.MustCompile(`filepath\.Clean`), - Severity: "high", - Category: "security", - CWE: "CWE-22", - Fix: "Use filepath.Clean() and validate the resolved path is within the expected directory", - }, - { - ID: "SEC-GO-004", - Name: "Hardcoded Secret", - Description: "Hardcoded password or secret string detected", - Language: "go", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*(?::?=|=)\s*"[^"]{4,}`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", - }, - { - ID: "SEC-GO-005", - Name: "Insecure TLS", - Description: "TLS certificate verification is disabled", - Language: "go", - Pattern: regexp.MustCompile(`InsecureSkipVerify\s*:\s*true`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-295", - Fix: "Remove InsecureSkipVerify: true; configure proper TLS certificate validation", - }, - { - ID: "SEC-GO-006", - Name: "Weak Crypto (MD5)", - Description: "MD5 is cryptographically broken and should not be used for security purposes", - Language: "go", - Pattern: regexp.MustCompile(`md5\.(?:New|Sum)`), - Antipattern: regexp.MustCompile(`(?i)(?:checksum|fingerprint|cache|etag|non.?security)`), - Severity: "medium", - Category: "security", - CWE: "CWE-328", - Fix: "Use crypto/sha256 or crypto/sha512 instead of MD5 for security-sensitive operations", - }, - { - ID: "SEC-GO-007", - Name: "Weak Crypto (SHA1)", - Description: "SHA-1 is deprecated for security use; collisions are practical", - Language: "go", - Pattern: regexp.MustCompile(`sha1\.(?:New|Sum)`), - Antipattern: regexp.MustCompile(`(?i)(?:git|fingerprint|cache|etag|non.?security)`), - Severity: "medium", - Category: "security", - CWE: "CWE-328", - Fix: "Use crypto/sha256 or crypto/sha512 instead of SHA-1 for security-sensitive operations", - }, - { - ID: "SEC-GO-008", - Name: "Unvalidated Redirect", - Description: "HTTP redirect using user-controlled input without validation", - Language: "go", - Pattern: regexp.MustCompile(`http\.Redirect\s*\(.*(?:r\.(?:URL|Form|Query)|req\.|param|input)`), - Antipattern: nil, - Severity: "medium", - Category: "security", - CWE: "CWE-601", - Fix: "Validate redirect URLs against an allowlist of permitted destinations", - }, - { - ID: "SEC-GO-009", - Name: "Sensitive Data in Log", - Description: "Potentially logging sensitive data (passwords, tokens, secrets)", - Language: "go", - Pattern: regexp.MustCompile(`(?:log\.|slog\.|logger\.)(?:Print|Info|Debug|Warn|Error|Fatal).*(?i)(?:password|token|secret|key|credential)`), - Antipattern: regexp.MustCompile(`(?i)(?:redact|mask|\*\*\*|)`), - Severity: "medium", - Category: "security", - CWE: "CWE-532", - Fix: "Redact sensitive values before logging; never log passwords, tokens, or API keys", - }, - { - ID: "SEC-GO-010", - Name: "Defer in Loop", - Description: "defer inside a loop can accumulate resources until the function returns", - Language: "go", - Pattern: regexp.MustCompile(`^\s*defer\s+`), - Antipattern: nil, - Severity: "medium", - Category: "correctness", - CWE: "", - Fix: "Move the deferred call into a helper function or handle resource cleanup explicitly in the loop", - }, - // ===================================================================== - // SECURITY - Python - // ===================================================================== - { - ID: "SEC-PY-001", - Name: "SQL Injection (f-string)", - Description: "f-string used in SQL query construction; use parameterized queries", - Language: "python", - Pattern: regexp.MustCompile(`f["'](?:[^"']*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"']*)\{`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-89", - Fix: "Use parameterized queries with %s or ? placeholders passed as a tuple", - }, - { - ID: "SEC-PY-002", - Name: "eval() Usage", - Description: "eval() executes arbitrary code and is a common injection vector", - Language: "python", - Pattern: regexp.MustCompile(`\beval\s*\(`), - Antipattern: regexp.MustCompile(`(?i)(?:#.*safe|#.*trusted|ast\.literal_eval)`), - Severity: "critical", - Category: "security", - CWE: "CWE-95", - Fix: "Use ast.literal_eval() for data parsing or find a safer alternative to eval()", - }, - { - ID: "SEC-PY-003", - Name: "exec() Usage", - Description: "exec() executes arbitrary code strings and is dangerous with user input", - Language: "python", - Pattern: regexp.MustCompile(`\bexec\s*\(`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-95", - Fix: "Avoid exec(); use structured approaches like importlib or predefined function dispatch", - }, - { - ID: "SEC-PY-004", - Name: "Pickle Deserialization", - Description: "pickle.loads() can execute arbitrary code during deserialization", - Language: "python", - Pattern: regexp.MustCompile(`pickle\.loads?\s*\(`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-502", - Fix: "Use JSON or another safe serialization format; if pickle is required, only load from trusted sources", - }, - { - ID: "SEC-PY-005", - Name: "Subprocess Shell Injection", - Description: "subprocess with shell=True is vulnerable to shell injection", - Language: "python", - Pattern: regexp.MustCompile(`subprocess\.(?:call|run|Popen|check_output|check_call)\s*\(.*shell\s*=\s*True`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-78", - Fix: "Use shell=False (default) and pass command as a list instead of a string", - }, - { - ID: "SEC-PY-006", - Name: "Assert in Production", - Description: "assert statements are stripped with -O flag; do not use for validation", - Language: "python", - Pattern: regexp.MustCompile(`^\s*assert\s+`), - Antipattern: regexp.MustCompile(`(?i)(?:test_|_test\.py|conftest|pytest)`), - Severity: "low", - Category: "correctness", - CWE: "CWE-617", - Fix: "Replace assert with explicit validation that raises an appropriate exception", - }, - { - ID: "SEC-PY-007", - Name: "Hardcoded Secret (Python)", - Description: "Hardcoded password or secret string detected", - Language: "python", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*["'][^"']{4,}["']`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", - }, - { - ID: "SEC-PY-008", - Name: "YAML Unsafe Load", - Description: "yaml.load() without SafeLoader can execute arbitrary Python objects", - Language: "python", - Pattern: regexp.MustCompile(`yaml\.load\s*\(`), - Antipattern: regexp.MustCompile(`Loader\s*=\s*(?:yaml\.)?SafeLoader`), - Severity: "high", - Category: "security", - CWE: "CWE-502", - Fix: "Use yaml.safe_load() or pass Loader=yaml.SafeLoader explicitly", - }, - // ===================================================================== - // SECURITY - TypeScript/JavaScript - // ===================================================================== - { - ID: "SEC-TS-001", - Name: "innerHTML Assignment", - Description: "Direct innerHTML assignment enables XSS attacks", - Language: "typescript", - Pattern: regexp.MustCompile(`\.innerHTML\s*=`), - Antipattern: regexp.MustCompile(`(?i)(?:sanitize|DOMPurify|escape)`), - Severity: "high", - Category: "security", - CWE: "CWE-79", - Fix: "Use textContent for plain text or a sanitization library (DOMPurify) for HTML", - }, - { - ID: "SEC-JS-001", - Name: "innerHTML Assignment", - Description: "Direct innerHTML assignment enables XSS attacks", - Language: "javascript", - Pattern: regexp.MustCompile(`\.innerHTML\s*=`), - Antipattern: regexp.MustCompile(`(?i)(?:sanitize|DOMPurify|escape)`), - Severity: "high", - Category: "security", - CWE: "CWE-79", - Fix: "Use textContent for plain text or a sanitization library (DOMPurify) for HTML", - }, - { - ID: "SEC-TS-002", - Name: "eval() Usage", - Description: "eval() executes arbitrary code and is a common injection vector", - Language: "typescript", - Pattern: regexp.MustCompile(`\beval\s*\(`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-95", - Fix: "Use JSON.parse() for data or Function constructor with extreme caution", - }, - { - ID: "SEC-JS-002", - Name: "eval() Usage", - Description: "eval() executes arbitrary code and is a common injection vector", - Language: "javascript", - Pattern: regexp.MustCompile(`\beval\s*\(`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-95", - Fix: "Use JSON.parse() for data or Function constructor with extreme caution", - }, - { - ID: "SEC-TS-003", - Name: "Prototype Pollution", - Description: "__proto__ access can lead to prototype pollution attacks", - Language: "typescript", - Pattern: regexp.MustCompile(`__proto__`), - Antipattern: regexp.MustCompile(`(?i)(?:hasOwnProperty|Object\.create\(null\))`), - Severity: "high", - Category: "security", - CWE: "CWE-1321", - Fix: "Use Object.create(null) for lookup objects or validate keys against __proto__, constructor, prototype", - }, - { - ID: "SEC-JS-003", - Name: "Prototype Pollution", - Description: "__proto__ access can lead to prototype pollution attacks", - Language: "javascript", - Pattern: regexp.MustCompile(`__proto__`), - Antipattern: regexp.MustCompile(`(?i)(?:hasOwnProperty|Object\.create\(null\))`), - Severity: "high", - Category: "security", - CWE: "CWE-1321", - Fix: "Use Object.create(null) for lookup objects or validate keys against __proto__, constructor, prototype", - }, - { - ID: "SEC-TS-004", - Name: "Regex DoS", - Description: "Complex regex with nested quantifiers may be vulnerable to ReDoS", - Language: "typescript", - Pattern: regexp.MustCompile(`(?:new RegExp|/)\s*.*(?:\+\+|\*\*|\{\d+,\}.*\{\d+,\}|(?:\.\*){2,}|\([^)]*\+\)[^)]*\+)`), - Antipattern: nil, - Severity: "medium", - Category: "security", - CWE: "CWE-1333", - Fix: "Simplify regex; add bounds to quantifiers; consider using re2 or a timeout", - }, - { - ID: "SEC-JS-004", - Name: "Regex DoS", - Description: "Complex regex with nested quantifiers may be vulnerable to ReDoS", - Language: "javascript", - Pattern: regexp.MustCompile(`(?:new RegExp|/)\s*.*(?:\+\+|\*\*|\{\d+,\}.*\{\d+,\}|(?:\.\*){2,}|\([^)]*\+\)[^)]*\+)`), - Antipattern: nil, - Severity: "medium", - Category: "security", - CWE: "CWE-1333", - Fix: "Simplify regex; add bounds to quantifiers; consider using re2 or a timeout", - }, - { - ID: "SEC-TS-005", - Name: "Hardcoded Secret (TS)", - Description: "Hardcoded password or secret string detected", - Language: "typescript", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*[=:]\s*["'][^"']{4,}["']`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|process\.env|import)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", - }, - { - ID: "SEC-JS-005", - Name: "Hardcoded Secret (JS)", - Description: "Hardcoded password or secret string detected", - Language: "javascript", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*[=:]\s*["'][^"']{4,}["']`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|process\.env|import)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", - }, - // ===================================================================== - // CORRECTNESS - Go - // ===================================================================== - { - ID: "COR-GO-001", - Name: "Unchecked Error", - Description: "Function return value that likely includes an error is discarded", - Language: "go", - Pattern: regexp.MustCompile(`^\s*[a-zA-Z_][a-zA-Z0-9_.]*\s*\(.*\)\s*$`), - Antipattern: regexp.MustCompile(`(?:^//|^\s*//|defer|go\s+|fmt\.Print|log\.|println|print\()`), - Severity: "medium", - Category: "correctness", - CWE: "CWE-252", - Fix: "Capture and handle the returned error: if err != nil { return err }", - }, - { - ID: "COR-GO-002", - Name: "Goroutine Leak Risk", - Description: "Goroutine launched without visible context cancellation or done channel", - Language: "go", - Pattern: regexp.MustCompile(`\bgo\s+func\s*\(`), - Antipattern: regexp.MustCompile(`(?:ctx|context|done|cancel|Done\(\)|quit|stop|timer|ticker)`), - Severity: "medium", - Category: "correctness", - CWE: "", - Fix: "Pass a context.Context or done channel to goroutines to enable graceful shutdown", - }, - { - ID: "COR-GO-003", - Name: "Race Condition Risk", - Description: "Shared variable access in goroutine without synchronization", - Language: "go", - Pattern: regexp.MustCompile(`go\s+func\s*\([^)]*\)\s*\{[^}]*(?:[a-z]+\s*(?:\+\+|--|(?:\+|-|\*|/)=))`), - Antipattern: regexp.MustCompile(`(?:mutex|Mutex|sync\.|atomic\.|Lock\(\)|chan\s)`), - Severity: "high", - Category: "correctness", - CWE: "CWE-362", - Fix: "Use sync.Mutex, sync/atomic, or channels to synchronize shared state access", - }, - { - ID: "COR-GO-004", - Name: "nil Map Write", - Description: "Writing to a potentially nil map causes a runtime panic", - Language: "go", - Pattern: regexp.MustCompile(`var\s+\w+\s+map\[`), - Antipattern: regexp.MustCompile(`=\s*(?:make|map\[)`), - Severity: "high", - Category: "correctness", - CWE: "", - Fix: "Initialize the map with make() before writing to it", - }, - // ===================================================================== - // PERFORMANCE - // ===================================================================== - { - ID: "PERF-GO-001", - Name: "String Concat in Loop", - Description: "String concatenation with += in a loop causes quadratic allocation", - Language: "go", - Pattern: regexp.MustCompile(`\w+\s*\+=\s*(?:"|\w+)`), - Antipattern: regexp.MustCompile(`(?:int|float|byte|rune|uint|count|total|sum|num|idx|index|offset|i\s*\+)`), - Severity: "low", - Category: "performance", - CWE: "", - Fix: "Use strings.Builder for string concatenation in loops", - }, - { - ID: "PERF-GO-002", - Name: "Unbounded Allocation", - Description: "Slice allocation with user-controlled size without bounds check", - Language: "go", - Pattern: regexp.MustCompile(`make\s*\(\s*\[\]\w+\s*,\s*(?:req\.|r\.|input|param|query|size|length|count|n\b)`), - Antipattern: regexp.MustCompile(`(?:maxSize|maxLen|cap|min\(|max\(|limit|<=|>=|<\s*\d|>\s*\d)`), - Severity: "medium", - Category: "performance", - CWE: "CWE-789", - Fix: "Validate and cap allocation sizes to prevent denial of service via memory exhaustion", - }, - { - ID: "PERF-GO-003", - Name: "N+1 Query Pattern", - Description: "Database query inside a loop suggests N+1 query problem", - Language: "go", - Pattern: regexp.MustCompile(`(?:\.Query|\.Exec|\.Get|\.Find|\.First|\.Select|\.Where)\s*\(`), - Antipattern: regexp.MustCompile(`(?:batch|bulk|IN\s*\(|ids|Preload|Join)`), - Severity: "medium", - Category: "performance", - CWE: "", - Fix: "Batch database queries outside the loop; use IN clauses or JOINs", - }, - { - ID: "PERF-PY-001", - Name: "N+1 Query Pattern (Python)", - Description: "Database query inside a loop suggests N+1 query problem", - Language: "python", - Pattern: regexp.MustCompile(`(?:\.execute|\.query|\.filter|\.get|session\.)\s*\(`), - Antipattern: regexp.MustCompile(`(?:bulk|batch|in_|prefetch|select_related|join)`), - Severity: "medium", - Category: "performance", - CWE: "", - Fix: "Use select_related/prefetch_related (Django) or eager loading (SQLAlchemy) to batch queries", - }, - { - ID: "PERF-ANY-001", - Name: "Synchronous Sleep", - Description: "Hardcoded sleep/delay found; consider exponential backoff or event-driven approach", - Language: "any", - Pattern: regexp.MustCompile(`(?:time\.Sleep|sleep\(|setTimeout.*\d{4,}|Thread\.sleep)`), - Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock|backoff|retry)`), - Severity: "low", - Category: "performance", - CWE: "", - Fix: "Use exponential backoff for retries or event-driven signaling instead of fixed sleeps", - }, - // ===================================================================== - // SECURITY - Any Language - // ===================================================================== - { - ID: "SEC-ANY-001", - Name: "TODO Security", - Description: "TODO/FIXME/HACK comment related to security found in code", - Language: "any", - Pattern: regexp.MustCompile(`(?i)(?://|#|/\*)\s*(?:TODO|FIXME|HACK|XXX).*(?:security|auth|crypt|password|token|vuln|inject|xss|csrf)`), - Antipattern: nil, - Severity: "medium", - Category: "security", - CWE: "", - Fix: "Address security-related TODOs before shipping; they indicate known unresolved issues", - }, - { - ID: "SEC-ANY-002", - Name: "Private Key in Source", - Description: "Private key material appears to be embedded in source code", - Language: "any", - Pattern: regexp.MustCompile(`-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|sample|mock|fixture)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Remove private keys from source; load them from secure storage or environment at runtime", - }, - { - ID: "SEC-ANY-003", - Name: "HTTP in Production", - Description: "Insecure HTTP URL used where HTTPS should be expected", - Language: "any", - Pattern: regexp.MustCompile(`http://[a-zA-Z][a-zA-Z0-9._-]*`), - Antipattern: regexp.MustCompile(`(?i)(?:http://localhost|http://127\.0\.0\.1|http://0\.0\.0\.0|http://\[?::1|http://example\.com|test|spec|mock|doc|xmlns|http://www\.w3\.org)`), - Severity: "low", - Category: "security", - CWE: "CWE-319", - Fix: "Use HTTPS for all production URLs to prevent man-in-the-middle attacks", - }, - // ===================================================================== - // SECURITY - Rust - // ===================================================================== - { - ID: "SEC-RS-001", - Name: "Unsafe Block", - Description: "unsafe block bypasses Rust's memory safety guarantees; ensure the invariant is upheld", - Language: "rust", - Pattern: regexp.MustCompile(`\bunsafe\s*\{`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-242", - Fix: "Avoid unsafe blocks; use safe abstractions. If required, document the safety invariant in a comment", - }, - { - ID: "SEC-RS-002", - Name: "unwrap() in Production", - Description: "unwrap() will panic at runtime if the value is None/Err; handle errors gracefully instead", - Language: "rust", - Pattern: regexp.MustCompile(`\.unwrap\(\)`), - Antipattern: regexp.MustCompile(`(?i)(?:test|#\[test|#\[cfg\(test|expect\()`), - Severity: "medium", - Category: "correctness", - CWE: "CWE-252", - Fix: "Use match, if let, or the ? operator to handle Option/Result properly", - }, - { - ID: "SEC-RS-003", - Name: "Hardcoded Secret (Rust)", - Description: "Hardcoded password or secret string detected", - Language: "rust", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*(?:=>|:)\s*"[^"]{4,}`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", - }, - { - ID: "SEC-RS-004", - Name: "SQL Injection (Rust)", - Description: "format! macro used in SQL query construction; use parameterized queries", - Language: "rust", - Pattern: regexp.MustCompile(`format!\s*\(\s*"[^"]*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"]*\{`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-89", - Fix: "Use parameterized queries with $1 placeholders (e.g., sqlx or diesel) instead of format!", - }, - { - ID: "SEC-RS-005", - Name: "Command Injection (Rust)", - Description: "Command::new with user-controlled input may allow command injection", - Language: "rust", - Pattern: regexp.MustCompile(`Command::new\s*\([^"'][^)]*\)`), - Antipattern: regexp.MustCompile(`Command::new\s*\(\s*"[^"]+"\s*\)`), - Severity: "critical", - Category: "security", - CWE: "CWE-78", - Fix: "Validate and sanitize all input passed to Command::new; use an allowlist of permitted commands", - }, - // ===================================================================== - // SECURITY - Java - // ===================================================================== - { - ID: "SEC-JV-001", - Name: "SQL Injection (Java)", - Description: "String concatenation used in SQL statement execution; use PreparedStatement instead", - Language: "java", - Pattern: regexp.MustCompile(`(?:(?:execute|executeQuery|executeUpdate)\s*\(.*\+)|(?:(?:Statement|createStatement).*\+)`), - Antipattern: regexp.MustCompile(`PreparedStatement|setParameter|bindValue`), - Severity: "critical", - Category: "security", - CWE: "CWE-89", - Fix: "Use PreparedStatement with parameter placeholders (?) instead of string concatenation", - }, - { - ID: "SEC-JV-002", - Name: "Insecure Deserialization (Java)", - Description: "ObjectInputStream.readObject() can execute arbitrary code during deserialization", - Language: "java", - Pattern: regexp.MustCompile(`ObjectInputStream.*\.readObject\s*\(`), - Antipattern: regexp.MustCompile(`(?i)(?:ValidatingObjectInputStream|ObjectInputFilter|whiteList|allowList)`), - Severity: "critical", - Category: "security", - CWE: "CWE-502", - Fix: "Use ValidatingObjectInputStream or an ObjectInputFilter with a strict allowlist", - }, - { - ID: "SEC-JV-003", - Name: "SSRF (Java)", - Description: "new URL() with user-controlled input may lead to Server-Side Request Forgery", - Language: "java", - Pattern: regexp.MustCompile(`new\s+URL\s*\(.*(?:req\.|request\.|param|input|query|user)`), - Antipattern: regexp.MustCompile(`(?i)(?:allowList|whitelist|validate.*url|UriUtils)`), - Severity: "high", - Category: "security", - CWE: "CWE-918", - Fix: "Validate URLs against an allowlist of permitted domains and schemes", - }, - { - ID: "SEC-JV-004", - Name: "Hardcoded Secret (Java)", - Description: "Hardcoded password or secret string detected", - Language: "java", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake|System\.getenv)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables, a secrets manager, or a vault instead of hardcoded credentials", - }, - { - ID: "SEC-JV-005", - Name: "Path Traversal (Java)", - Description: "new File() with user-controlled input may allow path traversal attacks", - Language: "java", - Pattern: regexp.MustCompile(`new\s+File\s*\(.*(?:req\.|request\.|param|input|query|user|\+)`), - Antipattern: regexp.MustCompile(`(?i)(?:canonicalPath|normalize|sanitizePath|getCanonicalPath)`), - Severity: "high", - Category: "security", - CWE: "CWE-22", - Fix: "Validate and normalize paths; ensure the resolved path is within the expected directory", - }, - // ===================================================================== - // SECURITY - C/C++ - // ===================================================================== - { - ID: "SEC-C-001", - Name: "Buffer Overflow", - Description: "Unsafe string function (strcpy, strcat, sprintf, gets) used without bounds checking", - Language: "c", - Pattern: regexp.MustCompile(`\b(?:strcpy|strcat|sprintf|gets)\s*\(`), - Antipattern: regexp.MustCompile(`(?:strncpy|strncat|snprintf|fgets|safe)`), - Severity: "critical", - Category: "security", - CWE: "CWE-120", - Fix: "Use bounded variants: strncpy, strncat, snprintf, or fgets instead", - }, - { - ID: "SEC-C-002", - Name: "Format String Vulnerability", - Description: "printf-family function called with non-literal format string; may enable format string attacks", - Language: "c", - Pattern: regexp.MustCompile(`\b(?:printf|fprintf|sprintf|snprintf)\s*\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*[,)]`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-134", - Fix: "Use a literal format string: printf(\"%s\", var) instead of printf(var)", - }, - { - ID: "SEC-C-003", - Name: "Use-After-Free Risk", - Description: "Pointer used after free() call without being set to NULL", - Language: "c", - Pattern: regexp.MustCompile(`\bfree\s*\(\s*\w+\s*\)`), - Antipattern: regexp.MustCompile(`(?:(?:\w+)\s*=\s*NULL|=\s*0\b)`), - Severity: "high", - Category: "security", - CWE: "CWE-416", - Fix: "Set pointer to NULL after free() to prevent use-after-free and double-free", - }, - { - ID: "SEC-C-004", - Name: "Hardcoded Secret (C/C++)", - Description: "Hardcoded password or secret string detected", - Language: "c", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secure configuration mechanism instead of hardcoded secrets", - }, - { - ID: "SEC-C-005", - Name: "Command Injection (C)", - Description: "system() or popen() with user-controlled input may allow command injection", - Language: "c", - Pattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*[a-zA-Z_]`), - Antipattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*"[^"]*"\s*\)`), - Severity: "critical", - Category: "security", - CWE: "CWE-78", - Fix: "Use execve() with an explicit argument vector instead of system()/popen() with shell interpretation", - }, - // Duplicate C/C++ rules for cpp language tag - { - ID: "SEC-CPP-001", - Name: "Buffer Overflow (C++)", - Description: "Unsafe string function (strcpy, strcat, sprintf, gets) used without bounds checking", - Language: "cpp", - Pattern: regexp.MustCompile(`\b(?:strcpy|strcat|sprintf|gets)\s*\(`), - Antipattern: regexp.MustCompile(`(?:strncpy|strncat|snprintf|fgets|std::string|safe)`), - Severity: "critical", - Category: "security", - CWE: "CWE-120", - Fix: "Use std::string or bounded variants: strncpy, strncat, snprintf, or fgets instead", - }, - { - ID: "SEC-CPP-002", - Name: "Format String Vulnerability (C++)", - Description: "printf-family function called with non-literal format string", - Language: "cpp", - Pattern: regexp.MustCompile(`\b(?:printf|fprintf|sprintf|snprintf)\s*\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*[,)]`), - Antipattern: nil, - Severity: "high", - Category: "security", - CWE: "CWE-134", - Fix: "Use a literal format string or std::cout / fmt::format instead", - }, - { - ID: "SEC-CPP-003", - Name: "Use-After-Free Risk (C++)", - Description: "Pointer used after free()/delete without being set to nullptr", - Language: "cpp", - Pattern: regexp.MustCompile(`\b(?:free\s*\(\s*\w+\s*\)|delete\s+\w+)`), - Antipattern: regexp.MustCompile(`(?:(?:\w+)\s*=\s*(?:NULL|nullptr)|=\s*0\b|unique_ptr|shared_ptr)`), - Severity: "high", - Category: "security", - CWE: "CWE-416", - Fix: "Use smart pointers (unique_ptr, shared_ptr); set raw pointers to nullptr after delete", - }, - { - ID: "SEC-CPP-004", - Name: "Hardcoded Secret (C++)", - Description: "Hardcoded password or secret string detected", - Language: "cpp", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use environment variables or a secure configuration mechanism instead of hardcoded secrets", - }, - { - ID: "SEC-CPP-005", - Name: "Command Injection (C++)", - Description: "system() or popen() with user-controlled input may allow command injection", - Language: "cpp", - Pattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*[a-zA-Z_]`), - Antipattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*"[^"]*"\s*\)`), - Severity: "critical", - Category: "security", - CWE: "CWE-78", - Fix: "Use execve() or boost::process with an explicit argument vector instead of system()/popen()", - }, - // ===================================================================== - // SECURITY - Ruby - // ===================================================================== - { - ID: "SEC-RB-001", - Name: "SQL Injection (Ruby)", - Description: "String interpolation in SQL query; use parameterized queries instead", - Language: "ruby", - Pattern: regexp.MustCompile(`(?:(?:\.where|\.find_by_sql|\.execute|\.query)\s*\(?\s*["'].*#\{)|(?:%[qQwWiI]?\{.*(?:SELECT|INSERT|UPDATE|DELETE).*#\{)`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-89", - Fix: "Use parameterized queries: Model.where('id = ?', id) or ActiveRecord::sanitize_sql", - }, - { - ID: "SEC-RB-002", - Name: "Command Injection (Ruby)", - Description: "system(), exec(), or backtick command with user-controlled input", - Language: "ruby", - Pattern: regexp.MustCompile(`\b(?:system|exec|` + "`" + `)\s*[\(]?.*#\{`), - Antipattern: nil, - Severity: "critical", - Category: "security", - CWE: "CWE-78", - Fix: "Use Open3.capture2 or Kernel#system with separate arguments to avoid shell injection", - }, - { - ID: "SEC-RB-003", - Name: "Mass Assignment", - Description: "permit! or unfiltered params usage allows arbitrary attribute assignment", - Language: "ruby", - Pattern: regexp.MustCompile(`\.permit!|params\s*\[|params\.require\(.*\)\.permit\s*[^(\w]`), - Antipattern: regexp.MustCompile(`\.permit\s*\(\s*[:\w]`), - Severity: "high", - Category: "security", - CWE: "CWE-915", - Fix: "Use strong parameters: params.require(:model).permit(:field1, :field2) with explicit allowlist", - }, - { - ID: "SEC-RB-004", - Name: "Hardcoded Secret (Ruby)", - Description: "Hardcoded password or secret string detected", - Language: "ruby", - Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*['"][^'"]{4,}['"]`), - Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake|ENV\[)`), - Severity: "critical", - Category: "security", - CWE: "CWE-798", - Fix: "Use ENV['VAR_NAME'], Rails credentials, or a secrets manager instead of hardcoded credentials", - }, - { - ID: "SEC-RB-005", - Name: "ERB XSS", - Description: "<%= %> tag without sanitization may render user content unsafely", - Language: "ruby", - Pattern: regexp.MustCompile(`<%=\s*`), - Antipattern: regexp.MustCompile(`(?i)(?:h\(|html_escape|sanitize|escape_javascript|CGI\.escape)`), - Severity: "high", - Category: "security", - CWE: "CWE-79", - Fix: "Use <%=h ... %> or <%= sanitize(...) %> to escape user content in ERB templates", - }, - // ===================================================================== - // SECURITY - SQL - // ===================================================================== - { - ID: "SEC-SQL-001", - Name: "DROP TABLE in Migration", - Description: "DROP TABLE statement found; data loss is irreversible without backup", - Language: "sql", - Pattern: regexp.MustCompile(`(?i)\bDROP\s+TABLE\b`), - Antipattern: regexp.MustCompile(`(?i)(?:IF\s+EXISTS|migration|rollback|revert)`), - Severity: "high", - Category: "security", - CWE: "", - Fix: "Use 'DROP TABLE IF EXISTS' and ensure a rollback migration exists; consider soft deletes", - }, - { - ID: "SEC-SQL-002", - Name: "Always-True Condition", - Description: "Tautological condition (1=1, OR 1=1) found; may indicate SQL injection or logic error", - Language: "sql", - Pattern: regexp.MustCompile(`(?i)\b(?:OR\s+1\s*=\s*1|AND\s+1\s*=\s*1)\b`), - Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock)`), - Severity: "high", - Category: "security", - CWE: "CWE-89", - Fix: "Remove tautological conditions; if this is injection testing, use parameterized queries in production", - }, - { - ID: "SEC-SQL-003", - Name: "UNION-Based Injection Pattern", - Description: "UNION SELECT pattern may indicate SQL injection attack vector", - Language: "sql", - Pattern: regexp.MustCompile(`(?i)\bUNION\s+(?:ALL\s+)?SELECT\b`), - Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock|migration)`), - Severity: "high", - Category: "security", - CWE: "CWE-89", - Fix: "Use parameterized queries; never concatenate user input into SQL statements", - }, - } -} +// defaultRules and the built-in rule set live in static_rules_defaults.go. diff --git a/static_rules_defaults.go b/static_rules_defaults.go new file mode 100644 index 0000000..457164a --- /dev/null +++ b/static_rules_defaults.go @@ -0,0 +1,859 @@ +package sight + +import "regexp" + +// defaultRules returns the built-in set of 30+ static analysis rules. +func defaultRules() []StaticRule { + return []StaticRule{ + // ===================================================================== + // SECURITY - Go + // ===================================================================== + { + ID: "SEC-GO-001", + Name: "SQL Injection", + Description: "String formatting used in SQL query construction; use parameterized queries instead", + Language: "go", + Pattern: regexp.MustCompile(`fmt\.Sprintf\s*\(\s*"[^"]*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"]*%[sv]`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-89", + Fix: "Use parameterized queries with ? or $1 placeholders instead of fmt.Sprintf for SQL", + }, + { + ID: "SEC-GO-002", + Name: "Command Injection", + Description: "Potentially unsanitized input passed to exec.Command", + Language: "go", + Pattern: regexp.MustCompile(`exec\.Command\s*\([^"` + "`" + `][^,)]*\)`), + Antipattern: regexp.MustCompile(`exec\.Command\s*\(\s*"[^"]+"\s*\)`), + Severity: "critical", + Category: "security", + CWE: "CWE-78", + Fix: "Validate and sanitize all input passed to exec.Command; use an allowlist of permitted commands", + }, + { + ID: "SEC-GO-003", + Name: "Path Traversal", + Description: "User-controlled path used in file operations without filepath.Clean or validation", + Language: "go", + Pattern: regexp.MustCompile(`(?:os\.(?:Open|ReadFile|Create|WriteFile)|filepath\.Join)\s*\(.*(?:req\.|r\.|input|param|query|user)`), + Antipattern: regexp.MustCompile(`filepath\.Clean`), + Severity: "high", + Category: "security", + CWE: "CWE-22", + Fix: "Use filepath.Clean() and validate the resolved path is within the expected directory", + }, + { + ID: "SEC-GO-004", + Name: "Hardcoded Secret", + Description: "Hardcoded password or secret string detected", + Language: "go", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*(?::?=|=)\s*"[^"]{4,}`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", + }, + { + ID: "SEC-GO-005", + Name: "Insecure TLS", + Description: "TLS certificate verification is disabled", + Language: "go", + Pattern: regexp.MustCompile(`InsecureSkipVerify\s*:\s*true`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-295", + Fix: "Remove InsecureSkipVerify: true; configure proper TLS certificate validation", + }, + { + ID: "SEC-GO-006", + Name: "Weak Crypto (MD5)", + Description: "MD5 is cryptographically broken and should not be used for security purposes", + Language: "go", + Pattern: regexp.MustCompile(`md5\.(?:New|Sum)`), + Antipattern: regexp.MustCompile(`(?i)(?:checksum|fingerprint|cache|etag|non.?security)`), + Severity: "medium", + Category: "security", + CWE: "CWE-328", + Fix: "Use crypto/sha256 or crypto/sha512 instead of MD5 for security-sensitive operations", + }, + { + ID: "SEC-GO-007", + Name: "Weak Crypto (SHA1)", + Description: "SHA-1 is deprecated for security use; collisions are practical", + Language: "go", + Pattern: regexp.MustCompile(`sha1\.(?:New|Sum)`), + Antipattern: regexp.MustCompile(`(?i)(?:git|fingerprint|cache|etag|non.?security)`), + Severity: "medium", + Category: "security", + CWE: "CWE-328", + Fix: "Use crypto/sha256 or crypto/sha512 instead of SHA-1 for security-sensitive operations", + }, + { + ID: "SEC-GO-008", + Name: "Unvalidated Redirect", + Description: "HTTP redirect using user-controlled input without validation", + Language: "go", + Pattern: regexp.MustCompile(`http\.Redirect\s*\(.*(?:r\.(?:URL|Form|Query)|req\.|param|input)`), + Antipattern: nil, + Severity: "medium", + Category: "security", + CWE: "CWE-601", + Fix: "Validate redirect URLs against an allowlist of permitted destinations", + }, + { + ID: "SEC-GO-009", + Name: "Sensitive Data in Log", + Description: "Potentially logging sensitive data (passwords, tokens, secrets)", + Language: "go", + Pattern: regexp.MustCompile(`(?:log\.|slog\.|logger\.)(?:Print|Info|Debug|Warn|Error|Fatal).*(?i)(?:password|token|secret|key|credential)`), + Antipattern: regexp.MustCompile(`(?i)(?:redact|mask|\*\*\*|)`), + Severity: "medium", + Category: "security", + CWE: "CWE-532", + Fix: "Redact sensitive values before logging; never log passwords, tokens, or API keys", + }, + { + ID: "SEC-GO-010", + Name: "Defer in Loop", + Description: "defer inside a loop can accumulate resources until the function returns", + Language: "go", + Pattern: regexp.MustCompile(`^\s*defer\s+`), + Antipattern: nil, + Severity: "medium", + Category: "correctness", + CWE: "", + Fix: "Move the deferred call into a helper function or handle resource cleanup explicitly in the loop", + }, + // ===================================================================== + // SECURITY - Python + // ===================================================================== + { + ID: "SEC-PY-001", + Name: "SQL Injection (f-string)", + Description: "f-string used in SQL query construction; use parameterized queries", + Language: "python", + Pattern: regexp.MustCompile(`f["'](?:[^"']*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"']*)\{`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-89", + Fix: "Use parameterized queries with %s or ? placeholders passed as a tuple", + }, + { + ID: "SEC-PY-002", + Name: "eval() Usage", + Description: "eval() executes arbitrary code and is a common injection vector", + Language: "python", + Pattern: regexp.MustCompile(`\beval\s*\(`), + Antipattern: regexp.MustCompile(`(?i)(?:#.*safe|#.*trusted|ast\.literal_eval)`), + Severity: "critical", + Category: "security", + CWE: "CWE-95", + Fix: "Use ast.literal_eval() for data parsing or find a safer alternative to eval()", + }, + { + ID: "SEC-PY-003", + Name: "exec() Usage", + Description: "exec() executes arbitrary code strings and is dangerous with user input", + Language: "python", + Pattern: regexp.MustCompile(`\bexec\s*\(`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-95", + Fix: "Avoid exec(); use structured approaches like importlib or predefined function dispatch", + }, + { + ID: "SEC-PY-004", + Name: "Pickle Deserialization", + Description: "pickle.loads() can execute arbitrary code during deserialization", + Language: "python", + Pattern: regexp.MustCompile(`pickle\.loads?\s*\(`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-502", + Fix: "Use JSON or another safe serialization format; if pickle is required, only load from trusted sources", + }, + { + ID: "SEC-PY-005", + Name: "Subprocess Shell Injection", + Description: "subprocess with shell=True is vulnerable to shell injection", + Language: "python", + Pattern: regexp.MustCompile(`subprocess\.(?:call|run|Popen|check_output|check_call)\s*\(.*shell\s*=\s*True`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-78", + Fix: "Use shell=False (default) and pass command as a list instead of a string", + }, + { + ID: "SEC-PY-006", + Name: "Assert in Production", + Description: "assert statements are stripped with -O flag; do not use for validation", + Language: "python", + Pattern: regexp.MustCompile(`^\s*assert\s+`), + Antipattern: regexp.MustCompile(`(?i)(?:test_|_test\.py|conftest|pytest)`), + Severity: "low", + Category: "correctness", + CWE: "CWE-617", + Fix: "Replace assert with explicit validation that raises an appropriate exception", + }, + { + ID: "SEC-PY-007", + Name: "Hardcoded Secret (Python)", + Description: "Hardcoded password or secret string detected", + Language: "python", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*["'][^"']{4,}["']`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", + }, + { + ID: "SEC-PY-008", + Name: "YAML Unsafe Load", + Description: "yaml.load() without SafeLoader can execute arbitrary Python objects", + Language: "python", + Pattern: regexp.MustCompile(`yaml\.load\s*\(`), + Antipattern: regexp.MustCompile(`Loader\s*=\s*(?:yaml\.)?SafeLoader`), + Severity: "high", + Category: "security", + CWE: "CWE-502", + Fix: "Use yaml.safe_load() or pass Loader=yaml.SafeLoader explicitly", + }, + // ===================================================================== + // SECURITY - TypeScript/JavaScript + // ===================================================================== + { + ID: "SEC-TS-001", + Name: "innerHTML Assignment", + Description: "Direct innerHTML assignment enables XSS attacks", + Language: "typescript", + Pattern: regexp.MustCompile(`\.innerHTML\s*=`), + Antipattern: regexp.MustCompile(`(?i)(?:sanitize|DOMPurify|escape)`), + Severity: "high", + Category: "security", + CWE: "CWE-79", + Fix: "Use textContent for plain text or a sanitization library (DOMPurify) for HTML", + }, + { + ID: "SEC-JS-001", + Name: "innerHTML Assignment", + Description: "Direct innerHTML assignment enables XSS attacks", + Language: "javascript", + Pattern: regexp.MustCompile(`\.innerHTML\s*=`), + Antipattern: regexp.MustCompile(`(?i)(?:sanitize|DOMPurify|escape)`), + Severity: "high", + Category: "security", + CWE: "CWE-79", + Fix: "Use textContent for plain text or a sanitization library (DOMPurify) for HTML", + }, + { + ID: "SEC-TS-002", + Name: "eval() Usage", + Description: "eval() executes arbitrary code and is a common injection vector", + Language: "typescript", + Pattern: regexp.MustCompile(`\beval\s*\(`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-95", + Fix: "Use JSON.parse() for data or Function constructor with extreme caution", + }, + { + ID: "SEC-JS-002", + Name: "eval() Usage", + Description: "eval() executes arbitrary code and is a common injection vector", + Language: "javascript", + Pattern: regexp.MustCompile(`\beval\s*\(`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-95", + Fix: "Use JSON.parse() for data or Function constructor with extreme caution", + }, + { + ID: "SEC-TS-003", + Name: "Prototype Pollution", + Description: "__proto__ access can lead to prototype pollution attacks", + Language: "typescript", + Pattern: regexp.MustCompile(`__proto__`), + Antipattern: regexp.MustCompile(`(?i)(?:hasOwnProperty|Object\.create\(null\))`), + Severity: "high", + Category: "security", + CWE: "CWE-1321", + Fix: "Use Object.create(null) for lookup objects or validate keys against __proto__, constructor, prototype", + }, + { + ID: "SEC-JS-003", + Name: "Prototype Pollution", + Description: "__proto__ access can lead to prototype pollution attacks", + Language: "javascript", + Pattern: regexp.MustCompile(`__proto__`), + Antipattern: regexp.MustCompile(`(?i)(?:hasOwnProperty|Object\.create\(null\))`), + Severity: "high", + Category: "security", + CWE: "CWE-1321", + Fix: "Use Object.create(null) for lookup objects or validate keys against __proto__, constructor, prototype", + }, + { + ID: "SEC-TS-004", + Name: "Regex DoS", + Description: "Complex regex with nested quantifiers may be vulnerable to ReDoS", + Language: "typescript", + Pattern: regexp.MustCompile(`(?:new RegExp|/)\s*.*(?:\+\+|\*\*|\{\d+,\}.*\{\d+,\}|(?:\.\*){2,}|\([^)]*\+\)[^)]*\+)`), + Antipattern: nil, + Severity: "medium", + Category: "security", + CWE: "CWE-1333", + Fix: "Simplify regex; add bounds to quantifiers; consider using re2 or a timeout", + }, + { + ID: "SEC-JS-004", + Name: "Regex DoS", + Description: "Complex regex with nested quantifiers may be vulnerable to ReDoS", + Language: "javascript", + Pattern: regexp.MustCompile(`(?:new RegExp|/)\s*.*(?:\+\+|\*\*|\{\d+,\}.*\{\d+,\}|(?:\.\*){2,}|\([^)]*\+\)[^)]*\+)`), + Antipattern: nil, + Severity: "medium", + Category: "security", + CWE: "CWE-1333", + Fix: "Simplify regex; add bounds to quantifiers; consider using re2 or a timeout", + }, + { + ID: "SEC-TS-005", + Name: "Hardcoded Secret (TS)", + Description: "Hardcoded password or secret string detected", + Language: "typescript", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*[=:]\s*["'][^"']{4,}["']`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|process\.env|import)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", + }, + { + ID: "SEC-JS-005", + Name: "Hardcoded Secret (JS)", + Description: "Hardcoded password or secret string detected", + Language: "javascript", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*[=:]\s*["'][^"']{4,}["']`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|process\.env|import)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", + }, + // ===================================================================== + // CORRECTNESS - Go + // ===================================================================== + { + ID: "COR-GO-001", + Name: "Unchecked Error", + Description: "Function return value that likely includes an error is discarded", + Language: "go", + Pattern: regexp.MustCompile(`^\s*[a-zA-Z_][a-zA-Z0-9_.]*\s*\(.*\)\s*$`), + Antipattern: regexp.MustCompile(`(?:^//|^\s*//|defer|go\s+|fmt\.Print|log\.|println|print\()`), + Severity: "medium", + Category: "correctness", + CWE: "CWE-252", + Fix: "Capture and handle the returned error: if err != nil { return err }", + }, + { + ID: "COR-GO-002", + Name: "Goroutine Leak Risk", + Description: "Goroutine launched without visible context cancellation or done channel", + Language: "go", + Pattern: regexp.MustCompile(`\bgo\s+func\s*\(`), + Antipattern: regexp.MustCompile(`(?:ctx|context|done|cancel|Done\(\)|quit|stop|timer|ticker)`), + Severity: "medium", + Category: "correctness", + CWE: "", + Fix: "Pass a context.Context or done channel to goroutines to enable graceful shutdown", + }, + { + ID: "COR-GO-003", + Name: "Race Condition Risk", + Description: "Shared variable access in goroutine without synchronization", + Language: "go", + Pattern: regexp.MustCompile(`go\s+func\s*\([^)]*\)\s*\{[^}]*(?:[a-z]+\s*(?:\+\+|--|(?:\+|-|\*|/)=))`), + Antipattern: regexp.MustCompile(`(?:mutex|Mutex|sync\.|atomic\.|Lock\(\)|chan\s)`), + Severity: "high", + Category: "correctness", + CWE: "CWE-362", + Fix: "Use sync.Mutex, sync/atomic, or channels to synchronize shared state access", + }, + { + ID: "COR-GO-004", + Name: "nil Map Write", + Description: "Writing to a potentially nil map causes a runtime panic", + Language: "go", + Pattern: regexp.MustCompile(`var\s+\w+\s+map\[`), + Antipattern: regexp.MustCompile(`=\s*(?:make|map\[)`), + Severity: "high", + Category: "correctness", + CWE: "", + Fix: "Initialize the map with make() before writing to it", + }, + // ===================================================================== + // PERFORMANCE + // ===================================================================== + { + ID: "PERF-GO-001", + Name: "String Concat in Loop", + Description: "String concatenation with += in a loop causes quadratic allocation", + Language: "go", + Pattern: regexp.MustCompile(`\w+\s*\+=\s*(?:"|\w+)`), + Antipattern: regexp.MustCompile(`(?:int|float|byte|rune|uint|count|total|sum|num|idx|index|offset|i\s*\+)`), + Severity: "low", + Category: "performance", + CWE: "", + Fix: "Use strings.Builder for string concatenation in loops", + }, + { + ID: "PERF-GO-002", + Name: "Unbounded Allocation", + Description: "Slice allocation with user-controlled size without bounds check", + Language: "go", + Pattern: regexp.MustCompile(`make\s*\(\s*\[\]\w+\s*,\s*(?:req\.|r\.|input|param|query|size|length|count|n\b)`), + Antipattern: regexp.MustCompile(`(?:maxSize|maxLen|cap|min\(|max\(|limit|<=|>=|<\s*\d|>\s*\d)`), + Severity: "medium", + Category: "performance", + CWE: "CWE-789", + Fix: "Validate and cap allocation sizes to prevent denial of service via memory exhaustion", + }, + { + ID: "PERF-GO-003", + Name: "N+1 Query Pattern", + Description: "Database query inside a loop suggests N+1 query problem", + Language: "go", + Pattern: regexp.MustCompile(`(?:\.Query|\.Exec|\.Get|\.Find|\.First|\.Select|\.Where)\s*\(`), + Antipattern: regexp.MustCompile(`(?:batch|bulk|IN\s*\(|ids|Preload|Join)`), + Severity: "medium", + Category: "performance", + CWE: "", + Fix: "Batch database queries outside the loop; use IN clauses or JOINs", + }, + { + ID: "PERF-PY-001", + Name: "N+1 Query Pattern (Python)", + Description: "Database query inside a loop suggests N+1 query problem", + Language: "python", + Pattern: regexp.MustCompile(`(?:\.execute|\.query|\.filter|\.get|session\.)\s*\(`), + Antipattern: regexp.MustCompile(`(?:bulk|batch|in_|prefetch|select_related|join)`), + Severity: "medium", + Category: "performance", + CWE: "", + Fix: "Use select_related/prefetch_related (Django) or eager loading (SQLAlchemy) to batch queries", + }, + { + ID: "PERF-ANY-001", + Name: "Synchronous Sleep", + Description: "Hardcoded sleep/delay found; consider exponential backoff or event-driven approach", + Language: "any", + Pattern: regexp.MustCompile(`(?:time\.Sleep|sleep\(|setTimeout.*\d{4,}|Thread\.sleep)`), + Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock|backoff|retry)`), + Severity: "low", + Category: "performance", + CWE: "", + Fix: "Use exponential backoff for retries or event-driven signaling instead of fixed sleeps", + }, + // ===================================================================== + // SECURITY - Any Language + // ===================================================================== + { + ID: "SEC-ANY-001", + Name: "TODO Security", + Description: "TODO/FIXME/HACK comment related to security found in code", + Language: "any", + Pattern: regexp.MustCompile(`(?i)(?://|#|/\*)\s*(?:TODO|FIXME|HACK|XXX).*(?:security|auth|crypt|password|token|vuln|inject|xss|csrf)`), + Antipattern: nil, + Severity: "medium", + Category: "security", + CWE: "", + Fix: "Address security-related TODOs before shipping; they indicate known unresolved issues", + }, + { + ID: "SEC-ANY-002", + Name: "Private Key in Source", + Description: "Private key material appears to be embedded in source code", + Language: "any", + Pattern: regexp.MustCompile(`-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|sample|mock|fixture)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Remove private keys from source; load them from secure storage or environment at runtime", + }, + { + ID: "SEC-ANY-003", + Name: "HTTP in Production", + Description: "Insecure HTTP URL used where HTTPS should be expected", + Language: "any", + Pattern: regexp.MustCompile(`http://[a-zA-Z][a-zA-Z0-9._-]*`), + Antipattern: regexp.MustCompile(`(?i)(?:http://localhost|http://127\.0\.0\.1|http://0\.0\.0\.0|http://\[?::1|http://example\.com|test|spec|mock|doc|xmlns|http://www\.w3\.org)`), + Severity: "low", + Category: "security", + CWE: "CWE-319", + Fix: "Use HTTPS for all production URLs to prevent man-in-the-middle attacks", + }, + // ===================================================================== + // SECURITY - Rust + // ===================================================================== + { + ID: "SEC-RS-001", + Name: "Unsafe Block", + Description: "unsafe block bypasses Rust's memory safety guarantees; ensure the invariant is upheld", + Language: "rust", + Pattern: regexp.MustCompile(`\bunsafe\s*\{`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-242", + Fix: "Avoid unsafe blocks; use safe abstractions. If required, document the safety invariant in a comment", + }, + { + ID: "SEC-RS-002", + Name: "unwrap() in Production", + Description: "unwrap() will panic at runtime if the value is None/Err; handle errors gracefully instead", + Language: "rust", + Pattern: regexp.MustCompile(`\.unwrap\(\)`), + Antipattern: regexp.MustCompile(`(?i)(?:test|#\[test|#\[cfg\(test|expect\()`), + Severity: "medium", + Category: "correctness", + CWE: "CWE-252", + Fix: "Use match, if let, or the ? operator to handle Option/Result properly", + }, + { + ID: "SEC-RS-003", + Name: "Hardcoded Secret (Rust)", + Description: "Hardcoded password or secret string detected", + Language: "rust", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*(?:=>|:)\s*"[^"]{4,}`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secrets manager instead of hardcoded credentials", + }, + { + ID: "SEC-RS-004", + Name: "SQL Injection (Rust)", + Description: "format! macro used in SQL query construction; use parameterized queries", + Language: "rust", + Pattern: regexp.MustCompile(`format!\s*\(\s*"[^"]*(?:SELECT|INSERT|UPDATE|DELETE|DROP)[^"]*\{`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-89", + Fix: "Use parameterized queries with $1 placeholders (e.g., sqlx or diesel) instead of format!", + }, + { + ID: "SEC-RS-005", + Name: "Command Injection (Rust)", + Description: "Command::new with user-controlled input may allow command injection", + Language: "rust", + Pattern: regexp.MustCompile(`Command::new\s*\([^"'][^)]*\)`), + Antipattern: regexp.MustCompile(`Command::new\s*\(\s*"[^"]+"\s*\)`), + Severity: "critical", + Category: "security", + CWE: "CWE-78", + Fix: "Validate and sanitize all input passed to Command::new; use an allowlist of permitted commands", + }, + // ===================================================================== + // SECURITY - Java + // ===================================================================== + { + ID: "SEC-JV-001", + Name: "SQL Injection (Java)", + Description: "String concatenation used in SQL statement execution; use PreparedStatement instead", + Language: "java", + Pattern: regexp.MustCompile(`(?:(?:execute|executeQuery|executeUpdate)\s*\(.*\+)|(?:(?:Statement|createStatement).*\+)`), + Antipattern: regexp.MustCompile(`PreparedStatement|setParameter|bindValue`), + Severity: "critical", + Category: "security", + CWE: "CWE-89", + Fix: "Use PreparedStatement with parameter placeholders (?) instead of string concatenation", + }, + { + ID: "SEC-JV-002", + Name: "Insecure Deserialization (Java)", + Description: "ObjectInputStream.readObject() can execute arbitrary code during deserialization", + Language: "java", + Pattern: regexp.MustCompile(`ObjectInputStream.*\.readObject\s*\(`), + Antipattern: regexp.MustCompile(`(?i)(?:ValidatingObjectInputStream|ObjectInputFilter|whiteList|allowList)`), + Severity: "critical", + Category: "security", + CWE: "CWE-502", + Fix: "Use ValidatingObjectInputStream or an ObjectInputFilter with a strict allowlist", + }, + { + ID: "SEC-JV-003", + Name: "SSRF (Java)", + Description: "new URL() with user-controlled input may lead to Server-Side Request Forgery", + Language: "java", + Pattern: regexp.MustCompile(`new\s+URL\s*\(.*(?:req\.|request\.|param|input|query|user)`), + Antipattern: regexp.MustCompile(`(?i)(?:allowList|whitelist|validate.*url|UriUtils)`), + Severity: "high", + Category: "security", + CWE: "CWE-918", + Fix: "Validate URLs against an allowlist of permitted domains and schemes", + }, + { + ID: "SEC-JV-004", + Name: "Hardcoded Secret (Java)", + Description: "Hardcoded password or secret string detected", + Language: "java", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake|System\.getenv)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables, a secrets manager, or a vault instead of hardcoded credentials", + }, + { + ID: "SEC-JV-005", + Name: "Path Traversal (Java)", + Description: "new File() with user-controlled input may allow path traversal attacks", + Language: "java", + Pattern: regexp.MustCompile(`new\s+File\s*\(.*(?:req\.|request\.|param|input|query|user|\+)`), + Antipattern: regexp.MustCompile(`(?i)(?:canonicalPath|normalize|sanitizePath|getCanonicalPath)`), + Severity: "high", + Category: "security", + CWE: "CWE-22", + Fix: "Validate and normalize paths; ensure the resolved path is within the expected directory", + }, + // ===================================================================== + // SECURITY - C/C++ + // ===================================================================== + { + ID: "SEC-C-001", + Name: "Buffer Overflow", + Description: "Unsafe string function (strcpy, strcat, sprintf, gets) used without bounds checking", + Language: "c", + Pattern: regexp.MustCompile(`\b(?:strcpy|strcat|sprintf|gets)\s*\(`), + Antipattern: regexp.MustCompile(`(?:strncpy|strncat|snprintf|fgets|safe)`), + Severity: "critical", + Category: "security", + CWE: "CWE-120", + Fix: "Use bounded variants: strncpy, strncat, snprintf, or fgets instead", + }, + { + ID: "SEC-C-002", + Name: "Format String Vulnerability", + Description: "printf-family function called with non-literal format string; may enable format string attacks", + Language: "c", + Pattern: regexp.MustCompile(`\b(?:printf|fprintf|sprintf|snprintf)\s*\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*[,)]`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-134", + Fix: "Use a literal format string: printf(\"%s\", var) instead of printf(var)", + }, + { + ID: "SEC-C-003", + Name: "Use-After-Free Risk", + Description: "Pointer used after free() call without being set to NULL", + Language: "c", + Pattern: regexp.MustCompile(`\bfree\s*\(\s*\w+\s*\)`), + Antipattern: regexp.MustCompile(`(?:(?:\w+)\s*=\s*NULL|=\s*0\b)`), + Severity: "high", + Category: "security", + CWE: "CWE-416", + Fix: "Set pointer to NULL after free() to prevent use-after-free and double-free", + }, + { + ID: "SEC-C-004", + Name: "Hardcoded Secret (C/C++)", + Description: "Hardcoded password or secret string detected", + Language: "c", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secure configuration mechanism instead of hardcoded secrets", + }, + { + ID: "SEC-C-005", + Name: "Command Injection (C)", + Description: "system() or popen() with user-controlled input may allow command injection", + Language: "c", + Pattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*[a-zA-Z_]`), + Antipattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*"[^"]*"\s*\)`), + Severity: "critical", + Category: "security", + CWE: "CWE-78", + Fix: "Use execve() with an explicit argument vector instead of system()/popen() with shell interpretation", + }, + // Duplicate C/C++ rules for cpp language tag + { + ID: "SEC-CPP-001", + Name: "Buffer Overflow (C++)", + Description: "Unsafe string function (strcpy, strcat, sprintf, gets) used without bounds checking", + Language: "cpp", + Pattern: regexp.MustCompile(`\b(?:strcpy|strcat|sprintf|gets)\s*\(`), + Antipattern: regexp.MustCompile(`(?:strncpy|strncat|snprintf|fgets|std::string|safe)`), + Severity: "critical", + Category: "security", + CWE: "CWE-120", + Fix: "Use std::string or bounded variants: strncpy, strncat, snprintf, or fgets instead", + }, + { + ID: "SEC-CPP-002", + Name: "Format String Vulnerability (C++)", + Description: "printf-family function called with non-literal format string", + Language: "cpp", + Pattern: regexp.MustCompile(`\b(?:printf|fprintf|sprintf|snprintf)\s*\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*[,)]`), + Antipattern: nil, + Severity: "high", + Category: "security", + CWE: "CWE-134", + Fix: "Use a literal format string or std::cout / fmt::format instead", + }, + { + ID: "SEC-CPP-003", + Name: "Use-After-Free Risk (C++)", + Description: "Pointer used after free()/delete without being set to nullptr", + Language: "cpp", + Pattern: regexp.MustCompile(`\b(?:free\s*\(\s*\w+\s*\)|delete\s+\w+)`), + Antipattern: regexp.MustCompile(`(?:(?:\w+)\s*=\s*(?:NULL|nullptr)|=\s*0\b|unique_ptr|shared_ptr)`), + Severity: "high", + Category: "security", + CWE: "CWE-416", + Fix: "Use smart pointers (unique_ptr, shared_ptr); set raw pointers to nullptr after delete", + }, + { + ID: "SEC-CPP-004", + Name: "Hardcoded Secret (C++)", + Description: "Hardcoded password or secret string detected", + Language: "cpp", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*"[^"]{4,}"`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use environment variables or a secure configuration mechanism instead of hardcoded secrets", + }, + { + ID: "SEC-CPP-005", + Name: "Command Injection (C++)", + Description: "system() or popen() with user-controlled input may allow command injection", + Language: "cpp", + Pattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*[a-zA-Z_]`), + Antipattern: regexp.MustCompile(`\b(?:system|popen)\s*\(\s*"[^"]*"\s*\)`), + Severity: "critical", + Category: "security", + CWE: "CWE-78", + Fix: "Use execve() or boost::process with an explicit argument vector instead of system()/popen()", + }, + // ===================================================================== + // SECURITY - Ruby + // ===================================================================== + { + ID: "SEC-RB-001", + Name: "SQL Injection (Ruby)", + Description: "String interpolation in SQL query; use parameterized queries instead", + Language: "ruby", + Pattern: regexp.MustCompile(`(?:(?:\.where|\.find_by_sql|\.execute|\.query)\s*\(?\s*["'].*#\{)|(?:%[qQwWiI]?\{.*(?:SELECT|INSERT|UPDATE|DELETE).*#\{)`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-89", + Fix: "Use parameterized queries: Model.where('id = ?', id) or ActiveRecord::sanitize_sql", + }, + { + ID: "SEC-RB-002", + Name: "Command Injection (Ruby)", + Description: "system(), exec(), or backtick command with user-controlled input", + Language: "ruby", + Pattern: regexp.MustCompile(`\b(?:system|exec|` + "`" + `)\s*[\(]?.*#\{`), + Antipattern: nil, + Severity: "critical", + Category: "security", + CWE: "CWE-78", + Fix: "Use Open3.capture2 or Kernel#system with separate arguments to avoid shell injection", + }, + { + ID: "SEC-RB-003", + Name: "Mass Assignment", + Description: "permit! or unfiltered params usage allows arbitrary attribute assignment", + Language: "ruby", + Pattern: regexp.MustCompile(`\.permit!|params\s*\[|params\.require\(.*\)\.permit\s*[^(\w]`), + Antipattern: regexp.MustCompile(`\.permit\s*\(\s*[:\w]`), + Severity: "high", + Category: "security", + CWE: "CWE-915", + Fix: "Use strong parameters: params.require(:model).permit(:field1, :field2) with explicit allowlist", + }, + { + ID: "SEC-RB-004", + Name: "Hardcoded Secret (Ruby)", + Description: "Hardcoded password or secret string detected", + Language: "ruby", + Pattern: regexp.MustCompile(`(?i)(?:password|secret|api_?key|token|private_?key)\s*=\s*['"][^'"]{4,}['"]`), + Antipattern: regexp.MustCompile(`(?i)(?:test|example|placeholder|TODO|CHANGE|xxx|dummy|fake|ENV\[)`), + Severity: "critical", + Category: "security", + CWE: "CWE-798", + Fix: "Use ENV['VAR_NAME'], Rails credentials, or a secrets manager instead of hardcoded credentials", + }, + { + ID: "SEC-RB-005", + Name: "ERB XSS", + Description: "<%= %> tag without sanitization may render user content unsafely", + Language: "ruby", + Pattern: regexp.MustCompile(`<%=\s*`), + Antipattern: regexp.MustCompile(`(?i)(?:h\(|html_escape|sanitize|escape_javascript|CGI\.escape)`), + Severity: "high", + Category: "security", + CWE: "CWE-79", + Fix: "Use <%=h ... %> or <%= sanitize(...) %> to escape user content in ERB templates", + }, + // ===================================================================== + // SECURITY - SQL + // ===================================================================== + { + ID: "SEC-SQL-001", + Name: "DROP TABLE in Migration", + Description: "DROP TABLE statement found; data loss is irreversible without backup", + Language: "sql", + Pattern: regexp.MustCompile(`(?i)\bDROP\s+TABLE\b`), + Antipattern: regexp.MustCompile(`(?i)(?:IF\s+EXISTS|migration|rollback|revert)`), + Severity: "high", + Category: "security", + CWE: "", + Fix: "Use 'DROP TABLE IF EXISTS' and ensure a rollback migration exists; consider soft deletes", + }, + { + ID: "SEC-SQL-002", + Name: "Always-True Condition", + Description: "Tautological condition (1=1, OR 1=1) found; may indicate SQL injection or logic error", + Language: "sql", + Pattern: regexp.MustCompile(`(?i)\b(?:OR\s+1\s*=\s*1|AND\s+1\s*=\s*1)\b`), + Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock)`), + Severity: "high", + Category: "security", + CWE: "CWE-89", + Fix: "Remove tautological conditions; if this is injection testing, use parameterized queries in production", + }, + { + ID: "SEC-SQL-003", + Name: "UNION-Based Injection Pattern", + Description: "UNION SELECT pattern may indicate SQL injection attack vector", + Language: "sql", + Pattern: regexp.MustCompile(`(?i)\bUNION\s+(?:ALL\s+)?SELECT\b`), + Antipattern: regexp.MustCompile(`(?i)(?:test|spec|mock|migration)`), + Severity: "high", + Category: "security", + CWE: "CWE-89", + Fix: "Use parameterized queries; never concatenate user input into SQL statements", + }, + } +}