python.django.security.injection.path-traversal.path-traversal-file-name.path-traversal-file-name

Verifed by r2c
Community Favorite
profile photo of semgrepsemgrep
Author
99,207
Download Count*

Data from request is passed to a file name $FILE. This is a path traversal vulnerability, which can lead to 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-file-name
    message: Data from request is passed to a file name `$FILE`. This is a path
      traversal vulnerability, which can lead to 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: LOW
      impact: MEDIUM
      confidence: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Path Traversal
    patterns:
      - pattern-inside: |
          def $F(...):
            ...
      - pattern-not-inside: |
          def $F(...):
            ...
            os.path.realpath(...)
            ...
      - pattern-not-inside: |
          def $F(...):
            ...
            os.path.abspath(...)
            ...
      - pattern-either:
          - pattern: |
              $V = request.$W.get($X)
              ...
              $FILE % ($V)
          - pattern: |
              $V = request.$W[$X]
              ...
              $FILE % ($V)
          - pattern: |
              $V = request.$W($X)
              ...
              $FILE % ($V)
          - pattern: |
              $V = request.$W
              ...
              $FILE % ($V)
              # match format use cases
          - pattern: |
              $V = request.$W.get($X)
              ...
              $FILE.format(..., $V, ...)
          - pattern: |
              $V = request.$W[$X]
              ...
              $FILE.format(..., $V, ...)
          - pattern: |
              $V = request.$W($X)
              ...
              $FILE.format(..., $V, ...)
          - pattern: |
              $V = request.$W
              ...
              $FILE.format(..., $V, ...)
      - metavariable-regex:
          metavariable: $FILE
          regex: .*\.(log|zip|txt|csv|xml|html).*
    languages:
      - python
    severity: WARNING

Examples

path-traversal-file-name.py

import logging
import subprocess

def post1(request, format=None):
  try:
      # ruleid: path-traversal-file-name
      app_log_path = request.data['app_log_path']
      host = request.data['host']
      connect = connect_init(host)
      commands = "find %s -name '*.log'" % (app_log_path)
      result = connect.run(commands).stdout
      res = []
      for i in result.split():
          res.append(i)
      res = filter(None, res)
      connect.close()
      http_status = OK
  except:
      print("foo")

def post2(request, format=None):
  try:
      # ruleid: path-traversal-file-name
      app_log_path = request.data['app_log_path']
      host = request.data['host']
      connect = connect_init(host)
      commands = 'hours_{0}.csv'.format(app_log_path)
      result = connect.run(commands).stdout
      res = []
      for i in result.split():
          res.append(i)
      res = filter(None, res)
      connect.close()
      http_status = OK
  except:
      print("foo")

def post3(request, format=None):
  try:
      # ok: path-traversal-file-name
      app_log_path = request.data['app_log_path']
      app_log_path = os.path.abspath(app_log_path)
      host = request.data['host']
      connect = connect_init(host)
      commands = "find %s -name '*.log'" % (app_log_path)
      result = connect.run(commands).stdout
      res = []
      for i in result.split():
          res.append(i)
      res = filter(None, res)
      connect.close()
      http_status = OK
  except:
      print("foo")

def post4(request, format=None):
    # ok: path-traversal-file-name
    filename = request.GET.get('filename')
    host = request.data['host']
    commands = "find %s -name ." % (filename)
    result = subprocess.check_output(commands)
    logging.info("{} ran command: {}".format(host, commands))