diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 6871716ec8ca97..3274a4cdb5fafb 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -660,7 +660,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "607") => (RuleGroup::Stable, rules::flake8_bandit::rules::StartProcessWithPartialPath), (Flake8Bandit, "608") => (RuleGroup::Stable, rules::flake8_bandit::rules::HardcodedSQLExpression), (Flake8Bandit, "609") => (RuleGroup::Stable, rules::flake8_bandit::rules::UnixCommandWildcardInjection), - (Flake8Bandit, "610") => (RuleGroup::Preview, rules::flake8_bandit::rules::DjangoExtra), + (Flake8Bandit, "610") => (RuleGroup::Stable, rules::flake8_bandit::rules::DjangoExtra), (Flake8Bandit, "611") => (RuleGroup::Stable, rules::flake8_bandit::rules::DjangoRawSql), (Flake8Bandit, "612") => (RuleGroup::Stable, rules::flake8_bandit::rules::LoggingConfigInsecureListen), (Flake8Bandit, "701") => (RuleGroup::Stable, rules::flake8_bandit::rules::Jinja2AutoescapeFalse), diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/django_extra.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/django_extra.rs index 258b2381fad67a..acfdea1bacd0cc 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/django_extra.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/django_extra.rs @@ -6,7 +6,8 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for uses of Django's `extra` function. +/// Checks for uses of Django's `extra` function where one or more arguments +/// passed are not literal expressions. /// /// ## Why is this bad? /// Django's `extra` function can be used to execute arbitrary SQL queries, @@ -16,9 +17,19 @@ use crate::checkers::ast::Checker; /// ```python /// from django.contrib.auth.models import User /// +/// # String interpolation creates a security loophole that could be used +/// # for SQL injection: /// User.objects.all().extra(select={"test": "%secure" % "nos"}) /// ``` /// +/// ## Use instead: +/// ```python +/// from django.contrib.auth.models import User +/// +/// # SQL injection is impossible if all arguments are literal expressions: +/// User.objects.all().extra(select={"test": "secure"}) +/// ``` +/// /// ## References /// - [Django documentation: SQL injection protection](https://docs.djangoproject.com/en/dev/topics/security/#sql-injection-protection) /// - [Common Weakness Enumeration: CWE-89](https://cwe.mitre.org/data/definitions/89.html)