python.django.security.audit.avoid-mark-safe.avoid-mark-safe

Community Favorite
profile photo of semgrepsemgrep
Author
49,023
Download Count*

'mark_safe()' is used to mark a string as "safe" for HTML output. This disables escaping and could therefore subject the content to XSS attacks. Use 'django.utils.html.format_html()' to build HTML for rendering instead.

Run Locally

Run in CI

Defintion

rules:
  - id: avoid-mark-safe
    patterns:
      - pattern-not-inside: django.utils.html.format_html(...)
      - pattern-not: django.utils.safestring.mark_safe("...")
      - pattern: django.utils.safestring.mark_safe(...)
    message: "'mark_safe()' is used to mark a string as \"safe\" for HTML output.
      This disables escaping and could therefore subject the content to XSS
      attacks. Use 'django.utils.html.format_html()' to build HTML for rendering
      instead."
    metadata:
      source-rule-url: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html
      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.0/ref/utils/#django.utils.safestring.mark_safe
        - https://docs.djangoproject.com/en/3.0/ref/utils/#django.utils.html.format_html
      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]
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    languages:
      - python
    severity: WARNING

Examples

avoid-mark-safe.py

from django.utils.safestring import mark_safe
from django.utils.html import format_html
from django.http import HttpResponse
from django.template import loader

def not_really_safe(request):
    template = loader.get_template('contents.html')
    # ruleid:avoid-mark-safe
    not_actually_safe = mark_safe(
        """
        <div>
            <p>Contents! %s</p>
        </div>
        """ % request.POST.get("contents")
    )
    return HttpResponse(template.render({"html_example": not_actually_safe}, request))

def fine(request):
    template = loader.get_template('contents.html')
    # ok:avoid-mark-safe
    fine = mark_safe(
        """
        <div>
            <p>Contents!</p>
        </div>
        """
    )
    return HttpResponse(template.render({"html_example": fine}, request))

def not_really_safe(request):
    template = loader.get_template('contents.html')
    # ok:avoid-mark-safe
    this_is_ok = format_html(
        """
        <div>
            <p>Contents! {}</p>
        </div>
        """,
        request.POST.get("contents")
    )
    return HttpResponse(template.render({"html_example": this_is_ok}, request))