dravix-agent 0.1.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.
- package/.claude/settings.example.json +30 -0
- package/ARCHITECTURE.md +410 -0
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/ROADMAP.md +117 -0
- package/data/vulnkb.json +666 -0
- package/dist/bin/aegis.d.ts +3 -0
- package/dist/bin/aegis.d.ts.map +1 -0
- package/dist/bin/aegis.js +489 -0
- package/dist/bin/aegis.js.map +1 -0
- package/dist/cache.d.ts +9 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +146 -0
- package/dist/cache.js.map +1 -0
- package/dist/engines/ai-sinks.d.ts +52 -0
- package/dist/engines/ai-sinks.d.ts.map +1 -0
- package/dist/engines/ai-sinks.js +204 -0
- package/dist/engines/ai-sinks.js.map +1 -0
- package/dist/engines/eslint.d.ts +9 -0
- package/dist/engines/eslint.d.ts.map +1 -0
- package/dist/engines/eslint.js +245 -0
- package/dist/engines/eslint.js.map +1 -0
- package/dist/engines/joern.d.ts +3 -0
- package/dist/engines/joern.d.ts.map +1 -0
- package/dist/engines/joern.js +98 -0
- package/dist/engines/joern.js.map +1 -0
- package/dist/engines/js-sinks.d.ts +70 -0
- package/dist/engines/js-sinks.d.ts.map +1 -0
- package/dist/engines/js-sinks.js +370 -0
- package/dist/engines/js-sinks.js.map +1 -0
- package/dist/engines/llm-critic.d.ts +130 -0
- package/dist/engines/llm-critic.d.ts.map +1 -0
- package/dist/engines/llm-critic.js +551 -0
- package/dist/engines/llm-critic.js.map +1 -0
- package/dist/engines/pragma.d.ts +20 -0
- package/dist/engines/pragma.d.ts.map +1 -0
- package/dist/engines/pragma.js +83 -0
- package/dist/engines/pragma.js.map +1 -0
- package/dist/engines/property-test.d.ts +3 -0
- package/dist/engines/property-test.d.ts.map +1 -0
- package/dist/engines/property-test.js +134 -0
- package/dist/engines/property-test.js.map +1 -0
- package/dist/engines/pyright.d.ts +10 -0
- package/dist/engines/pyright.d.ts.map +1 -0
- package/dist/engines/pyright.js +143 -0
- package/dist/engines/pyright.js.map +1 -0
- package/dist/engines/pysa.d.ts +3 -0
- package/dist/engines/pysa.d.ts.map +1 -0
- package/dist/engines/pysa.js +83 -0
- package/dist/engines/pysa.js.map +1 -0
- package/dist/engines/python-sinks.d.ts +82 -0
- package/dist/engines/python-sinks.d.ts.map +1 -0
- package/dist/engines/python-sinks.js +459 -0
- package/dist/engines/python-sinks.js.map +1 -0
- package/dist/engines/registry.d.ts +26 -0
- package/dist/engines/registry.d.ts.map +1 -0
- package/dist/engines/registry.js +70 -0
- package/dist/engines/registry.js.map +1 -0
- package/dist/engines/secret-scan.d.ts +22 -0
- package/dist/engines/secret-scan.d.ts.map +1 -0
- package/dist/engines/secret-scan.js +179 -0
- package/dist/engines/secret-scan.js.map +1 -0
- package/dist/engines/semgrep.d.ts +10 -0
- package/dist/engines/semgrep.d.ts.map +1 -0
- package/dist/engines/semgrep.js +200 -0
- package/dist/engines/semgrep.js.map +1 -0
- package/dist/engines/treesitter.d.ts +18 -0
- package/dist/engines/treesitter.d.ts.map +1 -0
- package/dist/engines/treesitter.js +135 -0
- package/dist/engines/treesitter.js.map +1 -0
- package/dist/engines/tsc.d.ts +10 -0
- package/dist/engines/tsc.d.ts.map +1 -0
- package/dist/engines/tsc.js +142 -0
- package/dist/engines/tsc.js.map +1 -0
- package/dist/engines/types.d.ts +47 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +27 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/findings.d.ts +121 -0
- package/dist/findings.d.ts.map +1 -0
- package/dist/findings.js +98 -0
- package/dist/findings.js.map +1 -0
- package/dist/hooks/claude-code.d.ts +3 -0
- package/dist/hooks/claude-code.d.ts.map +1 -0
- package/dist/hooks/claude-code.js +187 -0
- package/dist/hooks/claude-code.js.map +1 -0
- package/dist/index/context.d.ts +127 -0
- package/dist/index/context.d.ts.map +1 -0
- package/dist/index/context.js +267 -0
- package/dist/index/context.js.map +1 -0
- package/dist/index/embeddings.d.ts +68 -0
- package/dist/index/embeddings.d.ts.map +1 -0
- package/dist/index/embeddings.js +570 -0
- package/dist/index/embeddings.js.map +1 -0
- package/dist/index/graph_routing.d.ts +36 -0
- package/dist/index/graph_routing.d.ts.map +1 -0
- package/dist/index/graph_routing.js +170 -0
- package/dist/index/graph_routing.js.map +1 -0
- package/dist/index/joern.d.ts +76 -0
- package/dist/index/joern.d.ts.map +1 -0
- package/dist/index/joern.js +782 -0
- package/dist/index/joern.js.map +1 -0
- package/dist/index/property-test.d.ts +88 -0
- package/dist/index/property-test.d.ts.map +1 -0
- package/dist/index/property-test.js +466 -0
- package/dist/index/property-test.js.map +1 -0
- package/dist/index/proto/scip.proto +897 -0
- package/dist/index/pysa.d.ts +91 -0
- package/dist/index/pysa.d.ts.map +1 -0
- package/dist/index/pysa.js +617 -0
- package/dist/index/pysa.js.map +1 -0
- package/dist/index/scip.d.ts +76 -0
- package/dist/index/scip.d.ts.map +1 -0
- package/dist/index/scip.js +541 -0
- package/dist/index/scip.js.map +1 -0
- package/dist/index/vulrag.d.ts +86 -0
- package/dist/index/vulrag.d.ts.map +1 -0
- package/dist/index/vulrag.js +242 -0
- package/dist/index/vulrag.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/install/claude-code.d.ts +31 -0
- package/dist/install/claude-code.d.ts.map +1 -0
- package/dist/install/claude-code.js +447 -0
- package/dist/install/claude-code.js.map +1 -0
- package/dist/lang.d.ts +5 -0
- package/dist/lang.d.ts.map +1 -0
- package/dist/lang.js +52 -0
- package/dist/lang.js.map +1 -0
- package/dist/learning/suppressions.d.ts +70 -0
- package/dist/learning/suppressions.d.ts.map +1 -0
- package/dist/learning/suppressions.js +179 -0
- package/dist/learning/suppressions.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +187 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/explain.d.ts +58 -0
- package/dist/mcp/tools/explain.d.ts.map +1 -0
- package/dist/mcp/tools/explain.js +60 -0
- package/dist/mcp/tools/explain.js.map +1 -0
- package/dist/mcp/tools/precheck.d.ts +29 -0
- package/dist/mcp/tools/precheck.d.ts.map +1 -0
- package/dist/mcp/tools/precheck.js +42 -0
- package/dist/mcp/tools/precheck.js.map +1 -0
- package/dist/mcp/tools/validate.d.ts +73 -0
- package/dist/mcp/tools/validate.d.ts.map +1 -0
- package/dist/mcp/tools/validate.js +66 -0
- package/dist/mcp/tools/validate.js.map +1 -0
- package/dist/mcp/warm.d.ts +88 -0
- package/dist/mcp/warm.d.ts.map +1 -0
- package/dist/mcp/warm.js +331 -0
- package/dist/mcp/warm.js.map +1 -0
- package/dist/orchestrator.d.ts +46 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +596 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/policy.d.ts +51 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +201 -0
- package/dist/policy.js.map +1 -0
- package/dist/risk.d.ts +31 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +92 -0
- package/dist/risk.js.map +1 -0
- package/dist/stats.d.ts +72 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +217 -0
- package/dist/stats.js.map +1 -0
- package/dist/telemetry/collector.d.ts +10 -0
- package/dist/telemetry/collector.d.ts.map +1 -0
- package/dist/telemetry/collector.js +75 -0
- package/dist/telemetry/collector.js.map +1 -0
- package/dist/telemetry/consent.d.ts +9 -0
- package/dist/telemetry/consent.d.ts.map +1 -0
- package/dist/telemetry/consent.js +42 -0
- package/dist/telemetry/consent.js.map +1 -0
- package/dist/telemetry/installation.d.ts +2 -0
- package/dist/telemetry/installation.d.ts.map +1 -0
- package/dist/telemetry/installation.js +32 -0
- package/dist/telemetry/installation.js.map +1 -0
- package/dist/telemetry/sanitizer.d.ts +5 -0
- package/dist/telemetry/sanitizer.d.ts.map +1 -0
- package/dist/telemetry/sanitizer.js +60 -0
- package/dist/telemetry/sanitizer.js.map +1 -0
- package/dist/telemetry/types.d.ts +39 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +4 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/telemetry/uploader.d.ts +12 -0
- package/dist/telemetry/uploader.d.ts.map +1 -0
- package/dist/telemetry/uploader.js +92 -0
- package/dist/telemetry/uploader.js.map +1 -0
- package/dist/util/logger.d.ts +19 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +58 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/safe-paths.d.ts +8 -0
- package/dist/util/safe-paths.d.ts.map +1 -0
- package/dist/util/safe-paths.js +102 -0
- package/dist/util/safe-paths.js.map +1 -0
- package/dist/util/subprocess.d.ts +32 -0
- package/dist/util/subprocess.d.ts.map +1 -0
- package/dist/util/subprocess.js +137 -0
- package/dist/util/subprocess.js.map +1 -0
- package/package.json +93 -0
package/data/vulnkb.json
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema_version": 1,
|
|
3
|
+
"$comment": "Aegis-v2 Vul-RAG knowledge base. Each entry: a known CWE with one canonical vulnerable pattern and the fixed counterpart, drawn from MITRE/OWASP/CVEfixes. The vulnerable_pattern field is what we EMBED for retrieval. fix_pattern is what the LLM critic shows when suggesting remediation. Languages covered: Python, JavaScript, TypeScript, Go. Curated, not auto-generated. Last review: 2026-05-25.",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"cwe": "CWE-89",
|
|
7
|
+
"name": "SQL Injection",
|
|
8
|
+
"category": "api_misuse",
|
|
9
|
+
"severity": "high",
|
|
10
|
+
"cause": "User-controlled input is concatenated into a SQL query string instead of bound as a parameter.",
|
|
11
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
12
|
+
"vulnerable_pattern": "def get_user(name):\n conn = sqlite3.connect('db')\n cur = conn.cursor()\n cur.execute(f\"SELECT * FROM users WHERE name='{name}'\")\n return cur.fetchall()",
|
|
13
|
+
"fix_pattern": "def get_user(name):\n conn = sqlite3.connect('db')\n cur = conn.cursor()\n cur.execute('SELECT * FROM users WHERE name = ?', (name,))\n return cur.fetchall()",
|
|
14
|
+
"detection_hints": ["f-string in execute(", "string concatenation with SELECT/INSERT/UPDATE", ".format() inside execute(", "${var} inside raw SQL template"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"cwe": "CWE-78",
|
|
18
|
+
"name": "OS Command Injection",
|
|
19
|
+
"category": "api_misuse",
|
|
20
|
+
"severity": "critical",
|
|
21
|
+
"cause": "User input is passed to a shell with shell=True or concatenated into os.system / exec / spawn.",
|
|
22
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
23
|
+
"vulnerable_pattern": "def run_cmd(user_arg):\n os.system('ls ' + user_arg)",
|
|
24
|
+
"fix_pattern": "def run_cmd(user_arg):\n subprocess.run(['ls', user_arg], check=True, shell=False)",
|
|
25
|
+
"detection_hints": ["os.system(", "subprocess with shell=True", "child_process.exec(", "string concat into command"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"cwe": "CWE-79",
|
|
29
|
+
"name": "Cross-site Scripting (XSS)",
|
|
30
|
+
"category": "api_misuse",
|
|
31
|
+
"severity": "high",
|
|
32
|
+
"cause": "User input is rendered to HTML without escaping, allowing attacker-controlled <script>.",
|
|
33
|
+
"languages": ["javascript", "typescript", "python"],
|
|
34
|
+
"vulnerable_pattern": "function render(comment) {\n document.getElementById('out').innerHTML = '<div>' + comment + '</div>';\n}",
|
|
35
|
+
"fix_pattern": "function render(comment) {\n const div = document.createElement('div');\n div.textContent = comment;\n document.getElementById('out').replaceChildren(div);\n}",
|
|
36
|
+
"detection_hints": ["innerHTML with user data", "dangerouslySetInnerHTML", "Markup() with user input", "|safe filter in Jinja"]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"cwe": "CWE-22",
|
|
40
|
+
"name": "Path Traversal",
|
|
41
|
+
"category": "api_misuse",
|
|
42
|
+
"severity": "high",
|
|
43
|
+
"cause": "User-controlled path joined with a base directory without normalising + checking containment.",
|
|
44
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
45
|
+
"vulnerable_pattern": "def read_user_file(name):\n with open('/data/users/' + name, 'r') as f:\n return f.read()",
|
|
46
|
+
"fix_pattern": "from pathlib import Path\n\ndef read_user_file(name):\n base = Path('/data/users').resolve()\n target = (base / name).resolve()\n if not str(target).startswith(str(base) + os.sep):\n raise ValueError('path escapes base')\n return target.read_text()",
|
|
47
|
+
"detection_hints": ["open(base + user_input)", "path.join with raw user input", "fs.readFile(req.params.x)", "filepath.Join without filepath.Clean"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"cwe": "CWE-918",
|
|
51
|
+
"name": "Server-Side Request Forgery (SSRF)",
|
|
52
|
+
"category": "api_misuse",
|
|
53
|
+
"severity": "high",
|
|
54
|
+
"cause": "Server fetches a URL whose host is attacker-controlled, exposing internal services.",
|
|
55
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
56
|
+
"vulnerable_pattern": "async function fetchUrl(req) {\n const r = await fetch(req.body.url);\n return r.text();\n}",
|
|
57
|
+
"fix_pattern": "import ipaddress, urllib.parse\n\nALLOWED = {'api.example.com'}\n\nasync def fetchUrl(req):\n u = urllib.parse.urlparse(req.body.url)\n if u.hostname not in ALLOWED: raise ValueError('host blocked')\n return (await httpx.get(req.body.url)).text",
|
|
58
|
+
"detection_hints": ["fetch(req.body.x)", "requests.get(user_url)", "http.Get(userURL)", "no allowlist check"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"cwe": "CWE-94",
|
|
62
|
+
"name": "Code Injection via eval/exec",
|
|
63
|
+
"category": "api_misuse",
|
|
64
|
+
"severity": "critical",
|
|
65
|
+
"cause": "User input reaches eval()/exec()/Function()/setTimeout(string) and is executed as code.",
|
|
66
|
+
"languages": ["python", "javascript", "typescript"],
|
|
67
|
+
"vulnerable_pattern": "function calc(formula) {\n return eval(formula);\n}",
|
|
68
|
+
"fix_pattern": "// Use a real expression parser (e.g. mathjs) instead of eval.\nimport { evaluate } from 'mathjs';\nfunction calc(formula) {\n return evaluate(formula);\n}",
|
|
69
|
+
"detection_hints": ["eval(", "Function('return ' + x)(", "setTimeout(string, ...)", "exec(user_input)"]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"cwe": "CWE-502",
|
|
73
|
+
"name": "Deserialization of Untrusted Data",
|
|
74
|
+
"category": "api_misuse",
|
|
75
|
+
"severity": "critical",
|
|
76
|
+
"cause": "pickle.loads / yaml.load / unserialize on data the attacker can supply executes arbitrary code.",
|
|
77
|
+
"languages": ["python", "javascript", "typescript"],
|
|
78
|
+
"vulnerable_pattern": "def from_session(blob):\n return pickle.loads(blob)",
|
|
79
|
+
"fix_pattern": "def from_session(blob):\n return json.loads(blob) # or a typed schema (pydantic) over JSON",
|
|
80
|
+
"detection_hints": ["pickle.loads(", "yaml.load( # without SafeLoader", "marshal.loads(", "node-serialize.unserialize"]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"cwe": "CWE-352",
|
|
84
|
+
"name": "Cross-Site Request Forgery (CSRF)",
|
|
85
|
+
"category": "api_misuse",
|
|
86
|
+
"severity": "medium",
|
|
87
|
+
"cause": "State-changing endpoint accepts cookies but has no CSRF token / SameSite protection.",
|
|
88
|
+
"languages": ["javascript", "typescript", "python"],
|
|
89
|
+
"vulnerable_pattern": "app.post('/transfer', (req, res) => {\n doTransfer(req.session.user, req.body.amount);\n res.send('ok');\n});",
|
|
90
|
+
"fix_pattern": "import csurf from 'csurf';\nconst csrf = csurf({ cookie: { sameSite: 'strict', httpOnly: true } });\napp.post('/transfer', csrf, (req, res) => { /* … */ });",
|
|
91
|
+
"detection_hints": ["state-changing POST without CSRF middleware", "missing SameSite cookie", "no double-submit token"]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"cwe": "CWE-611",
|
|
95
|
+
"name": "XML External Entity (XXE)",
|
|
96
|
+
"category": "api_misuse",
|
|
97
|
+
"severity": "high",
|
|
98
|
+
"cause": "XML parser configured to resolve external entities; attacker reads local files or SSRFs.",
|
|
99
|
+
"languages": ["python", "javascript", "typescript"],
|
|
100
|
+
"vulnerable_pattern": "from xml.etree import ElementTree as ET\nroot = ET.fromstring(user_xml)",
|
|
101
|
+
"fix_pattern": "from defusedxml.ElementTree import fromstring\nroot = fromstring(user_xml)",
|
|
102
|
+
"detection_hints": ["xml.etree.ElementTree.fromstring on user input", "libxml2 without DTDLOAD off"]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"cwe": "CWE-601",
|
|
106
|
+
"name": "Open Redirect",
|
|
107
|
+
"category": "api_misuse",
|
|
108
|
+
"severity": "medium",
|
|
109
|
+
"cause": "Redirect target taken from user input without allowlisting.",
|
|
110
|
+
"languages": ["javascript", "typescript", "python", "go"],
|
|
111
|
+
"vulnerable_pattern": "app.get('/login/cb', (req, res) => {\n res.redirect(req.query.next);\n});",
|
|
112
|
+
"fix_pattern": "const ALLOW = new Set(['/home', '/profile']);\napp.get('/login/cb', (req, res) => {\n const next = String(req.query.next || '/home');\n res.redirect(ALLOW.has(next) ? next : '/home');\n});",
|
|
113
|
+
"detection_hints": ["res.redirect(req.query.x)", "Location header from user input"]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"cwe": "CWE-287",
|
|
117
|
+
"name": "Improper Authentication",
|
|
118
|
+
"category": "api_misuse",
|
|
119
|
+
"severity": "high",
|
|
120
|
+
"cause": "Auth check uses == on secrets (timing-leak) or falls back to anonymous when token absent.",
|
|
121
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
122
|
+
"vulnerable_pattern": "def check_token(presented, expected):\n return presented == expected",
|
|
123
|
+
"fix_pattern": "import hmac\n\ndef check_token(presented, expected):\n return hmac.compare_digest(presented.encode(), expected.encode())",
|
|
124
|
+
"detection_hints": ["== between tokens/HMAC", "missing auth check fallback to user", "if not token: pass"]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"cwe": "CWE-285",
|
|
128
|
+
"name": "Improper Authorization",
|
|
129
|
+
"category": "api_misuse",
|
|
130
|
+
"severity": "high",
|
|
131
|
+
"cause": "Endpoint authenticates the user but never checks if they are AUTHORISED for the specific resource.",
|
|
132
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
133
|
+
"vulnerable_pattern": "@app.get('/orders/{order_id}')\ndef get_order(order_id, user=Depends(current_user)):\n return db.orders.find_one({'id': order_id})",
|
|
134
|
+
"fix_pattern": "@app.get('/orders/{order_id}')\ndef get_order(order_id, user=Depends(current_user)):\n o = db.orders.find_one({'id': order_id})\n if o['user_id'] != user.id and not user.is_admin:\n raise HTTPException(403)\n return o",
|
|
135
|
+
"detection_hints": ["resource fetch without owner check", "{id} from URL passed straight to DB"]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"cwe": "CWE-862",
|
|
139
|
+
"name": "Missing Authorization",
|
|
140
|
+
"category": "api_misuse",
|
|
141
|
+
"severity": "high",
|
|
142
|
+
"cause": "Sensitive route has no auth decorator at all.",
|
|
143
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
144
|
+
"vulnerable_pattern": "@app.delete('/admin/users/{id}')\ndef delete_user(id):\n db.users.delete_one({'id': id})",
|
|
145
|
+
"fix_pattern": "@app.delete('/admin/users/{id}')\ndef delete_user(id, user=Depends(require_admin)):\n db.users.delete_one({'id': id})",
|
|
146
|
+
"detection_hints": ["admin/destructive route without auth middleware", "no @login_required"]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"cwe": "CWE-863",
|
|
150
|
+
"name": "Incorrect Authorization (default-allow)",
|
|
151
|
+
"category": "api_misuse",
|
|
152
|
+
"severity": "high",
|
|
153
|
+
"cause": "Authorisation flag defaults to a permissive value (e.g. is_admin = True by default).",
|
|
154
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
155
|
+
"vulnerable_pattern": "class User(BaseModel):\n id: int\n name: str\n is_superuser: bool = True",
|
|
156
|
+
"fix_pattern": "class User(BaseModel):\n id: int\n name: str\n is_superuser: bool = False # default-DENY",
|
|
157
|
+
"detection_hints": ["= True default on permission/role field", "default admin role", "open-by-default flag"]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"cwe": "CWE-798",
|
|
161
|
+
"name": "Hardcoded Credentials",
|
|
162
|
+
"category": "api_misuse",
|
|
163
|
+
"severity": "critical",
|
|
164
|
+
"cause": "API keys, passwords, or tokens are literal string constants in source.",
|
|
165
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
166
|
+
"vulnerable_pattern": "const STRIPE_KEY = 'sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678';",
|
|
167
|
+
"fix_pattern": "const STRIPE_KEY = process.env.STRIPE_KEY!;\nif (!STRIPE_KEY) throw new Error('STRIPE_KEY env var required');",
|
|
168
|
+
"detection_hints": ["literal API/secret keys assigned to constants", "AKIA…/ghp_…/sk_live_… in source"]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"cwe": "CWE-321",
|
|
172
|
+
"name": "Hardcoded Cryptographic Key",
|
|
173
|
+
"category": "api_misuse",
|
|
174
|
+
"severity": "critical",
|
|
175
|
+
"cause": "Symmetric/asymmetric key embedded in source; rotating requires a code change.",
|
|
176
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
177
|
+
"vulnerable_pattern": "const SECRET = 'my-jwt-signing-key-12345';\njwt.sign({ user }, SECRET);",
|
|
178
|
+
"fix_pattern": "const SECRET = process.env.JWT_SECRET!; // rotate by changing env, not code\njwt.sign({ user }, SECRET);",
|
|
179
|
+
"detection_hints": ["BEGIN PRIVATE KEY in source", "literal HMAC/JWT secret"]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"cwe": "CWE-327",
|
|
183
|
+
"name": "Broken Cryptographic Algorithm",
|
|
184
|
+
"category": "api_misuse",
|
|
185
|
+
"severity": "high",
|
|
186
|
+
"cause": "Uses MD5/SHA1 for security-sensitive purposes; uses DES/3DES; uses ECB mode.",
|
|
187
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
188
|
+
"vulnerable_pattern": "import hashlib\nh = hashlib.md5(password.encode()).hexdigest()",
|
|
189
|
+
"fix_pattern": "import bcrypt\nh = bcrypt.hashpw(password.encode(), bcrypt.gensalt())",
|
|
190
|
+
"detection_hints": ["hashlib.md5(", "hashlib.sha1( for security", "Cipher.getInstance('DES')", "AES.MODE_ECB"]
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"cwe": "CWE-330",
|
|
194
|
+
"name": "Insufficient Randomness",
|
|
195
|
+
"category": "api_misuse",
|
|
196
|
+
"severity": "high",
|
|
197
|
+
"cause": "Math.random / random.random / rand() used for tokens / passwords / session ids.",
|
|
198
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
199
|
+
"vulnerable_pattern": "function newSessionId() {\n return Math.random().toString(36).slice(2);\n}",
|
|
200
|
+
"fix_pattern": "import crypto from 'node:crypto';\nfunction newSessionId() {\n return crypto.randomBytes(32).toString('base64url');\n}",
|
|
201
|
+
"detection_hints": ["Math.random for token/session", "random.random for secret", "rand() for crypto"]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"cwe": "CWE-209",
|
|
205
|
+
"name": "Information Exposure via Error Message",
|
|
206
|
+
"category": "api_misuse",
|
|
207
|
+
"severity": "medium",
|
|
208
|
+
"cause": "Stack traces / DB errors returned to the client.",
|
|
209
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
210
|
+
"vulnerable_pattern": "app.use((err, req, res, _next) => {\n res.status(500).send(err.stack);\n});",
|
|
211
|
+
"fix_pattern": "app.use((err, req, res, _next) => {\n log.error(err);\n res.status(500).send('internal error');\n});",
|
|
212
|
+
"detection_hints": ["err.stack in HTTP response", "traceback returned to client"]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"cwe": "CWE-532",
|
|
216
|
+
"name": "Sensitive Data Written to Logs",
|
|
217
|
+
"category": "api_misuse",
|
|
218
|
+
"severity": "medium",
|
|
219
|
+
"cause": "Passwords / tokens / PII written to application logs.",
|
|
220
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
221
|
+
"vulnerable_pattern": "log.info('user login', { email, password });",
|
|
222
|
+
"fix_pattern": "log.info('user login', { email }); // never log raw passwords/tokens",
|
|
223
|
+
"detection_hints": ["log\\.\\w+\\(.*password", "console.log of req.body", "JSON.stringify(user)"]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"cwe": "CWE-200",
|
|
227
|
+
"name": "Exposure of Sensitive Information",
|
|
228
|
+
"category": "api_misuse",
|
|
229
|
+
"severity": "medium",
|
|
230
|
+
"cause": "Endpoint returns more data than the consumer needs (full user record with hash).",
|
|
231
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
232
|
+
"vulnerable_pattern": "@app.get('/me')\ndef me(user=Depends(current_user)):\n return user.dict() # includes password_hash, recovery_token, ...",
|
|
233
|
+
"fix_pattern": "class MeOut(BaseModel):\n id: int\n name: str\n email: str\n\n@app.get('/me', response_model=MeOut)\ndef me(user=Depends(current_user)):\n return user",
|
|
234
|
+
"detection_hints": ["return user.dict() of internal model", "no response_model schema"]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"cwe": "CWE-20",
|
|
238
|
+
"name": "Improper Input Validation",
|
|
239
|
+
"category": "api_misuse",
|
|
240
|
+
"severity": "medium",
|
|
241
|
+
"cause": "Server trusts the shape / range / type of inbound data.",
|
|
242
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
243
|
+
"vulnerable_pattern": "app.post('/users', (req, res) => {\n db.users.insert(req.body); // ANY shape accepted\n});",
|
|
244
|
+
"fix_pattern": "const NewUser = z.object({ email: z.string().email(), name: z.string().max(80) });\napp.post('/users', (req, res) => {\n const u = NewUser.parse(req.body);\n db.users.insert(u);\n});",
|
|
245
|
+
"detection_hints": ["db insert of raw req.body", "no schema parse/validation"]
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"cwe": "CWE-915",
|
|
249
|
+
"name": "Mass Assignment",
|
|
250
|
+
"category": "api_misuse",
|
|
251
|
+
"severity": "high",
|
|
252
|
+
"cause": "Object updated by spreading user input — attacker can set is_admin / balance / etc.",
|
|
253
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
254
|
+
"vulnerable_pattern": "app.patch('/me', (req, res) => {\n Object.assign(user, req.body);\n user.save();\n});",
|
|
255
|
+
"fix_pattern": "app.patch('/me', (req, res) => {\n const allowed = ['displayName', 'avatarUrl'];\n for (const k of allowed) if (req.body[k] !== undefined) user[k] = req.body[k];\n user.save();\n});",
|
|
256
|
+
"detection_hints": ["Object.assign(user, req.body)", "user.update(**body)"]
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"cwe": "CWE-190",
|
|
260
|
+
"name": "Integer Overflow / Wraparound",
|
|
261
|
+
"category": "logic",
|
|
262
|
+
"severity": "medium",
|
|
263
|
+
"cause": "Arithmetic on user-controlled integers overflows the language's int width (Go int32, JS bit-ops).",
|
|
264
|
+
"languages": ["go", "javascript", "typescript"],
|
|
265
|
+
"vulnerable_pattern": "func totalPrice(unitCents, qty int32) int32 {\n return unitCents * qty\n}",
|
|
266
|
+
"fix_pattern": "func totalPrice(unitCents, qty int32) (int32, error) {\n v := int64(unitCents) * int64(qty)\n if v > math.MaxInt32 || v < math.MinInt32 { return 0, errors.New(\"overflow\") }\n return int32(v), nil\n}",
|
|
267
|
+
"detection_hints": ["unbounded multiplication of user input", "int32 arithmetic without bound check"]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"cwe": "CWE-369",
|
|
271
|
+
"name": "Division by Zero",
|
|
272
|
+
"category": "logic",
|
|
273
|
+
"severity": "medium",
|
|
274
|
+
"cause": "Divisor can be 0 because the validation was only on the numerator.",
|
|
275
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
276
|
+
"vulnerable_pattern": "def average(values):\n return sum(values) / len(values)",
|
|
277
|
+
"fix_pattern": "def average(values):\n if not values: return 0\n return sum(values) / len(values)",
|
|
278
|
+
"detection_hints": ["a / b without checking b == 0", "len(x) in denominator"]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"cwe": "CWE-476",
|
|
282
|
+
"name": "Null / Undefined Dereference",
|
|
283
|
+
"category": "logic",
|
|
284
|
+
"severity": "medium",
|
|
285
|
+
"cause": "Optional value used without checking for null/undefined.",
|
|
286
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
287
|
+
"vulnerable_pattern": "function pickEmail(user) {\n return user.profile.contact.email;\n}",
|
|
288
|
+
"fix_pattern": "function pickEmail(user) {\n return user?.profile?.contact?.email ?? null;\n}",
|
|
289
|
+
"detection_hints": ["chained .x.y without ?.", "*p without nil check in Go"]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"cwe": "CWE-754",
|
|
293
|
+
"name": "Missing Check for Unusual Conditions",
|
|
294
|
+
"category": "logic",
|
|
295
|
+
"severity": "medium",
|
|
296
|
+
"cause": "Return value of a fallible call is not inspected.",
|
|
297
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
298
|
+
"vulnerable_pattern": "func write(path string, data []byte) {\n os.WriteFile(path, data, 0644)\n}",
|
|
299
|
+
"fix_pattern": "func write(path string, data []byte) error {\n if err := os.WriteFile(path, data, 0644); err != nil {\n return err\n }\n return nil\n}",
|
|
300
|
+
"detection_hints": ["unreturned err in Go", "promise without await/catch"]
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"cwe": "CWE-755",
|
|
304
|
+
"name": "Improper Handling of Exceptional Conditions",
|
|
305
|
+
"category": "logic",
|
|
306
|
+
"severity": "medium",
|
|
307
|
+
"cause": "Bare except / catch swallows errors without logging.",
|
|
308
|
+
"languages": ["python", "javascript", "typescript"],
|
|
309
|
+
"vulnerable_pattern": "try:\n do_work()\nexcept Exception:\n pass",
|
|
310
|
+
"fix_pattern": "try:\n do_work()\nexcept SomeKnownError as exc:\n log.warning('work failed', exc_info=exc)\n raise",
|
|
311
|
+
"detection_hints": ["bare except: pass", "catch(e) {} empty", "swallowed promise rejection"]
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"cwe": "CWE-682",
|
|
315
|
+
"name": "Incorrect Calculation",
|
|
316
|
+
"category": "logic",
|
|
317
|
+
"severity": "medium",
|
|
318
|
+
"cause": "Math used for money / discounts / tax has wrong operator order or float-for-currency.",
|
|
319
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
320
|
+
"vulnerable_pattern": "function totalAfterDiscount(price, taxRate, discount) {\n return price * (1 + taxRate) - discount; // tax on pre-discount price\n}",
|
|
321
|
+
"fix_pattern": "function totalAfterDiscount(price, taxRate, discount) {\n return (price - discount) * (1 + taxRate); // discount first, then tax\n}",
|
|
322
|
+
"detection_hints": ["price arithmetic with float", "discount applied after tax"]
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"cwe": "CWE-617",
|
|
326
|
+
"name": "Reachable Assertion",
|
|
327
|
+
"category": "logic",
|
|
328
|
+
"severity": "low",
|
|
329
|
+
"cause": "assert used as runtime check; stripped in -O / disabled in prod builds.",
|
|
330
|
+
"languages": ["python", "go"],
|
|
331
|
+
"vulnerable_pattern": "def transfer(amount):\n assert amount > 0\n book(amount)",
|
|
332
|
+
"fix_pattern": "def transfer(amount):\n if amount <= 0:\n raise ValueError('amount must be positive')\n book(amount)",
|
|
333
|
+
"detection_hints": ["assert as validation in Python", "panic in Go on user input"]
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"cwe": "CWE-401",
|
|
337
|
+
"name": "Resource Leak (missing close)",
|
|
338
|
+
"category": "resource",
|
|
339
|
+
"severity": "medium",
|
|
340
|
+
"cause": "File / DB / socket opened without `with` / `defer Close()` / `try-finally`.",
|
|
341
|
+
"languages": ["python", "go", "javascript", "typescript"],
|
|
342
|
+
"vulnerable_pattern": "def read_config(p):\n f = open(p)\n data = f.read()\n return data",
|
|
343
|
+
"fix_pattern": "def read_config(p):\n with open(p) as f:\n return f.read()",
|
|
344
|
+
"detection_hints": ["open() without with", "missing defer Close()", "no try/finally around resource"]
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"cwe": "CWE-770",
|
|
348
|
+
"name": "Allocation Without Limits",
|
|
349
|
+
"category": "resource",
|
|
350
|
+
"severity": "medium",
|
|
351
|
+
"cause": "Server allocates response/list/buffer proportional to user input with no upper bound.",
|
|
352
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
353
|
+
"vulnerable_pattern": "app.post('/echo', (req, res) => {\n const out = req.body.text.repeat(req.body.n); // attacker sets n = 1e9\n res.send(out);\n});",
|
|
354
|
+
"fix_pattern": "app.post('/echo', (req, res) => {\n const n = Math.min(1000, Number(req.body.n) || 0);\n res.send(String(req.body.text).repeat(n));\n});",
|
|
355
|
+
"detection_hints": ["user-controlled multiplier", "Array(n) with n from request"]
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"cwe": "CWE-400",
|
|
359
|
+
"name": "Uncontrolled Resource Consumption (DoS)",
|
|
360
|
+
"category": "resource",
|
|
361
|
+
"severity": "medium",
|
|
362
|
+
"cause": "Regex with catastrophic backtracking, JSON.parse on huge input, fork-bomb-like loop.",
|
|
363
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
364
|
+
"vulnerable_pattern": "const RE = /^(a+)+$/;\nfunction check(s) { return RE.test(s); }",
|
|
365
|
+
"fix_pattern": "// Use an atomic / linear-time regex (re2 / RE2) or an explicit parser.\nimport RE2 from 're2';\nconst RE = new RE2('^a+$');",
|
|
366
|
+
"detection_hints": ["nested quantifiers in regex", "no body-size limit", "loop bound = user input"]
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"cwe": "CWE-362",
|
|
370
|
+
"name": "Race Condition (TOCTOU)",
|
|
371
|
+
"category": "concurrency",
|
|
372
|
+
"severity": "medium",
|
|
373
|
+
"cause": "Check-then-act on shared state without a lock / transaction; another caller sneaks in.",
|
|
374
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
375
|
+
"vulnerable_pattern": "async def place_order(product_id, qty):\n inv = await db.get_inventory(product_id)\n if inv.qty >= qty:\n await db.set_inventory(product_id, inv.qty - qty)\n await db.create_order(product_id, qty)",
|
|
376
|
+
"fix_pattern": "async def place_order(product_id, qty):\n async with db.transaction():\n # atomic UPDATE … WHERE qty >= :qty RETURNING …\n ok = await db.deduct_if_enough(product_id, qty)\n if not ok: return {'status': 'out_of_stock'}\n await db.create_order(product_id, qty)",
|
|
377
|
+
"detection_hints": ["check then act on shared inventory / counter", "get + set without lock", "exists() then create()"]
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"cwe": "CWE-367",
|
|
381
|
+
"name": "TOCTOU File Check",
|
|
382
|
+
"category": "concurrency",
|
|
383
|
+
"severity": "medium",
|
|
384
|
+
"cause": "os.path.exists / stat then open — attacker swaps the file in between.",
|
|
385
|
+
"languages": ["python", "go", "javascript", "typescript"],
|
|
386
|
+
"vulnerable_pattern": "if os.path.exists(path):\n with open(path) as f: data = f.read()",
|
|
387
|
+
"fix_pattern": "try:\n with open(path) as f: data = f.read()\nexcept FileNotFoundError:\n data = None",
|
|
388
|
+
"detection_hints": ["exists() then open()", "stat() then chmod()"]
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"cwe": "CWE-833",
|
|
392
|
+
"name": "Deadlock",
|
|
393
|
+
"category": "concurrency",
|
|
394
|
+
"severity": "high",
|
|
395
|
+
"cause": "Two locks acquired in different orders by different code paths; both block forever.",
|
|
396
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
397
|
+
"vulnerable_pattern": "func transfer(a, b *Account, amount int) {\n a.lock.Lock(); defer a.lock.Unlock()\n b.lock.Lock(); defer b.lock.Unlock()\n a.bal -= amount; b.bal += amount\n}",
|
|
398
|
+
"fix_pattern": "func transfer(a, b *Account, amount int) {\n // global ordering: always lock the lower-id account first\n first, second := a, b\n if a.id > b.id { first, second = b, a }\n first.lock.Lock(); defer first.lock.Unlock()\n second.lock.Lock(); defer second.lock.Unlock()\n a.bal -= amount; b.bal += amount\n}",
|
|
399
|
+
"detection_hints": ["two mutexes locked without global ordering", "nested locks on different objects"]
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"cwe": "CWE-664",
|
|
403
|
+
"name": "Improper Control of Resource Through Lifetime",
|
|
404
|
+
"category": "resource",
|
|
405
|
+
"severity": "medium",
|
|
406
|
+
"cause": "Use-after-free / use after .close() / handle invalidated by another caller.",
|
|
407
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
408
|
+
"vulnerable_pattern": "f = open(path)\nf.close()\ndata = f.read() # use after close",
|
|
409
|
+
"fix_pattern": "with open(path) as f:\n data = f.read()",
|
|
410
|
+
"detection_hints": ["read after close()", "use after Free()/Destroy()"]
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"cwe": "CWE-1188",
|
|
414
|
+
"name": "Insecure Default Value",
|
|
415
|
+
"category": "api_misuse",
|
|
416
|
+
"severity": "high",
|
|
417
|
+
"cause": "Field with permissive default (admin, public, off-by-default safety) silently grants access.",
|
|
418
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
419
|
+
"vulnerable_pattern": "class Settings(BaseModel):\n debug_endpoint_enabled: bool = True\n auth_required: bool = False",
|
|
420
|
+
"fix_pattern": "class Settings(BaseModel):\n debug_endpoint_enabled: bool = False # explicit opt-in only\n auth_required: bool = True",
|
|
421
|
+
"detection_hints": ["= True default for debug/admin/superuser flag", "= False for auth/security flag"]
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"cwe": "CWE-345",
|
|
425
|
+
"name": "Insufficient Verification of Data Authenticity",
|
|
426
|
+
"category": "api_misuse",
|
|
427
|
+
"severity": "high",
|
|
428
|
+
"cause": "Webhook / JWT / signed message accepted without verifying signature.",
|
|
429
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
430
|
+
"vulnerable_pattern": "app.post('/webhook', (req, res) => {\n process(req.body);\n res.sendStatus(200);\n});",
|
|
431
|
+
"fix_pattern": "app.post('/webhook', (req, res) => {\n const sig = req.header('X-Signature');\n if (!verifyHmac(req.rawBody, sig, SECRET)) return res.sendStatus(401);\n process(req.body);\n res.sendStatus(200);\n});",
|
|
432
|
+
"detection_hints": ["webhook handler without signature check", "JWT.decode without verify"]
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"cwe": "CWE-384",
|
|
436
|
+
"name": "Session Fixation",
|
|
437
|
+
"category": "api_misuse",
|
|
438
|
+
"severity": "high",
|
|
439
|
+
"cause": "Session id reused across login; attacker who pre-set it on the victim now has authenticated access.",
|
|
440
|
+
"languages": ["python", "javascript", "typescript"],
|
|
441
|
+
"vulnerable_pattern": "@app.post('/login')\ndef login(form):\n if check_password(form.user, form.password):\n request.session['user'] = form.user # session id unchanged\n return redirect('/')",
|
|
442
|
+
"fix_pattern": "@app.post('/login')\ndef login(form):\n if check_password(form.user, form.password):\n request.session.regenerate() # NEW session id post-auth\n request.session['user'] = form.user\n return redirect('/')",
|
|
443
|
+
"detection_hints": ["set session[user] without regenerate", "no session_regenerate_id() at login"]
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"cwe": "CWE-1004",
|
|
447
|
+
"name": "Sensitive Cookie Without HttpOnly",
|
|
448
|
+
"category": "api_misuse",
|
|
449
|
+
"severity": "medium",
|
|
450
|
+
"cause": "Session cookie set without HttpOnly; readable from JS → XSS upgrades to session theft.",
|
|
451
|
+
"languages": ["javascript", "typescript", "python"],
|
|
452
|
+
"vulnerable_pattern": "res.cookie('sid', sessionId, { secure: true });",
|
|
453
|
+
"fix_pattern": "res.cookie('sid', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' });",
|
|
454
|
+
"detection_hints": ["res.cookie( without httpOnly", "set_cookie without HttpOnly"]
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
"cwe": "CWE-614",
|
|
458
|
+
"name": "Sensitive Cookie Without Secure",
|
|
459
|
+
"category": "api_misuse",
|
|
460
|
+
"severity": "medium",
|
|
461
|
+
"cause": "Cookie carrying session over HTTP; intercepted on any open network.",
|
|
462
|
+
"languages": ["javascript", "typescript", "python"],
|
|
463
|
+
"vulnerable_pattern": "res.cookie('sid', sessionId, { httpOnly: true });",
|
|
464
|
+
"fix_pattern": "res.cookie('sid', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' });",
|
|
465
|
+
"detection_hints": ["cookie without secure flag on auth-bearing app"]
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"cwe": "CWE-426",
|
|
469
|
+
"name": "Untrusted Search Path",
|
|
470
|
+
"category": "api_misuse",
|
|
471
|
+
"severity": "medium",
|
|
472
|
+
"cause": "PATH / module-import path includes a user-writable directory; attacker plants a binary.",
|
|
473
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
474
|
+
"vulnerable_pattern": "import subprocess\nsubprocess.run(['my-tool', '--help']) # relies on PATH",
|
|
475
|
+
"fix_pattern": "import subprocess, shutil\ntool = shutil.which('my-tool')\nif not tool or not tool.startswith('/usr/'): raise RuntimeError('tool not trusted')\nsubprocess.run([tool, '--help'])",
|
|
476
|
+
"detection_hints": ["bare exec() relying on PATH", "import from cwd-relative path"]
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"cwe": "CWE-732",
|
|
480
|
+
"name": "Incorrect Permission Assignment",
|
|
481
|
+
"category": "api_misuse",
|
|
482
|
+
"severity": "high",
|
|
483
|
+
"cause": "File created world-writable / world-readable when it holds secrets.",
|
|
484
|
+
"languages": ["python", "go", "javascript", "typescript"],
|
|
485
|
+
"vulnerable_pattern": "with open('.secrets', 'w') as f:\n f.write(token)",
|
|
486
|
+
"fix_pattern": "import os\nfd = os.open('.secrets', os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)\nwith os.fdopen(fd, 'w') as f:\n f.write(token)",
|
|
487
|
+
"detection_hints": ["mode 0o644/0o666/0o777 for secret file", "no umask"]
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"cwe": "CWE-922",
|
|
491
|
+
"name": "Insecure Storage of Sensitive Information",
|
|
492
|
+
"category": "api_misuse",
|
|
493
|
+
"severity": "high",
|
|
494
|
+
"cause": "Secrets stored plaintext in localStorage / sqlite / files; recoverable post-breach.",
|
|
495
|
+
"languages": ["javascript", "typescript", "python"],
|
|
496
|
+
"vulnerable_pattern": "localStorage.setItem('token', token);",
|
|
497
|
+
"fix_pattern": "// Tokens belong in HttpOnly+Secure cookies, NOT in localStorage (XSS-reachable).\nres.cookie('sid', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' });",
|
|
498
|
+
"detection_hints": ["localStorage.setItem with token/password", "plaintext secret in sqlite/json"]
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"cwe": "CWE-704",
|
|
502
|
+
"name": "Incorrect Type Conversion",
|
|
503
|
+
"category": "logic",
|
|
504
|
+
"severity": "low",
|
|
505
|
+
"cause": "parseInt('1.5') == 1; '5'+'5' = '55'; user_id == '0' truthy in some langs.",
|
|
506
|
+
"languages": ["javascript", "typescript", "python"],
|
|
507
|
+
"vulnerable_pattern": "function total(items) {\n let s = 0;\n for (const x of items) s = s + x.price; // strings concatenate!\n return s;\n}",
|
|
508
|
+
"fix_pattern": "function total(items) {\n let s = 0;\n for (const x of items) s += Number(x.price);\n return s;\n}",
|
|
509
|
+
"detection_hints": ["+ on string|number union", "loose == on heterogeneous types"]
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"cwe": "CWE-471",
|
|
513
|
+
"name": "Modification of Assumed Immutable Data",
|
|
514
|
+
"category": "logic",
|
|
515
|
+
"severity": "medium",
|
|
516
|
+
"cause": "Caller mutates an argument; second caller sees the mutated value.",
|
|
517
|
+
"languages": ["javascript", "typescript", "python"],
|
|
518
|
+
"vulnerable_pattern": "DEFAULTS = {'limit': 10}\n\ndef configure(overrides=DEFAULTS):\n overrides['limit'] += 1 # mutates the module-level dict!\n return overrides",
|
|
519
|
+
"fix_pattern": "DEFAULTS = {'limit': 10}\n\ndef configure(overrides=None):\n cfg = {**DEFAULTS, **(overrides or {})}\n cfg['limit'] += 1\n return cfg",
|
|
520
|
+
"detection_hints": ["mutable default arg", "in-place modify of caller's arg"]
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"cwe": "CWE-209c",
|
|
524
|
+
"name": "Verbose Error in Response Body",
|
|
525
|
+
"category": "api_misuse",
|
|
526
|
+
"severity": "low",
|
|
527
|
+
"cause": "App returns `{stack, query, env}` on 500 — useful for attacker recon.",
|
|
528
|
+
"languages": ["javascript", "typescript", "python"],
|
|
529
|
+
"vulnerable_pattern": "app.use((err, req, res, _next) => {\n res.status(500).json({ error: err.message, stack: err.stack, env: process.env });\n});",
|
|
530
|
+
"fix_pattern": "app.use((err, req, res, _next) => {\n log.error(err);\n res.status(500).json({ error: 'internal' });\n});",
|
|
531
|
+
"detection_hints": ["process.env in response", "err.stack in response"]
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"cwe": "CWE-913",
|
|
535
|
+
"name": "Improper Control of Dynamically-Managed Code Resources",
|
|
536
|
+
"category": "api_misuse",
|
|
537
|
+
"severity": "high",
|
|
538
|
+
"cause": "require()/import()/dlopen() target from user input.",
|
|
539
|
+
"languages": ["javascript", "typescript", "python"],
|
|
540
|
+
"vulnerable_pattern": "const mod = require(req.body.plugin); // user-chosen module",
|
|
541
|
+
"fix_pattern": "const ALLOWED = new Set(['stripe', 'paypal']);\nif (!ALLOWED.has(req.body.plugin)) throw new Error('unknown plugin');\nconst mod = require(req.body.plugin);",
|
|
542
|
+
"detection_hints": ["require(user_input)", "import(user_string)", "__import__(user_input)"]
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
"cwe": "CWE-1336",
|
|
546
|
+
"name": "Improper Neutralization of Special Elements Used in a Template Engine",
|
|
547
|
+
"category": "api_misuse",
|
|
548
|
+
"severity": "critical",
|
|
549
|
+
"cause": "User input rendered as Jinja2 / Handlebars / template source instead of as value.",
|
|
550
|
+
"languages": ["python", "javascript", "typescript"],
|
|
551
|
+
"vulnerable_pattern": "from jinja2 import Template\nt = Template('Hello ' + user_message) # user controls template!\nreturn t.render()",
|
|
552
|
+
"fix_pattern": "from jinja2 import Template\nt = Template('Hello {{ msg }}') # template is fixed\nreturn t.render(msg=user_message)",
|
|
553
|
+
"detection_hints": ["Template(string+user_input)", "render_template_string( with user data in template"]
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"cwe": "CWE-915b",
|
|
557
|
+
"name": "Trust Boundary Violation",
|
|
558
|
+
"category": "api_misuse",
|
|
559
|
+
"severity": "medium",
|
|
560
|
+
"cause": "Trusted-side variable populated from untrusted source without re-validation.",
|
|
561
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
562
|
+
"vulnerable_pattern": "session['user_id'] = request.json['user_id'] # trusts client",
|
|
563
|
+
"fix_pattern": "session['user_id'] = authenticate_request(request).id # derived server-side",
|
|
564
|
+
"detection_hints": ["session[…] = request.body[…]", "auth fields set from client payload"]
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
"cwe": "CWE-1284",
|
|
568
|
+
"name": "Improper Validation of Specified Quantity in Input",
|
|
569
|
+
"category": "logic",
|
|
570
|
+
"severity": "medium",
|
|
571
|
+
"cause": "Count / size / limit from user not bounded; loops over it.",
|
|
572
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
573
|
+
"vulnerable_pattern": "for i in range(int(request.args['n'])):\n do_work()",
|
|
574
|
+
"fix_pattern": "n = max(1, min(1000, int(request.args.get('n', 1))))\nfor i in range(n):\n do_work()",
|
|
575
|
+
"detection_hints": ["range(int(user_input))", "for i := 0; i < n; i++ where n from request"]
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"cwe": "CWE-201",
|
|
579
|
+
"name": "Insertion of Sensitive Information Into Sent Data",
|
|
580
|
+
"category": "api_misuse",
|
|
581
|
+
"severity": "medium",
|
|
582
|
+
"cause": "Response includes internal IDs/email of OTHER users (e.g. listing endpoint without filtering).",
|
|
583
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
584
|
+
"vulnerable_pattern": "@app.get('/users')\ndef list_users():\n return [u.dict() for u in db.users.all()] # leaks everyone's email + hash",
|
|
585
|
+
"fix_pattern": "@app.get('/users')\ndef list_users(user=Depends(current_user)):\n return [{'id': u.id, 'name': u.name} for u in db.users.public()]",
|
|
586
|
+
"detection_hints": ["public listing of full user records", "no role-based filtering"]
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"cwe": "CWE-1395",
|
|
590
|
+
"name": "Dependency on Vulnerable Component (transitive)",
|
|
591
|
+
"category": "api_misuse",
|
|
592
|
+
"severity": "medium",
|
|
593
|
+
"cause": "package.json / requirements.txt pins a version known to have an active CVE.",
|
|
594
|
+
"languages": ["javascript", "typescript", "python"],
|
|
595
|
+
"vulnerable_pattern": "\"lodash\": \"4.17.20\"",
|
|
596
|
+
"fix_pattern": "\"lodash\": \"^4.17.21\" // CVE-2021-23337 fix",
|
|
597
|
+
"detection_hints": ["pinning to a known-CVE version", "yarn.lock with known-vuln entry"]
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
"cwe": "CWE-915c",
|
|
601
|
+
"name": "Prototype Pollution (JS)",
|
|
602
|
+
"category": "api_misuse",
|
|
603
|
+
"severity": "high",
|
|
604
|
+
"cause": "Deep-merge of user object into Object.prototype lets attacker set global props on all objects.",
|
|
605
|
+
"languages": ["javascript", "typescript"],
|
|
606
|
+
"vulnerable_pattern": "function merge(t, s) {\n for (const k in s) t[k] = (typeof s[k]==='object') ? merge(t[k]||{}, s[k]) : s[k];\n return t;\n}\nmerge({}, JSON.parse(req.body));",
|
|
607
|
+
"fix_pattern": "function safeAssign(t, s) {\n for (const k of Object.keys(s)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n t[k] = s[k];\n }\n return t;\n}",
|
|
608
|
+
"detection_hints": ["deep merge of user JSON", "for…in without hasOwnProperty filter"]
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
"cwe": "CWE-444",
|
|
612
|
+
"name": "HTTP Request Smuggling",
|
|
613
|
+
"category": "api_misuse",
|
|
614
|
+
"severity": "high",
|
|
615
|
+
"cause": "Conflicting Content-Length / Transfer-Encoding headers from a proxy → desync with backend.",
|
|
616
|
+
"languages": ["javascript", "typescript", "python", "go"],
|
|
617
|
+
"vulnerable_pattern": "// custom HTTP parser that trusts both CL and TE",
|
|
618
|
+
"fix_pattern": "// Use a well-maintained HTTP server (nginx/node-http2) that rejects ambiguous framing.",
|
|
619
|
+
"detection_hints": ["raw HTTP parsing", "trusting both Content-Length and Transfer-Encoding"]
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
"cwe": "CWE-732b",
|
|
623
|
+
"name": "Path-controlled chown/chmod",
|
|
624
|
+
"category": "api_misuse",
|
|
625
|
+
"severity": "high",
|
|
626
|
+
"cause": "chmod / chown called on a user-controlled path; victim's files acquire attacker permissions.",
|
|
627
|
+
"languages": ["python", "go"],
|
|
628
|
+
"vulnerable_pattern": "os.chmod(request.form['path'], 0o777)",
|
|
629
|
+
"fix_pattern": "# Refuse path traversal first; whitelist parents.\n# Then derive the path from server-side data, not user input.",
|
|
630
|
+
"detection_hints": ["chmod/chown with user path", "no containment check"]
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"cwe": "CWE-787",
|
|
634
|
+
"name": "Out-of-Bounds Write",
|
|
635
|
+
"category": "resource",
|
|
636
|
+
"severity": "critical",
|
|
637
|
+
"cause": "Buffer indexed with user-controlled offset without bounds check; mostly C/C++ but also Buffer in Node.",
|
|
638
|
+
"languages": ["javascript", "typescript"],
|
|
639
|
+
"vulnerable_pattern": "const buf = Buffer.alloc(16);\nbuf.writeUInt32LE(value, offset); // offset from request",
|
|
640
|
+
"fix_pattern": "const buf = Buffer.alloc(16);\nconst off = Number.isInteger(offset) && offset >= 0 && offset <= 12 ? offset : 0;\nbuf.writeUInt32LE(value, off);",
|
|
641
|
+
"detection_hints": ["Buffer write with user offset", "no bound check on index"]
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
"cwe": "CWE-1295",
|
|
645
|
+
"name": "Debug Endpoint Exposed in Production",
|
|
646
|
+
"category": "api_misuse",
|
|
647
|
+
"severity": "high",
|
|
648
|
+
"cause": "/debug, /metrics, /admin reachable without auth or behind weak token.",
|
|
649
|
+
"languages": ["python", "javascript", "typescript", "go"],
|
|
650
|
+
"vulnerable_pattern": "@app.get('/debug/db')\ndef db_dump():\n return db.dump()",
|
|
651
|
+
"fix_pattern": "@app.get('/debug/db')\ndef db_dump(user=Depends(require_admin)):\n if not settings.DEBUG_ENDPOINTS_ENABLED: raise HTTPException(404)\n return db.dump()",
|
|
652
|
+
"detection_hints": ["/debug/, /admin/ without auth", "DEBUG=True route"]
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"cwe": "CWE-829",
|
|
656
|
+
"name": "Inclusion of Functionality from Untrusted Source",
|
|
657
|
+
"category": "api_misuse",
|
|
658
|
+
"severity": "high",
|
|
659
|
+
"cause": "<script src> / import URL from a third party with no SRI / pinning.",
|
|
660
|
+
"languages": ["javascript", "typescript"],
|
|
661
|
+
"vulnerable_pattern": "<script src=\"https://cdn.example.com/lib.js\"></script>",
|
|
662
|
+
"fix_pattern": "<script src=\"https://cdn.example.com/lib.js\" integrity=\"sha384-...\" crossorigin=\"anonymous\"></script>",
|
|
663
|
+
"detection_hints": ["<script src> without SRI", "fetch dynamic JS from untrusted CDN"]
|
|
664
|
+
}
|
|
665
|
+
]
|
|
666
|
+
}
|