python.django.security.injection.path-traversal.path-traversal-open.path-traversal-open
Community Favorite
semgrep
Author
12,102
Download Count*
License
Found request data in a call to 'open'. Ensure the request data is validated or sanitized, otherwise it could result in path traversal attacks and therefore sensitive data being leaked. To mitigate, consider using os.path.abspath or os.path.realpath or the pathlib library.
Run Locally
Run in CI
Defintion
rules:
- id: path-traversal-open
message: Found request data in a call to 'open'. Ensure the request data is
validated or sanitized, otherwise it could result in path traversal
attacks and therefore sensitive data being leaked. To mitigate, consider
using os.path.abspath or os.path.realpath or the pathlib library.
metadata:
cwe:
- "CWE-22: Improper Limitation of a Pathname to a Restricted Directory
('Path Traversal')"
owasp:
- A05:2017 - Broken Access Control
- A01:2021 - Broken Access Control
references:
- https://owasp.org/www-community/attacks/Path_Traversal
category: security
technology:
- django
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- vuln
likelihood: MEDIUM
impact: MEDIUM
confidence: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- Path Traversal
languages:
- python
severity: WARNING
patterns:
- pattern-inside: |
def $FUNC(...):
...
- pattern-either:
- pattern: open(..., request.$W.get(...), ...)
- pattern: open(..., $S.format(..., request.$W.get(...), ...), ...)
- pattern: open(..., $S % request.$W.get(...), ...)
- pattern: open(..., f"...{request.$W.get(...)}...", ...)
- pattern: |
$DATA = request.$W.get(...)
...
open(..., $DATA, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W.get(...)
...
open(..., $STR.format(..., $DATA, ...), ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR.format(..., $DATA, ...)
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR.format(..., $DATA, ...)
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W.get(...)
...
open(..., $STR % $DATA, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR % $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR % $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W.get(...)
...
open(..., f"...{$DATA}...", ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = f"...{$DATA}..."
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = f"...{$DATA}..."
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W.get(...)
...
open(..., $STR + $DATA, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR + $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W.get(...)
...
$INTERM = $STR + $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: $A = open(..., request.$W.get(...), ...)
- pattern: $A = open(..., $S.format(..., request.$W.get(...), ...), ...)
- pattern: $A = open(..., $S % request.$W.get(...), ...)
- pattern: $A = open(..., f"...{request.$W.get(...)}...", ...)
- pattern: return open(..., request.$W.get(...), ...)
- pattern: return open(..., $S.format(..., request.$W.get(...), ...), ...)
- pattern: return open(..., $S % request.$W.get(...), ...)
- pattern: return open(..., f"...{request.$W.get(...)}...", ...)
- pattern: |
$DATA = request.$W.get(...)
...
with open(..., $DATA, ...) as $FD:
...
- pattern: open(..., request.$W(...), ...)
- pattern: open(..., $S.format(..., request.$W(...), ...), ...)
- pattern: open(..., $S % request.$W(...), ...)
- pattern: open(..., f"...{request.$W(...)}...", ...)
- pattern: |
$DATA = request.$W(...)
...
open(..., $DATA, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W(...)
...
open(..., $STR.format(..., $DATA, ...), ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR.format(..., $DATA, ...)
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR.format(..., $DATA, ...)
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W(...)
...
open(..., $STR % $DATA, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR % $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR % $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W(...)
...
open(..., f"...{$DATA}...", ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = f"...{$DATA}..."
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = f"...{$DATA}..."
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W(...)
...
open(..., $STR + $DATA, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR + $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W(...)
...
$INTERM = $STR + $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: $A = open(..., request.$W(...), ...)
- pattern: $A = open(..., $S.format(..., request.$W(...), ...), ...)
- pattern: $A = open(..., $S % request.$W(...), ...)
- pattern: $A = open(..., f"...{request.$W(...)}...", ...)
- pattern: return open(..., request.$W(...), ...)
- pattern: return open(..., $S.format(..., request.$W(...), ...), ...)
- pattern: return open(..., $S % request.$W(...), ...)
- pattern: return open(..., f"...{request.$W(...)}...", ...)
- pattern: |
$DATA = request.$W(...)
...
with open(..., $DATA, ...) as $FD:
...
- pattern: open(..., request.$W[...], ...)
- pattern: open(..., $S.format(..., request.$W[...], ...), ...)
- pattern: open(..., $S % request.$W[...], ...)
- pattern: open(..., f"...{request.$W[...]}...", ...)
- pattern: |
$DATA = request.$W[...]
...
open(..., $DATA, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W[...]
...
open(..., $STR.format(..., $DATA, ...), ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR.format(..., $DATA, ...)
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR.format(..., $DATA, ...)
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W[...]
...
open(..., $STR % $DATA, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR % $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR % $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W[...]
...
open(..., f"...{$DATA}...", ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = f"...{$DATA}..."
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = f"...{$DATA}..."
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W[...]
...
open(..., $STR + $DATA, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR + $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W[...]
...
$INTERM = $STR + $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: $A = open(..., request.$W[...], ...)
- pattern: $A = open(..., $S.format(..., request.$W[...], ...), ...)
- pattern: $A = open(..., $S % request.$W[...], ...)
- pattern: $A = open(..., f"...{request.$W[...]}...", ...)
- pattern: return open(..., request.$W[...], ...)
- pattern: return open(..., $S.format(..., request.$W[...], ...), ...)
- pattern: return open(..., $S % request.$W[...], ...)
- pattern: return open(..., f"...{request.$W[...]}...", ...)
- pattern: |
$DATA = request.$W[...]
...
with open(..., $DATA, ...) as $FD:
...
- pattern: open(..., request.$W, ...)
- pattern: open(..., $S.format(..., request.$W, ...), ...)
- pattern: open(..., $S % request.$W, ...)
- pattern: open(..., f"...{request.$W}...", ...)
- pattern: |
$DATA = request.$W
...
open(..., $DATA, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W
...
open(..., $STR.format(..., $DATA, ...), ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR.format(..., $DATA, ...)
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR.format(..., $DATA, ...)
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W
...
open(..., $STR % $DATA, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR % $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR % $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W
...
open(..., f"...{$DATA}...", ...)
- pattern: |
$DATA = request.$W
...
$INTERM = f"...{$DATA}..."
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = f"...{$DATA}..."
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: |
$DATA = request.$W
...
open(..., $STR + $DATA, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR + $DATA
...
open(..., $INTERM, ...)
- pattern: |
$DATA = request.$W
...
$INTERM = $STR + $DATA
...
with open(..., $INTERM, ...) as $FD:
...
- pattern: $A = open(..., request.$W, ...)
- pattern: $A = open(..., $S.format(..., request.$W, ...), ...)
- pattern: $A = open(..., $S % request.$W, ...)
- pattern: $A = open(..., f"...{request.$W}...", ...)
- pattern: return open(..., request.$W, ...)
- pattern: return open(..., $S.format(..., request.$W, ...), ...)
- pattern: return open(..., $S % request.$W, ...)
- pattern: return open(..., f"...{request.$W}...", ...)
- pattern: |
$DATA = request.$W
...
with open(..., $DATA, ...) as $FD:
...
Examples
path-traversal-open.py
import re, os
from django.http import HttpResponse
from somewhere import APIView
def unsafe(request):
# ruleid: path-traversal-open
filename = request.POST.get('filename')
contents = request.POST.get('contents')
print("something")
f = open(filename, 'r')
f.write(contents)
f.close()
def unsafe_inline(request):
# ruleid: path-traversal-open
f = open(request.GET.get('filename'))
f.write(request.POST.get('contents'))
f.close()
def unsafe_dict(request):
# ruleid: path-traversal-open
f = open(request.POST['filename'])
f.write("hello")
f.close()
def unsafe_with(request):
# ruleid: path-traversal-open
filename = request.POST.get("filename")
with open(filename, 'r') as fin:
data = fin.read()
return HttpResponse(data)
def safe(request):
# ok: path-traversal-open
filename = "/tmp/data.txt"
f = open(filename)
f.write("hello")
f.close()
# Real-world finding
def download_doc(request):
# ruleid: path-traversal-open
url = request.GET.get("url")
format_doc = url.split(".")
if format_doc[-1] == "docx":
file_name = str(int(time.time())) + ".docx"
else:
file_name = str(int(time.time())) + ".xlsx"
def file_iterator(_file, chunk_size=512):
while True:
c = _file.read(chunk_size)
if c:
yield c
else:
break
_file = open(url, "rb")
response = StreamingHttpResponse(file_iterator(_file))
response["Content-Type"] = "application/octet-stream"
response["Content-Disposition"] = "attachment;filename=\"{0}\"".format(file_name)
return response
class GenerateUserAPI(APIView):
def get(self, request):
"""
download users excel
"""
# ruleid: path-traversal-open
file_id = request.GET.get("file_id")
if not file_id:
return self.error("Invalid Parameter, file_id is required")
if not re.match(r"^[a-zA-Z0-9]+$", file_id):
return self.error("Illegal file_id")
file_path = f"/tmp/{file_id}.xlsx"
if not os.path.isfile(file_path):
return self.error("File does not exist")
with open(file_path, "rb") as f:
raw_data = f.read()
os.remove(file_path)
response = HttpResponse(raw_data)
response["Content-Disposition"] = f"attachment; filename=users.xlsx"
response["Content-Type"] = "application/xlsx"
return response
Short Link: https://sg.run/W8qg