Skip to content

bake: validate app.bundlerOptions and its subfields are objects#30409

Closed
robobun wants to merge 1 commit into
mainfrom
farm/8893613d/bake-bundler-options-validate
Closed

bake: validate app.bundlerOptions and its subfields are objects#30409
robobun wants to merge 1 commit into
mainfrom
farm/8893613d/bake-bundler-options-validate

Conversation

@robobun

@robobun robobun commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes a debug assertion failure (reached unreachable code in JSValue.get) when Bun.serve is passed an app.bundlerOptions config where bundlerOptions, server, client, ssr, or minify are non-object primitives.

Bun.serve({ app: { bundlerOptions: { server: 551 } } });
// before: panic(main thread): reached unreachable code
// after:  TypeError: 'app.bundlerOptions.server' must be an object

Also fixes the minify handling to correctly break out of the block when minify: false is passed (previously fell through to property access on a boolean, which happened to work only because false.whitespace returns undefined in JS but would assert in debug builds).

Found by Fuzzilli. Fingerprint: f511e8d983505005

Bun.serve({ app: { bundlerOptions: { server: 551 } } }) and similar
non-object values for bundlerOptions / server / client / ssr / minify
would hit a debug assertion in JSValue.get instead of throwing a
TypeError.
@robobun

robobun commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:55 PM PT - May 8th, 2026

@robobun, your commit f2d01de has 1 failures in Build #52869 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30409

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

bun-30409 --bun

@github-actions github-actions Bot added the claude label May 8, 2026
@coderabbitai

coderabbitai Bot commented May 8, 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: 9f9f4ea7-4cec-43ef-ba1a-f4214c8555bd

📥 Commits

Reviewing files that changed from the base of the PR and between 1600acc and f2d01de.

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

Walkthrough

The PR adds runtime type validation to Bun.serve's configuration parsing. UserOptions.fromJS now validates bundlerOptions and its nested server, client, and ssr properties are objects. BuildConfigSubset.fromJS refactors minify handling with explicit boolean branching and type checking. Tests verify the validation behavior and error messages.

Changes

bundlerOptions Validation

Layer / File(s) Summary
UserOptions bundlerOptions Validation
src/bake/bake.zig
UserOptions.fromJS validates bundlerOptions and nested server/client/ssr are objects, throwing targeted errors when type constraints are violated.
BuildConfigSubset minify Refactoring
src/bake/bake.zig
BuildConfigSubset.fromJS refactors minify with explicit boolean branching (true enables all minification flags, false disables) and type validation for non-boolean/non-object values.
Validation Tests
test/js/bun/http/bun-serve-args.test.ts
New test suite validates bundlerOptions object-type constraints and minify property type checking, asserting exact error messages for invalid inputs.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: adding validation for app.bundlerOptions and its subfields to ensure they are objects.
Description check ✅ Passed The description includes both required sections: a clear explanation of what the PR does (fixing validation and error handling) and verification details (before/after example and reference to Fuzzilli discovery).
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.

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. bake: validate bundlerOptions values are objects before property access #30125 - Also adds isObject() guards for bundlerOptions, bundlerOptions.{server,client,ssr}, and fixes minify boolean handling in BuildConfigSubset.fromJS in src/bake/bake.zig; triggered by the same Fuzzilli fuzzer crashes.
  2. Validate app.bundlerOptions types in Bun.serve #30402 - Also validates app.bundlerOptions and sub-field types in Bun.serve, fixes the same minify: false fall-through bug, and adds tests to the same file (test/js/bun/http/bun-serve-args.test.ts).

🤖 Generated with Claude Code

@robobun

robobun commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #30125.

@robobun robobun closed this May 8, 2026
@robobun robobun deleted the farm/8893613d/bake-bundler-options-validate branch May 8, 2026 19:54
Comment thread src/bake/bake.zig
Comment on lines +230 to 237
if (minify_options.isBoolean()) {
if (minify_options.asBoolean()) {
options.minify_syntax = true;
options.minify_identifiers = true;
options.minify_whitespace = true;
}
break :brk;
}

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.

🔴 When minify is the boolean false, this enters the isBoolean() branch, skips the inner if (asBoolean()) block, and breaks with minify_syntax/minify_identifiers/minify_whitespace still null — so in production (bake.zig:792-794 does orelse (mode != .development)) an explicit minify: false is silently ignored and minification stays on. This is technically pre-existing release-build behavior, but since this PR is rewriting these exact lines to "fix the minify handling" it's the right place to also fix the semantic: drop the inner guard and assign minify_options.asBoolean() to all three fields (matching JSBundler.zig:694-698).

Extended reasoning...

What the bug is

BuildConfigSubset.fromJS parses app.bundlerOptions.{server,client,ssr}.minify. After this PR the boolean path is:

if (minify_options.isBoolean()) {
    if (minify_options.asBoolean()) {
        options.minify_syntax = true;
        options.minify_identifiers = true;
        options.minify_whitespace = true;
    }
    break :brk;
}

When the user passes minify: false, isBoolean() is true so we enter the outer branch, but asBoolean() is false so we skip the assignments and break :brk. The three ?bool fields on BuildConfigSubset keep their default value of null.

Code path that triggers it

  1. production.zig:238-241 calls framework.initTranspilerWithOptions(..., .production_static, ..., options.bundler_options.server.minify_whitespace, options.bundler_options.server.minify_syntax, options.bundler_options.server.minify_identifiers) (and likewise for client / ssr).
  2. In bake.zig:792-794:
    out.options.minify_syntax = minify_syntax orelse (mode != .development);
    out.options.minify_identifiers = minify_identifiers orelse (mode != .development);
    out.options.minify_whitespace = minify_whitespace orelse (mode != .development);
    With mode = .production_static, null orelse (mode != .development) evaluates to true.

So app.bundlerOptions.server.minify = false (and client/ssr) produces a fully-minified production build.

Why existing code doesn't prevent it

Nothing else writes these fields. The null → default fallback in initTranspilerWithOptions is intentional for the unset case, but here the user explicitly set a value and we dropped it. Note that the object form works correctlyminify: { whitespace: false, syntax: false, identifiers: false } reaches the getBooleanLoose calls and sets the fields to false, so the boolean shorthand is now inconsistent with the long form.

Step-by-step proof

Config:

Bun.serve({ app: { bundlerOptions: { client: { minify: false } }, framework: ... } });
  • fromJS sees minify_options = JS false.
  • minify_options.isBoolean()true, enter branch.
  • minify_options.asBoolean()false, skip inner block.
  • break :brkoptions.minify_whitespace == null, options.minify_syntax == null, options.minify_identifiers == null.
  • Production build: production.zig:239 passes those three nulls into initTranspilerWithOptions.
  • bake.zig:792-794: null orelse (.production_static != .development)null orelse truetrue.
  • Result: client transpiler has minify_syntax = minify_identifiers = minify_whitespace = true. The user's minify: false was a no-op.

Impact

Users who try to disable minification for a Bake production build via the boolean shorthand get minified output anyway, with no error or warning. The only escape hatches are the object form or --debug-disable-minify (production.zig:244-248). This is surprising because every other minify parser in the repo honors false.

Relationship to this PR / pre-existing behavior

In release builds the old code had the same net effect: the old condition was if (minify_options.isBoolean() and minify_options.asBoolean()), which was false for minify: false, and execution then fell through to getBooleanLoose(global, "whitespace") on a boolean primitive — false.whitespace is undefined, so the fields stayed null. So the semantic bug pre-dates this PR.

However, this PR explicitly restructures these exact lines and the description says it "fixes the minify handling to correctly break out of the block when minify: false is passed." It fixes the debug assert (good) but preserves the semantic no-op. Since the author is already rewriting this hunk and the correct behavior is well-established elsewhere, this is the right PR to fix it rather than leave minify: false half-broken.

Fix

Drop the inner guard and assign the boolean to all three fields, matching JSBundler.zig:694-698, JSTranspiler.zig, and bunfig.zig:

if (minify_options.isBoolean()) {
    const value = minify_options.asBoolean();
    options.minify_syntax = value;
    options.minify_identifiers = value;
    options.minify_whitespace = value;
    break :brk;
}

(Equivalently, add an else that sets all three to false.)

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