From 93b20ff997739ef8d03ff39218027184cf955ac0 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Thu, 22 Jan 2026 03:56:22 +0000 Subject: [PATCH] fix(install): match npmrc auth tokens by both host and path When .npmrc contains multiple auth token entries for the same host but different paths, Bun was incorrectly using the last token for all registries because the matching logic only compared the host portion of URLs, ignoring the path. This fix ensures that auth tokens are matched by both host AND pathname, so that `//somehost.com/org1/npm/registry/:_authToken=jwt1` only applies to registry URLs with the exact path `/org1/npm/registry/`. Fixes #26350 Co-Authored-By: Claude Opus 4.5 --- src/ini.zig | 8 ++++++-- test/cli/install/npmrc.test.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/ini.zig b/src/ini.zig index 23e8b57380..b6940372ef 100644 --- a/src/ini.zig +++ b/src/ini.zig @@ -1306,7 +1306,9 @@ pub fn loadNpmrc( for (configs.items) |conf_item| { const conf_item_url = bun.URL.parse(conf_item.registry_url); - if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(default_registry_url.host), bun.strings.withoutTrailingSlash(conf_item_url.host))) { + if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(default_registry_url.host), bun.strings.withoutTrailingSlash(conf_item_url.host)) and + std.mem.eql(u8, bun.strings.withoutTrailingSlash(default_registry_url.pathname), bun.strings.withoutTrailingSlash(conf_item_url.pathname))) + { // Apply config to default registry const v: *bun.schema.api.NpmRegistry = brk: { if (install.default_registry) |*r| break :brk r; @@ -1343,7 +1345,9 @@ pub fn loadNpmrc( for (registry_map.scopes.keys(), registry_map.scopes.values()) |*k, *v| { const url = url_map.get(k.*) orelse unreachable; - if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.host), bun.strings.withoutTrailingSlash(conf_item_url.host))) { + if (std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.host), bun.strings.withoutTrailingSlash(conf_item_url.host)) and + std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.pathname), bun.strings.withoutTrailingSlash(conf_item_url.pathname))) + { if (conf_item_url.hostname.len > 0) { if (!std.mem.eql(u8, bun.strings.withoutTrailingSlash(url.hostname), bun.strings.withoutTrailingSlash(conf_item_url.hostname))) { continue; diff --git a/test/cli/install/npmrc.test.ts b/test/cli/install/npmrc.test.ts index 1dfa1086da..81e0dbfe7f 100644 --- a/test/cli/install/npmrc.test.ts +++ b/test/cli/install/npmrc.test.ts @@ -464,4 +464,33 @@ ${Object.keys(opts) expect(result.default_registry_email).toEqual("test@example.com"); }, ); + + test("applies auth tokens to default registry correctly - same host different paths", () => { + // Regression test for https://github.com/oven-sh/bun/issues/26350 + // When multiple auth tokens exist for the same host but different paths, + // Bun should match the token by both host AND path, not just host. + const ini = ` +registry=https://somehost.com/org1/npm/registry/ +//somehost.com/org1/npm/registry/:_authToken=jwt1 +//somehost.com/org2/npm/registry/:_authToken=jwt2 +//somehost.com/org3/npm/registry/:_authToken=jwt3 +`; + const result = loadNpmrc(ini); + expect(result.default_registry_url).toEqual("https://somehost.com/org1/npm/registry/"); + expect(result.default_registry_token).toBe("jwt1"); + }); + + test("auth token not applied when paths don't match - same host", () => { + // Regression test for https://github.com/oven-sh/bun/issues/26350 + // When auth tokens exist for a different path on the same host, + // they should not be applied to the default registry. + const ini = ` +registry=https://somehost.com/org1/npm/registry/ +//somehost.com/org2/npm/registry/:_authToken=jwt2 +`; + const result = loadNpmrc(ini); + expect(result.default_registry_url).toEqual("https://somehost.com/org1/npm/registry/"); + // Should be empty since there's no matching token for /org1/npm/registry/ + expect(result.default_registry_token).toBe(""); + }); });