Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
9d9d6773ea fix: validate define keys are valid JavaScript identifiers in Bun.build
Previously, Bun.build would accept any string as a define key, including
invalid JavaScript identifiers like "invalid-name" or "123invalid". This
could lead to generated code with syntax errors.

Now validates that define keys are valid JavaScript identifiers using
js_lexer.isIdentifier() and throws a clear error message when invalid.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 23:26:42 +00:00
2 changed files with 74 additions and 0 deletions

View File

@@ -577,6 +577,12 @@ pub const JSBundler = struct {
const key = try prop.toOwnedSlice(bun.default_allocator);
// Validate that the key is a valid JS identifier
if (!js_lexer.isIdentifier(key)) {
defer bun.default_allocator.free(key);
return globalThis.throwInvalidArguments("define \"{s}\" is not a valid JavaScript identifier", .{key});
}
// value is always cloned
const value = val.toSlice(bun.default_allocator);
defer value.deinit();
@@ -1540,6 +1546,7 @@ const CompileTarget = @import("../../compile_target.zig");
const Fs = @import("../../fs.zig");
const resolve_path = @import("../../resolver/resolve_path.zig");
const std = @import("std");
const js_lexer = @import("../../js_lexer.zig");
const options = @import("../../options.zig");
const Loader = options.Loader;

View File

@@ -0,0 +1,67 @@
import { test, expect } from "bun:test";
import { tempDir } from "harness";
test("Bun.build should validate that define keys are valid JavaScript identifiers", async () => {
using dir = tempDir("define-validation", {
"entry.js": `console.log("test");`,
});
// Test invalid identifiers
const invalidCases = [
["123invalid", "starts with number"],
["invalid-name", "contains hyphen"],
["invalid.name", "contains dot"],
["invalid name", "contains space"],
// Note: empty string is silently skipped by the property iterator
];
for (const [invalidId, description] of invalidCases) {
let errorThrown = false;
let errorMessage = "";
try {
await Bun.build({
entrypoints: [`${dir}/entry.js`],
define: {
[invalidId]: '"test"',
},
outdir: `${dir}/out`,
});
} catch (err) {
errorThrown = true;
errorMessage = err.message;
}
expect(errorThrown).toBe(true);
expect(errorMessage).toContain(`define "${invalidId}" is not a valid JavaScript identifier`);
}
// Test valid identifiers
const validIdentifiers = [
"validName",
"_private",
"$jquery",
"CONSTANT",
"camelCase",
"snake_case",
"PascalCase",
"a123",
"_",
"$",
"ñ", // Unicode letter
"日本語", // Unicode identifiers
];
for (const validId of validIdentifiers) {
const result = await Bun.build({
entrypoints: [`${dir}/entry.js`],
define: {
[validId]: '"test"',
},
outdir: `${dir}/out-${validId}`,
});
expect(result.success).toBe(true);
expect(result.logs).toHaveLength(0);
}
});