Compare commits

...

2 Commits

Author SHA1 Message Date
Jarred Sumner
fe04f8fdad Merge branch 'main' into riskymh/disable-default-trusted 2025-07-25 22:14:21 -07:00
RiskyMH
aea947a91e add install.disableDefaultTrustedDependencies to bunfig 2025-07-25 23:59:49 +10:00
9 changed files with 213 additions and 8 deletions

View File

@@ -3041,6 +3041,8 @@ pub const api = struct {
node_linker: ?bun.install.PackageManager.Options.NodeLinker = null,
disable_default_trusted_dependencies: ?bool = null,
pub fn decode(reader: anytype) anyerror!BunInstall {
var this = std.mem.zeroes(BunInstall);

View File

@@ -499,6 +499,12 @@ pub const Bunfig = struct {
}
}
if (install_obj.get("disableDefaultTrustedDependencies")) |disable_default_trusted_expr| {
if (disable_default_trusted_expr.asBool()) |value| {
install.disable_default_trusted_dependencies = value;
}
}
if (install_obj.get("lockfile")) |lockfile_expr| {
if (lockfile_expr.get("print")) |lockfile| {
try this.expectString(lockfile);

View File

@@ -38,7 +38,9 @@ pub const UntrustedCommand = struct {
// called alias because a dependency name is not always the package name
const alias = dep.name.slice(buf);
if (!pm.lockfile.hasTrustedDependency(alias)) {
const is_trusted = pm.lockfile.hasTrustedDependency(alias, !pm.options.disable_default_trusted_dependencies);
if (!is_trusted) {
try untrusted_dep_ids.put(ctx.allocator, dep_id, {});
}
}
@@ -80,6 +82,7 @@ pub const UntrustedCommand = struct {
&node_modules_path,
alias,
resolution,
!pm.options.disable_default_trusted_dependencies,
) catch |err| {
if (err == error.ENOENT) continue;
return err;
@@ -187,7 +190,9 @@ pub const TrustCommand = struct {
const alias = dep.name.slice(buf);
if (!pm.lockfile.hasTrustedDependency(alias)) {
const is_trusted = pm.lockfile.hasTrustedDependency(alias, !pm.options.disable_default_trusted_dependencies);
if (!is_trusted) {
try untrusted_dep_ids.put(ctx.allocator, dep_id, {});
}
}
@@ -246,6 +251,7 @@ pub const TrustCommand = struct {
&node_modules_path,
alias,
resolution,
!pm.options.disable_default_trusted_dependencies,
) catch |err| {
if (err == error.ENOENT) continue;
return err;
@@ -256,7 +262,9 @@ pub const TrustCommand = struct {
if (trust_all) break :brk false;
for (packages_to_trust.items) |package_name_from_cli| {
if (strings.eqlLong(package_name_from_cli, alias, true) and !pm.lockfile.hasTrustedDependency(alias)) {
const is_already_trusted = pm.lockfile.hasTrustedDependency(alias, !pm.options.disable_default_trusted_dependencies);
if (strings.eqlLong(package_name_from_cli, alias, true) and !is_already_trusted) {
break :brk false;
}
}

View File

@@ -1094,7 +1094,7 @@ pub const PackageInstaller = struct {
const truncated_dep_name_hash: TruncatedPackageNameHash = @truncate(dep.name_hash);
const is_trusted, const is_trusted_through_update_request = brk: {
if (this.trusted_dependencies_from_update_requests.contains(truncated_dep_name_hash)) break :brk .{ true, true };
if (this.lockfile.hasTrustedDependency(alias.slice(this.lockfile.buffers.string_bytes.items))) break :brk .{ true, false };
if (this.lockfile.hasTrustedDependency(alias.slice(this.lockfile.buffers.string_bytes.items), !this.options.disable_default_trusted_dependencies)) break :brk .{ true, false };
break :brk .{ false, false };
};
@@ -1325,6 +1325,7 @@ pub const PackageInstaller = struct {
package_path,
folder_name,
resolution,
!this.options.disable_default_trusted_dependencies,
) catch |err| {
if (log_level != .silent) {
const fmt = "\n<r><red>error:<r> failed to enqueue lifecycle scripts for <b>{s}<r>: {s}\n";

View File

@@ -71,6 +71,8 @@ depth: ?usize = null,
/// isolated installs (pnpm-like) or hoisted installs (yarn-like, original)
node_linker: NodeLinker = .auto,
disable_default_trusted_dependencies: bool = false,
pub const PublishConfig = struct {
access: ?Access = null,
tag: string = "",
@@ -244,6 +246,9 @@ pub fn load(
if (config.link_workspace_packages) |link_workspace_packages| {
this.link_workspace_packages = link_workspace_packages;
}
if (config.disable_default_trusted_dependencies) |disable_default_trusted_dependencies| {
this.disable_default_trusted_dependencies = disable_default_trusted_dependencies;
}
}
if (base.url.len == 0) base.url = Npm.Registry.default_url;

View File

@@ -846,7 +846,7 @@ pub const Installer = struct {
if (installer.trusted_dependencies_from_update_requests.contains(truncated_dep_name_hash)) {
break :brk .{ true, true };
}
if (installer.lockfile.hasTrustedDependency(dep.name.slice(string_buf))) {
if (installer.lockfile.hasTrustedDependency(dep.name.slice(string_buf), !installer.manager.options.disable_default_trusted_dependencies)) {
break :brk .{ true, false };
}
break :brk .{ false, false };
@@ -869,6 +869,7 @@ pub const Installer = struct {
&pkg_cwd,
dep.name.slice(string_buf),
&pkg_res,
!installer.manager.options.disable_default_trusted_dependencies,
) catch |err| {
return .failure(.{ .run_preinstall = err });
};

View File

@@ -1974,13 +1974,13 @@ pub const default_trusted_dependencies = brk: {
break :brk &final;
};
pub fn hasTrustedDependency(this: *const Lockfile, name: []const u8) bool {
pub fn hasTrustedDependency(this: *const Lockfile, name: []const u8, check_default: bool) bool {
if (this.trusted_dependencies) |trusted_dependencies| {
const hash = @as(u32, @truncate(String.Builder.stringHash(name)));
return trusted_dependencies.contains(hash);
}
return default_trusted_dependencies.has(name);
return if (check_default) default_trusted_dependencies.has(name) else false;
}
pub const NameHashMap = std.ArrayHashMapUnmanaged(PackageNameHash, String, ArrayIdentityContext.U64, false);

View File

@@ -267,9 +267,10 @@ pub const Scripts = extern struct {
folder_path: *bun.AbsPath(.{ .sep = .auto }),
folder_name: string,
resolution: *const Resolution,
check_default_trusted: bool,
) !?Package.Scripts.List {
if (this.hasAny()) {
const add_node_gyp_rebuild_script = if (lockfile.hasTrustedDependency(folder_name) and
const add_node_gyp_rebuild_script = if (lockfile.hasTrustedDependency(folder_name, check_default_trusted) and
this.install.isEmpty() and
this.preinstall.isEmpty())
brk: {

View File

@@ -1845,6 +1845,187 @@ for (const forceWaiterThread of isLinux ? [false, true] : [false]) {
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
});
describe("disableDefaultTrustedDependencies bunfig option", async () => {
test("install respects it", async () => {
await verdaccio.writeBunfig(packageDir, { saveTextLockfile: false });
const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env;
// Append disableDefaultTrustedDependencies to the existing bunfig.toml
const existingBunfig = await file(join(packageDir, "bunfig.toml")).text();
await writeFile(join(packageDir, "bunfig.toml"), existingBunfig + "\ndisableDefaultTrustedDependencies = true");
await writeFile(
packageJson,
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
// electron is in the default trusted dependencies list
electron: "1.0.0",
},
}),
);
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
});
var err = await stderr.text();
var out = await stdout.text();
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(out.replace(/\s*\[[0-9\.]+m?s\]$/m, "").split(/\r?\n/)).toEqual([
expect.stringContaining("bun install v1."),
"",
"+ electron@1.0.0",
"",
"1 package installed",
"",
"Blocked 1 postinstall. Run `bun pm untrusted` for details.",
"",
]);
expect(await exited).toBe(0);
assertManifestsPopulated(join(packageDir, ".bun-cache"), verdaccio.registryUrl());
// electron scripts should NOT have run
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
// Now add electron to trustedDependencies and it should run
await writeFile(
packageJson,
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
trustedDependencies: ["electron"],
}),
);
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
}));
err = await stderr.text();
out = await stdout.text();
expect(err).toContain("Saved lockfile");
expect(await exited).toBe(0);
// Now electron scripts SHOULD have run
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
});
test("bun pm untrusted respects it", async () => {
await verdaccio.writeBunfig(packageDir, { saveTextLockfile: false });
const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env;
const existingBunfig = await file(join(packageDir, "bunfig.toml")).text();
await writeFile(join(packageDir, "bunfig.toml"), existingBunfig + "\ndisableDefaultTrustedDependencies = true");
await writeFile(
packageJson,
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
"uses-what-bin": "1.0.0",
},
}),
);
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
});
await exited;
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "pm", "untrusted"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
}));
var out = await stdout.text();
expect(await exited).toBe(0);
// Both packages should be listed as untrusted
expect(out).toContain("electron");
expect(out).toContain("uses-what-bin");
expect(out).toContain("preinstall");
expect(out).toContain("install");
});
test("bun pm trust respects it", async () => {
await verdaccio.writeBunfig(packageDir, { saveTextLockfile: false });
const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env;
const existingBunfig = await file(join(packageDir, "bunfig.toml")).text();
await writeFile(join(packageDir, "bunfig.toml"), existingBunfig + "\ndisableDefaultTrustedDependencies = true");
await writeFile(
packageJson,
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
}),
);
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
});
await exited;
// electron script should not have run
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
// Trust electron
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "pm", "trust", "electron"],
cwd: packageDir,
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: testEnv,
}));
var out = await stdout.text();
expect(await exited).toBe(0);
// electron script should now have run
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
});
});
describe("--trust", async () => {
test("unhoisted untrusted scripts, none at root node_modules", async () => {
const testEnv = forceWaiterThread ? { ...env, BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" } : env;