mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix(install): manifest package name mismatch (#11549)
This commit is contained in:
@@ -1077,7 +1077,7 @@ pub const PackageManifest = struct {
|
||||
};
|
||||
|
||||
if (json.asProperty("name")) |name_q| {
|
||||
const field = name_q.expr.asString(allocator) orelse return null;
|
||||
const received_name = name_q.expr.asString(allocator) orelse return null;
|
||||
|
||||
// This is intentionally a case insensitive comparision. If the registry is running on a system
|
||||
// with a case insensitive filesystem, you'll be able to install dependencies with casing that doesn't match.
|
||||
@@ -1091,12 +1091,52 @@ pub const PackageManifest = struct {
|
||||
// }
|
||||
//
|
||||
// https://github.com/oven-sh/bun/issues/5189
|
||||
if (!strings.eqlCaseInsensitiveASCII(expected_name, field, true)) {
|
||||
Output.panic("<r>internal: <red>package name mismatch<r> expected <b>\"{s}\"<r> but received <red>\"{s}\"<r>", .{ expected_name, field });
|
||||
const equal = if (expected_name.len == 0 or expected_name[0] != '@')
|
||||
// Unscoped package, just normal case insensitive comparison
|
||||
strings.eqlCaseInsensitiveASCII(expected_name, received_name, true)
|
||||
else brk: {
|
||||
// Scoped package. The registry might url encode the package name changing either or both `@` and `/` into `%40` and `%2F`.
|
||||
// e.g. "name": "@std%2fsemver" // real world example from crash report
|
||||
|
||||
// Expected name `@` exists, check received has either `@` or `%40`
|
||||
var received_remain = received_name;
|
||||
if (received_remain.len > 0 and received_remain[0] == '@') {
|
||||
received_remain = received_remain[1..];
|
||||
} else if (received_remain.len > 2 and strings.eqlComptime(received_remain[0..3], "%40")) {
|
||||
received_remain = received_remain[3..];
|
||||
} else {
|
||||
break :brk false;
|
||||
}
|
||||
|
||||
var expected_remain = expected_name[1..];
|
||||
|
||||
// orelse is invalid because scoped package is missing `/`, but we allow just in case
|
||||
const slash_index = strings.indexOfChar(expected_remain, '/') orelse break :brk strings.eqlCaseInsensitiveASCII(expected_remain, received_remain, true);
|
||||
|
||||
if (slash_index >= received_remain.len) break :brk false;
|
||||
|
||||
if (!strings.eqlCaseInsensitiveASCIIIgnoreLength(expected_remain[0..slash_index], received_remain[0..slash_index])) break :brk false;
|
||||
expected_remain = expected_remain[slash_index + 1 ..];
|
||||
|
||||
// Expected name `/` exists, check that received is either `/`, `%2f`, or `%2F`
|
||||
received_remain = received_remain[slash_index..];
|
||||
if (received_remain.len > 0 and received_remain[0] == '/') {
|
||||
received_remain = received_remain[1..];
|
||||
} else if (received_remain.len > 2 and strings.eqlCaseInsensitiveASCIIIgnoreLength(received_remain[0..3], "%2f")) {
|
||||
received_remain = received_remain[3..];
|
||||
} else {
|
||||
break :brk false;
|
||||
}
|
||||
|
||||
break :brk strings.eqlCaseInsensitiveASCII(expected_remain, received_remain, true);
|
||||
};
|
||||
|
||||
if (!equal) {
|
||||
Output.panic("<r>internal: <red>Package name mismatch.<r> Expected <b>\"{s}\"<r> but received <red>\"{s}\"<r>", .{ expected_name, received_name });
|
||||
return null;
|
||||
}
|
||||
|
||||
string_builder.count(field);
|
||||
string_builder.count(expected_name);
|
||||
}
|
||||
|
||||
if (json.asProperty("modified")) |name_q| {
|
||||
@@ -1290,10 +1330,10 @@ pub const PackageManifest = struct {
|
||||
string_buf = ptr[0..string_builder.cap];
|
||||
}
|
||||
|
||||
if (json.asProperty("name")) |name_q| {
|
||||
const field = name_q.expr.asString(allocator) orelse return null;
|
||||
result.pkg.name = string_builder.append(ExternalString, field);
|
||||
}
|
||||
// Using `expected_name` instead of the name from the manifest. We've already
|
||||
// checked that they are equal above, but `expected_name` will not have `@`
|
||||
// or `/` changed to `%40` or `%2f`, ensuring lookups will work later
|
||||
result.pkg.name = string_builder.append(ExternalString, expected_name);
|
||||
|
||||
get_versions: {
|
||||
if (json.asProperty("versions")) |versions_q| {
|
||||
|
||||
@@ -2370,6 +2370,33 @@ describe("workspaces", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("name from manifest is scoped and url encoded", async () => {
|
||||
await write(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
dependencies: {
|
||||
// `name` in the manifest for these packages is manually changed
|
||||
// to use `%40` and `%2f`
|
||||
"@url/encoding.2": "1.0.1",
|
||||
"@url/encoding.3": "1.0.1",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await runBunInstall(env, packageDir);
|
||||
|
||||
const files = await Promise.all([
|
||||
file(join(packageDir, "node_modules", "@url", "encoding.2", "package.json")).json(),
|
||||
file(join(packageDir, "node_modules", "@url", "encoding.3", "package.json")).json(),
|
||||
]);
|
||||
|
||||
expect(files).toEqual([
|
||||
{ name: "@url/encoding.2", version: "1.0.1" },
|
||||
{ name: "@url/encoding.3", version: "1.0.1" },
|
||||
]);
|
||||
});
|
||||
|
||||
describe("update", () => {
|
||||
test("duplicate peer dependency (one package is invalid_package_id)", async () => {
|
||||
await write(
|
||||
|
||||
BIN
test/cli/install/registry/packages/@url/encoding.2/encoding.2-1.0.1.tgz
vendored
Normal file
BIN
test/cli/install/registry/packages/@url/encoding.2/encoding.2-1.0.1.tgz
vendored
Normal file
Binary file not shown.
38
test/cli/install/registry/packages/@url/encoding.2/package.json
vendored
Normal file
38
test/cli/install/registry/packages/@url/encoding.2/package.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@url%2fencoding.2",
|
||||
"versions": {
|
||||
"1.0.1": {
|
||||
"name": "@url/encoding.2",
|
||||
"version": "1.0.1",
|
||||
"_id": "@url/encoding.2@1.0.1",
|
||||
"_nodeVersion": "22.2.0",
|
||||
"_npmVersion": "10.8.1",
|
||||
"dist": {
|
||||
"integrity": "sha512-IWtV06UQpxWKEbRgmgnInjdPSVqaj88gLcbsJKbX4TuvmU9PpArzyHK5h5H73q5CzKoBDIwptb+cKvr98j+QNA==",
|
||||
"shasum": "bc2994336b291322c242f3570cc486cf8fcc9756",
|
||||
"tarball": "http://localhost:4873/@url/encoding.2/-/@url/encoding.2-1.0.1.tgz"
|
||||
},
|
||||
"contributors": []
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"modified": "2024-06-03T00:01:11.853Z",
|
||||
"created": "2024-06-03T00:01:11.853Z",
|
||||
"1.0.1": "2024-06-03T00:01:11.853Z"
|
||||
},
|
||||
"users": {},
|
||||
"dist-tags": {
|
||||
"latest": "1.0.1"
|
||||
},
|
||||
"_uplinks": {},
|
||||
"_distfiles": {},
|
||||
"_attachments": {
|
||||
"encoding.2-1.0.1.tgz": {
|
||||
"shasum": "bc2994336b291322c242f3570cc486cf8fcc9756",
|
||||
"version": "1.0.1"
|
||||
}
|
||||
},
|
||||
"_rev": "",
|
||||
"_id": "@url/encoding.2",
|
||||
"readme": "ERROR: No README data found!"
|
||||
}
|
||||
BIN
test/cli/install/registry/packages/@url/encoding.3/encoding.3-1.0.1.tgz
vendored
Normal file
BIN
test/cli/install/registry/packages/@url/encoding.3/encoding.3-1.0.1.tgz
vendored
Normal file
Binary file not shown.
38
test/cli/install/registry/packages/@url/encoding.3/package.json
vendored
Normal file
38
test/cli/install/registry/packages/@url/encoding.3/package.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "%40url%2fencoding.3",
|
||||
"versions": {
|
||||
"1.0.1": {
|
||||
"name": "@url/encoding.3",
|
||||
"version": "1.0.1",
|
||||
"_id": "@url/encoding.3@1.0.1",
|
||||
"_nodeVersion": "22.2.0",
|
||||
"_npmVersion": "10.8.1",
|
||||
"dist": {
|
||||
"integrity": "sha512-LkuYnUyQgbhee/Sz/QL+WSMzvRElhJqzdYCs6oZFcAlZwEMcmyE10X0LfN6UcQ8zX7z0vjSezs+WinFafTlDSw==",
|
||||
"shasum": "34a69650f7a471f29578381144110db7319c6992",
|
||||
"tarball": "http://localhost:4873/@url/encoding.3/-/@url/encoding.3-1.0.1.tgz"
|
||||
},
|
||||
"contributors": []
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"modified": "2024-06-03T00:01:16.079Z",
|
||||
"created": "2024-06-03T00:01:16.079Z",
|
||||
"1.0.1": "2024-06-03T00:01:16.079Z"
|
||||
},
|
||||
"users": {},
|
||||
"dist-tags": {
|
||||
"latest": "1.0.1"
|
||||
},
|
||||
"_uplinks": {},
|
||||
"_distfiles": {},
|
||||
"_attachments": {
|
||||
"encoding.3-1.0.1.tgz": {
|
||||
"shasum": "34a69650f7a471f29578381144110db7319c6992",
|
||||
"version": "1.0.1"
|
||||
}
|
||||
},
|
||||
"_rev": "",
|
||||
"_id": "@url/encoding.3",
|
||||
"readme": "ERROR: No README data found!"
|
||||
}
|
||||
Reference in New Issue
Block a user