diff --git a/src/cli/Arguments.zig b/src/cli/Arguments.zig index e0556a97a1..27440c8c67 100644 --- a/src/cli/Arguments.zig +++ b/src/cli/Arguments.zig @@ -650,7 +650,32 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C else Bunfig.OfflineMode.online; - if (args.flag("--no-install")) { + const has_security_provider = if (ctx.install) |install| + install.security_provider != null + else + false; + + if (has_security_provider) { + if (args.flag("-i")) { + Output.prettyErrorln("warning: Autoinstall is disabled because a security provider is configured. The -i flag will be ignored.", .{}); + Output.flush(); + } else if (args.option("--install")) |enum_value| { + const requested_mode = options.GlobalCache.Map.get(enum_value) orelse + (if (enum_value.len == 0) options.GlobalCache.force else null); + + if (requested_mode) |mode| { + if (mode != .disable) { + Output.prettyErrorln("warning: Autoinstall is disabled because a security provider is configured. The --install={s} flag will be ignored.", .{enum_value}); + Output.flush(); + } + } + } + + // disable autoinstall because it doesn't make sense we enable + // autoinstall when something designed to block installing packages + // exists... + ctx.debug.global_cache = .disable; + } else if (args.flag("--no-install")) { ctx.debug.global_cache = .disable; } else if (args.flag("-i")) { ctx.debug.global_cache = .fallback; diff --git a/test/cli/run/run-autoinstall-security.test.ts b/test/cli/run/run-autoinstall-security.test.ts new file mode 100644 index 0000000000..69abf8c48e --- /dev/null +++ b/test/cli/run/run-autoinstall-security.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, normalizeBunSnapshot, tempDirWithFiles } from "harness"; + +describe("autoinstall with security provider", () => { + test("should disable autoinstall when security provider is configured", async () => { + const dir = tempDirWithFiles("autoinstall-security", { + "index.js": "import isEven from 'is-even'; console.log(isEven(2));", + "bunfig.toml": ` +[install] +auto = "force" # This should be overridden by security provider + +[install.security] +provider = "example-security-provider" +`, + }); + + const { stdout, stderr, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), "index.js"], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + // Should not autoinstall when security provider is set + expect(normalizeBunSnapshot(stderr?.toString("utf8") || "", dir)).toMatchInlineSnapshot(` + "error: Cannot find package 'is-even' from '/index.js' + + Bun v+b30ae0956 (macOS arm64)" + `); + expect(exitCode).not.toBe(0); + }); + + test("should allow autoinstall without security provider", async () => { + const dir = tempDirWithFiles("autoinstall-no-security", { + "index.js": "import isEven from 'is-even'; console.log(isEven(2));", + "bunfig.toml": ` +[install] +auto = "force" # Should work without security provider +`, + }); + + const { stdout, stderr, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), "index.js"], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + // Should autoinstall when no security provider is set + expect(normalizeBunSnapshot(stdout?.toString("utf8") || "", dir)).toMatchInlineSnapshot(`"true"`); + expect(normalizeBunSnapshot(stderr?.toString("utf8") || "", dir)).toMatchInlineSnapshot(`""`); + expect(exitCode).toBe(0); + }); + + test.each(["-i", "--install=force", "--install=fallback"])("CLI flag %s should be disabled when security provider is configured and show warning", async (flag) => { + const dir = tempDirWithFiles("autoinstall-security-cli", { + "index.js": "import isEven from 'is-even'; console.log(isEven(2));", + "bunfig.toml": ` +[install.security] +provider = "example-security-provider" +`, + }); + + const { stderr, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), flag, "index.js"], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const stderrStr = stderr?.toString("utf8") || ""; + + // Should show warning about autoinstall being disabled + expect(stderrStr).toContain("warning: Autoinstall is disabled because a security provider is configured"); + + // Should not autoinstall even with explicit CLI flags when security provider is set + expect(stderrStr).toContain("error: Cannot find package 'is-even'"); + expect(exitCode).not.toBe(0); + }); +});