{"name":"Orvanta Open Rule Catalog","version":1,"license":"MIT","source":"https://orvantalabs.dev/rules","count":28,"rules":[{"id":"PY-SQLI-001","title":"SQL injection via string-built query","severity":"critical","category":"injection","cwe":"CWE-89","owasp":"A03:2021 Injection","languages":["python"],"confidence":"high","description":"A SQL statement is assembled with f-strings, %-formatting, .format() or concatenation and passed to the database driver. Any value that reaches the string becomes part of the SQL grammar.","remediation":"Use parameterized queries: pass values as the second argument to execute() (e.g. cursor.execute(\"SELECT * FROM users WHERE id = %s\", (user_id,))) and never interpolate input into SQL text."},{"id":"JS-SQLI-001","title":"SQL injection via template literal or concatenation","severity":"critical","category":"injection","cwe":"CWE-89","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"high","description":"A SQL query is built with a template literal or string concatenation and handed to the driver. Attacker-controlled values become executable SQL.","remediation":"Use parameterized queries or a query builder: db.query(\"SELECT * FROM users WHERE id = $1\", [id]) (pg) or connection.execute(\"... WHERE id = ?\", [id]) (mysql2). Never interpolate values into SQL strings."},{"id":"PY-CMD-001","title":"OS command injection","severity":"critical","category":"injection","cwe":"CWE-78","owasp":"A03:2021 Injection","languages":["python"],"confidence":"high","description":"A shell command is built from dynamic values and executed via os.system, os.popen, or subprocess with shell=True. Shell metacharacters in the input become part of the command.","remediation":"Call subprocess.run([...]) with a list argument and shell=False, validate inputs against an allowlist, and use shlex.quote() only as a last resort."},{"id":"JS-CMD-001","title":"OS command injection via child_process","severity":"critical","category":"injection","cwe":"CWE-78","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"high","description":"child_process.exec/execSync runs a shell command assembled from dynamic values. Input containing ;, |, or $() executes arbitrary commands on the server.","remediation":"Use execFile/spawn with an argument array (no shell parsing), validate inputs against an allowlist, and never pass user input into a shell string."},{"id":"PY-DESER-001","title":"Unsafe deserialization (pickle)","severity":"critical","category":"deserialization","cwe":"CWE-502","owasp":"A08:2021 Software and Data Integrity Failures","languages":["python"],"confidence":"high","description":"pickle.loads() deserializes attacker-suppliable bytes. Pickle payloads can execute arbitrary Python during deserialization — this is remote code execution, not just data corruption.","remediation":"Never unpickle untrusted data. Exchange data as JSON (json.loads) and, if you must sign binary blobs, verify an HMAC before deserializing."},{"id":"PY-DESER-002","title":"Unsafe YAML load","severity":"high","category":"deserialization","cwe":"CWE-502","owasp":"A08:2021 Software and Data Integrity Failures","languages":["python"],"confidence":"high","description":"yaml.load() without SafeLoader can instantiate arbitrary Python objects from a YAML document, which attackers use for code execution.","remediation":"Use yaml.safe_load(), or pass Loader=yaml.SafeLoader explicitly."},{"id":"PY-EVAL-001","title":"Dynamic code execution (eval/exec)","severity":"critical","category":"injection","cwe":"CWE-94","owasp":"A03:2021 Injection","languages":["python"],"confidence":"medium","description":"eval()/exec() runs a string as Python. If any part of that string is influenced by input, this is direct remote code execution.","remediation":"Remove eval/exec. Parse values with ast.literal_eval for literals, or dispatch through an explicit mapping of allowed operations."},{"id":"JS-EVAL-001","title":"Dynamic code execution (eval / new Function)","severity":"critical","category":"injection","cwe":"CWE-94","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"medium","description":"eval() or new Function() compiles a runtime string into executable JavaScript. Input reaching it is remote code execution on the server, or XSS in the browser.","remediation":"Remove eval/new Function. Use JSON.parse for data, and explicit function maps for dynamic dispatch."},{"id":"JS-XSS-001","title":"DOM XSS via innerHTML","severity":"high","category":"xss","cwe":"CWE-79","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"high","description":"A dynamic value is written into the DOM with innerHTML/outerHTML/insertAdjacentHTML/document.write. HTML and script in the value executes in the victim's browser.","remediation":"Use textContent for text, or build elements with createElement/setAttribute. If HTML rendering is a requirement, sanitize with DOMPurify first."},{"id":"JS-XSS-002","title":"React XSS via dangerouslySetInnerHTML","severity":"high","category":"xss","cwe":"CWE-79","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"medium","description":"dangerouslySetInnerHTML renders a runtime string as raw HTML, bypassing React's escaping. Unsanitized values become stored or reflected XSS.","remediation":"Render as text, or sanitize with DOMPurify (or isomorphic-dompurify on the server) before passing to dangerouslySetInnerHTML."},{"id":"JS-XSS-003","title":"Reflected XSS in server response","severity":"high","category":"xss","cwe":"CWE-79","owasp":"A03:2021 Injection","languages":["javascript","typescript"],"confidence":"high","description":"An HTTP response body is built by interpolating request data into HTML. The value is reflected to the browser unescaped and executes as markup.","remediation":"Render through a template engine with auto-escaping, or escape entities explicitly before interpolation. Never echo raw request values into HTML."},{"id":"PY-XSS-001","title":"Server-side template injection / reflected XSS","severity":"high","category":"xss","cwe":"CWE-79","owasp":"A03:2021 Injection","languages":["python"],"confidence":"high","description":"render_template_string (or a hand-built HTML f-string response) includes request data. In Jinja this is template injection — {{...}} payloads run with server-side power, not just XSS.","remediation":"Render a file-based template and pass values as context variables (render_template(\"page.html\", name=name)); Jinja escapes them. Never format user input into template source or raw HTML."},{"id":"PY-AUTH-001","title":"JWT signature verification disabled","severity":"critical","category":"auth","cwe":"CWE-347","owasp":"A07:2021 Identification and Authentication Failures","languages":["python"],"confidence":"high","description":"jwt.decode is called with signature verification off (verify=False / verify_signature: False) or with the \"none\" algorithm allowed. Anyone can mint a token with any claims.","remediation":"Always verify: jwt.decode(token, key, algorithms=[\"HS256\"]) with an explicit algorithm allowlist, and never include \"none\"."},{"id":"JS-AUTH-001","title":"JWT accepted without verification","severity":"critical","category":"auth","cwe":"CWE-347","owasp":"A07:2021 Identification and Authentication Failures","languages":["javascript","typescript"],"confidence":"high","description":"jwt.decode() only parses a token — it does not check the signature. Using its output for authentication lets anyone forge an identity. Allowing the \"none\" algorithm in verify() has the same effect.","remediation":"Use jwt.verify(token, secret, { algorithms: [\"HS256\"] }) and treat decode() as debugging output only."},{"id":"SEC-AUTH-002","title":"Hardcoded signing/session secret","severity":"critical","category":"secrets","cwe":"CWE-798","owasp":"A02:2021 Cryptographic Failures","languages":["python","javascript","typescript"],"confidence":"high","description":"A JWT/session signing secret is a string literal in source. Anyone with repo access — or the git history — can forge valid sessions for any user.","remediation":"Move the secret to an environment variable (process.env.JWT_SECRET / os.environ), rotate the leaked value, and invalidate sessions signed with it."},{"id":"SEC-COOKIE-001","title":"Session cookie without HttpOnly/Secure","severity":"medium","category":"auth","cwe":"CWE-614","owasp":"A05:2021 Security Misconfiguration","languages":["javascript","typescript"],"confidence":"medium","description":"An auth/session cookie is set without HttpOnly (readable by injected scripts) or with secure:false (sent over plain HTTP).","remediation":"Set { httpOnly: true, secure: true, sameSite: \"lax\" } on every cookie that references a session or token."},{"id":"SEC-CRYPTO-001","title":"Weak hash used for passwords","severity":"high","category":"crypto","cwe":"CWE-328","owasp":"A02:2021 Cryptographic Failures","languages":["python","javascript","typescript"],"confidence":"high","description":"MD5/SHA-1 are fast, unsalted hashes; GPUs try billions per second. Password digests in these algorithms fall to offline cracking within hours of a database leak.","remediation":"Hash passwords with bcrypt, scrypt, or argon2 with a per-user salt. Migrate existing hashes on next successful login."},{"id":"SEC-CRYPTO-002","title":"Insecure randomness for a security token","severity":"medium","category":"crypto","cwe":"CWE-330","owasp":"A02:2021 Cryptographic Failures","languages":["python","javascript","typescript"],"confidence":"medium","description":"Math.random()/random module output is predictable — its internal state can be recovered from a few observed values. Tokens minted from it (reset links, session ids, API keys) can be predicted by an attacker.","remediation":"Use crypto.randomBytes / crypto.randomUUID in Node, or the secrets module in Python, for anything an attacker must not guess."},{"id":"SEC-TLS-001","title":"TLS certificate verification disabled","severity":"high","category":"config","cwe":"CWE-295","owasp":"A05:2021 Security Misconfiguration","languages":["python","javascript","typescript"],"confidence":"high","description":"Certificate verification is turned off (verify=False / rejectUnauthorized:false / NODE_TLS_REJECT_UNAUTHORIZED=0), so any on-path attacker can impersonate the remote service and read or modify the traffic.","remediation":"Re-enable verification. If an internal CA is in play, add its root certificate to the trust store instead of disabling checks."},{"id":"SEC-SSRF-001","title":"Server-side request forgery","severity":"high","category":"ssrf","cwe":"CWE-918","owasp":"A10:2021 Server-Side Request Forgery","languages":["python","javascript","typescript"],"confidence":"medium","description":"The server fetches a URL taken from the request. Attackers point it at internal services — cloud metadata endpoints (169.254.169.254), admin panels, databases — that are unreachable from outside.","remediation":"Validate the URL against an allowlist of hosts/schemes, resolve and reject private/link-local IP ranges, and disable redirects on the outbound call."},{"id":"SEC-PATH-001","title":"Path traversal in file access","severity":"high","category":"traversal","cwe":"CWE-22","owasp":"A01:2021 Broken Access Control","languages":["python","javascript","typescript"],"confidence":"medium","description":"A filesystem path is built from request data without normalization. Sequences like ../../ walk out of the intended directory and read or overwrite arbitrary files — /etc/passwd, .env, application source.","remediation":"Resolve the path (path.resolve / os.path.realpath), verify it is inside the intended base directory, and reject names containing traversal sequences. Prefer id-to-filename lookup tables over raw names."},{"id":"SEC-REDIRECT-001","title":"Open redirect","severity":"medium","category":"access","cwe":"CWE-601","owasp":"A01:2021 Broken Access Control","languages":["python","javascript","typescript"],"confidence":"high","description":"The server redirects to a URL taken directly from the request. Phishers wrap malicious destinations in your trusted domain (yourapp.com/login?next=https://evil.example).","remediation":"Allow only relative paths, or validate the destination against an allowlist of hosts before redirecting."},{"id":"PY-CONF-001","title":"Debug mode enabled","severity":"medium","category":"config","cwe":"CWE-489","owasp":"A05:2021 Security Misconfiguration","languages":["python"],"confidence":"high","description":"Flask/Django debug mode serves interactive tracebacks. The Werkzeug debugger includes a console that executes arbitrary Python on the server — debug in production is remote code execution.","remediation":"Set debug from an environment flag that defaults to off (app.run(debug=os.environ.get(\"FLASK_DEBUG\") == \"1\")), and keep DEBUG = False in production settings."},{"id":"SEC-CORS-001","title":"CORS allows any origin","severity":"medium","category":"config","cwe":"CWE-942","owasp":"A05:2021 Security Misconfiguration","languages":["python","javascript","typescript"],"confidence":"high","description":"Access-Control-Allow-Origin is a wildcard (or reflects the caller) on an API that uses cookies or credentials, so any website can script authenticated calls against it from a visitor's browser.","remediation":"List the exact origins allowed to call the API and keep credentials off wildcard responses."},{"id":"SEC-KEY-001","title":"Cloud/provider API key committed to source","severity":"critical","category":"secrets","cwe":"CWE-798","owasp":"A02:2021 Cryptographic Failures","languages":["python","javascript","typescript"],"confidence":"high","description":"A provider credential (AWS, Stripe, OpenAI, GitHub, Google, Slack) is committed in source. Bots scrape repos for these formats within minutes of exposure; a live key means account takeover or a five-figure cloud bill.","remediation":"Revoke and rotate the key at the provider now — removal from the file does not un-leak it from git history. Load it from an environment variable or secret manager."},{"id":"SEC-KEY-002","title":"Hardcoded credential in source","severity":"high","category":"secrets","cwe":"CWE-798","owasp":"A02:2021 Cryptographic Failures","languages":["python","javascript","typescript"],"confidence":"medium","description":"A password/API key/token is assigned as a string literal. Everyone with read access to the repo — and every clone, fork, and CI log — has the credential.","remediation":"Move the value to an environment variable or secret manager, rotate it, and add a secret scanner to CI so the pattern cannot return."},{"id":"SEC-BAC-001","title":"State-changing route without an auth check","severity":"medium","category":"access","cwe":"CWE-862","owasp":"A01:2021 Broken Access Control","languages":["python","javascript","typescript"],"confidence":"low","description":"A route that mutates data (POST/PUT/DELETE) or lives under an admin path has no visible authentication or authorization check in its handler.","remediation":"Apply auth middleware (or a decorator) on the route, and verify object-level authorization — that the authenticated user owns the resource being changed."},{"id":"SEC-LOG-001","title":"Secrets or credentials written to logs","severity":"medium","category":"secrets","cwe":"CWE-532","owasp":"A09:2021 Security Logging and Monitoring Failures","languages":["python","javascript","typescript"],"confidence":"medium","description":"A password/token/secret variable is passed to a logger or print/console call. Log pipelines are broadly readable and long-retained — credentials in them outlive every rotation policy.","remediation":"Never log credential values. Log an event with a redacted marker instead, and add a redaction filter at the logger level as a backstop."}]}