diff --git a/docs/bundler/loaders.md b/docs/bundler/loaders.md index c3e70535cd..72ec911ca2 100644 --- a/docs/bundler/loaders.md +++ b/docs/bundler/loaders.md @@ -141,7 +141,7 @@ During bundling, the parsed YAML is inlined into the bundle as a JavaScript obje var config = { database: { host: "localhost", - port: 5432 + port: 5432, }, // ...other fields }; diff --git a/docs/guides/runtime/import-yaml.md b/docs/guides/runtime/import-yaml.md index 791d6c96a2..c13e1d6cd8 100644 --- a/docs/guides/runtime/import-yaml.md +++ b/docs/guides/runtime/import-yaml.md @@ -73,4 +73,4 @@ console.log(data.hobbies); // => ["reading", "coding"] --- -See [Docs > API > YAML](https://bun.com/docs/api/yaml) for complete documentation on YAML support in Bun. \ No newline at end of file +See [Docs > API > YAML](https://bun.com/docs/api/yaml) for complete documentation on YAML support in Bun. diff --git a/src/cli.zig b/src/cli.zig index c00479ab6d..4640b5277c 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -635,15 +635,18 @@ pub const Command = struct { // bun build --compile entry point if (!bun.getRuntimeFeatureFlag(.BUN_BE_BUN)) { if (try bun.StandaloneModuleGraph.fromExecutable(bun.default_allocator)) |graph| { - var offset_for_passthrough: usize = if (bun.argv.len > 1) 1 else 0; + var offset_for_passthrough: usize = 0; const ctx: *ContextData = brk: { if (graph.compile_exec_argv.len > 0) { + const original_argv_len = bun.argv.len; var argv_list = std.ArrayList([:0]const u8).fromOwnedSlice(bun.default_allocator, bun.argv); try bun.appendOptionsEnv(graph.compile_exec_argv, &argv_list, bun.default_allocator); - offset_for_passthrough += (argv_list.items.len -| bun.argv.len); bun.argv = argv_list.items; + // Calculate offset: skip executable name + all exec argv options + offset_for_passthrough = if (bun.argv.len > 1) 1 + (bun.argv.len -| original_argv_len) else 0; + // Handle actual options to parse. break :brk try Command.init(allocator, log, .AutoCommand); } @@ -655,6 +658,10 @@ pub const Command = struct { .allocator = bun.default_allocator, }; global_cli_ctx = &context_data; + + // If no compile_exec_argv, set offset normally + offset_for_passthrough = if (bun.argv.len > 1) 1 else 0; + break :brk global_cli_ctx; }; diff --git a/test/bundler/compile-argv.test.ts b/test/bundler/compile-argv.test.ts index b1fad2c487..d81df175fe 100644 --- a/test/bundler/compile-argv.test.ts +++ b/test/bundler/compile-argv.test.ts @@ -46,4 +46,130 @@ describe("bundler", () => { stdout: /SUCCESS: process.title and process.execArgv are both set correctly/, }, }); + + // Test that exec argv options don't leak into process.argv when no user arguments are provided + itBundled("compile/CompileExecArgvNoLeak", { + compile: { + execArgv: ["--user-agent=test-agent", "--smol"], + }, + files: { + "/entry.ts": /* js */ ` + // Test that compile-exec-argv options don't appear in process.argv + console.log("execArgv:", JSON.stringify(process.execArgv)); + console.log("argv:", JSON.stringify(process.argv)); + + // Check that execArgv contains the expected options + if (process.execArgv.length !== 2) { + console.error("FAIL: Expected exactly 2 items in execArgv, got", process.execArgv.length); + process.exit(1); + } + + if (process.execArgv[0] !== "--user-agent=test-agent") { + console.error("FAIL: Expected --user-agent=test-agent in execArgv[0], got", process.execArgv[0]); + process.exit(1); + } + + if (process.execArgv[1] !== "--smol") { + console.error("FAIL: Expected --smol in execArgv[1], got", process.execArgv[1]); + process.exit(1); + } + + // Check that argv only contains the executable and script name, NOT the exec argv options + if (process.argv.length !== 2) { + console.error("FAIL: Expected exactly 2 items in argv (executable and script), got", process.argv.length, "items:", process.argv); + process.exit(1); + } + + // argv[0] should be "bun" for standalone executables + if (process.argv[0] !== "bun") { + console.error("FAIL: Expected argv[0] to be 'bun', got", process.argv[0]); + process.exit(1); + } + + // argv[1] should be the script path (contains the bundle path) + if (!process.argv[1].includes("bunfs")) { + console.error("FAIL: Expected argv[1] to contain 'bunfs' path, got", process.argv[1]); + process.exit(1); + } + + // Make sure exec argv options are NOT in process.argv + for (const arg of process.argv) { + if (arg.includes("--user-agent") || arg === "--smol") { + console.error("FAIL: exec argv option leaked into process.argv:", arg); + process.exit(1); + } + } + + console.log("SUCCESS: exec argv options are properly separated from process.argv"); + `, + }, + run: { + // No user arguments provided - this is the key test case + args: [], + stdout: /SUCCESS: exec argv options are properly separated from process.argv/, + }, + }); + + // Test that user arguments are properly passed through when exec argv is present + itBundled("compile/CompileExecArgvWithUserArgs", { + compile: { + execArgv: ["--user-agent=test-agent", "--smol"], + }, + files: { + "/entry.ts": /* js */ ` + // Test that user arguments are properly included when exec argv is present + console.log("execArgv:", JSON.stringify(process.execArgv)); + console.log("argv:", JSON.stringify(process.argv)); + + // Check execArgv + if (process.execArgv.length !== 2) { + console.error("FAIL: Expected exactly 2 items in execArgv, got", process.execArgv.length); + process.exit(1); + } + + if (process.execArgv[0] !== "--user-agent=test-agent" || process.execArgv[1] !== "--smol") { + console.error("FAIL: Unexpected execArgv:", process.execArgv); + process.exit(1); + } + + // Check argv contains executable, script, and user arguments + if (process.argv.length !== 4) { + console.error("FAIL: Expected exactly 4 items in argv, got", process.argv.length, "items:", process.argv); + process.exit(1); + } + + if (process.argv[0] !== "bun") { + console.error("FAIL: Expected argv[0] to be 'bun', got", process.argv[0]); + process.exit(1); + } + + if (!process.argv[1].includes("bunfs")) { + console.error("FAIL: Expected argv[1] to contain 'bunfs' path, got", process.argv[1]); + process.exit(1); + } + + if (process.argv[2] !== "user-arg1") { + console.error("FAIL: Expected argv[2] to be 'user-arg1', got", process.argv[2]); + process.exit(1); + } + + if (process.argv[3] !== "user-arg2") { + console.error("FAIL: Expected argv[3] to be 'user-arg2', got", process.argv[3]); + process.exit(1); + } + + // Make sure exec argv options are NOT mixed with user arguments + if (process.argv.includes("--user-agent=test-agent") || process.argv.includes("--smol")) { + console.error("FAIL: exec argv options leaked into process.argv"); + process.exit(1); + } + + console.log("SUCCESS: user arguments properly passed with exec argv present"); + `, + }, + run: { + args: ["user-arg1", "user-arg2"], + stdout: /SUCCESS: user arguments properly passed with exec argv present/, + }, + }); });