Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
93b20ff997 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 <noreply@anthropic.com>
2026-01-22 03:56:22 +00:00
2 changed files with 35 additions and 2 deletions

View File

@@ -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;

View File

@@ -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("");
});
});