From 72d848dd9cf8053e1b03321530fb6811f77d9b16 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sat, 13 Jul 2024 19:06:09 -0400 Subject: [PATCH] Support explicit string concatenations in S310 HTTP detection --- .../test/fixtures/flake8_bandit/S310.py | 5 + .../rules/suspicious_function_call.rs | 102 +++---- ...s__flake8_bandit__tests__S310_S310.py.snap | 260 ++++++++++-------- crates/ruff_python_ast/src/nodes.rs | 2 +- 4 files changed, 197 insertions(+), 172 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S310.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S310.py index 734ee185a7e73..7467f79cc65d5 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S310.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S310.py @@ -2,6 +2,7 @@ urllib.request.urlopen(url='http://www.google.com') urllib.request.urlopen(url=f'http://www.google.com') +urllib.request.urlopen(url='http://' + 'www' + '.google.com') urllib.request.urlopen(url='http://www.google.com', **kwargs) urllib.request.urlopen(url=f'http://www.google.com', **kwargs) urllib.request.urlopen('http://www.google.com') @@ -11,6 +12,7 @@ urllib.request.Request(url='http://www.google.com') urllib.request.Request(url=f'http://www.google.com') +urllib.request.Request(url='http://' + 'www' + '.google.com') urllib.request.Request(url='http://www.google.com', **kwargs) urllib.request.Request(url=f'http://www.google.com', **kwargs) urllib.request.Request('http://www.google.com') @@ -20,15 +22,18 @@ urllib.request.URLopener().open(fullurl='http://www.google.com') urllib.request.URLopener().open(fullurl=f'http://www.google.com') +urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) urllib.request.URLopener().open('http://www.google.com') urllib.request.URLopener().open(f'http://www.google.com') +urllib.request.URLopener().open('http://' + 'www' + '.google.com') urllib.request.URLopener().open('file:///foo/bar/baz') urllib.request.URLopener().open(url) urllib.request.urlopen(url=urllib.request.Request('http://www.google.com')) urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com')) +urllib.request.urlopen(url=urllib.request.Request('http://' + 'www' + '.google.com')) urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs) urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs) urllib.request.urlopen(urllib.request.Request('http://www.google.com')) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs index 34babdbe717a4..aeccb74510d97 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs @@ -1,9 +1,10 @@ //! Check for calls to suspicious functions, or calls into suspicious modules. //! //! See: +use itertools::Either; use ruff_diagnostics::{Diagnostic, DiagnosticKind, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::{self as ast, Decorator, Expr, ExprCall}; +use ruff_python_ast::{self as ast, Decorator, Expr, ExprCall, Operator}; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -838,6 +839,43 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) { true } + /// Returns `true` if the iterator starts with an HTTP or HTTPS prefix. + fn has_http_prefix(chars: impl Iterator + Clone) -> bool { + has_prefix(chars.clone().skip_while(|c| c.is_whitespace()), "http://") + || has_prefix(chars.skip_while(|c| c.is_whitespace()), "https://") + } + + /// Return the leading characters for an expression, if it's a string literal, f-string, or + /// string concatenation. + fn leading_chars(expr: &Expr) -> Option + Clone + '_> { + match expr { + // Ex) `"foo"` + Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => { + Some(Either::Left(value.chars())) + } + // Ex) f"foo" + Expr::FString(ast::ExprFString { value, .. }) => { + value.elements().next().and_then(|element| { + if let ast::FStringElement::Literal(ast::FStringLiteralElement { + value, .. + }) = element + { + Some(Either::Right(value.chars())) + } else { + None + } + }) + } + // Ex) "foo" + "bar" + Expr::BinOp(ast::ExprBinOp { + op: Operator::Add, + left, + .. + }) => leading_chars(left), + _ => None, + } + } + let Some(diagnostic_kind) = checker.semantic().resolve_qualified_name(call.func.as_ref()).and_then(|qualified_name| { match qualified_name.segments() { // Pickle @@ -864,25 +902,11 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) { ["django", "utils", "safestring" | "html", "mark_safe"] => Some(SuspiciousMarkSafeUsage.into()), // URLOpen (`Request`) ["urllib", "request", "Request"] | - ["six", "moves", "urllib", "request","Request"] => { - // If the `url` argument is a string literal or an f string, allow `http` and `https` schemes. + ["six", "moves", "urllib", "request", "Request"] => { + // If the `url` argument is a string literal or an f-string, allow `http` and `https` schemes. if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) { - match call.arguments.find_argument("url", 0) { - // If the `url` argument is a string literal, allow `http` and `https` schemes. - Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { - return None; - } - }, - // If the `url` argument is an f-string literal, allow `http` and `https` schemes. - Some(Expr::FString(ast::ExprFString { value, .. })) => { - if let Some(ast::FStringElement::Literal(ast::FStringLiteralElement { value, .. })) = value.elements().next() { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { - return None; - } - } - }, - _ => {} + if call.arguments.find_argument("url", 0).and_then(leading_chars).is_some_and(has_http_prefix) { + return None; } } Some(SuspiciousURLOpenUsage.into()) @@ -892,43 +916,19 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) { ["six", "moves", "urllib", "request", "urlopen" | "urlretrieve" ] => { if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) { match call.arguments.find_argument("url", 0) { - // If the `url` argument is a string literal, allow `http` and `https` schemes. - Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { - return None; - } - }, - - // If the `url` argument is an f-string literal, allow `http` and `https` schemes. - Some(Expr::FString(ast::ExprFString { value, .. })) => { - if let Some(ast::FStringElement::Literal(ast::FStringLiteralElement { value, .. })) = value.elements().next() { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { + // If the `url` argument is a `urllib.request.Request` object, allow `http` and `https` schemes. + Some(Expr::Call(ExprCall { func, arguments, .. })) => { + if checker.semantic().resolve_qualified_name(func.as_ref()).is_some_and(|name| name.segments() == ["urllib", "request", "Request"]) { + if arguments.find_argument("url", 0).and_then(leading_chars).is_some_and(has_http_prefix) { return None; } } }, - // If the `url` argument is a `urllib.request.Request` object, allow `http` and `https` schemes. - Some(Expr::Call(ExprCall { func, arguments, .. })) => { - if checker.semantic().resolve_qualified_name(func.as_ref()).is_some_and(|name| name.segments() == ["urllib", "request", "Request"]) { - match arguments.find_argument("url", 0) { - // If the `url` argument is a string literal, allow `http` and `https` schemes. - Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { - return None; - } - }, - - // If the `url` argument is an f-string literal, allow `http` and `https` schemes. - Some(Expr::FString(ast::ExprFString { value, .. })) => { - if let Some(ast::FStringElement::Literal(ast::FStringLiteralElement { value, .. })) = value.elements().next() { - if has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "http://") || has_prefix(value.chars().skip_while(|c| c.is_whitespace()), "https://") { - return None; - } - } - }, - _ => {} - } + // If the `url` argument is a string literal, allow `http` and `https` schemes. + Some(expr) => { + if leading_chars(expr).is_some_and(has_http_prefix) { + return None; } }, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S310_S310.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S310_S310.py.snap index 7cb003c7ba9e5..26612b698f752 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S310_S310.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S310_S310.py.snap @@ -1,212 +1,232 @@ --- source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs --- -S310.py:5:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:6:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -3 | urllib.request.urlopen(url='http://www.google.com') 4 | urllib.request.urlopen(url=f'http://www.google.com') -5 | urllib.request.urlopen(url='http://www.google.com', **kwargs) +5 | urllib.request.urlopen(url='http://' + 'www' + '.google.com') +6 | urllib.request.urlopen(url='http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -6 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs) -7 | urllib.request.urlopen('http://www.google.com') +7 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs) +8 | urllib.request.urlopen('http://www.google.com') | -S310.py:6:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:7:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -4 | urllib.request.urlopen(url=f'http://www.google.com') -5 | urllib.request.urlopen(url='http://www.google.com', **kwargs) -6 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs) +5 | urllib.request.urlopen(url='http://' + 'www' + '.google.com') +6 | urllib.request.urlopen(url='http://www.google.com', **kwargs) +7 | urllib.request.urlopen(url=f'http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -7 | urllib.request.urlopen('http://www.google.com') -8 | urllib.request.urlopen(f'http://www.google.com') +8 | urllib.request.urlopen('http://www.google.com') +9 | urllib.request.urlopen(f'http://www.google.com') | -S310.py:9:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:10:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | - 7 | urllib.request.urlopen('http://www.google.com') - 8 | urllib.request.urlopen(f'http://www.google.com') - 9 | urllib.request.urlopen('file:///foo/bar/baz') + 8 | urllib.request.urlopen('http://www.google.com') + 9 | urllib.request.urlopen(f'http://www.google.com') +10 | urllib.request.urlopen('file:///foo/bar/baz') | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -10 | urllib.request.urlopen(url) +11 | urllib.request.urlopen(url) | -S310.py:10:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:11:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | - 8 | urllib.request.urlopen(f'http://www.google.com') - 9 | urllib.request.urlopen('file:///foo/bar/baz') -10 | urllib.request.urlopen(url) + 9 | urllib.request.urlopen(f'http://www.google.com') +10 | urllib.request.urlopen('file:///foo/bar/baz') +11 | urllib.request.urlopen(url) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -11 | -12 | urllib.request.Request(url='http://www.google.com') +12 | +13 | urllib.request.Request(url='http://www.google.com') | -S310.py:14:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:16:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -12 | urllib.request.Request(url='http://www.google.com') -13 | urllib.request.Request(url=f'http://www.google.com') -14 | urllib.request.Request(url='http://www.google.com', **kwargs) +14 | urllib.request.Request(url=f'http://www.google.com') +15 | urllib.request.Request(url='http://' + 'www' + '.google.com') +16 | urllib.request.Request(url='http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -15 | urllib.request.Request(url=f'http://www.google.com', **kwargs) -16 | urllib.request.Request('http://www.google.com') +17 | urllib.request.Request(url=f'http://www.google.com', **kwargs) +18 | urllib.request.Request('http://www.google.com') | -S310.py:15:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:17:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -13 | urllib.request.Request(url=f'http://www.google.com') -14 | urllib.request.Request(url='http://www.google.com', **kwargs) -15 | urllib.request.Request(url=f'http://www.google.com', **kwargs) +15 | urllib.request.Request(url='http://' + 'www' + '.google.com') +16 | urllib.request.Request(url='http://www.google.com', **kwargs) +17 | urllib.request.Request(url=f'http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -16 | urllib.request.Request('http://www.google.com') -17 | urllib.request.Request(f'http://www.google.com') +18 | urllib.request.Request('http://www.google.com') +19 | urllib.request.Request(f'http://www.google.com') | -S310.py:18:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:20:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -16 | urllib.request.Request('http://www.google.com') -17 | urllib.request.Request(f'http://www.google.com') -18 | urllib.request.Request('file:///foo/bar/baz') +18 | urllib.request.Request('http://www.google.com') +19 | urllib.request.Request(f'http://www.google.com') +20 | urllib.request.Request('file:///foo/bar/baz') | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -19 | urllib.request.Request(url) +21 | urllib.request.Request(url) | -S310.py:19:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:21:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -17 | urllib.request.Request(f'http://www.google.com') -18 | urllib.request.Request('file:///foo/bar/baz') -19 | urllib.request.Request(url) +19 | urllib.request.Request(f'http://www.google.com') +20 | urllib.request.Request('file:///foo/bar/baz') +21 | urllib.request.Request(url) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -20 | -21 | urllib.request.URLopener().open(fullurl='http://www.google.com') +22 | +23 | urllib.request.URLopener().open(fullurl='http://www.google.com') | -S310.py:21:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:23:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -19 | urllib.request.Request(url) -20 | -21 | urllib.request.URLopener().open(fullurl='http://www.google.com') +21 | urllib.request.Request(url) +22 | +23 | urllib.request.URLopener().open(fullurl='http://www.google.com') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -22 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') -23 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) +24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') +25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') | -S310.py:22:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:24:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -21 | urllib.request.URLopener().open(fullurl='http://www.google.com') -22 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') +23 | urllib.request.URLopener().open(fullurl='http://www.google.com') +24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -23 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) -24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) +25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') +26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) | -S310.py:23:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:25:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -21 | urllib.request.URLopener().open(fullurl='http://www.google.com') -22 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') -23 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) +23 | urllib.request.URLopener().open(fullurl='http://www.google.com') +24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') +25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) -25 | urllib.request.URLopener().open('http://www.google.com') +26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) +27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) | -S310.py:24:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:26:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -22 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') -23 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) -24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) +24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com') +25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') +26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -25 | urllib.request.URLopener().open('http://www.google.com') -26 | urllib.request.URLopener().open(f'http://www.google.com') +27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) +28 | urllib.request.URLopener().open('http://www.google.com') | -S310.py:25:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:27:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -23 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) -24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) -25 | urllib.request.URLopener().open('http://www.google.com') +25 | urllib.request.URLopener().open(fullurl='http://' + 'www' + '.google.com') +26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) +27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -26 | urllib.request.URLopener().open(f'http://www.google.com') -27 | urllib.request.URLopener().open('file:///foo/bar/baz') +28 | urllib.request.URLopener().open('http://www.google.com') +29 | urllib.request.URLopener().open(f'http://www.google.com') | -S310.py:26:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:28:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -24 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) -25 | urllib.request.URLopener().open('http://www.google.com') -26 | urllib.request.URLopener().open(f'http://www.google.com') +26 | urllib.request.URLopener().open(fullurl='http://www.google.com', **kwargs) +27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) +28 | urllib.request.URLopener().open('http://www.google.com') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -27 | urllib.request.URLopener().open('file:///foo/bar/baz') -28 | urllib.request.URLopener().open(url) +29 | urllib.request.URLopener().open(f'http://www.google.com') +30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com') | -S310.py:27:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:29:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -25 | urllib.request.URLopener().open('http://www.google.com') -26 | urllib.request.URLopener().open(f'http://www.google.com') -27 | urllib.request.URLopener().open('file:///foo/bar/baz') +27 | urllib.request.URLopener().open(fullurl=f'http://www.google.com', **kwargs) +28 | urllib.request.URLopener().open('http://www.google.com') +29 | urllib.request.URLopener().open(f'http://www.google.com') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -28 | urllib.request.URLopener().open(url) +30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com') +31 | urllib.request.URLopener().open('file:///foo/bar/baz') | -S310.py:28:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:30:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. + | +28 | urllib.request.URLopener().open('http://www.google.com') +29 | urllib.request.URLopener().open(f'http://www.google.com') +30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com') + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 +31 | urllib.request.URLopener().open('file:///foo/bar/baz') +32 | urllib.request.URLopener().open(url) + | + +S310.py:31:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -26 | urllib.request.URLopener().open(f'http://www.google.com') -27 | urllib.request.URLopener().open('file:///foo/bar/baz') -28 | urllib.request.URLopener().open(url) +29 | urllib.request.URLopener().open(f'http://www.google.com') +30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com') +31 | urllib.request.URLopener().open('file:///foo/bar/baz') | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -29 | -30 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com')) +32 | urllib.request.URLopener().open(url) | S310.py:32:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -30 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com')) -31 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com')) -32 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs) +30 | urllib.request.URLopener().open('http://' + 'www' + '.google.com') +31 | urllib.request.URLopener().open('file:///foo/bar/baz') +32 | urllib.request.URLopener().open(url) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 +33 | +34 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com')) + | + +S310.py:37:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. + | +35 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com')) +36 | urllib.request.urlopen(url=urllib.request.Request('http://' + 'www' + '.google.com')) +37 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -33 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs) -34 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) +38 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs) +39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) | -S310.py:33:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:38:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -31 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com')) -32 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs) -33 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs) +36 | urllib.request.urlopen(url=urllib.request.Request('http://' + 'www' + '.google.com')) +37 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs) +38 | urllib.request.urlopen(url=urllib.request.Request(f'http://www.google.com'), **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -34 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) -35 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) +39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) +40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) | -S310.py:36:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:41:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -34 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) -35 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) -36 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) +39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) +40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) +41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -37 | urllib.request.urlopen(urllib.request.Request(url)) +42 | urllib.request.urlopen(urllib.request.Request(url)) | -S310.py:36:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:41:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -34 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) -35 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) -36 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) +39 | urllib.request.urlopen(urllib.request.Request('http://www.google.com')) +40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) +41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 -37 | urllib.request.urlopen(urllib.request.Request(url)) +42 | urllib.request.urlopen(urllib.request.Request(url)) | -S310.py:37:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:42:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -35 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) -36 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) -37 | urllib.request.urlopen(urllib.request.Request(url)) +40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) +41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) +42 | urllib.request.urlopen(urllib.request.Request(url)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 | -S310.py:37:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. +S310.py:42:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. | -35 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) -36 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) -37 | urllib.request.urlopen(urllib.request.Request(url)) +40 | urllib.request.urlopen(urllib.request.Request(f'http://www.google.com')) +41 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz')) +42 | urllib.request.urlopen(urllib.request.Request(url)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310 | diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index 5e6308d0867e3..8861218153b3a 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -1720,7 +1720,7 @@ impl StringLiteralValue { } /// Returns an iterator over the [`char`]s of each string literal part. - pub fn chars(&self) -> impl Iterator + '_ { + pub fn chars(&self) -> impl Iterator + Clone + '_ { self.iter().flat_map(|part| part.value.chars()) }