python.django.security.injection.path-traversal.path-traversal-open.path-traversal-open

Community Favorite
profile photo of semgrepsemgrep
Author
12,102
Download Count*

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