diff --git a/src/install/PackageManager/install_with_manager.zig b/src/install/PackageManager/install_with_manager.zig index f32b49da24..a355824670 100644 --- a/src/install/PackageManager/install_with_manager.zig +++ b/src/install/PackageManager/install_with_manager.zig @@ -1243,20 +1243,22 @@ fn performSecurityScanAfterResolution( Global.exit(1); }; - // Level is optional, defaults to "warn" - var level = SecurityAdvisoryLevel.warn; - if (item_obj.get("level")) |level_expr| { - if (level_expr.asString(manager.allocator)) |level_str| { - if (std.mem.eql(u8, level_str, "fatal")) { - level = .fatal; - } else if (std.mem.eql(u8, level_str, "warn")) { - level = .warn; - } else { - Output.errGeneric("Security advisory at index {d} 'level' field must be 'fatal' or 'warn', got: '{s}'", .{ i, level_str }); - Global.exit(1); - } - } - } + const level_expr = item_obj.get("level") orelse { + Output.errGeneric("Security advisory at index {d} missing required 'level' field", .{i}); + Global.exit(1); + }; + const level_str = level_expr.asString(manager.allocator) orelse { + Output.errGeneric("Security advisory at index {d} 'level' field must be a string", .{i}); + Global.exit(1); + }; + const level = if (std.mem.eql(u8, level_str, "fatal")) + SecurityAdvisoryLevel.fatal + else if (std.mem.eql(u8, level_str, "warn")) + SecurityAdvisoryLevel.warn + else { + Output.errGeneric("Security advisory at index {d} 'level' field must be 'fatal' or 'warn', got: '{s}'", .{ i, level_str }); + Global.exit(1); + }; const advisory = SecurityAdvisory{ .level = level, diff --git a/test/cli/install/bun-install-security-provider.test.ts b/test/cli/install/bun-install-security-provider.test.ts index 546941dded..4a1550341e 100644 --- a/test/cli/install/bun-install-security-provider.test.ts +++ b/test/cli/install/bun-install-security-provider.test.ts @@ -334,6 +334,20 @@ describe("Invalid Advisory Formats", () => { }, }); + run("advisory missing level field", { + scanner: async () => [ + { + package: "bar", + description: "Missing level", + url: "https://example.com", + } as any, + ], + expectedExitCode: 1, + expect: ({ err }) => { + expect(err).toContain("Security advisory at index 0 missing required 'level' field"); + }, + }); + run("advisory url field not string", { scanner: async () => [ { @@ -375,7 +389,7 @@ describe("Invalid Advisory Formats", () => { ], expectedExitCode: 1, expect: ({ err }) => { - expect(err).toContain("Security advisory at index 0 'level' field must be 'fatal' or 'warn'"); + expect(err).toContain("Security advisory at index 0 'level' field must be a string"); }, }); @@ -573,7 +587,7 @@ describe("Edge Cases", () => { }, }); - run("advisory without optional level field defaults to warn", { + run("advisory without level field", { scanner: async ({ packages }) => [ { package: packages[0].name, @@ -581,14 +595,13 @@ describe("Edge Cases", () => { url: "https://example.com", } as any, ], - expectedExitCode: 0, - expect: ({ out }) => { - expect(out).toContain("WARN: bar"); - expect(out).toContain("No level specified"); + expectedExitCode: 1, + expect: ({ err }) => { + expect(err).toContain("Security advisory at index 0 missing required 'level' field"); }, }); - run("null values in optional fields", { + run("null values in level field", { scanner: async ({ packages }) => [ { package: packages[0].name, @@ -597,9 +610,9 @@ describe("Edge Cases", () => { url: "https://example.com", }, ], - expectedExitCode: 0, // null level should default to warn - expect: ({ out }) => { - expect(out).toContain("WARN: bar"); + expectedExitCode: 1, + expect: ({ err }) => { + expect(err).toContain("Security advisory at index 0 'level' field must be a string"); }, }); });