From 4964d2736ea3c8f5a320652c19997386fae67675 Mon Sep 17 00:00:00 2001 From: Chaojie Date: Tue, 7 Nov 2023 11:16:50 +0800 Subject: [PATCH] [flake8-bandit] Add Rule for S702 (use of mako templates) --- .../test/fixtures/flake8_bandit/S702.py | 11 +++++++ .../src/checkers/ast/analyze/expression.rs | 3 ++ crates/ruff_linter/src/codes.rs | 1 + .../src/rules/flake8_bandit/mod.rs | 1 + .../src/rules/flake8_bandit/rules/mod.rs | 2 ++ .../rules/use_of_mako_templates.rs | 32 +++++++++++++++++++ ...s__flake8_bandit__tests__S702_S702.py.snap | 31 ++++++++++++++++++ ruff.schema.json | 1 + 8 files changed, 82 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bandit/S702.py create mode 100644 crates/ruff_linter/src/rules/flake8_bandit/rules/use_of_mako_templates.rs create mode 100644 crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S702_S702.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S702.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S702.py new file mode 100644 index 00000000000000..29dd38c9f7ebc0 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S702.py @@ -0,0 +1,11 @@ +from mako.template import Template +import mako + +from mako import template + +Template("hello") + +# XXX(fletcher): for some reason, bandit is missing the one below. keeping it +# in for now so that if it gets fixed inadvertitently we know. +mako.template.Template("hern") +template.Template("hern") diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index e769f40e3cd7ec..96ef448f06cedf 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -571,6 +571,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::Jinja2AutoescapeFalse) { flake8_bandit::rules::jinja2_autoescape_false(checker, call); } + if checker.enabled(Rule::UseOfMakoTemplates) { + flake8_bandit::rules::use_of_mako_templates(checker, call); + } if checker.enabled(Rule::HardcodedPasswordFuncArg) { flake8_bandit::rules::hardcoded_password_func_arg(checker, keywords); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 320b62020d4b31..3994e4b2e7d4f5 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -629,6 +629,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "609") => (RuleGroup::Stable, rules::flake8_bandit::rules::UnixCommandWildcardInjection), (Flake8Bandit, "612") => (RuleGroup::Stable, rules::flake8_bandit::rules::LoggingConfigInsecureListen), (Flake8Bandit, "701") => (RuleGroup::Stable, rules::flake8_bandit::rules::Jinja2AutoescapeFalse), + (Flake8Bandit, "702") => (RuleGroup::Stable, rules::flake8_bandit::rules::UseOfMakoTemplates), // flake8-boolean-trap (Flake8BooleanTrap, "001") => (RuleGroup::Stable, rules::flake8_boolean_trap::rules::BooleanTypeHintPositionalArgument), diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index f915fd88190ad5..133fe3332bcbeb 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -28,6 +28,7 @@ mod tests { #[test_case(Rule::HardcodedTempFile, Path::new("S108.py"))] #[test_case(Rule::HashlibInsecureHashFunction, Path::new("S324.py"))] #[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"))] + #[test_case(Rule::UseOfMakoTemplates, Path::new("S702.py"))] #[test_case(Rule::LoggingConfigInsecureListen, Path::new("S612.py"))] #[test_case(Rule::ParamikoCall, Path::new("S601.py"))] #[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/mod.rs index 24ad5a3721c856..876ba1fb63a4cd 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/mod.rs @@ -22,6 +22,7 @@ pub(crate) use suspicious_function_call::*; pub(crate) use try_except_continue::*; pub(crate) use try_except_pass::*; pub(crate) use unsafe_yaml_load::*; +pub(crate) use use_of_mako_templates::*; pub(crate) use weak_cryptographic_key::*; mod assert_used; @@ -48,4 +49,5 @@ mod suspicious_function_call; mod try_except_continue; mod try_except_pass; mod unsafe_yaml_load; +mod use_of_mako_templates; mod weak_cryptographic_key; diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/use_of_mako_templates.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/use_of_mako_templates.rs new file mode 100644 index 00000000000000..3cf194a826873b --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/use_of_mako_templates.rs @@ -0,0 +1,32 @@ +use crate::checkers::ast::Checker; +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast}; +use ruff_text_size::Ranged; + +#[violation] +pub struct UseOfMakoTemplates; + +impl Violation for UseOfMakoTemplates { + #[derive_message_formats] + fn message(&self) -> String { + format!( + "Mako templates allow HTML/JS rendering by default and are inherently open to XSS attacks.\ + Ensure variables in all templates are properly sanitized via the 'n', 'h' or 'x' flags (depending on context).\ + For example, to HTML escape the variable 'data' do ${{ data |h }}." + ) + } +} + +/// S702 +pub(crate) fn use_of_mako_templates(checker: &mut Checker, call: &ast::ExprCall) { + if checker + .semantic() + .resolve_call_path(&call.func) + .is_some_and(|call_path| matches!(call_path.as_slice(), ["mako", "template", "Template"])) + { + checker + .diagnostics + .push(Diagnostic::new(UseOfMakoTemplates, call.func.range())); + } +} diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S702_S702.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S702_S702.py.snap new file mode 100644 index 00000000000000..8b504138b4cecc --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S702_S702.py.snap @@ -0,0 +1,31 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S702.py:6:1: S702 Mako templates allow HTML/JS rendering by default and are inherently open to XSS attacks.Ensure variables in all templates are properly sanitized via the 'n', 'h' or 'x' flags (depending on context).For example, to HTML escape the variable 'data' do ${ data |h }. + | +4 | from mako import template +5 | +6 | Template("hello") + | ^^^^^^^^ S702 +7 | +8 | # XXX(fletcher): for some reason, bandit is missing the one below. keeping it + | + +S702.py:10:1: S702 Mako templates allow HTML/JS rendering by default and are inherently open to XSS attacks.Ensure variables in all templates are properly sanitized via the 'n', 'h' or 'x' flags (depending on context).For example, to HTML escape the variable 'data' do ${ data |h }. + | + 8 | # XXX(fletcher): for some reason, bandit is missing the one below. keeping it + 9 | # in for now so that if it gets fixed inadvertitently we know. +10 | mako.template.Template("hern") + | ^^^^^^^^^^^^^^^^^^^^^^ S702 +11 | template.Template("hern") + | + +S702.py:11:1: S702 Mako templates allow HTML/JS rendering by default and are inherently open to XSS attacks.Ensure variables in all templates are properly sanitized via the 'n', 'h' or 'x' flags (depending on context).For example, to HTML escape the variable 'data' do ${ data |h }. + | + 9 | # in for now so that if it gets fixed inadvertitently we know. +10 | mako.template.Template("hern") +11 | template.Template("hern") + | ^^^^^^^^^^^^^^^^^ S702 + | + + diff --git a/ruff.schema.json b/ruff.schema.json index c77e480c468605..30f4b342889def 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3386,6 +3386,7 @@ "S7", "S70", "S701", + "S702", "SIM", "SIM1", "SIM10",