Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions vul.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
vuln_examples.py
Intentionally vulnerable Python code for security testing
"""

import os
import sqlite3
import pickle
import requests
import hashlib
from flask import Flask, request

app = Flask(__name__)

# 1️⃣ HARDCODED SECRET
API_KEY = "sk_test_1234567890abcdef"
Comment on lines +13 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "vul.py" -type f

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 95


🏁 Script executed:

git ls-files | grep -E "(vul\.py|test|example|vulnerable)" | head -20

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 93


🏁 Script executed:

cat -n vul.py | head -25

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 664


This is intentional test code—add an import guard to prevent accidental deployment.

The hardcoded API_KEY is confirmed at line 16 and is intentionally vulnerable for testing/education purposes. However, add a safeguard to prevent this file from being accidentally imported in production environments:

import sys
if not (sys.modules.get("pytest") or sys.modules.get("unittest")):
    raise ImportError("This module is for testing only. Use only in test environments.")

Alternatively, document clearly in the module docstring and repository that this file must never be deployed to production, or use a naming convention (e.g., test_vul.py or place in a tests/ directory) to prevent accidental import.

🧰 Tools
🪛 Gitleaks (8.30.0)

[high] 16-16: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)

🤖 Prompt for AI Agents
In `@vul.py` around lines 13 - 16, The file defines a hardcoded API_KEY and should
be protected from accidental production import; add a module-level import guard
at the top (before API_KEY and app/Flask usage) that checks for test runners
(e.g., presence of "pytest" or "unittest" in sys.modules) and raises ImportError
if not running under tests, so the module can only be imported in test
environments and will fail fast otherwise; update references around API_KEY and
app (Flask(__name__)) to ensure the guard runs first.



# 2️⃣ COMMAND INJECTION
def list_files(user_path):
os.system("ls " + user_path)
Comment on lines +19 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 1527


Correct filename and replace os.system with os.listdir to prevent command injection.

The file is vuln_examples.py (not vul.py). The code at lines 19-21 uses os.system("ls " + user_path), which allows arbitrary command execution when user_path is untrusted. Use os.listdir(user_path) instead, and implement path validation (e.g., ensure the path stays within an allowed directory) to prevent path traversal.

🔧 Proposed fix
 def list_files(user_path):
-    os.system("ls " + user_path)
+    return os.listdir(user_path)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 2️⃣ COMMAND INJECTION
def list_files(user_path):
os.system("ls " + user_path)
def list_files(user_path):
return os.listdir(user_path)
🧰 Tools
🪛 Ruff (0.14.14)

[error] 21-21: Starting a process with a shell, possible injection detected

(S605)

🤖 Prompt for AI Agents
In `@vul.py` around lines 19 - 21, The file is misnamed—rename vul.py to
vuln_examples.py and replace the unsafe os.system call inside function
list_files(user_path) with a safe os.listdir(user_path) implementation; before
calling os.listdir, validate and normalize user_path (e.g., use os.path.abspath
and ensure it is under an allowed base directory or matches an allowlist) and
raise or return an error for invalid/unauthorized paths, then return the
directory listing rather than executing shell commands.



# 3️⃣ SQL INJECTION
def get_user(username):
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
return cursor.fetchall()
Comment on lines +24 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find and read the vul.py file
find . -name "vul.py" -type f | head -5

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 95


🏁 Script executed:

# Check if vul.py exists and read lines 24-30
if [ -f "./vul.py" ]; then
  echo "=== File found. Reading lines 24-30 ==="
  sed -n '24,30p' ./vul.py
  echo ""
  echo "=== Full file for context ==="
  wc -l ./vul.py
fi

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 399


🏁 Script executed:

# If not in root, search recursively
rg -l "def get_user" --type py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 93


Parameterize the SQL query and close the connection using a context manager.

The function is vulnerable to SQL injection—the f-string directly interpolates the username parameter into the query without escaping. Additionally, the database connection is never closed, causing a resource leak. Use bound parameters and wrap the connection in a context manager to fix both issues.

🔧 Proposed fix
 def get_user(username):
-    conn = sqlite3.connect("users.db")
-    cursor = conn.cursor()
-    query = f"SELECT * FROM users WHERE username = '{username}'"
-    cursor.execute(query)
-    return cursor.fetchall()
+    with sqlite3.connect("users.db") as conn:
+        cursor = conn.cursor()
+        cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
+        return cursor.fetchall()
🧰 Tools
🪛 Ruff (0.14.14)

[error] 28-28: Possible SQL injection vector through string-based query construction

(S608)

🤖 Prompt for AI Agents
In `@vul.py` around lines 24 - 30, The get_user function is vulnerable to SQL
injection and leaks the DB connection; fix it by using a context manager for the
connection and a parameterized query: open the DB with "with sqlite3.connect(...
) as conn", obtain a cursor from conn, execute the query using a placeholder
(e.g., "SELECT * FROM users WHERE username = ?") with (username,) as parameters,
fetch and return results so the connection is closed automatically; update
references to get_user accordingly.




# 7️⃣ UNSAFE EVAL
def calculate(expression):
return eval(expression)


# 8️⃣ WEAK PASSWORD HASHING
def hash_password(password):
return hashlib.md5(password.encode()).hexdigest()

# 4️⃣ INSECURE DESERIALIZATION (RCE)
def load_session(data):
return pickle.loads(data)
Comment on lines +44 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "vul.py" -type f

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 95


🏁 Script executed:

cat -n vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 2066


Block unsafe deserialization in load_session.
pickle.loads on untrusted data can lead to RCE; use JSON or a signed serializer instead.

🔧 Suggested fix (JSON)
-import pickle
+import json
@@
-def load_session(data):
-    return pickle.loads(data)
+def load_session(data):
+    return json.loads(data)
🧰 Tools
🪛 Ruff (0.14.14)

[error] 45-45: pickle and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue

(S301)

🤖 Prompt for AI Agents
In `@vul.py` around lines 44 - 45, The load_session function currently calls
pickle.loads on input data which allows unsafe deserialization; replace this
with a safe parser such as json.loads or a verified/signed deserializer and
reject raw pickle input. Update the load_session implementation to (1) detect
and refuse pickle-serialized blobs, (2) parse the session using json.loads (or a
signed serializer API) and validate required session fields, and (3) raise a
clear exception on malformed/untrusted data so no direct pickle.loads call
remains in load_session.



# 5️⃣ PATH TRAVERSAL
def read_file(filename):
with open("uploads/" + filename, "r") as f:
return f.read()
Comment on lines +38 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "vul.py" | head -20

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 95


🏁 Script executed:

cat -n vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 2066


Correct the line numbers and prevent path traversal by resolving against a fixed base directory.

The code is at lines 48-51 (not 38-41). Concatenating "uploads/" + filename allows ../ escapes to access files outside the uploads directory. Resolve and verify the path stays under the uploads directory.

🔧 Proposed fix
+from pathlib import Path
+
+# Base directory for uploads
+UPLOADS_DIR = Path("uploads").resolve()
+
 # 5️⃣ PATH TRAVERSAL
 def read_file(filename):
-    with open("uploads/" + filename, "r") as f:
-        return f.read()
+    path = (UPLOADS_DIR / filename).resolve()
+    if UPLOADS_DIR not in path.parents and path != UPLOADS_DIR:
+        raise ValueError("Invalid filename")
+    return path.read_text()
🤖 Prompt for AI Agents
In `@vul.py` around lines 38 - 41, The read_file function concatenates "uploads/"
with the user-supplied filename and is vulnerable to path traversal; change it
to resolve the filename against a fixed base uploads directory (use pathlib.Path
or os.path.realpath) and then verify the resolved path is inside that base
(e.g., compare resolved_path.commonpath/base or
resolved_path.startswith(base_resolved)); if the check fails, raise an error and
do not open the file. Ensure all references in read_file use the resolved,
validated path.



# 6️⃣ SSRF
def fetch_url(url):
return requests.get(url).text
Comment on lines +44 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n vul.py | head -50

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 1402


🏁 Script executed:

rg "fetch_url" vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 106


🏁 Script executed:

wc -l vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 96


🏁 Script executed:

cat -n vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 2066


Add SSRF protections and timeouts for outbound HTTP (lines 55–56).

Accepting arbitrary URLs can hit internal services and the call can hang without a timeout. Restrict hosts/schemes (and block private IPs) and set a timeout.

🔧 Proposed fix
+from urllib.parse import urlparse
@@
+# Example allowlist — tighten for your environment
+ALLOWED_HOSTS = {"example.com"}
@@
 def fetch_url(url):
-    return requests.get(url).text
+    parsed = urlparse(url)
+    if parsed.scheme not in {"http", "https"}:
+        raise ValueError("Invalid URL scheme")
+    if parsed.hostname not in ALLOWED_HOSTS:
+        raise ValueError("Host not allowed")
+    return requests.get(url, timeout=5, allow_redirects=False).text
🧰 Tools
🪛 Ruff (0.14.14)

[error] 46-46: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
In `@vul.py` around lines 44 - 46, The fetch_url function currently accepts
arbitrary URLs and calls requests.get without validation or timeout; update
fetch_url to (1) parse the URL (use urllib.parse) and reject non-http/https
schemes, (2) resolve the hostname to IP(s) (use socket.getaddrinfo) and refuse
any private/reserved addresses (RFC1918, IPv6 unique/local, loopback,
link-local), (3) optionally enforce a host allowlist or denylist, and (4) call
requests.get with a sensible timeout and raise or return a controlled error on
validation failures or timeouts; reference the fetch_url function when making
these changes.




# 9️⃣ FLASK DEBUG MODE + USER INPUT EXECUTION
@app.route("/debug")
def debug():
cmd = request.args.get("cmd")
return os.popen(cmd).read()
Comment on lines +59 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "(vul|debug)" | head -20

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 93


🏁 Script executed:

find . -name "vul.py" -o -name "*vul*" 2>/dev/null | head -20

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 95


🏁 Script executed:

# Search for Flask debug routes or os.popen usage
rg "os\.popen|@app\.route.*debug" -A 3 -B 1

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 333


🏁 Script executed:

cat -n vul.py | head -70

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 1875


🏁 Script executed:

# Check Flask app setup and any middleware/auth
rg "def.*auth|@.*auth|before_request|after_request" vul.py -B 2 -A 2

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 69


Remove the /debug command‑execution endpoint.

This provides unauthenticated RCE. The debug() function at lines 61-64 in vul.py directly executes user-supplied commands from query parameters via os.popen(cmd) without any authentication checks. Drop the route entirely or replace it with authenticated admin tooling that never shells out.

🔧 Proposed fix
-@app.route("/debug")
-def debug():
-    cmd = request.args.get("cmd")
-    return os.popen(cmd).read()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 9️⃣ FLASK DEBUG MODE + USER INPUT EXECUTION
@app.route("/debug")
def debug():
cmd = request.args.get("cmd")
return os.popen(cmd).read()
🧰 Tools
🪛 Ruff (0.14.14)

[error] 63-63: Starting a process with a shell, possible injection detected

(S605)

🤖 Prompt for AI Agents
In `@vul.py` around lines 59 - 63, The /debug endpoint exposing debug() should be
removed or replaced: delete the `@app.route`("/debug") handler and the debug()
function that calls os.popen(cmd). If you need equivalent admin tooling,
implement an authenticated admin-only endpoint (verify admin
session/credentials) and avoid executing shell commands from user input—use
safe, explicit server-side operations or vetted library calls instead of
os.popen(cmd). Ensure no remaining references to debug() or os.popen(cmd) remain
in vul.py.



# 🔟 OVERLY BROAD EXCEPTION (ERROR HIDING)
def divide(a, b):
try:
return a / b
except:
return None
Comment on lines +66 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine vul.py
fd vul.py -t f

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 93


🏁 Script executed:

# Read vul.py to examine the code at lines 66-71
cat -n vul.py | sed -n '60,75p'

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 498


Avoid bare except to prevent error hiding.

Catching everything masks bugs; handle specific exceptions and let others surface.

🔧 Proposed fix
     try:
         return a / b
-    except:
+    except ZeroDivisionError:
         return None
🧰 Tools
🪛 Ruff (0.14.14)

[error] 70-70: Do not use bare except

(E722)

🤖 Prompt for AI Agents
In `@vul.py` around lines 66 - 71, The function divide currently uses a bare
except which hides all errors; update divide to catch specific exceptions (e.g.,
catch ZeroDivisionError and return None) and do not swallow other
exceptions—either let them propagate or re-raise them so calling code can see
real failures; locate the divide function and replace the bare except with an
explicit except ZeroDivisionError (and optionally an except TypeError if you
want to handle non-numeric inputs) while ensuring other exceptions are not
caught silently.



if __name__ == "__main__":
# 11️⃣ DEBUG MODE ENABLED
app.run(host="0.0.0.0", port=5000, debug=True)
Comment on lines +75 to +77

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n vul.py | sed -n '70,80p'

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 305


🏁 Script executed:

# Check if os module is imported
rg "^import os|^from os" vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 96


🏁 Script executed:

# Get full context of the file
wc -l vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 96


🏁 Script executed:

head -30 vul.py

Repository: AbhishekPrecogsAI/Mern-stack-Authentication

Length of output: 675


Don't run Flask with debug=True and host 0.0.0.0 together.
This exposes the debugger publicly, creating a remote code execution vulnerability; gate via environment variables and default to localhost.

🔧 Safer defaults
 if __name__ == "__main__":
-    app.run(host="0.0.0.0", port=5000, debug=True)
+    debug = os.getenv("FLASK_DEBUG") == "1"
+    host = os.getenv("FLASK_HOST", "127.0.0.1")
+    app.run(host=host, port=5000, debug=debug)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if __name__ == "__main__":
# 11️⃣ DEBUG MODE ENABLED
app.run(host="0.0.0.0", port=5000, debug=True)
if __name__ == "__main__":
debug = os.getenv("FLASK_DEBUG") == "1"
host = os.getenv("FLASK_HOST", "127.0.0.1")
app.run(host=host, port=5000, debug=debug)
🧰 Tools
🪛 ast-grep (0.40.5)

[warning] 76-76: Running flask app with host 0.0.0.0 could expose the server publicly.
Context: app.run(host="0.0.0.0", port=5000, debug=True)
Note: [CWE-668]: Exposure of Resource to Wrong Sphere [OWASP A01:2021]: Broken Access Control [REFERENCES]
https://owasp.org/Top10/A01_2021-Broken_Access_Control

(avoid_app_run_with_bad_host-python)


[warning] 76-76: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(host="0.0.0.0", port=5000, debug=True)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/

(debug-enabled-python)

🪛 Ruff (0.14.14)

[error] 77-77: Possible binding to all interfaces

(S104)


[error] 77-77: Use of debug=True in Flask app detected

(S201)

🤖 Prompt for AI Agents
In `@vul.py` around lines 75 - 77, The app currently calls app.run(host="0.0.0.0",
port=5000, debug=True) in the if __name__ == "__main__" block which exposes the
Flask debugger publicly; change this to default to host="127.0.0.1" and set
debug from an environment flag (e.g., os.environ.get("FLASK_DEBUG") == "1" or
similar) so debug=True is only enabled when explicitly allowed, and ensure any
allowlist for host binding is enforced before calling app.run; update the
app.run invocation and gating logic around __main__ accordingly.