Open rule catalog
Every detection rule Orvanta ships, published in full — what it looks for, its CWE and OWASP mapping, and the fix it drives. The engine is deterministic and taint-aware; the AI layer only explains and repairs what these rules find. Open under MIT.
Injection & code execution6
SQL injection via string-built query
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.
Fix: 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.
SQL injection via template literal or concatenation
A SQL query is built with a template literal or string concatenation and handed to the driver. Attacker-controlled values become executable SQL.
Fix: 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.
OS command injection
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.
Fix: Call subprocess.run([...]) with a list argument and shell=False, validate inputs against an allowlist, and use shlex.quote() only as a last resort.
OS command injection via child_process
child_process.exec/execSync runs a shell command assembled from dynamic values. Input containing ;, |, or $() executes arbitrary commands on the server.
Fix: Use execFile/spawn with an argument array (no shell parsing), validate inputs against an allowlist, and never pass user input into a shell string.
Dynamic code execution (eval/exec)
eval()/exec() runs a string as Python. If any part of that string is influenced by input, this is direct remote code execution.
Fix: Remove eval/exec. Parse values with ast.literal_eval for literals, or dispatch through an explicit mapping of allowed operations.
Dynamic code execution (eval / new Function)
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.
Fix: Remove eval/new Function. Use JSON.parse for data, and explicit function maps for dynamic dispatch.
Unsafe deserialization2
Unsafe deserialization (pickle)
pickle.loads() deserializes attacker-suppliable bytes. Pickle payloads can execute arbitrary Python during deserialization — this is remote code execution, not just data corruption.
Fix: Never unpickle untrusted data. Exchange data as JSON (json.loads) and, if you must sign binary blobs, verify an HMAC before deserializing.
Unsafe YAML load
yaml.load() without SafeLoader can instantiate arbitrary Python objects from a YAML document, which attackers use for code execution.
Fix: Use yaml.safe_load(), or pass Loader=yaml.SafeLoader explicitly.
Cross-site scripting4
DOM XSS via innerHTML
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.
Fix: Use textContent for text, or build elements with createElement/setAttribute. If HTML rendering is a requirement, sanitize with DOMPurify first.
React XSS via dangerouslySetInnerHTML
dangerouslySetInnerHTML renders a runtime string as raw HTML, bypassing React's escaping. Unsanitized values become stored or reflected XSS.
Fix: Render as text, or sanitize with DOMPurify (or isomorphic-dompurify on the server) before passing to dangerouslySetInnerHTML.
Reflected XSS in server response
An HTTP response body is built by interpolating request data into HTML. The value is reflected to the browser unescaped and executes as markup.
Fix: Render through a template engine with auto-escaping, or escape entities explicitly before interpolation. Never echo raw request values into HTML.
Server-side template injection / reflected XSS
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.
Fix: 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.
Authentication3
JWT signature verification disabled
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.
Fix: Always verify: jwt.decode(token, key, algorithms=["HS256"]) with an explicit algorithm allowlist, and never include "none".
JWT accepted without verification
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.
Fix: Use jwt.verify(token, secret, { algorithms: ["HS256"] }) and treat decode() as debugging output only.
Session cookie without HttpOnly/Secure
An auth/session cookie is set without HttpOnly (readable by injected scripts) or with secure:false (sent over plain HTTP).
Fix: Set { httpOnly: true, secure: true, sameSite: "lax" } on every cookie that references a session or token.
Cryptography2
Weak hash used for passwords
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.
Fix: Hash passwords with bcrypt, scrypt, or argon2 with a per-user salt. Migrate existing hashes on next successful login.
Insecure randomness for a security token
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.
Fix: Use crypto.randomBytes / crypto.randomUUID in Node, or the secrets module in Python, for anything an attacker must not guess.
Secrets in source4
Hardcoded signing/session secret
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.
Fix: Move the secret to an environment variable (process.env.JWT_SECRET / os.environ), rotate the leaked value, and invalidate sessions signed with it.
Cloud/provider API key committed to source
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.
Fix: 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.
Hardcoded credential in source
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.
Fix: Move the value to an environment variable or secret manager, rotate it, and add a secret scanner to CI so the pattern cannot return.
Secrets or credentials written to logs
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.
Fix: Never log credential values. Log an event with a redacted marker instead, and add a redaction filter at the logger level as a backstop.
Server-side request forgery1
Server-side request forgery
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.
Fix: Validate the URL against an allowlist of hosts/schemes, resolve and reject private/link-local IP ranges, and disable redirects on the outbound call.
Path traversal1
Path traversal in file access
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.
Fix: 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.
Broken access control2
Open redirect
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).
Fix: Allow only relative paths, or validate the destination against an allowlist of hosts before redirecting.
State-changing route without an auth check
A route that mutates data (POST/PUT/DELETE) or lives under an admin path has no visible authentication or authorization check in its handler.
Fix: Apply auth middleware (or a decorator) on the route, and verify object-level authorization — that the authenticated user owns the resource being changed.
Security misconfiguration3
TLS certificate verification disabled
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.
Fix: Re-enable verification. If an internal CA is in play, add its root certificate to the trust store instead of disabling checks.
Debug mode enabled
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.
Fix: 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.
CORS allows any origin
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.
Fix: List the exact origins allowed to call the API and keep credentials off wildcard responses.
Run these rules on your code
Free for solo developers, and free for open-source maintainers.