diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S403.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S403.py index 28459628021787..2aec54eb8c9367 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S403.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S403.py @@ -1,2 +1,8 @@ import dill # S403 from dill import objects # S403 +import shelve +from shelve import open +import cPickle +from cPickle import load +import pickle +from pickle import load diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S410.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S410.py index 07182c1a7c05a7..5ff308d53d35a1 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S410.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S410.py @@ -1,2 +1,2 @@ import lxml # S410 -from lxml import etree # S410 +from lxml import etree # S410 diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S412.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S412.py index 036f9930c4fe2e..5728162557ce72 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S412.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S412.py @@ -1,3 +1,3 @@ -import wsgiref.handlers # S413 -from twisted.internet import reactor # S413 -from twisted.web import static, server, twcgi # S413 +import wsgiref.handlers # S412 +from twisted.internet import reactor # S412 +from twisted.web import static, server, twcgi # S412 diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S415.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S415.py new file mode 100644 index 00000000000000..4c9a5c267e2aa8 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S415.py @@ -0,0 +1,3 @@ +import pyghmi # S415 +from pyghmi import foo # S415 + diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 146d366a2b734e..8775f885cd2505 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -557,8 +557,16 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::DeprecatedMockImport) { pyupgrade::rules::deprecated_mock_import(checker, stmt); } - if checker.enabled(Rule::SuspiciousTelnetlibImport) { - flake8_bandit::rules::suspicious_imports(checker, stmt) + if checker.any_enabled(&[ + Rule::SuspiciousTelnetlibImport, + Rule::SuspiciousFtplibImport, + Rule::SuspiciousPickleImport, + Rule::SuspiciousSubprocessImport, + Rule::SuspiciousLxmlImport, + Rule::SuspiciousXmlrpclibImport, + Rule::SuspiciousPyghmiImport, + ]) { + flake8_bandit::rules::suspicious_imports(checker, stmt); } for alias in names { @@ -759,8 +767,16 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pyupgrade::rules::unnecessary_builtin_import(checker, stmt, module, names); } } - if checker.enabled(Rule::SuspiciousTelnetlibImport) { - flake8_bandit::rules::suspicious_imports(checker, stmt) + if checker.any_enabled(&[ + Rule::SuspiciousTelnetlibImport, + Rule::SuspiciousFtplibImport, + Rule::SuspiciousPickleImport, + Rule::SuspiciousSubprocessImport, + Rule::SuspiciousLxmlImport, + Rule::SuspiciousXmlrpclibImport, + Rule::SuspiciousPyghmiImport, + ]) { + flake8_bandit::rules::suspicious_imports(checker, stmt); } if checker.enabled(Rule::BannedApi) { if let Some(module) = diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index 3a00b21e02f4f1..ba434a441ccc68 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -46,6 +46,12 @@ mod tests { #[test_case(Rule::SuspiciousURLOpenUsage, Path::new("S310.py"))] #[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"))] #[test_case(Rule::SuspiciousTelnetlibImport, Path::new("S401.py"))] + #[test_case(Rule::SuspiciousFtplibImport, Path::new("S402.py"))] + #[test_case(Rule::SuspiciousPickleImport, Path::new("S403.py"))] + #[test_case(Rule::SuspiciousSubprocessImport, Path::new("S404.py"))] + #[test_case(Rule::SuspiciousLxmlImport, Path::new("S410.py"))] + #[test_case(Rule::SuspiciousXmlrpclibImport, Path::new("S411.py"))] + #[test_case(Rule::SuspiciousPyghmiImport, Path::new("S415.py"))] #[test_case(Rule::TryExceptContinue, Path::new("S112.py"))] #[test_case(Rule::TryExceptPass, Path::new("S110.py"))] #[test_case(Rule::UnixCommandWildcardInjection, Path::new("S609.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs index 0c908d5f65dcba..aea67f7b956bb9 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs @@ -3,11 +3,10 @@ //! See: use ruff_diagnostics::{Diagnostic, DiagnosticKind, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::{self as ast, Expr, ExprCall, Stmt}; -use ruff_text_size::Ranged; +use ruff_python_ast::{self as ast, Stmt}; +use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; -use crate::codes::Rule::SuspiciousFTPLibUsage; use crate::registry::AsRule; // TODO: Docs @@ -157,25 +156,100 @@ impl Violation for SuspiciousPyghmiImport { /// S401, S402, S403, S404, S405, S406, S407, S408, S409, S410, S411, S412, S413 pub(crate) fn suspicious_imports(checker: &mut Checker, stmt: &Stmt) { match stmt { - Stmt::Import(ast::StmtImport { names, ..}) => { + Stmt::Import(ast::StmtImport { names, .. }) => { for name in names { match name.name.as_str() { - "telnetlib" => { - checker.diagnostics.push(Diagnostic::new(SuspiciousTelnetlibImport, name.range)) - }, + "telnetlib" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousTelnetlibImport), + name.range, + ), + "ftplib" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousFtplibImport), + name.range, + ), + "pickle" | "cPickle" | "dill" | "shelve" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousPickleImport), + name.range, + ), + "subprocess" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousSubprocessImport), + name.range, + ), + "lxml" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousLxmlImport), + name.range, + ), + "xmlrpc" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousXmlrpclibImport), + name.range, + ), + "pyghmi" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousPyghmiImport), + name.range, + ), _ => {} } } - }, + } Stmt::ImportFrom(ast::StmtImportFrom { module, .. }) => { let Some(identifier) = module else { return }; match identifier.as_str() { - "telnetlib" => { - checker.diagnostics.push(Diagnostic::new(SuspiciousTelnetlibImport, identifier.range())) - }, + "telnetlib" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousTelnetlibImport), + identifier.range(), + ), + "ftplib" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousFtplibImport), + identifier.range(), + ), + "pickle" | "cPickle" | "dill" | "shelve" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousPickleImport), + identifier.range(), + ), + "subprocess" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousSubprocessImport), + identifier.range(), + ), + "lxml" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousLxmlImport), + identifier.range(), + ), + "xmlrpc" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousXmlrpclibImport), + identifier.range(), + ), + "pyghmi" => check_and_push_diagnostic( + checker, + DiagnosticKind::from(SuspiciousPyghmiImport), + identifier.range(), + ), _ => {} } - }, + } _ => panic!("Expected Stmt::Import | Stmt::ImportFrom"), }; -} \ No newline at end of file +} + +fn check_and_push_diagnostic( + checker: &mut Checker, + diagnostic_kind: DiagnosticKind, + range: TextRange, +) { + let diagnostic = Diagnostic::new::(diagnostic_kind, range); + if checker.enabled(diagnostic.kind.rule()) { + checker.diagnostics.push(diagnostic); + } +} diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S401_S401.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S401_S401.py.snap new file mode 100644 index 00000000000000..e886364f594b22 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S401_S401.py.snap @@ -0,0 +1,18 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S401.py:1:8: S401 `telnetlib` and related modules are considered insecure. Use SSH or some other encrypted protocol + | +1 | import telnetlib # S401 + | ^^^^^^^^^ S401 +2 | from telnetlib import Telnet # S401 + | + +S401.py:2:6: S401 `telnetlib` and related modules are considered insecure. Use SSH or some other encrypted protocol + | +1 | import telnetlib # S401 +2 | from telnetlib import Telnet # S401 + | ^^^^^^^^^ S401 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S402_S402.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S402_S402.py.snap new file mode 100644 index 00000000000000..a61b13c07a886e --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S402_S402.py.snap @@ -0,0 +1,18 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S402.py:1:8: S402 `ftplib` and related modules are considered insecure. Use SSH/SFTP/SCP or some other encrypted protocol + | +1 | import ftplib # S402 + | ^^^^^^ S402 +2 | from ftplib import FTP # S402 + | + +S402.py:2:6: S402 `ftplib` and related modules are considered insecure. Use SSH/SFTP/SCP or some other encrypted protocol + | +1 | import ftplib # S402 +2 | from ftplib import FTP # S402 + | ^^^^^^ S402 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S403_S403.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S403_S403.py.snap new file mode 100644 index 00000000000000..2933b0436e96fe --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S403_S403.py.snap @@ -0,0 +1,78 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S403.py:1:8: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +1 | import dill # S403 + | ^^^^ S403 +2 | from dill import objects # S403 +3 | import shelve + | + +S403.py:2:6: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +1 | import dill # S403 +2 | from dill import objects # S403 + | ^^^^ S403 +3 | import shelve +4 | from shelve import open + | + +S403.py:3:8: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +1 | import dill # S403 +2 | from dill import objects # S403 +3 | import shelve + | ^^^^^^ S403 +4 | from shelve import open +5 | import cPickle + | + +S403.py:4:6: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +2 | from dill import objects # S403 +3 | import shelve +4 | from shelve import open + | ^^^^^^ S403 +5 | import cPickle +6 | from cPickle import load + | + +S403.py:5:8: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +3 | import shelve +4 | from shelve import open +5 | import cPickle + | ^^^^^^^ S403 +6 | from cPickle import load +7 | import pickle + | + +S403.py:6:6: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +4 | from shelve import open +5 | import cPickle +6 | from cPickle import load + | ^^^^^^^ S403 +7 | import pickle +8 | from pickle import load + | + +S403.py:7:8: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +5 | import cPickle +6 | from cPickle import load +7 | import pickle + | ^^^^^^ S403 +8 | from pickle import load + | + +S403.py:8:6: S403 `pickle`, `cPickle`, `dill` and `shelve` modules are possibly insecure + | +6 | from cPickle import load +7 | import pickle +8 | from pickle import load + | ^^^^^^ S403 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S404_S404.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S404_S404.py.snap new file mode 100644 index 00000000000000..f67e404f427e48 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S404_S404.py.snap @@ -0,0 +1,28 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S404.py:1:8: S404 `subprocess` module is possibly insecure + | +1 | import subprocess # S404 + | ^^^^^^^^^^ S404 +2 | from subprocess import Popen # S404 +3 | from subprocess import Popen as pop # S404 + | + +S404.py:2:6: S404 `subprocess` module is possibly insecure + | +1 | import subprocess # S404 +2 | from subprocess import Popen # S404 + | ^^^^^^^^^^ S404 +3 | from subprocess import Popen as pop # S404 + | + +S404.py:3:6: S404 `subprocess` module is possibly insecure + | +1 | import subprocess # S404 +2 | from subprocess import Popen # S404 +3 | from subprocess import Popen as pop # S404 + | ^^^^^^^^^^ S404 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S410_S410.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S410_S410.py.snap new file mode 100644 index 00000000000000..56304d26707e08 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S410_S410.py.snap @@ -0,0 +1,18 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S410.py:1:8: S410 `lxml` is vulnerable to XML attacks + | +1 | import lxml # S410 + | ^^^^ S410 +2 | from lxml import etree # S410 + | + +S410.py:2:6: S410 `lxml` is vulnerable to XML attacks + | +1 | import lxml # S410 +2 | from lxml import etree # S410 + | ^^^^ S410 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S411_S411.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S411_S411.py.snap new file mode 100644 index 00000000000000..09948c33ab82d6 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S411_S411.py.snap @@ -0,0 +1,18 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S411.py:1:8: S411 XMLRPC is particularly dangerous as it is also concerned with communicating data over a network + | +1 | import xmlrpc # S411 + | ^^^^^^ S411 +2 | from xmlrpc import server # S411 + | + +S411.py:2:6: S411 XMLRPC is particularly dangerous as it is also concerned with communicating data over a network + | +1 | import xmlrpc # S411 +2 | from xmlrpc import server # S411 + | ^^^^^^ S411 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S415_S415.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S415_S415.py.snap new file mode 100644 index 00000000000000..af87b7c06dbfac --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S415_S415.py.snap @@ -0,0 +1,18 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S415.py:1:8: S415 An IPMI-related module is being imported. IPMI is considered insecure. Use an encrypted protocol + | +1 | import pyghmi # S415 + | ^^^^^^ S415 +2 | from pyghmi import foo # S415 + | + +S415.py:2:6: S415 An IPMI-related module is being imported. IPMI is considered insecure. Use an encrypted protocol + | +1 | import pyghmi # S415 +2 | from pyghmi import foo # S415 + | ^^^^^^ S415 + | + +