python.django.security.audit.query-set-extra.avoid-query-set-extra
Community Favorite
semgrep
Author
82,283
Download Count*
License
QuerySet.extra' does not provide safeguards against SQL injection and requires very careful use. SQL injection can lead to critical data being stolen by attackers. Instead of using '.extra', use the Django ORM and parameterized queries such as People.objects.get(name='Bob')
.
Run Locally
Run in CI
Defintion
rules:
- id: avoid-query-set-extra
message: QuerySet.extra' does not provide safeguards against SQL injection and
requires very careful use. SQL injection can lead to critical data being
stolen by attackers. Instead of using '.extra', use the Django ORM and
parameterized queries such as `People.objects.get(name='Bob')`.
metadata:
source-rule-url: https://bandit.readthedocs.io/en/latest/plugins/b610_django_extra_used.html
cwe:
- "CWE-89: Improper Neutralization of Special Elements used in an SQL
Command ('SQL Injection')"
owasp:
- A01:2017 - Injection
- A03:2021 - Injection
references:
- https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.extra
- https://semgrep.dev/blog/2020/preventing-sql-injection-a-django-authors-perspective/
category: security
technology:
- django
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- audit
likelihood: LOW
impact: HIGH
confidence: LOW
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- SQL Injection
languages:
- python
severity: WARNING
patterns:
- pattern: $MODEL.extra(...)
- pattern-not-inside: '$MODEL.extra(select = {$KEY: "..."})'
Examples
query-set-extra.py
# ruleid:avoid-query-set-extra
Entry.objects.get().extra()
# ruleid:avoid-query-set-extra
Entry.objects.filter().extra()
# ruleid:avoid-query-set-extra
Entry.objects.update().extra()
# ruleid:avoid-query-set-extra
Entry.objects.filter().update().extra()
# ruleid:avoid-query-set-extra
Entry.objects.get({}).filter().update().extra()
# ok:avoid-query-set-extra
findings = Finding.objects.filter(verified=True,
severity__in=('Critical', 'High', 'Medium', 'Low', 'Info')).prefetch_related(
'test__engagement__product',
'test__engagement__product__prod_type',
'test__engagement__risk_acceptance',
'risk_acceptance_set',
'reporter').extra(
select={
'ra_count': 'SELECT COUNT(*) FROM dojo_risk_acceptance INNER JOIN '
'dojo_risk_acceptance_accepted_findings ON '
'( dojo_risk_acceptance.id = dojo_risk_acceptance_accepted_findings.risk_acceptance_id ) '
'WHERE dojo_risk_acceptance_accepted_findings.finding_id = dojo_finding.id',
},
)
example = 1
# ruleid:avoid-query-set-extra
active_findings = Finding.objects.filter(verified=True, active=True,
severity__in=('Critical', 'High', 'Medium', 'Low', 'Info')).prefetch_related(
'test__engagement__product',
'test__engagement__product__prod_type',
'test__engagement__risk_acceptance',
'risk_acceptance_set',
'reporter').extra(
select={
'ra_count': f'SELECT COUNT(*) FROM dojo_risk_acceptance INNER JOIN '
f'dojo_risk_acceptance_accepted_findings ON '
f'( dojo_risk_acceptance.id = dojo_risk_acceptance_accepted_findings.risk_acceptance_id ) '
f'WHERE dojo_risk_acceptance_accepted_findings.finding_id = {example}',
},
)
Short Link: https://sg.run/kXZP