From ded1aa7d00974a9af2b831569bf9cc6d3f000063 Mon Sep 17 00:00:00 2001 From: deepshekhardas Date: Sat, 15 Aug 2026 11:00:39 +0530 Subject: [PATCH] fix: recognize bunx.EXE/node.EXE argv0 on Windows (case-insensitive) --- src/bun_core/string/immutable.rs | 19 +++++++++++++++++++ src/runtime/cli/mod.rs | 10 ++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/bun_core/string/immutable.rs b/src/bun_core/string/immutable.rs index 2754d33c370e..bc8a92239576 100644 --- a/src/bun_core/string/immutable.rs +++ b/src/bun_core/string/immutable.rs @@ -1007,6 +1007,14 @@ pub fn ends_with(self_: &[u8], str: &[u8]) -> bool { str.is_empty() || self_.ends_with(str) } +/// Case-insensitive (ASCII-only) sibling of [`ends_with`] — mirrors +/// [`starts_with_case_insensitive_ascii`] for suffix matching. +#[inline] +pub fn ends_with_case_insensitive_ascii(self_: &[u8], suffix: &[u8]) -> bool { + self_.len() >= suffix.len() + && eql_case_insensitive_ascii(&self_[self_.len() - suffix.len()..], suffix, false) +} + #[inline] pub fn starts_with_char(self_: &[u8], char: u8) -> bool { !self_.is_empty() && self_[0] == char @@ -2739,6 +2747,17 @@ mod tests { assert!(!super::eql_case_insensitive_ascii(b"Ab", b"a", true)); } + #[test] + fn ends_with_case_insensitive_ascii_matches_case_variants() { + assert!(super::ends_with_case_insensitive_ascii(b"bunx.EXE", b"bunx.exe")); + assert!(super::ends_with_case_insensitive_ascii(b"bunx.exe", b"bunx.exe")); + assert!(super::ends_with_case_insensitive_ascii(b"bunx.EXE", b"bunx")); + assert!(super::ends_with_case_insensitive_ascii(b"BUNX", b"bunx")); + assert!(!super::ends_with_case_insensitive_ascii(b"bun.exe", b"bunx")); + assert!(!super::ends_with_case_insensitive_ascii(b"xbunx", b"bunx.exe")); + assert!(!super::ends_with_case_insensitive_ascii(b"bunx", b"bunx.exe")); + } + #[test] fn convert_utf8_to_utf16_in_buffer_fallback_rejects_malformed_sequences() { let mut buf = [0u16; 16]; diff --git a/src/runtime/cli/mod.rs b/src/runtime/cli/mod.rs index f609dfdd4a0b..66df16b59852 100644 --- a/src/runtime/cli/mod.rs +++ b/src/runtime/cli/mod.rs @@ -827,7 +827,10 @@ pub mod command { fn is_bun_x(argv0: &[u8]) -> bool { #[cfg(windows)] { - return strings::ends_with(argv0, b"bunx.exe") || strings::ends_with(argv0, b"bunx"); + // Windows paths are case-insensitive: `bunx.EXE` (e.g. produced by + // PATHEXT resolution) must be recognized the same as `bunx.exe`. + return strings::ends_with_case_insensitive_ascii(argv0, b"bunx.exe") + || strings::ends_with_case_insensitive_ascii(argv0, b"bunx"); } #[cfg(not(windows))] { @@ -838,7 +841,10 @@ pub mod command { fn is_node(argv0: &[u8]) -> bool { #[cfg(windows)] { - return strings::ends_with(argv0, b"node.exe") || strings::ends_with(argv0, b"node"); + // Windows paths are case-insensitive: `NODE.EXE` must be + // recognized the same as `node.exe`. + return strings::ends_with_case_insensitive_ascii(argv0, b"node.exe") + || strings::ends_with_case_insensitive_ascii(argv0, b"node"); } #[cfg(not(windows))] {