Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flake8-bandit] Add Rule for S506 (unsafe use of yaml load) #1664

Merged
merged 3 commits into from Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Expand Up @@ -767,11 +767,12 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on
| S102 | ExecUsed | Use of `exec` detected | |
| S103 | BadFilePermissions | `os.chmod` setting a permissive mask `0o777` on file or directory | |
| S104 | HardcodedBindAllInterfaces | Possible binding to all interfaces | |
| S105 | HardcodedPasswordString | Possible hardcoded password: `"..."` | |
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: `"..."` | |
| S107 | HardcodedPasswordDefault | Possible hardcoded password: `"..."` | |
| S108 | HardcodedTempFile | Probable insecure usage of temp file/directory: `"..."` | |
| S324 | HashlibInsecureHashFunction | Probable use of insecure hash functions in hashlib: `"..."` | |
| S105 | HardcodedPasswordString | Possible hardcoded password: "..." | |
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: "..." | |
| S107 | HardcodedPasswordDefault | Possible hardcoded password: "..." | |
| S108 | HardcodedTempFile | Probable insecure usage of temporary file or directory: "..." | |
| S324 | HashlibInsecureHashFunction | Probable use of insecure hash functions in `hashlib`: "..." | |
| S506 | UnsafeYAMLLoad | Probable use of unsafe `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`. | |

### flake8-blind-except (BLE)

Expand Down
31 changes: 31 additions & 0 deletions resources/test/fixtures/flake8_bandit/S506.py
@@ -0,0 +1,31 @@
import json
import yaml
from yaml import CSafeLoader
from yaml import SafeLoader
from yaml import SafeLoader as NewSafeLoader


def test_yaml_load():
ystr = yaml.dump({"a": 1, "b": 2, "c": 3})
y = yaml.load(ystr)
yaml.dump(y)
try:
y = yaml.load(ystr, Loader=yaml.CSafeLoader)
except AttributeError:
# CSafeLoader only exists if you build yaml with LibYAML
y = yaml.load(ystr, Loader=yaml.SafeLoader)


def test_json_load():
# no issue should be found
j = json.load("{}")


yaml.load("{}", Loader=yaml.Loader)

# no issue should be found
yaml.load("{}", SafeLoader)
yaml.load("{}", yaml.SafeLoader)
yaml.load("{}", CSafeLoader)
yaml.load("{}", yaml.CSafeLoader)
yaml.load("{}", NewSafeLoader)
3 changes: 3 additions & 0 deletions ruff.schema.json
Expand Up @@ -930,6 +930,9 @@
"S3",
"S32",
"S324",
"S5",
"S50",
"S506",
"SIM",
"SIM1",
"SIM10",
Expand Down
11 changes: 11 additions & 0 deletions src/checkers/ast.rs
Expand Up @@ -1896,6 +1896,17 @@ where
self.add_check(check);
}
}
if self.settings.enabled.contains(&CheckCode::S506) {
if let Some(check) = flake8_bandit::checks::unsafe_yaml_load(
func,
args,
keywords,
&self.from_imports,
&self.import_aliases,
) {
self.add_check(check);
}
}
if self.settings.enabled.contains(&CheckCode::S106) {
self.add_checks(
flake8_bandit::checks::hardcoded_password_func_arg(keywords).into_iter(),
Expand Down
2 changes: 2 additions & 0 deletions src/flake8_bandit/checks/mod.rs
Expand Up @@ -9,6 +9,7 @@ pub use hardcoded_password_string::{
};
pub use hardcoded_tmp_directory::hardcoded_tmp_directory;
pub use hashlib_insecure_hash_functions::hashlib_insecure_hash_functions;
pub use unsafe_yaml_load::unsafe_yaml_load;

mod assert_used;
mod bad_file_permissions;
Expand All @@ -19,3 +20,4 @@ mod hardcoded_password_func_arg;
mod hardcoded_password_string;
mod hardcoded_tmp_directory;
mod hashlib_insecure_hash_functions;
mod unsafe_yaml_load;
50 changes: 50 additions & 0 deletions src/flake8_bandit/checks/unsafe_yaml_load.rs
@@ -0,0 +1,50 @@
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Expr, ExprKind, Keyword};

use crate::ast::helpers::{match_module_member, SimpleCallArgs};
use crate::ast::types::Range;
use crate::registry::{Check, CheckKind};

/// S506
pub fn unsafe_yaml_load(
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> Option<Check> {
if match_module_member(func, "yaml", "load", from_imports, import_aliases) {
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(loader_arg) = call_args.get_argument("Loader", Some(1)) {
if !match_module_member(
loader_arg,
"yaml",
"SafeLoader",
from_imports,
import_aliases,
) && !match_module_member(
loader_arg,
"yaml",
"CSafeLoader",
from_imports,
import_aliases,
) {
let loader = match &loader_arg.node {
ExprKind::Attribute { attr, .. } => Some(attr.to_string()),
ExprKind::Name { id, .. } => Some(id.to_string()),
_ => None,
};
return Some(Check::new(
CheckKind::UnsafeYAMLLoad(loader),
Range::from_located(loader_arg),
));
}
} else {
return Some(Check::new(
CheckKind::UnsafeYAMLLoad(None),
Range::from_located(func),
));
}
}
None
}
1 change: 1 addition & 0 deletions src/flake8_bandit/mod.rs
Expand Up @@ -22,6 +22,7 @@ mod tests {
#[test_case(CheckCode::S107, Path::new("S107.py"); "S107")]
#[test_case(CheckCode::S108, Path::new("S108.py"); "S108")]
#[test_case(CheckCode::S324, Path::new("S324.py"); "S324")]
#[test_case(CheckCode::S506, Path::new("S506.py"); "S506")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let checks = test_path(
Expand Down
@@ -0,0 +1,25 @@
---
source: src/flake8_bandit/mod.rs
expression: checks
---
- kind:
UnsafeYAMLLoad: ~
location:
row: 10
column: 8
end_location:
row: 10
column: 17
fix: ~
parent: ~
- kind:
UnsafeYAMLLoad: Loader
location:
row: 24
column: 23
end_location:
row: 24
column: 34
fix: ~
parent: ~

25 changes: 19 additions & 6 deletions src/registry.rs
Expand Up @@ -335,6 +335,7 @@ pub enum CheckCode {
S107,
S108,
S324,
S506,
// flake8-boolean-trap
FBT001,
FBT002,
Expand Down Expand Up @@ -1075,6 +1076,7 @@ pub enum CheckKind {
HardcodedPasswordDefault(String),
HardcodedTempFile(String),
HashlibInsecureHashFunction(String),
UnsafeYAMLLoad(Option<String>),
// mccabe
FunctionIsTooComplex(String, usize),
// flake8-boolean-trap
Expand Down Expand Up @@ -1538,6 +1540,7 @@ impl CheckCode {
CheckCode::S107 => CheckKind::HardcodedPasswordDefault("...".to_string()),
CheckCode::S108 => CheckKind::HardcodedTempFile("...".to_string()),
CheckCode::S324 => CheckKind::HashlibInsecureHashFunction("...".to_string()),
CheckCode::S506 => CheckKind::UnsafeYAMLLoad(None),
// mccabe
CheckCode::C901 => CheckKind::FunctionIsTooComplex("...".to_string(), 10),
// flake8-boolean-trap
Expand Down Expand Up @@ -1941,6 +1944,7 @@ impl CheckCode {
CheckCode::S107 => CheckCategory::Flake8Bandit,
CheckCode::S108 => CheckCategory::Flake8Bandit,
CheckCode::S324 => CheckCategory::Flake8Bandit,
CheckCode::S506 => CheckCategory::Flake8Bandit,
// flake8-simplify
CheckCode::SIM102 => CheckCategory::Flake8Simplify,
CheckCode::SIM105 => CheckCategory::Flake8Simplify,
Expand Down Expand Up @@ -2317,6 +2321,7 @@ impl CheckKind {
CheckKind::HardcodedPasswordDefault(..) => &CheckCode::S107,
CheckKind::HardcodedTempFile(..) => &CheckCode::S108,
CheckKind::HashlibInsecureHashFunction(..) => &CheckCode::S324,
CheckKind::UnsafeYAMLLoad(..) => &CheckCode::S506,
// mccabe
CheckKind::FunctionIsTooComplex(..) => &CheckCode::C901,
// flake8-boolean-trap
Expand Down Expand Up @@ -3260,23 +3265,31 @@ impl CheckKind {
CheckKind::HardcodedPasswordString(string)
| CheckKind::HardcodedPasswordFuncArg(string)
| CheckKind::HardcodedPasswordDefault(string) => {
format!(
"Possible hardcoded password: `\"{}\"`",
string.escape_debug()
)
format!("Possible hardcoded password: \"{}\"", string.escape_debug())
}
CheckKind::HardcodedTempFile(string) => {
format!(
"Probable insecure usage of temp file/directory: `\"{}\"`",
"Probable insecure usage of temporary file or directory: \"{}\"",
string.escape_debug()
)
}
CheckKind::HashlibInsecureHashFunction(string) => {
format!(
"Probable use of insecure hash functions in hashlib: `\"{}\"`",
"Probable use of insecure hash functions in `hashlib`: \"{}\"",
string.escape_debug()
)
}
CheckKind::UnsafeYAMLLoad(loader) => match loader {
Some(name) => {
format!(
"Probable use of unsafe loader `{name}` with `yaml.load`. Allows \
instantiation of arbitrary objects. Consider `yaml.safe_load`."
)
}
None => "Probable use of unsafe `yaml.load`. Allows instantiation of arbitrary \
objects. Consider `yaml.safe_load`."
.to_string(),
},
// flake8-blind-except
CheckKind::BlindExcept(name) => format!("Do not catch blind exception: `{name}`"),
// mccabe
Expand Down