Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/ruff/rules/non_octal_permissions.rs (Rust)

Code before:
    };

    let Expr::NumberLiteral(ast::ExprNumberLiteral {
        value: ast::Number::Int(int),
        ..
    }) = mode_arg
    else {
        return;
    };

    let mode_literal = &checker.locator().contents()[mode_arg.range()];

    if mode_literal.starts_with("0o") || mode_literal.starts_with("0O") || mode_literal == "0" {
        return;
    }

    let Some(mode) = int.as_u16() else {
        checker.report_diagnostic(NonOctalPermissions, mode_arg.range());
        return;
    };

    let mut diagnostic = checker.report_diagnostic(NonOctalPermissions, mode_arg.range());

    diagnostic.info(format!(
        "current value {mode_literal} will be interpreted as 0o{:o}, permissions: {}",
        mode & 0o777,
        get_permissions(mode)
    ));
    // Don't suggest a fix for 0x or 0b literals.
    if mode_literal.starts_with("0x") || mode_literal.starts_with("0b") {
        return;
    }

    if mode_literal.chars().all(|c| c == '0') {
        // Fix e.g. 000 as 0o000
        let edit = Edit::range_replacement(format!("0o{mode_literal}"), mode_arg.range());
        diagnostic.set_fix(Fix::safe_edit(edit));
        return;
    }

    let Some(suggested) = int.as_u16().and_then(suggest_fix) else {
        return;
    };
    let suggested_permissions = get_permissions(suggested);
    diagnostic.info(format!(
        "suggested value: 0o{suggested:o}, permissions: {suggested_permissions}"
    ));
    let edit = Edit::range_replacement(format!("{suggested:#o}"), mode_arg.range());
    diagnostic.set_fix(Fix::unsafe_edit(edit));
}


Review comment (security):
```suggestion
        mode & 0o7777,
```

`os.chmod()` supports setting sticky/setuid/setgid bits as well as permissions, so it could be helpful to correctly display those bits as well. Not sure it's worth displaying them in the permissions part though.

Code after:

    let Some(mode) = int.as_u16() else {
        return;
    };

    diagnostic.info(format_args!(
        "Current value of {mode_literal} ({:#05o}) sets permissions: {}",
        mode & 0o7777,
        get_permissions(mode)
    ));
    // Don't suggest a fix for 0x or 0b literals.
    if mode_literal.starts_with("0x") || mode_literal.starts_with("0b") {
        return;
    }

    if mode_literal.chars().all(|c| c == '0') {
        // Fix e.g. 000 as 0o000
        let edit = Edit::range_replacement(format!("0o{mode_literal}"), mode_arg.range());
        diagnostic.set_fix(Fix::safe_edit(edit));
        return;
    }

    let Some(suggested) = int.as_u16().and_then(suggest_fix) else {
        return;
    };
    let suggested_permissions = get_permissions(suggested);
    diagnostic.info(format_args!(
        "Suggested value of {suggested:#05o} sets permissions: {suggested_permissions}"
    ));
    let edit = Edit::range_replacement(format!("{suggested:#o}"), mode_arg.range());
    diagnostic.set_fix(Fix::unsafe_edit(edit));
}

fn find_func_mode_arg<'a>(call: &'a ExprCall, semantic: &SemanticModel) -> Option<&'a Expr> {
    let qualified_name = semantic.resolve_qualified_name(&call.func)?;

    match qualified_name.segments() {
        ["os", "umask"] => call.arguments.find_argument_value("mode", 0),
        [
            "os",
            "chmod" | "fchmod" | "lchmod" | "mkdir" | "makedirs" | "mkfifo" | "mknod",
        ] => call.arguments.find_argument_value("mode", 1),
        ["os", "open"] => call.arguments.find_argument_value("mode", 2),
        ["dbm", "open"] | ["dbm", "gnu" | "ndbm", "open"] => {
            call.arguments.find_argument_value("mode", 2)
        }
        _ => None,
    }
}

fn find_method_mode_arg<'a>(call: &'a ExprCall, semantic: &SemanticModel) -> Option<&'a Expr> {