Repository: nushell/nushell
File: crates/nu-utils/src/filesystem.rs (Rust)

Code before:
use std::path::Path;

// The result of checking whether we have permission to cd to a directory
#[derive(Debug)]
pub enum PermissionResult {
    PermissionOk,
    PermissionDenied,
}

// TODO: Maybe we should use file_attributes() from https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
// More on that here: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    match dir.as_ref().read_dir() {
        Err(e) => {
            if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
                PermissionResult::PermissionDenied
            } else {
                PermissionResult::PermissionOk
            }
        }
        Ok(_) => PermissionResult::PermissionOk,
    }
}

#[cfg(unix)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    // `faccessat()` from modern libc does not always take ACL into account.
    // We prefer call `access()` instead as possible.
    //
    // Android refuses to check permission on flag != 0 in their `faccessat()`,
    // see https://android.googlesource.com/platform/bionic/+/master/libc/bionic/faccessat.cpp.
    if Uid::current() == Uid::effective() || cfg!(target_os = "android") {
        return access(dir.as_ref(), AccessFlags::X_OK).into();
    }

    // Normally, this should happen only if user has set SETUID bit for nu executable
    // (which is strongly discouraged).
    // Modern sudo installation checks for owner=root plus SETUID bit on, thus,
    // `sudo nu` for example, will not lead to EUID != RUID.
    faccessat(None, dir.as_ref(), AccessFlags::X_OK, AtFlags::AT_EACCESS).into()
}

#[cfg(unix)]
pub mod users {
    use nix::unistd::{Gid, Group, Uid, User};

    pub fn get_user_by_uid(uid: Uid) -> Option<User> {
        User::from_uid(uid).ok().flatten()
    }


Review comment (security):
With this being `pub`, and the check explicitly going for the `X_OK` flag, `have_permission` is maybe a bit vague.

Might be worth narrowing it with a doccomment
```suggestion
/// Check that the process' user id has permissions to execute or in the case of a directory traverse the particular directory
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
```

Code after:
#[derive(Debug)]
pub enum PermissionResult {
    PermissionOk,
    PermissionDenied,
}

// TODO: Maybe we should use file_attributes() from https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
// More on that here: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    match dir.as_ref().read_dir() {
        Err(e) => {
            if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
                PermissionResult::PermissionDenied
            } else {
                PermissionResult::PermissionOk
            }
        }
        Ok(_) => PermissionResult::PermissionOk,
    }
}

#[cfg(unix)]
/// Check that the process' user id has permissions to execute or
/// in the case of a directory traverse the particular directory
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    // We check permissions for real user id, but that's fine, because in
    // proper installations of nushell, effective UID (EUID) rarely differs
    // from real UID (RUID). We strongly advise against setting the setuid bit
    // on the nushell executable or shebang scripts starts with `#!/usr/bin/env nu` e.g.
    // Most Unix systems ignore setuid on shebang by default anyway.
    access(dir.as_ref(), AccessFlags::X_OK).into()
}

#[cfg(unix)]
pub mod users {
    use nix::unistd::{Gid, Group, Uid, User};

    pub fn get_user_by_uid(uid: Uid) -> Option<User> {
        User::from_uid(uid).ok().flatten()
    }

    pub fn get_group_by_gid(gid: Gid) -> Option<Group> {
        Group::from_gid(gid).ok().flatten()
    }

    pub fn get_current_uid() -> Uid {
        Uid::current()
    }

    pub fn get_current_gid() -> Gid {