Skip to content

Validate app.bundlerOptions keys are objects in Bun.serve#30518

Closed
robobun wants to merge 2 commits into
mainfrom
farm/3e2839c7/bake-bundler-options-validate
Closed

Validate app.bundlerOptions keys are objects in Bun.serve#30518
robobun wants to merge 2 commits into
mainfrom
farm/3e2839c7/bake-bundler-options-validate

Conversation

@robobun

@robobun robobun commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Fuzzer found a debug assertion failure when passing non-object values for app.bundlerOptions (or its server / client / ssr sub-keys) to Bun.serve.

Bun.serve({ app: { bundlerOptions: { client: 128n } } });
// panic: reached unreachable code (debugAssert target.isObject() in JSValue.get)

getOptional(..., JSValue) returns any non-null/undefined value without type-checking, so a BigInt/number/string/etc. was passed straight into BuildConfigSubset.fromJS, which then called .getOptional() on it and tripped the isObject() assert.

Now throws a proper TypeError: 'app.bundlerOptions.client' must be an object instead.

Fingerprint: e7c62943558380b8

@robobun

robobun commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 2:54 PM PT - May 11th, 2026

@robobun, your commit 7d7c5fa has 1 failures in Build #53483 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30518

That installs a local version of the PR into your bun-30518 executable, so you can run:

bun-30518 --bun

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cdab4b88-0165-44fc-ab89-fa858f9775b8

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd0338 and 7d7c5fa.

📒 Files selected for processing (1)
  • test/js/bun/http/bun-serve-args.test.ts

Walkthrough

BuildConfigSubset.fromJS now accepts a comptime name parameter and includes it in validation error messages. UserOptions.fromJS calls this function with explicit subset identifiers for server, client, and ssr bundler options. New tests verify that these subsets reject primitive values with specific TypeError messages.

Changes

Bundler Options Validation

Layer / File(s) Summary
Validation Signature
src/bake/bake.zig
BuildConfigSubset.fromJS accepts comptime name parameter and embeds it in object-type validation error messages as app.bundlerOptions.<name>.
Validation Call Sites
src/bake/bake.zig
UserOptions.fromJS calls BuildConfigSubset.fromJS with explicit "server", "client", and "ssr" identifiers for each bundler option subset.
Validation Tests
test/js/bun/http/bun-serve-args.test.ts
New parameterized test suite validates that bundlerOptions and each of its subsets (server, client, ssr) reject primitive values (bigint, number, string, boolean, symbol) by throwing TypeError.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides context about the fuzzer-found issue, reproduction steps, root cause analysis, and the fix, but is missing the required template sections. Add 'What does this PR do?' and 'How did you verify your code works?' sections as specified in the description template.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding validation for bundlerOptions keys in Bun.serve to ensure they are objects.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/bun/http/bun-serve-args.test.ts`:
- Around line 677-690: The tests currently only assert the exception type
(toThrow(TypeError)); tighten them to assert the specific error message so
path-specific messaging is enforced: update the two expectations inside the
test.each blocks that call serve(...) to use toThrowError(...) (or
toThrowError(/regex/)) and match the exact message that your runtime now
produces (e.g. include "app.bundlerOptions" for the top-level case and
`bundlerOptions.${key}` for the per-key case) so the serve(...) validation error
text is verified rather than just its type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d1dbf57-ecfa-41c9-b4bd-291c5ba6762c

📥 Commits

Reviewing files that changed from the base of the PR and between 37bfbed and 0cd0338.

📒 Files selected for processing (2)
  • src/bake/bake.zig
  • test/js/bun/http/bun-serve-args.test.ts

Comment thread test/js/bun/http/bun-serve-args.test.ts
@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. bake: validate bundlerOptions values are objects before property access #30125 - Also validates bundlerOptions values are objects in src/bake/bake.zig to prevent assertion panics from primitives
  2. Validate app.bundlerOptions types in Bun.serve #30402 - Same isObject() validation fix for app.bundlerOptions and sub-keys in Bun.serve, modifies the same files

🤖 Generated with Claude Code

@robobun

robobun commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #30402 (and #30125), which additionally cover the minify sub-option crash that this PR misses.

@robobun robobun closed this May 11, 2026
@robobun robobun deleted the farm/3e2839c7/bake-bundler-options-validate branch May 11, 2026 21:53
Comment thread src/bake/bake.zig
Comment on lines +208 to +211
pub fn fromJS(global: *jsc.JSGlobalObject, comptime name: []const u8, js_options: JSValue) bun.JSError!BuildConfigSubset {
if (!js_options.isObject()) {
return global.throwInvalidArguments("'" ++ api_name ++ ".bundlerOptions." ++ name ++ "' must be an object", .{});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 This fix is incomplete — the nested minify option just below has the exact same bug. Bun.serve({ app: { bundlerOptions: { client: { minify: false } } } }) (or minify: 128n) still falls through to minify_options.getBooleanLoose(...) on a non-object and trips the same debugAssert(target.isObject()) this PR is fixing. The boolean guard should handle false (and non-objects should throw) before calling getBooleanLoose.

Extended reasoning...

What the bug is

This PR adds an isObject() guard at the top of BuildConfigSubset.fromJS (src/bake/bake.zig:209-211) to prevent debugAssert(target.isObject()) from firing when a non-object is passed for bundlerOptions.{server,client,ssr}. However, immediately below in the same function, the nested minify sub-option is read via getOptional(global, "minify", JSValue) and — if it isn't the boolean true — passed straight to getBooleanLoose, which has the exact same precondition.

Code path

In BuildConfigSubset.fromJS:

if (try js_options.getOptional(global, "minify", JSValue)) |minify_options| brk: {
    if (minify_options.isBoolean() and minify_options.asBoolean()) {
        // ... handle 'true'
        break :brk;
    }
    if (try minify_options.getBooleanLoose(global, "whitespace")) |value| { ... }

getBooleanLoose (src/jsc/JSValue.zig:1866-1868) calls this.get(global, property_name), and get() begins with bun.debugAssert(target.isObject()) (src/jsc/JSValue.zig:1534). So calling getBooleanLoose on a non-object primitive trips the same assertion this PR was opened to fix.

Why the existing guard doesn't help

The new isObject() check at line 209 validates js_options (the client/server/ssr object), but minify_options is fetched from inside it via getOptional(..., JSValue), which returns any non-null/undefined value untyped. The only guard on minify_options is isBoolean() and asBoolean(), which only short-circuits for true.

Step-by-step proof

With Bun.serve({ app: { bundlerOptions: { client: { minify: false } } } }):

  1. js_options = { minify: false } → passes the new isObject() check at line 209.
  2. getOptional(global, "minify", JSValue) returns the JS boolean false.
  3. Guard: minify_options.isBoolean() = true, minify_options.asBoolean() = falsetrue and false = false. Does not break :brk.
  4. Falls through to minify_options.getBooleanLoose(global, "whitespace").
  5. getBooleanLoose calls this.get(...)bun.debugAssert(false.isObject())panic in debug builds.

The same happens with minify: 128n / 5 / "str" / Symbol() (step 3: isBoolean() is false, so the AND is false, fall through). The minify: false case is particularly notable because it's a perfectly reasonable thing for a user to write.

Impact

Same fingerprint class (debugAssert target.isObject() in JSValue.get) as the crash this PR is fixing, in the very function this PR modified. The fuzzer will hit it next.

Fix

Either change the boolean guard to handle both values (if (minify_options.isBoolean()) { const b = minify_options.asBoolean(); options.minify_* = b; break :brk; }), or add an if (!minify_options.isObject()) return global.throwInvalidArguments(...) after the boolean check, mirroring what was done for js_options. The new tests should also cover { [key]: { minify: value } } for the same primitive set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant