[ { "pair": "damn-vulnerable-graphql-application-views-resolve-pastes", "expected": [ { "cwe": "CWE-89", "vulnerability_class": "sql_injection", "path": "core/views.py", "evidence": "SQL injection via unsanitized 'filter' GraphQL argument directly interpolated into raw SQL using Python string formatting: result.filter(text(\"title = '%s' or content = '%s'\" % (filter, filter))). An attacker can inject arbitrary SQL via th", "rule_id": null, "line": null, "function": "resolve_pastes", "sink": null, "mechanism": "source_reaches_sink", "obligation": null } ], "sides": { "vuln": [ { "path": "core/views.py", "line": 20, "title": "SQL Injection via unsanitized 'filter' parameter in resolve_pastes", "rationale": "The GraphQL resolver builds a raw SQL WHERE clause using Python string interpolation with the user-supplied 'filter' argument: `text(\"title = '%s' or content = '%s'\" % (filter, filter))`. Since 'filter' is passed directly from the GraphQL query without escaping or parameterization, an attacker can i", "in_labeled_function": true, "matches": false } ], "fixed": [ { "path": "core/views.py", "line": 14, "title": "Plaintext password comparison enables credential stuffing/timing attacks", "rationale": "User.query.filter_by(username=username, password=password) compares raw password directly against a stored value with no hashing (e.g., bcrypt) and no constant-time comparison, suggesting passwords are stored in plaintext in the database. This exposes credentials to trivial theft on any DB read acce", "in_labeled_function": false, "matches": false }, { "path": "core/views.py", "line": 14, "title": "No account lockout / rate limiting on login mutation", "rationale": "The mutate() resolver performs an unrestricted DB lookup with username+password on every call, with no rate limiting or lockout logic visible, making brute-force credential guessing against this GraphQL mutation straightforward given GraphQL's higher-density request surface.", "in_labeled_function": false, "matches": false }, { "path": "core/views.py", "line": 16, "title": "Generic exception message but no differentiation prevents user enumeration only weakly", "rationale": "While raising 'Authentication Failure' avoids leaking whether the username or password was wrong, the surrounding logic still allows unlimited attempts, and any variance in Audit.create_audit_entry timing/behavior before the check could still leak information about valid usernames.", "in_labeled_function": false, "matches": false }, { "path": "core/views.py", "line": 19, "title": "Tokens minted from username only, without verifying additional session/secret binding", "rationale": "create_access_token(username) and create_refresh_token(username) are generated using just the username string; if the underlying JWT implementation lacks proper signing/expiry configuration (not shown here), tokens could be forged or reused improperly, and there's no per-session revocation reference", "in_labeled_function": false, "matches": false } ] } }, { "pair": "djangoat-analytics-hits-by-ip", "expected": [ { "cwe": "CWE-89", "vulnerability_class": "sql_injection", "path": "app/models/Analytics/analytics.py", "evidence": "SQL injection via string formatting in raw query. Both ip and col parameters are interpolated directly into SQL: 'SELECT %s FROM %s WHERE ip_address=\\'%s\\' ORDER BY id DESC' % (col, table_name, ip). The ip parameter comes from request.GET['", "rule_id": null, "line": null, "function": "hits_by_ip", "sink": null, "mechanism": "source_reaches_sink", "obligation": null } ], "sides": { "vuln": [ { "path": "app/models/Analytics/analytics.py", "line": 13, "title": "SQL Injection via unparameterized string interpolation in hits_by_ip", "rationale": "The 'ip' and 'col' parameters are directly interpolated into a raw SQL string using Python %-formatting instead of using parameterized queries (e.g., cursor.execute(cmd, [ip])). If 'ip' or 'col' originates from user-controllable input (such as an HTTP request parameter), an attacker could inject arb", "in_labeled_function": true, "matches": false } ], "fixed": [ { "path": "app/models/Analytics/analytics.py", "line": 13, "title": "Orphaned/incomplete analytics function with no visible callers or surrounding query logic", "rationale": "The file only contains a nested parse_field() helper that whitelists field names ('ip_address','referrer','user_agent') defaulting to '1' for anything else, evidently used to sanitize a column name for dynamic SQL (as suggested by the header comment referencing hits_by_ip and 'source_reaches_sink').", "in_labeled_function": false, "matches": false } ] } }, { "pair": "djangoat-views-user-view", "expected": [ { "cwe": "CWE-89", "vulnerability_class": "sql_injection", "path": "app/views/users/views.py", "evidence": "SQL injection via string formatting in raw query. user_id_form from POST data is interpolated directly into SQL: User.objects.raw('SELECT * FROM %s WHERE user_id=\\'%s\\' ORDER BY is_admin=\\'0\\'' % (table_name, user_id_form)). An attacker can", "rule_id": null, "line": null, "function": "user_view", "sink": null, "mechanism": "source_reaches_sink", "obligation": null } ], "sides": { "vuln": [ { "path": "app/views/users/views.py", "line": 18, "title": "Broad exception swallowing hides injected/malformed query errors", "rationale": "The `try/except` around `users[0]` catches all exceptions silently and just returns a generic 'NOT FOUND' message. This makes it harder to detect exploitation attempts (e.g., malformed SQL injection payloads causing query errors) and could mask underlying security-relevant failures, aiding attackers", "in_labeled_function": true, "matches": false }, { "path": "app/views/users/views.py", "line": 21, "title": "SQL Injection via raw() query using unsanitized user_id from POST data", "rationale": "The function takes `user_id` directly from `request.POST['user_id']` (form['user_id']) and interpolates it unsanitized into a raw SQL string via Python string formatting (`% (table_name, user_id_form)`), then executes it with `User.objects.raw(...)`. An attacker controlling the POST body can inject ", "in_labeled_function": true, "matches": false }, { "path": "app/views/users/views.py", "line": 36, "title": "Mass-assignment / unchecked mutable update dict onto arbitrary model instance", "rationale": "The `update` dict is built from `User.validate_update_form(form, user, update)` and then applied directly via `User.objects.filter(pk=user.pk).update(**update)` or `user.__dict__.update(update)` + `user.save()`. If the validation function does not strictly whitelist fields (e.g., it could allow `is_", "in_labeled_function": true, "matches": false } ], "fixed": [ { "path": "app/views/users/views.py", "line": 13, "title": "Whitelist-based field validator with no visible call site or sink usage", "rationale": "The file only contains a nested `parse_field` helper that whitelists `ip_address`, `referrer`, `user_agent` and falls back to the literal `'1'` for anything else. This pattern is typical of sanitizing a column/field name before interpolating it into a raw SQL query (e.g., ORDER BY or WHERE clause bu", "in_labeled_function": false, "matches": false } ] } }, { "pair": "dsvpwa-dsvpwa-main", "expected": [ { "cwe": "CWE-326", "vulnerability_class": "security_misconfiguration", "path": "dsvpwa.py", "evidence": "Insecure SSL/TLS configuration in main(). Three dangerous SSL options are explicitly enabled: (1) SSLv3 re-enabled via ctx.options &= ~ssl.OP_NO_SSLv3 (vulnerable to POODLE), (2) TLS compression re-enabled via &= ~ssl.OP_NO_COMPRESSION (vul", "rule_id": null, "line": null, "function": "main", "sink": null, "mechanism": "permissive_default", "obligation": null } ], "sides": { "vuln": [ { "path": "dsvpwa.py", "line": 22, "title": "SSL enablement defaults from an environment variable with weak type handling", "rationale": "The --ssl flag defaults to os.getenv('DSVPWA_SSL', 0) but is defined as store_true with a non-boolean default; combined with the insecure SSL context modifications above, any deployment relying on environment-based configuration is likely to end up with a downgraded/misconfigured TLS setup by defaul", "in_labeled_function": true, "matches": false }, { "path": "dsvpwa.py", "line": 33, "title": "SSL context re-enables SSLv3 support", "rationale": "The code explicitly clears ssl.OP_NO_SSLv3 from ctx.options, re-enabling the obsolete and insecure SSLv3 protocol which is vulnerable to POODLE and other downgrade attacks. This is a permissive default that weakens transport security for any client that can negotiate SSLv3.", "in_labeled_function": true, "matches": false }, { "path": "dsvpwa.py", "line": 34, "title": "SSL context disables compression protection", "rationale": "Clearing ssl.OP_NO_COMPRESSION re-enables TLS compression, which exposes the connection to CRIME-style compression side-channel attacks that can leak sensitive session data (e.g., cookies).", "in_labeled_function": true, "matches": false }, { "path": "dsvpwa.py", "line": 35, "title": "Server cipher preference explicitly disabled", "rationale": "Clearing ssl.OP_CIPHER_SERVER_PREFERENCE allows the client to dictate the negotiated cipher suite instead of the server, letting a malicious or misconfigured client force use of a weak cipher, undermining the server's ability to enforce strong cryptography.", "in_labeled_function": true, "matches": false } ], "fixed": [ { "path": "dsvpwa.py", "line": 17, "title": "Unvalidated/unsanitized comment input accepted without length or content checks", "rationale": "The 'comment' parameter is taken directly from request params with only a default empty-string fallback and no validation, sanitization, or length limiting before being persisted to the comments table, allowing arbitrary and potentially malicious content (e.g., script payloads, oversized data) to be", "in_labeled_function": false, "matches": false }, { "path": "dsvpwa.py", "line": 20, "title": "Stored XSS via unescaped comment output in guestbook", "rationale": "User-supplied 'comment' parameter is inserted into the database via a parameterized query (safe from SQLi) but later rendered directly into HTML via string formatting ('