fix(audit): exclude workspace devDependencies in --prod mode (#26675)

When running `bun audit --prod` from a monorepo root, devDependencies
of workspace packages were incorrectly being included in the audit.
The bug was in the BFS traversal of buildProductionPackageSet(), which
didn't check isDev() for transitive dependencies.

This fix adds the missing isDev() check so workspace package
devDependencies are properly excluded when auditing production deps.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2026-02-01 19:23:28 +00:00
parent ddefa11070
commit 714c7bdcea
2 changed files with 174 additions and 1 deletions

View File

@@ -220,7 +220,9 @@ fn buildProductionPackageSet(allocator: std.mem.Allocator, pm: *PackageManager,
const current_dep_slice = current_deps.get(dependencies); const current_dep_slice = current_deps.get(dependencies);
const current_res_slice = current_resolutions.get(resolutions); const current_res_slice = current_resolutions.get(resolutions);
for (current_dep_slice, current_res_slice) |_, resolved_pkg_id| { for (current_dep_slice, current_res_slice) |dep, resolved_pkg_id| {
// Skip devDependencies - they should not be included in production audit
if (dep.behavior.isDev()) continue;
if (resolved_pkg_id >= pkg_names.len) continue; if (resolved_pkg_id >= pkg_names.len) continue;
const pkg_name = pkg_names[resolved_pkg_id].slice(buf); const pkg_name = pkg_names[resolved_pkg_id].slice(buf);

View File

@@ -0,0 +1,171 @@
import { spawn } from "bun";
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { bunEnv, bunExe, gunzipJsonRequest, tempDir } from "harness";
// Test for GitHub issue #26675:
// `bun audit --prod` should not include devDependencies of workspace packages
let server: Bun.Server;
beforeAll(() => {
server = Bun.serve({
port: 0,
fetch: async req => {
const body = await gunzipJsonRequest(req);
// Return vulnerabilities for ms@0.7.0, empty for everything else
if (body && body.ms && body.ms.includes("0.7.0")) {
return Response.json({
ms: [
{
id: 1094419,
url: "https://github.com/advisories/GHSA-w9mr-4mfr-499f",
title: "Vercel ms Inefficient Regular Expression Complexity vulnerability",
severity: "moderate",
vulnerable_versions: "<2.0.0",
},
],
});
}
return Response.json({});
},
});
});
afterAll(() => {
server.stop();
});
const fakeIntegrity = "sha512-V8E0l1jyyeSSS9R+J9oljx5eq2rqzClInuwaPcyuv0Mm3ViI/3/rcc4rCEO8i4eQ4I0O0FAGYDA2i5xWHHPhzg==";
describe("issue #26675 - bun audit --prod with workspace devDependencies", () => {
test("--prod flag should exclude devDependencies of workspace packages", async () => {
using dir = tempDir("bun-test-audit-workspace-prod", {
"package.json": JSON.stringify({
name: "test-monorepo",
private: true,
workspaces: ["packages/*"],
}),
"packages/frontend/package.json": JSON.stringify({
name: "test-frontend",
devDependencies: {
ms: "0.7.0",
},
}),
"bun.lock": JSON.stringify({
lockfileVersion: 1,
workspaces: {
"": {
name: "test-monorepo",
},
"packages/frontend": {
name: "test-frontend",
devDependencies: {
ms: "0.7.0",
},
},
},
packages: {
"test-frontend": ["test-frontend@workspace:packages/frontend"],
ms: ["ms@0.7.0", "", {}, fakeIntegrity],
},
}),
});
const url = server.url.toString().slice(0, -1);
// First, verify that without --prod, the vulnerability is reported
{
await using proc = spawn({
cmd: [bunExe(), "audit"],
stdout: "pipe",
stderr: "pipe",
cwd: String(dir),
env: {
...bunEnv,
NPM_CONFIG_REGISTRY: url,
},
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("ms");
expect(stdout).toContain("vulnerabilities");
expect(exitCode).toBe(1);
}
// Now verify that with --prod, the devDependency vulnerability is NOT reported
{
await using proc = spawn({
cmd: [bunExe(), "audit", "--prod"],
stdout: "pipe",
stderr: "pipe",
cwd: String(dir),
env: {
...bunEnv,
NPM_CONFIG_REGISTRY: url,
},
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("No vulnerabilities found");
expect(exitCode).toBe(0);
}
});
test("--prod flag should still report production dependencies of workspace packages", async () => {
using dir = tempDir("bun-test-audit-workspace-prod-deps", {
"package.json": JSON.stringify({
name: "test-monorepo",
private: true,
workspaces: ["packages/*"],
}),
"packages/frontend/package.json": JSON.stringify({
name: "test-frontend",
dependencies: {
ms: "0.7.0",
},
}),
"bun.lock": JSON.stringify({
lockfileVersion: 1,
workspaces: {
"": {
name: "test-monorepo",
},
"packages/frontend": {
name: "test-frontend",
dependencies: {
ms: "0.7.0",
},
},
},
packages: {
"test-frontend": ["test-frontend@workspace:packages/frontend"],
ms: ["ms@0.7.0", "", {}, fakeIntegrity],
},
}),
});
const url = server.url.toString().slice(0, -1);
// With --prod, production dependency vulnerabilities should still be reported
await using proc = spawn({
cmd: [bunExe(), "audit", "--prod"],
stdout: "pipe",
stderr: "pipe",
cwd: String(dir),
env: {
...bunEnv,
NPM_CONFIG_REGISTRY: url,
},
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("ms");
expect(stdout).toContain("vulnerabilities");
expect(exitCode).toBe(1);
});
});