From e585f900c9c8c6b97d8ec36c941f7cb0de38685f Mon Sep 17 00:00:00 2001 From: dave caruso Date: Thu, 1 Aug 2024 01:04:20 -0700 Subject: [PATCH] escape windows in bun upgrade (#12985) --- src/bun.js/WebKit | 2 +- src/cli/upgrade_command.zig | 6 +- src/fmt.zig | 95 +++++++++++++------ src/js/internal-for-testing.ts | 16 +++- test/internal/highlighter.test.ts | 7 ++ test/internal/powershell-escape.test.ts | 11 +++ test/{internal => }/package-json-lint.test.ts | 0 7 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 test/internal/highlighter.test.ts create mode 100644 test/internal/powershell-escape.test.ts rename test/{internal => }/package-json-lint.test.ts (100%) diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit index 49907bff878171..f9a0fda2d2b2fd 160000 --- a/src/bun.js/WebKit +++ b/src/bun.js/WebKit @@ -1 +1 @@ -Subproject commit 49907bff8781719bc2ded068b0c934f6d0074d1e +Subproject commit f9a0fda2d2b2fd001a00bfcf8e7917a56b382516 diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index c89b011c195053..c5fca9ef5417c4 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -652,10 +652,10 @@ pub const UpgradeCommand = struct { // Run a powershell script to unzip the file const unzip_script = try std.fmt.allocPrint( ctx.allocator, - "$global:ProgressPreference='SilentlyContinue';Expand-Archive -Path {s} {s} -Force", + "$global:ProgressPreference='SilentlyContinue';Expand-Archive -Path \"{}\" \"{}\" -Force", .{ - tmpname, - tmpdir_path, + bun.fmt.escapePowershell(tmpname), + bun.fmt.escapePowershell(tmpdir_path), }, ); diff --git a/src/fmt.zig b/src/fmt.zig index 938469c277a284..943805d276c201 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -881,35 +881,6 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } } } - - /// Function for testing in highlighter.test.ts - pub fn jsFunctionSyntaxHighlight(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(bun.JSC.conv) bun.JSC.JSValue { - const args = callframe.arguments(1); - if (args.len < 1) { - globalThis.throwNotEnoughArguments("code", 1, 0); - } - - const code = args.ptr[0].toSliceOrNull(globalThis) orelse return .zero; - defer code.deinit(); - var buffer = bun.MutableString.initEmpty(bun.default_allocator); - defer buffer.deinit(); - var writer = buffer.bufferedWriter(); - var formatter = bun.fmt.fmtJavaScript(code.slice(), true); - formatter.limited = false; - std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { - globalThis.throwError(err, "Error formatting code"); - return .zero; - }; - - writer.flush() catch |err| { - globalThis.throwError(err, "Error formatting code"); - return .zero; - }; - - var str = bun.String.createUTF8(buffer.list.items); - defer str.deref(); - return str.toJS(globalThis); - } }; pub fn quote(self: string) bun.fmt.QuotedFormatter { @@ -1260,3 +1231,69 @@ pub fn NullableFallback(comptime T: type) type { } }; } + +pub fn escapePowershell(str: []const u8) std.fmt.Formatter(escapePowershellImpl) { + return .{ .data = str }; +} + +fn escapePowershellImpl(str: []const u8, comptime f: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + comptime bun.assert(f.len == 0); + var remain = str; + while (bun.strings.indexOfAny(remain, "\"`")) |i| { + try writer.writeAll(remain[0..i]); + try writer.writeAll("`"); + try writer.writeByte(remain[i]); + remain = remain[i + 1 ..]; + } + try writer.writeAll(remain); +} + +pub const fmt_js_test_bindings = struct { + const Formatter = enum { + fmtJavaScript, + escapePowershell, + }; + + /// Internal function for testing in highlighter.test.ts + pub fn jsFunctionStringFormatter(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(bun.JSC.conv) bun.JSC.JSValue { + const args = callframe.arguments(2); + if (args.len < 2) { + globalThis.throwNotEnoughArguments("code", 1, 0); + } + + const code = args.ptr[0].toSliceOrNull(globalThis) orelse + return .zero; + defer code.deinit(); + + var buffer = bun.MutableString.initEmpty(bun.default_allocator); + defer buffer.deinit(); + var writer = buffer.bufferedWriter(); + + const formatter_id: Formatter = @enumFromInt(args.ptr[1].toInt32()); + switch (formatter_id) { + .fmtJavaScript => { + var formatter = bun.fmt.fmtJavaScript(code.slice(), true); + formatter.limited = false; + std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + }, + .escapePowershell => { + std.fmt.format(writer.writer(), "{}", .{escapePowershell(code.slice())}) catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + }, + } + + writer.flush() catch |err| { + globalThis.throwError(err, "Error formatting"); + return .zero; + }; + + var str = bun.String.createUTF8(buffer.list.items); + defer str.deref(); + return str.toJS(globalThis); + } +}; diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index 9314943ab712d4..9640e38544791d 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -7,11 +7,14 @@ /// -export const quickAndDirtyJavaScriptSyntaxHighlighter = $newZigFunction( +const fmtBinding = $newZigFunction( "fmt.zig", - "QuickAndDirtyJavaScriptSyntaxHighlighter.jsFunctionSyntaxHighlight", + "fmt_js_test_bindings.jsFunctionStringFormatter", 2, -) as (code: string) => string; +) as (code: string, id: number) => string; + +export const quickAndDirtyJavaScriptSyntaxHighlighter = (code: string) => fmtBinding(code, 0); +export const escapePowershell = (code: string) => fmtBinding(code, 1); export const TLSBinding = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); @@ -23,9 +26,12 @@ export const patchInternals = { makeDiff: $newZigFunction("patch.zig", "TestingAPIs.makeDiff", 2), }; +const shellLex = $newZigFunction("shell.zig", "TestingAPIs.shellLex", 2); +const shellParse = $newZigFunction("shell.zig", "TestingAPIs.shellParse", 2); + export const shellInternals = { - lex: (a, ...b) => $newZigFunction("shell.zig", "TestingAPIs.shellLex", 2)(a.raw, b), - parse: (a, ...b) => $newZigFunction("shell.zig", "TestingAPIs.shellParse", 2)(a.raw, b), + lex: (a, ...b) => shellLex(a.raw, b) , + parse: (a, ...b) => shellParse(a.raw, b), /** * Checks if the given builtin is disabled on the current platform * diff --git a/test/internal/highlighter.test.ts b/test/internal/highlighter.test.ts new file mode 100644 index 00000000000000..c2ae8201a03da8 --- /dev/null +++ b/test/internal/highlighter.test.ts @@ -0,0 +1,7 @@ +import { test, expect } from "bun:test"; +import { quickAndDirtyJavaScriptSyntaxHighlighter as highlighter } from "bun:internal-for-testing"; + +test("highlighter", () => { + expect(highlighter("`can do ${123} ${'123'} ${`123`}`").length).toBeLessThan(150); + expect(highlighter("`can do ${123} ${'123'} ${`123`}`123").length).toBeLessThan(150); +}); diff --git a/test/internal/powershell-escape.test.ts b/test/internal/powershell-escape.test.ts new file mode 100644 index 00000000000000..00657b0d509e1a --- /dev/null +++ b/test/internal/powershell-escape.test.ts @@ -0,0 +1,11 @@ + +import { escapePowershell } from "bun:internal-for-testing"; + +it("powershell escaping rules", () => { + // This formatter does not include quotes around the string intentionally + expect(escapePowershell("foo")).toBe("foo"); + expect(escapePowershell("foo bar")).toBe("foo bar"); + expect(escapePowershell("foo\" bar")).toBe("foo`\" bar"); + expect(escapePowershell("foo\" `bar")).toBe("foo`\" ``bar"); + expect(escapePowershell("foo\" ``\"bar")).toBe("foo`\" `````\"bar"); +}); diff --git a/test/internal/package-json-lint.test.ts b/test/package-json-lint.test.ts similarity index 100% rename from test/internal/package-json-lint.test.ts rename to test/package-json-lint.test.ts