|
| 1 | +package review |
| 2 | + |
| 3 | +import "strings" |
| 4 | + |
| 5 | +// CWEMapping maps a security finding pattern to a CWE identifier. |
| 6 | +type CWEMapping struct { |
| 7 | + ID string // e.g. "CWE-89" |
| 8 | + Name string // e.g. "SQL Injection" |
| 9 | + Keywords []string // lowercase keywords to match in finding messages |
| 10 | +} |
| 11 | + |
| 12 | +// cweDatabase is the built-in set of common security weakness patterns. |
| 13 | +var cweDatabase = []CWEMapping{ |
| 14 | + { |
| 15 | + ID: "CWE-89", |
| 16 | + Name: "SQL Injection", |
| 17 | + Keywords: []string{"sql injection", "sql concat", "string concatenation into sql", "raw query", "unsanitized sql"}, |
| 18 | + }, |
| 19 | + { |
| 20 | + ID: "CWE-79", |
| 21 | + Name: "Cross-site Scripting (XSS)", |
| 22 | + Keywords: []string{"xss", "cross-site scripting", "unescaped output", "unsanitized html", "reflected input"}, |
| 23 | + }, |
| 24 | + { |
| 25 | + ID: "CWE-78", |
| 26 | + Name: "OS Command Injection", |
| 27 | + Keywords: []string{"command injection", "os.exec", "exec.command", "shell injection", "unsanitized command"}, |
| 28 | + }, |
| 29 | + { |
| 30 | + ID: "CWE-22", |
| 31 | + Name: "Path Traversal", |
| 32 | + Keywords: []string{"path traversal", "directory traversal", "../ ", "dot dot slash", "file path manipulation"}, |
| 33 | + }, |
| 34 | + { |
| 35 | + ID: "CWE-918", |
| 36 | + Name: "Server-Side Request Forgery (SSRF)", |
| 37 | + Keywords: []string{"ssrf", "server-side request forgery", "unvalidated url", "open redirect to internal"}, |
| 38 | + }, |
| 39 | + { |
| 40 | + ID: "CWE-798", |
| 41 | + Name: "Hardcoded Credentials", |
| 42 | + Keywords: []string{"hardcoded secret", "hardcoded password", "hardcoded credential", "hardcoded api key", "embedded secret", "secret in code"}, |
| 43 | + }, |
| 44 | + { |
| 45 | + ID: "CWE-327", |
| 46 | + Name: "Use of Broken Crypto Algorithm", |
| 47 | + Keywords: []string{"weak crypto", "md5", "sha1 ", "des ", "broken crypto", "insecure hash", "weak hash"}, |
| 48 | + }, |
| 49 | + { |
| 50 | + ID: "CWE-502", |
| 51 | + Name: "Deserialization of Untrusted Data", |
| 52 | + Keywords: []string{"insecure deserialization", "unsafe deserialization", "untrusted deserialization", "pickle", "yaml.load"}, |
| 53 | + }, |
| 54 | + { |
| 55 | + ID: "CWE-611", |
| 56 | + Name: "XML External Entity (XXE)", |
| 57 | + Keywords: []string{"xxe", "xml external entity", "xml injection"}, |
| 58 | + }, |
| 59 | + { |
| 60 | + ID: "CWE-352", |
| 61 | + Name: "Cross-Site Request Forgery (CSRF)", |
| 62 | + Keywords: []string{"csrf", "cross-site request forgery", "missing csrf token"}, |
| 63 | + }, |
| 64 | + { |
| 65 | + ID: "CWE-200", |
| 66 | + Name: "Information Exposure", |
| 67 | + Keywords: []string{"information disclosure", "sensitive data exposure", "data leak", "credential in log", "logging sensitive"}, |
| 68 | + }, |
| 69 | + { |
| 70 | + ID: "CWE-362", |
| 71 | + Name: "Race Condition", |
| 72 | + Keywords: []string{"race condition", "data race", "toctou", "time of check"}, |
| 73 | + }, |
| 74 | + { |
| 75 | + ID: "CWE-190", |
| 76 | + Name: "Integer Overflow", |
| 77 | + Keywords: []string{"integer overflow", "integer underflow", "int overflow"}, |
| 78 | + }, |
| 79 | + { |
| 80 | + ID: "CWE-601", |
| 81 | + Name: "Open Redirect", |
| 82 | + Keywords: []string{"open redirect", "url redirect", "unvalidated redirect"}, |
| 83 | + }, |
| 84 | + { |
| 85 | + ID: "CWE-862", |
| 86 | + Name: "Missing Authorization", |
| 87 | + Keywords: []string{"missing authorization", "missing auth check", "authorization bypass", "broken access control"}, |
| 88 | + }, |
| 89 | +} |
| 90 | + |
| 91 | +// MatchCWE checks a finding's message (and fix) against the CWE database and |
| 92 | +// returns the CWE ID if a match is found. Returns empty string if no match. |
| 93 | +func MatchCWE(message, fix string) string { |
| 94 | + lower := strings.ToLower(message + " " + fix) |
| 95 | + for _, cwe := range cweDatabase { |
| 96 | + for _, keyword := range cwe.Keywords { |
| 97 | + if strings.Contains(lower, keyword) { |
| 98 | + return cwe.ID |
| 99 | + } |
| 100 | + } |
| 101 | + } |
| 102 | + return "" |
| 103 | +} |
| 104 | + |
| 105 | +// LookupCWEName returns the human-readable name for a CWE ID. |
| 106 | +func LookupCWEName(id string) string { |
| 107 | + for _, cwe := range cweDatabase { |
| 108 | + if cwe.ID == id { |
| 109 | + return cwe.Name |
| 110 | + } |
| 111 | + } |
| 112 | + return "" |
| 113 | +} |
0 commit comments