python.django.security.audit.xss.direct-use-of-httpresponse.direct-use-of-httpresponse

profile photo of returntocorpreturntocorp
Author
7,344
Download Count*

Detected data rendered directly to the end user via 'HttpResponse' or a similar object. This bypasses Django's built-in cross-site scripting (XSS) defenses and could result in an XSS vulnerability. Use Django's template engine to safely render HTML.

Run Locally

Run in CI

Defintion

rules:
  - id: direct-use-of-httpresponse
    message: Detected data rendered directly to the end user via 'HttpResponse' or a
      similar object. This bypasses Django's built-in cross-site scripting (XSS)
      defenses and could result in an XSS vulnerability. Use Django's template
      engine to safely render HTML.
    metadata:
      cwe:
        - "CWE-79: Improper Neutralization of Input During Web Page Generation
          ('Cross-site Scripting')"
      owasp:
        - A07:2017 - Cross-Site Scripting (XSS)
        - A03:2021 - Injection
      references:
        - https://docs.djangoproject.com/en/3.1/intro/tutorial03/#a-shortcut-render
        - https://docs.djangoproject.com/en/3.1/topics/http/shortcuts/#render
      category: security
      technology:
        - django
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: MEDIUM
      confidence: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    languages:
      - python
    severity: WARNING
    patterns:
      - pattern-not: django.http.$ANY("...", ...)
      - pattern-not: django.http.$ANY()
      - pattern-not: django.http.$ANY(..., content=None, ...)
      - pattern-not: django.http.$ANY(status=...)
      - pattern-not: django.http.HttpResponseNotAllowed([...])
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: django.http.HttpResponse(...)
                  - pattern: django.http.HttpResponseBadRequest(...)
                  - pattern: django.http.HttpResponseNotFound(...)
                  - pattern: django.http.HttpResponseForbidden(...)
                  - pattern: django.http.HttpResponseNotAllowed(...)
                  - pattern: django.http.HttpResponseGone(...)
                  - pattern: django.http.HttpResponseServerError(...)
              - pattern-not: django.http.$ANY(...,content_type=$TYPE,...)
          - patterns:
              - pattern-either:
                  - pattern: django.http.HttpResponse(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseBadRequest(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseNotFound(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseForbidden(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseNotAllowed(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseGone(...,content_type=$TYPE,...)
                  - pattern: django.http.HttpResponseServerError(...,content_type=$TYPE,...)
              - metavariable-regex:
                  metavariable: $TYPE
                  regex: .*[tT][eE][xX][tT]/[hH][tT][mM][lL].*

Examples

direct-use-of-httpresponse.py

import urllib
import json
from django.db.models import Q
from django.auth import User
from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.translation import ugettext as _

from org import engines, manageNoEngine, genericApiException

def search_certificates(request):
    user_filter = request.GET.get("user", "")
    if not user_filter:
        # ok:direct-use-of-httpresponse
        return HttpResponseBadRequest("user was not given")


    user = User.objects.get(Q(email=user_filter) | Q(username=user_filter))
    if user.DoesNotExist:
        # ruleid:direct-use-of-httpresponse
        return HttpResponseBadRequest(_("user '{user}' does not exist").format(user_filter))

def previewNode(request, uid):
    """Preview evaluante node"""
    try:
        if uid in engines:
            _nodeId = request.data.get('nodeId')
            engines[uid].stoppable = True
            _res = engines[uid].model.previewNode(_nodeId)
            if _res is None:
                # ok:direct-use-of-httpresponse
                return HttpResponse('', status=204)
            # ruleid:direct-use-of-httpresponse
            return HttpResponse(_res)
        return manageNoEngine()
    except Exception as e:
        return genericApiException(e, engines[uid])
    finally:
        engines[uid].stoppable = False

def inline_test(request):
    # ruleid:direct-use-of-httpresponse
    return HttpResponse("Received {}".format(request.POST.get('message')))


def vote(request, question_id):
    if request.method != "GET" and request.method != "POST":
        # ruleid:direct-use-of-httpresponse
        return HttpResponseBadRequest(
            "This view can not handle method {0}\n".format(request.method), status=405
        )

def endpoint():
    # ok:direct-use-of-httpresponse
    return HttpResponse(json.dumps({ 'status': 'ERROR', 'error': str(e) }), content_type='text/json')


def dangerous_endpoint():
    # ruleid:direct-use-of-httpresponse
    return HttpResponse(json.dumps({ 'status': 'ERROR', 'error': str(e) }), content_type='text/html')