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-005 --project .About This Rule
Understanding the vulnerability and how it is detected
This audit rule flags all usages of the RawSQL() expression in Django applications regardless of whether tainted data is detected flowing into the SQL string. It is a visibility rule designed to surface all raw SQL expressions for manual security review.
Django's RawSQL() allows embedding raw SQL fragments in ORM querysets via annotate(), filter(), and order_by(). When used correctly with a static SQL string and all dynamic values passed via the params tuple, it is safe. However, these usages are frequently modified by developers who later add dynamic values without realizing they must also update the params argument.
This rule uses pattern matching on DjangoExpressions.method("RawSQL") rather than taint analysis, providing full coverage of all RawSQL() call sites in the codebase. This makes it suitable for security audits and compliance reviews where a complete inventory of raw SQL usage is required.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Latent Injection Risk from Future Modifications
Even currently-safe RawSQL() calls with static SQL strings represent a latent risk. Developers who later modify these calls to include dynamic values may inadvertently embed them in the SQL string instead of the params tuple, introducing injection vulnerabilities that are not immediately obvious during code review.
Audit Coverage for Compliance
Security audits, penetration testing reports, and compliance frameworks often require a complete inventory of raw SQL usage. This rule provides that inventory for RawSQL() specifically, complementing taint-based rules that only flag confirmed injection flows.
Injection via Third-Party Code
RawSQL() usages in shared utility functions or mixins may be called from multiple views, some of which may pass user-controlled data as the SQL string. Auditing all call sites identifies these shared functions that need careful review of all their callers.
Database-Specific SQL in Annotations
RawSQL() is often used for database-specific functions not available in Django's ORM. These usages may be less scrutinized during code review because they are seen as necessary technical debt, making them a common location for overlooked injection vulnerabilities.
How to Fix
Recommended remediation steps
- 1Treat every RawSQL() call site as requiring security review to confirm the SQL string is a static literal.
- 2Migrate RawSQL() usages to equivalent Django ORM expressions (F(), Value(), Case(), database functions) wherever possible to eliminate the audit surface.
- 3Document the reason for each RawSQL() call in a code comment explaining why ORM alternatives are insufficient.
- 4Enforce code review policy requiring security sign-off on new RawSQL() additions.
- 5Use PYTHON-DJANGO-SEC-004 (taint-based rule) in CI to catch actual injection flows, and this audit rule for periodic compliance reviews.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule uses QueryType pattern matching rather than taint analysis. It matches all calls to DjangoExpressions.method("RawSQL") regardless of whether user-controlled data flows into the arguments. The .where() clause constrains matches to Python files in Django projects. This rule is intended as an audit/inventory tool and will produce findings for all RawSQL() usages including correctly-parameterized ones. Use it for security audits and compliance reviews. For CI-integrated injection detection, use PYTHON-DJANGO-SEC-004 instead to avoid noise from safe usages.
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 SQL Injection via cursor.execute()
User input flows to cursor.execute() without parameterization, enabling SQL injection attacks.
Django SQL Injection via QuerySet.raw()
User input flows to QuerySet.raw() without parameterization, enabling SQL injection through Django's ORM raw query interface.
Django SQL Injection via QuerySet.extra()
User input flows to QuerySet.extra() without parameterization, enabling SQL injection through Django's legacy ORM extension interface.
Frequently Asked Questions
Common questions about Raw SQL Usage Audit via RawSQL Expression
New feature
Get these findings posted directly on your GitHub pull requests
The Raw SQL Usage Audit via RawSQL Expression rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.