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
5 changes: 4 additions & 1 deletion vulnerable_path_traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ def get_user_file(user_id, filename):
@app.route('/image')
def serve_image():
img_name = request.args.get('name')
img_path = "./static/images/" + img_name
base_dir = os.path.realpath("./static/images/")
img_path = os.path.realpath(os.path.join(base_dir, img_name))
if not img_path.startswith(base_dir):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Path traversal check bypassable via sibling directory prefix

High Severity

The startswith check against base_dir can be bypassed because os.path.realpath strips trailing slashes. If base_dir resolves to e.g. /app/static/images, a request for a file in a sibling directory like /app/static/images_evil/secret.txt would pass the img_path.startswith(base_dir) check. The comparison needs to ensure the path is within the directory by appending os.sep to base_dir (or also allowing an exact match with base_dir itself).

Fix in Cursor Fix in Web

return 'Access denied', 403
Comment on lines +51 to +52
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Path traversal bypass via startswith without trailing separator

The startswith check on line 51 is bypassable. os.path.realpath strips trailing slashes, so base_dir will be e.g. /abs/path/to/static/images. An attacker can request ?name=../images_evil/secret.txt, which resolves to /abs/path/to/static/images_evil/secret.txt. This path passes img_path.startswith(base_dir) because the string /abs/path/to/static/images_evil/... starts with /abs/path/to/static/images, allowing access to files outside the intended directory. The check should use img_path.startswith(base_dir + os.sep) (or also allow an exact match with base_dir).

Suggested change
if not img_path.startswith(base_dir):
return 'Access denied', 403
if not (img_path == base_dir or img_path.startswith(base_dir + os.sep)):
return 'Access denied', 403
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


return send_file(img_path)

Expand Down
Loading