Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/bunfig/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ use crate::bunfig::Bunfig;
fn get_home_config_path(buf: &mut PathBuffer) -> Option<&ZStr> {
let paths: [&[u8]; 1] = [b".bunfig.toml"];

if let Some(data_dir) = env_var::XDG_CONFIG_HOME.get() {
if let Some(data_dir) = env_var::XDG_CONFIG_HOME
.get()
.filter(|p| bun_paths::is_absolute(p))
{
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
data_dir, &mut **buf, &paths,
));
}

if let Some(home_dir) = env_var::HOME.get() {
if let Some(home_dir) = env_var::HOME.get().filter(|p| bun_paths::is_absolute(p)) {
return Some(resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut **buf, &paths,
));
Expand Down
10 changes: 8 additions & 2 deletions src/install/PackageManager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1928,15 +1928,21 @@ pub fn init(
// npm reads `$HOME/.npmrc` and ignores XDG_CONFIG_HOME; keep
// `$XDG_CONFIG_HOME/.npmrc` only when that file actually exists.
let mut global_len: usize = 0;
if let Some(xdg_dir) = bun_core::env_var::XDG_CONFIG_HOME.get_not_empty() {
if let Some(xdg_dir) = bun_core::env_var::XDG_CONFIG_HOME
.get_not_empty()
.filter(|p| bun_paths::is_absolute(p))
{
let p =
resolve_path::join_abs_string_buf_z::<platform::Auto>(xdg_dir, &mut buf, &parts);
if bun_sys::exists_z(p) {
global_len = p.len();
}
}
if global_len == 0 {
if let Some(home_dir) = bun_core::env_var::HOME.get_not_empty() {
if let Some(home_dir) = bun_core::env_var::HOME
.get_not_empty()
.filter(|p| bun_paths::is_absolute(p))
{
global_len = resolve_path::join_abs_string_buf_z::<platform::Auto>(
home_dir, &mut buf, &parts,
)
Expand Down
24 changes: 14 additions & 10 deletions src/install/PackageManager/PackageManagerOptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,18 @@ pub use crate::config_version::ConfigVersion;
pub use bun_install_types::DependencyGroup;
pub use bun_install_types::NodeLinker::NodeLinker;

// `join_abs_string_buf` asserts an absolute base; skip non-absolute env values (BUN-2V31).
#[inline]
fn get_abs(v: Option<&'static [u8]>) -> Option<&'static [u8]> {
v.filter(|p| bun_paths::is_absolute(p))
}
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.

// mkdir -p + open the dir. Callers store the raw `Fd` (`options.global_bin_dir: Fd`).
pub fn open_global_dir(explicit_global_dir: &[u8]) -> crate::Result<bun_sys::Fd> {
use bun_paths::{platform, resolve_path::join_abs_string_buf};
use bun_sys::{Dir, OpenDirOptions};

if let Some(home_dir) = env_var::BUN_INSTALL_GLOBAL_DIR.get() {
if let Some(home_dir) = env_var::BUN_INSTALL_GLOBAL_DIR.get_not_empty() {
return Dir::cwd()
.make_open_path(home_dir, OpenDirOptions::default())
.map(|d| d.into_raw())
Expand All @@ -317,7 +323,7 @@ pub fn open_global_dir(explicit_global_dir: &[u8]) -> crate::Result<bun_sys::Fd>
.map_err(Into::into);
}

if let Some(home_dir) = env_var::BUN_INSTALL.get() {
if let Some(home_dir) = get_abs(env_var::BUN_INSTALL.get()) {
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 2] = [b"install", b"global"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
Expand All @@ -327,9 +333,8 @@ pub fn open_global_dir(explicit_global_dir: &[u8]) -> crate::Result<bun_sys::Fd>
.map_err(Into::into);
}

if let Some(home_dir) = env_var::XDG_CACHE_HOME
.get()
.or_else(|| env_var::HOME.get())
if let Some(home_dir) =
get_abs(env_var::XDG_CACHE_HOME.get()).or_else(|| get_abs(env_var::HOME.get()))
{
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 3] = [b".bun", b"install", b"global"];
Expand All @@ -347,7 +352,7 @@ pub(crate) fn open_global_bin_dir(opts_: Option<&Api::BunInstall>) -> crate::Res
use bun_paths::{platform, resolve_path::join_abs_string_buf};
use bun_sys::{Dir, OpenDirOptions};

if let Some(home_dir) = env_var::BUN_INSTALL_BIN.get() {
if let Some(home_dir) = env_var::BUN_INSTALL_BIN.get_not_empty() {
return Dir::cwd()
.make_open_path(home_dir, OpenDirOptions::default())
.map(|d| d.into_raw())
Expand All @@ -365,7 +370,7 @@ pub(crate) fn open_global_bin_dir(opts_: Option<&Api::BunInstall>) -> crate::Res
}
}

if let Some(home_dir) = env_var::BUN_INSTALL.get() {
if let Some(home_dir) = get_abs(env_var::BUN_INSTALL.get()) {
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 1] = [b"bin"];
let path = join_abs_string_buf::<platform::Auto>(home_dir, &mut buf.0, &parts);
Expand All @@ -375,9 +380,8 @@ pub(crate) fn open_global_bin_dir(opts_: Option<&Api::BunInstall>) -> crate::Res
.map_err(Into::into);
}

if let Some(home_dir) = env_var::XDG_CACHE_HOME
.get()
.or_else(|| env_var::HOME.get())
if let Some(home_dir) =
get_abs(env_var::XDG_CACHE_HOME.get()).or_else(|| get_abs(env_var::HOME.get()))
{
let mut buf = PathBuffer::uninit();
let parts: [&[u8]; 2] = [b".bun", b"bin"];
Expand Down
5 changes: 4 additions & 1 deletion src/install/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ impl SloppyGlobalGitConfig {
}

fn load_and_parse() -> SloppyGlobalGitConfig {
let Some(home_dir) = bun_core::env_var::HOME.get() else {
let Some(home_dir) = bun_core::env_var::HOME
.get()
.filter(|p| Path::is_absolute(p))
else {
return SloppyGlobalGitConfig::default();
};

Expand Down
7 changes: 6 additions & 1 deletion src/runtime/cli/pm_diff_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@ pub(crate) fn exec(
.iter()
.map(|&arg| {
use bun_paths::resolve_path::{join_abs_string, platform};
match (arg.strip_prefix(b"~/"), bun_core::env_var::HOME.get()) {
match (
arg.strip_prefix(b"~/"),
bun_core::env_var::HOME
.get()
.filter(|p| bun_paths::is_absolute(p)),
) {
(Some(rest), Some(home)) => {
join_abs_string::<platform::Auto>(home, &[rest]).to_vec()
}
Expand Down
47 changes: 46 additions & 1 deletion test/cli/install/bun-pm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { spawn } from "bun";
import { afterAll, afterEach, beforeAll, beforeEach, expect, it, test } from "bun:test";
import { exists, mkdir, writeFile } from "fs/promises";
import { bunEnv, bunExe, bunEnv as env, readdirSorted, tempDir, tmpdirSync } from "harness";
import { cpSync } from "node:fs";
import { cpSync, realpathSync } from "node:fs";
import { join } from "path";
import {
dummyAfterAll,
Expand Down Expand Up @@ -936,3 +936,48 @@ test("bun pm cache rm does not create the directory named by a project-local .en
expect(stderr).not.toContain("error");
expect(exitCode).toBe(0);
});

// https://bun-p9.sentry.io/issues/7403306202/
// Windows panicked in _joinAbsStringBufWindows when $BUN_INSTALL was not an
// absolute path; POSIX silently opened a mangled path rooted at "/". Now
// empty and relative values are skipped so the next candidate ($HOME) wins.
for (const [title, bunInstallValue, base] of [
["empty $BUN_INSTALL falls through to $HOME", "", ["fake-home", ".bun"]],
["relative $BUN_INSTALL falls through to $HOME", "relative-dir", ["fake-home", ".bun"]],
["absolute $BUN_INSTALL", null, ["abs-bun"]],
] as const) {
test(`global dir: ${title}`, async () => {
using dir = tempDir("pm-global-dir-env", {
"package.json": JSON.stringify({ name: "pm-global-dir-env", version: "1.0.0" }),
});
const cwd = String(dir);
const globalDir = join(cwd, ...base, "install", "global");
const binDir = join(cwd, ...base, "bin");
await mkdir(globalDir, { recursive: true });
await writeFile(join(globalDir, "package.json"), JSON.stringify({ name: "global", version: "1.0.0" }));

const spawnEnv: NodeJS.Dict<string> = {
...env,
BUN_INSTALL: bunInstallValue ?? join(cwd, "abs-bun"),
HOME: join(cwd, "fake-home"),
USERPROFILE: join(cwd, "fake-home"),
};
delete spawnEnv.BUN_INSTALL_GLOBAL_DIR;
delete spawnEnv.BUN_INSTALL_BIN;
delete spawnEnv.XDG_CACHE_HOME;
Comment thread
robobun marked this conversation as resolved.
delete spawnEnv.XDG_CONFIG_HOME;

await using proc = Bun.spawn({
cmd: [bunExe(), "pm", "bin", "-g"],
cwd,
stdout: "pipe",
stderr: "pipe",
env: spawnEnv,
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect({ stderr, exitCode }).toEqual({ stderr: "", exitCode: 0 });
expect(await exists(binDir)).toBeTrue();
expect(realpathSync(stdout.trim())).toBe(realpathSync(binDir));
});
}
Loading