Replace stat mapping with match statement (#5548)

This commit is contained in:
Charlie Marsh
2023-07-05 19:42:21 -04:00
committed by GitHub
parent ba7041b6bf
commit bf02c77fd7

View File

@@ -1,12 +1,11 @@
use num_traits::ToPrimitive;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::{self, Constant, Expr, Keyword, Operator, Ranged};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::compose_call_path;
use ruff_python_ast::call_path::CallPath;
use ruff_python_ast::helpers::SimpleCallArgs;
use ruff_python_semantic::SemanticModel;
use crate::checkers::ast::Checker;
@@ -23,83 +22,6 @@ impl Violation for BadFilePermissions {
}
}
const WRITE_WORLD: u16 = 0o2;
const EXECUTE_GROUP: u16 = 0o10;
static PYSTAT_MAPPING: Lazy<FxHashMap<&'static str, u16>> = Lazy::new(|| {
FxHashMap::from_iter([
("stat.ST_MODE", 0o0),
("stat.S_IFDOOR", 0o0),
("stat.S_IFPORT", 0o0),
("stat.ST_INO", 0o1),
("stat.S_IXOTH", 0o1),
("stat.UF_NODUMP", 0o1),
("stat.ST_DEV", 0o2),
("stat.S_IWOTH", 0o2),
("stat.UF_IMMUTABLE", 0o2),
("stat.ST_NLINK", 0o3),
("stat.ST_UID", 0o4),
("stat.S_IROTH", 0o4),
("stat.UF_APPEND", 0o4),
("stat.ST_GID", 0o5),
("stat.ST_SIZE", 0o6),
("stat.ST_ATIME", 0o7),
("stat.S_IRWXO", 0o7),
("stat.ST_MTIME", 0o10),
("stat.S_IXGRP", 0o10),
("stat.UF_OPAQUE", 0o10),
("stat.ST_CTIME", 0o11),
("stat.S_IWGRP", 0o20),
("stat.UF_NOUNLINK", 0o20),
("stat.S_IRGRP", 0o40),
("stat.UF_COMPRESSED", 0o40),
("stat.S_IRWXG", 0o70),
("stat.S_IEXEC", 0o100),
("stat.S_IXUSR", 0o100),
("stat.S_IWRITE", 0o200),
("stat.S_IWUSR", 0o200),
("stat.S_IREAD", 0o400),
("stat.S_IRUSR", 0o400),
("stat.S_IRWXU", 0o700),
("stat.S_ISVTX", 0o1000),
("stat.S_ISGID", 0o2000),
("stat.S_ENFMT", 0o2000),
("stat.S_ISUID", 0o4000),
])
});
fn get_int_value(expr: &Expr) -> Option<u16> {
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Int(value),
..
}) => value.to_u16(),
Expr::Attribute(_) => {
compose_call_path(expr).and_then(|path| PYSTAT_MAPPING.get(path.as_str()).copied())
}
Expr::BinOp(ast::ExprBinOp {
left,
op,
right,
range: _,
}) => {
if let (Some(left_value), Some(right_value)) =
(get_int_value(left), get_int_value(right))
{
match op {
Operator::BitAnd => Some(left_value & right_value),
Operator::BitOr => Some(left_value | right_value),
Operator::BitXor => Some(left_value ^ right_value),
_ => None,
}
} else {
None
}
}
_ => None,
}
}
/// S103
pub(crate) fn bad_file_permissions(
checker: &mut Checker,
@@ -116,7 +38,7 @@ pub(crate) fn bad_file_permissions(
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(mode_arg) = call_args.argument("mode", 1) {
if let Some(int_value) = get_int_value(mode_arg) {
if let Some(int_value) = int_value(mode_arg, checker.semantic()) {
if (int_value & WRITE_WORLD > 0) || (int_value & EXECUTE_GROUP > 0) {
checker.diagnostics.push(Diagnostic::new(
BadFilePermissions { mask: int_value },
@@ -127,3 +49,75 @@ pub(crate) fn bad_file_permissions(
}
}
}
const WRITE_WORLD: u16 = 0o2;
const EXECUTE_GROUP: u16 = 0o10;
fn py_stat(call_path: &CallPath) -> Option<u16> {
match call_path.as_slice() {
["stat", "ST_MODE"] => Some(0o0),
["stat", "S_IFDOOR"] => Some(0o0),
["stat", "S_IFPORT"] => Some(0o0),
["stat", "ST_INO"] => Some(0o1),
["stat", "S_IXOTH"] => Some(0o1),
["stat", "UF_NODUMP"] => Some(0o1),
["stat", "ST_DEV"] => Some(0o2),
["stat", "S_IWOTH"] => Some(0o2),
["stat", "UF_IMMUTABLE"] => Some(0o2),
["stat", "ST_NLINK"] => Some(0o3),
["stat", "ST_UID"] => Some(0o4),
["stat", "S_IROTH"] => Some(0o4),
["stat", "UF_APPEND"] => Some(0o4),
["stat", "ST_GID"] => Some(0o5),
["stat", "ST_SIZE"] => Some(0o6),
["stat", "ST_ATIME"] => Some(0o7),
["stat", "S_IRWXO"] => Some(0o7),
["stat", "ST_MTIME"] => Some(0o10),
["stat", "S_IXGRP"] => Some(0o10),
["stat", "UF_OPAQUE"] => Some(0o10),
["stat", "ST_CTIME"] => Some(0o11),
["stat", "S_IWGRP"] => Some(0o20),
["stat", "UF_NOUNLINK"] => Some(0o20),
["stat", "S_IRGRP"] => Some(0o40),
["stat", "UF_COMPRESSED"] => Some(0o40),
["stat", "S_IRWXG"] => Some(0o70),
["stat", "S_IEXEC"] => Some(0o100),
["stat", "S_IXUSR"] => Some(0o100),
["stat", "S_IWRITE"] => Some(0o200),
["stat", "S_IWUSR"] => Some(0o200),
["stat", "S_IREAD"] => Some(0o400),
["stat", "S_IRUSR"] => Some(0o400),
["stat", "S_IRWXU"] => Some(0o700),
["stat", "S_ISVTX"] => Some(0o1000),
["stat", "S_ISGID"] => Some(0o2000),
["stat", "S_ENFMT"] => Some(0o2000),
["stat", "S_ISUID"] => Some(0o4000),
_ => None,
}
}
fn int_value(expr: &Expr, model: &SemanticModel) -> Option<u16> {
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Int(value),
..
}) => value.to_u16(),
Expr::Attribute(_) => model.resolve_call_path(expr).as_ref().and_then(py_stat),
Expr::BinOp(ast::ExprBinOp {
left,
op,
right,
range: _,
}) => {
let left_value = int_value(left, model)?;
let right_value = int_value(right, model)?;
match op {
Operator::BitAnd => Some(left_value & right_value),
Operator::BitOr => Some(left_value | right_value),
Operator::BitXor => Some(left_value ^ right_value),
_ => None,
}
}
_ => None,
}
}