diff --git a/deno.json b/deno.json index 736c0cf..75c8948 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@cross/fs", - "version": "0.0.7", + "version": "0.0.8", "exports": { ".": "./mod.ts", "./stat": "./src/stat/mod.ts", @@ -12,7 +12,7 @@ "@cross/env": "jsr:@cross/env@^1.0.0", "@cross/runtime": "jsr:@cross/runtime@^1.0.0", "@cross/test": "jsr:@cross/test@^0.0.9", - "@cross/utils": "jsr:@cross/utils@^0.8.2", + "@cross/utils": "jsr:@cross/utils@^0.9.0", "@std/assert": "jsr:@std/assert@^0.221.0", "@std/path": "jsr:@std/path@^0.221.0" }, diff --git a/src/stat/which.ts b/src/stat/which.ts index 5a85b23..6c79ef0 100644 --- a/src/stat/which.ts +++ b/src/stat/which.ts @@ -1,10 +1,12 @@ -import { access, constants } from "./mod.ts"; +import { constants, stat } from "./mod.ts"; import { getEnv } from "@cross/env"; import { join } from "@std/path"; /** * Searches for an executable file within the directories specified by the "PATH" environment variable. * + * Uses stat to check if anyone (as apposed to the current user) has execute permission on the found executable, to avoid getting the current user id, which require higher permission in certain runtimes. + * * @param command - The name of the executable file to locate. * @returns The full path to the executable if found, otherwise null. */ @@ -14,8 +16,18 @@ export async function which(command: string): Promise { for (const dir of paths) { const fullPath = join(dir, command); try { - await access(fullPath, constants.X_OK); // Check if executable - return fullPath; + const fileInfo = await stat(fullPath); + // Soft Permission Check: Check if anyone has execute permission + if ( + fileInfo && fileInfo.mode && + ( + (fileInfo.mode & constants.S_IXOTH) || + (fileInfo.mode & constants.S_IXUSR) || + (fileInfo.mode & constants.S_IXGRP) + ) + ) { + return fullPath; + } } catch (_err) { // Ignore and try the next path }