Compare commits

..

1 Commits

Author SHA1 Message Date
Claude Bot
7f57c9c747 fix(debugger): use crypto.randomUUID() for WebSocket auth token
Replace Math.random().toString(36) with crypto.randomUUID() for generating
the debugger WebSocket path token. Math.random() is not cryptographically
secure and produces predictable output, making it possible for local
attackers to guess the token and connect to the debugger via Chrome DevTools
Protocol. This is the same class of vulnerability as Node.js CVE-2018-7160.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-12 04:44:25 +00:00
8 changed files with 16 additions and 166 deletions

View File

@@ -11,24 +11,13 @@ url: strings.StringOrTinyString,
package_manager: *PackageManager,
pub inline fn run(this: *const ExtractTarball, log: *logger.Log, bytes: []const u8) !Install.ExtractData {
if (!this.skip_verify) {
if (this.integrity.tag.isSupported()) {
if (!this.integrity.verify(bytes)) {
log.addErrorFmt(
null,
logger.Loc.Empty,
bun.default_allocator,
"Integrity check failed<r> for tarball: {s}",
.{this.name.slice()},
) catch unreachable;
return error.IntegrityCheckFailed;
}
} else if (this.resolution.tag == .npm) {
if (!this.skip_verify and this.integrity.tag.isSupported()) {
if (!this.integrity.verify(bytes)) {
log.addErrorFmt(
null,
logger.Loc.Empty,
bun.default_allocator,
"Missing or unsupported integrity hash for tarball: {s}",
"Integrity check failed<r> for tarball: {s}",
.{this.name.slice()},
) catch unreachable;
return error.IntegrityCheckFailed;

View File

@@ -1868,10 +1868,6 @@ pub fn parseIntoBinaryLockfile(
};
pkg.meta.integrity = Integrity.parse(integrity_str);
if (integrity_str.len > 0 and !pkg.meta.integrity.tag.isSupported()) {
try log.addError(source, integrity_expr.loc, "Unsupported integrity hash algorithm");
return error.InvalidPackageInfo;
}
},
inline .git, .github => |tag| {
// .bun-tag

View File

@@ -541,15 +541,13 @@ pub fn migrateNPMLockfile(
.false;
} else .false,
.integrity = if (pkg.get("integrity")) |integrity| blk: {
const integrity_str = integrity.asString(this.allocator) orelse
return error.InvalidNPMLockfile;
const parsed = Integrity.parse(integrity_str);
if (integrity_str.len > 0 and !parsed.tag.isSupported()) {
return error.InvalidNPMLockfile;
}
break :blk parsed;
} else Integrity{},
.integrity = if (pkg.get("integrity")) |integrity|
Integrity.parse(
integrity.asString(this.allocator) orelse
return error.InvalidNPMLockfile,
)
else
Integrity{},
},
.bin = if (pkg.get("bin")) |bin| bin: {
// we already check these conditions during counting

View File

@@ -613,9 +613,6 @@ pub fn migratePnpmLockfile(
};
pkg.meta.integrity = Integrity.parse(integrity_str);
if (integrity_str.len > 0 and !pkg.meta.integrity.tag.isSupported()) {
return invalidPnpmLockfile();
}
}
}

View File

@@ -997,14 +997,10 @@ pub fn migrateYarnLockfile(
} else .all,
.man_dir = String{},
.has_install_script = .false,
.integrity = if (entry.integrity) |integrity| blk: {
const parsed = Integrity.parse(integrity);
if (integrity.len > 0 and !parsed.tag.isSupported()) {
try log.addError(null, logger.Loc.Empty, "Unsupported integrity hash algorithm in yarn.lock");
return error.InvalidYarnLock;
}
break :blk parsed;
} else Integrity{},
.integrity = if (entry.integrity) |integrity|
Integrity.parse(integrity)
else
Integrity{},
},
.bin = Bin.init(),
.scripts = .{},

View File

@@ -591,7 +591,7 @@ function parseUrl(input: string): URL {
}
function randomId() {
return Math.random().toString(36).slice(2);
return crypto.randomUUID();
}
const { enableANSIColors } = Bun;

View File

@@ -9,7 +9,7 @@ import { InspectorSession, JUnitReporter, connect } from "./junit-reporter";
import { SocketFramer } from "./socket-framer";
let inspectee: Subprocess;
const anyPort = expect.stringMatching(/^\d+$/);
const anyPathname = expect.stringMatching(/^\/[a-z0-9]+$/);
const anyPathname = expect.stringMatching(/^\/[a-f0-9-]+$/);
/**
* Get a function that creates a random `.sock` file in the specified temporary directory.

View File

@@ -1,126 +0,0 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("lockfile with unsupported integrity hash algorithm should fail", async () => {
using dir = tempDir("unsupported-integrity", {
"package.json": JSON.stringify({
name: "test-unsupported-integrity",
dependencies: {
"is-number": "7.0.0",
},
}),
"bun.lock": JSON.stringify(
{
lockfileVersion: 1,
configVersion: 1,
workspaces: {
"": {
name: "test-unsupported-integrity",
dependencies: {
"is-number": "7.0.0",
},
},
},
packages: {
"is-number": ["is-number@7.0.0", "", {}, "md5-AAAAAAAAAA=="],
},
},
null,
2,
),
});
await using proc = Bun.spawn({
cmd: [bunExe(), "install", "--frozen-lockfile"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toContain("Unsupported integrity hash algorithm");
expect(exitCode).toBe(1);
});
test("lockfile with valid integrity hash algorithm should succeed", async () => {
// First, create a real lockfile by installing
using dir = tempDir("valid-integrity", {
"package.json": JSON.stringify({
name: "test-valid-integrity",
dependencies: {
"is-number": "7.0.0",
},
}),
});
// Run install to generate a valid lockfile
await using installProc = Bun.spawn({
cmd: [bunExe(), "install"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const installExitCode = await installProc.exited;
expect(installExitCode).toBe(0);
// Now run with --frozen-lockfile to verify it works
await using proc = Bun.spawn({
cmd: [bunExe(), "install", "--frozen-lockfile"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).not.toContain("Unsupported integrity hash algorithm");
expect(exitCode).toBe(0);
});
test("lockfile with garbage integrity string should fail", async () => {
using dir = tempDir("garbage-integrity", {
"package.json": JSON.stringify({
name: "test-garbage-integrity",
dependencies: {
"is-number": "7.0.0",
},
}),
"bun.lock": JSON.stringify(
{
lockfileVersion: 1,
configVersion: 1,
workspaces: {
"": {
name: "test-garbage-integrity",
dependencies: {
"is-number": "7.0.0",
},
},
},
packages: {
"is-number": ["is-number@7.0.0", "", {}, "not-a-real-hash"],
},
},
null,
2,
),
});
await using proc = Bun.spawn({
cmd: [bunExe(), "install", "--frozen-lockfile"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toContain("Unsupported integrity hash algorithm");
expect(exitCode).toBe(1);
});