Django SafeString Subclass Audit

MEDIUM

Class extends SafeString or SafeData, bypassing Django's auto-escaping for all instances. Audit to confirm the class properly sanitizes content.

Rule Information

Language
Python
Category
Django
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythondjangoxsssafestringsafedatasubclassauditCWE-79OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-053 --project .
1
2
3
4
5
6
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

About This Rule

Understanding the vulnerability and how it is detected

This audit rule flags all class definitions that extend SafeString or SafeData from django.utils.safestring. Classes that inherit from SafeString are treated as pre-validated safe HTML by Django's template engine, bypassing automatic escaping when their instances are rendered in templates.

SafeString is the internal type returned by mark_safe() and format_html(). It is designed for use within Django's own utilities, not as a base class for application code. Subclassing SafeString is an unusual pattern that bypasses auto-escaping at the class inheritance level, meaning all instances and operations on instances inherit the "safe" designation.

This rule surfaces all such subclasses for security review to verify that the class does not allow user-controlled data to flow through without escaping, and that the subclassing is intentional and justified.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Inherited Safety Bypass for All Instances

Unlike mark_safe() which marks a specific string value as safe, subclassing SafeString marks the entire class as safe. Any string operation on a SafeString subclass instance that incorporates user-controlled content will produce an output that is treated as safe by the template engine, potentially rendering XSS payloads without escaping.

2

String Operations Propagate Safety Flag

SafeString overrides string concatenation and other operations such that SafeString + regular_string returns a SafeString. This means user input concatenated onto a SafeString subclass instance inherits the safety flag and is rendered without escaping in templates.

3

Widespread Impact from Shared Types

SafeString subclasses are often used as shared utility types across the application. A vulnerability in the class definition affects every view, template, and component that uses that type, potentially exposing the entire application to XSS.

4

Difficult-to-Audit Safety Inheritance

SafeString subclassing creates implicit auto-escaping bypass that is not obvious at template rendering time. Unlike mark_safe() which appears inline where the unsafe content is introduced, SafeString inheritance separates the escape bypass declaration from the template rendering site by potentially many files and call stacks.

How to Fix

Recommended remediation steps

  • 1Avoid subclassing SafeString or SafeData in application code; use mark_safe() or format_html() at the specific point where content is validated to be safe instead.
  • 2If a SafeString subclass is unavoidable, ensure the constructor validates all input against an allowlist or escapes all user-controlled values before calling super().__new__().
  • 3Review all string operations on SafeString subclass instances to ensure user-controlled strings are never concatenated onto them without prior escaping.
  • 4Consider replacing SafeString subclasses with plain classes that have a render() method using format_html(), which makes the escaping explicit and local.
  • 5Add unit tests for SafeString subclasses that verify HTML-special characters in constructor arguments are properly escaped in the output.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule uses QueryType pattern matching rather than taint analysis. It matches all class definitions where the base class is SafeString or SafeData (checked via DjangoClasses.extends("SafeString") and DjangoClasses.extends("SafeData")). This is an audit rule providing visibility for manual review. Use PYTHON-DJANGO-SEC-050 for taint-based XSS detection in CI. The .where() clause constrains matches to Python files in Django project structures.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-79 ranked #2 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection (XSS)
PCI DSS v4.0
Requirement 6.2.4 and 6.3.2 - inventory of custom code; protect against XSS
NIST SP 800-53
SI-10: Information Input Validation; SI-15: Information Output Filtering
ISO 27001
A.14.2.5 - Secure system engineering principles; A.14.2.8 - System security testing

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Django SafeString Subclass Audit

SafeString subclassing is used to create custom string types that are always treated as safe HTML without needing to call mark_safe() at each use site. Examples include custom icon renderers, HTML widget types, and reusable UI components. The pattern trades convenience for safety -- the safety bypass is in the class definition, not at the rendering point, making it harder to audit.
Rarely. Django's own codebase uses SafeString internally as the return type of mark_safe() and format_html(). For application code, there is almost always a better pattern: a class with a render() method that returns format_html() output, or a custom template tag that handles escaping. If you find a SafeString subclass in your codebase, consider whether it can be refactored to a safer design.
Django's SafeString overrides __add__, __radd__, and other string operations. SafeString + str returns a SafeString (inherits safe flag). str + SafeString returns a regular str (does not inherit safe flag). This asymmetry means that appending user input to a SafeString instance produces output that is still treated as safe and rendered without escaping in templates.
PYTHON-DJANGO-SEC-050 detects flows from request parameters to HttpResponse. SafeString subclass XSS typically flows through the template rendering system rather than HttpResponse directly, so it may require SEC-051 or this audit rule (SEC-053) for detection. Both the audit rules and taint rules complement each other for complete XSS coverage.
Some do. Django's own form widgets use SafeString internally. Third-party widget libraries and rich text editors sometimes use SafeString subclasses. If a third-party package's SafeString subclass is flagged, review whether user input can influence the string content before escaping.

New feature

Get these findings posted directly on your GitHub pull requests

The Django SafeString Subclass Audit rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works