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-061 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects HTML injection vulnerabilities in Django applications where untrusted user input from HTTP request parameters flows into the html_message keyword argument of Django's send_mail() function without proper sanitization.
Django's send_mail() accepts an optional html_message parameter for sending HTML email alongside a plain text alternative. When user-controlled data is incorporated into the html_message string without HTML escaping, attackers can inject HTML tags, attributes, and potentially JavaScript that renders when the email recipient opens it.
This is functionally the same vulnerability as SEC-060 (EmailMessage HTML injection) but targeting the send_mail() API, which is the most commonly used Django email function due to its simpler interface. The send_mail() function is frequently called from Django views that process form submissions, making it a common location for user input to reach HTML email content.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Phishing Content Injection into Legitimate Emails
send_mail() is widely used for transactional emails (password resets, welcome emails, order confirmations). An attacker who controls input to any of these flows can inject fake instructions, deceptive links, or fraudulent content into emails that recipients trust because they originate from the legitimate application domain.
Contact Form Abuse for Phishing
Contact forms that send submitted content in HTML emails are a particularly common injection vector. An attacker submits HTML content through the contact form, which is then included in the html_message parameter and delivered to the recipient's inbox. The recipient sees what appears to be a normal contact submission but containing attacker-controlled HTML.
Password Reset Flow Manipulation
If user-controlled data (such as a submitted username or display name) flows into the html_message of a password reset email, attackers can inject content that makes the email appear to contain different reset instructions, a malicious link, or a fake customer service message.
Notification Email Compromise
Applications that send HTML notifications containing user-supplied content (comments, messages, names) without escaping allow attackers to distribute malicious HTML to other users through the application's email system, potentially at scale.
How to Fix
Recommended remediation steps
- 1Generate html_message content using render_to_string() with a template file; Django templates auto-escape all {{ variable }} values.
- 2When constructing html_message as a Python string, escape all user-controlled values with django.utils.html.escape() before inclusion.
- 3Generate the plain text message argument from the HTML by using strip_tags() on the escaped HTML content.
- 4Validate the email recipient address with Django's EmailValidator before passing it to send_mail().
- 5For emails triggered by user-submitted forms, consider whether including the raw user content in the HTML body is necessary, or whether a sanitized summary is sufficient.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule performs inter-procedural taint analysis with global scope. Sources include calls("request.GET.get"), calls("request.POST.get"), calls("request.GET.__getitem__"), calls("request.POST.__getitem__"), calls("request.body"), and calls("request.read"). The sink is the html_message keyword argument (tracked via .tracks(0) on the named parameter) of calls("send_mail") and calls("django.core.mail.send_mail"). Sanitizers include calls("django.utils.html.escape"), calls("render_to_string"), and calls("bleach.clean"). The rule follows taint across file and module boundaries.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Python
Django XSS via Direct HttpResponse with User Input
User input flows directly to HttpResponse without HTML escaping, enabling Cross-Site Scripting (XSS) attacks.
Django mark_safe() Usage Audit
mark_safe() bypasses Django's automatic HTML escaping. Audit all usages to confirm content is properly sanitized before being marked safe.
Django XSS in HTML Email Body via EmailMessage
User input flows into HTML email body content without sanitization, enabling HTML injection in emails.
Frequently Asked Questions
Common questions about Django XSS in send_mail html_message Parameter
New feature
Get these findings posted directly on your GitHub pull requests
The Django XSS in send_mail html_message Parameter rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.