From 62881ee36b166f49033e717cc19a2a65ca4acb1b Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:44:24 -0700 Subject: [PATCH] Redact secrets in `bunfig.toml` and `npmrc` logs (#14919) --- src/api/schema.zig | 5 +- src/bake/DevServer.zig | 8 +- src/bun.js/BuildMessage.zig | 2 +- src/bun.js/api/BunObject.zig | 8 +- src/bun.js/bindings/bindings.zig | 5 + src/bun.js/javascript.zig | 21 +- src/bun.zig | 2 +- src/bun_js.zig | 4 +- src/bundler.zig | 8 +- src/bundler/bundle_v2.zig | 10 +- src/bunfig.zig | 56 ++- src/cli.zig | 20 +- src/cli/build_command.zig | 6 +- src/cli/create_command.zig | 30 +- src/cli/install_command.zig | 2 +- src/cli/outdated_command.zig | 7 +- src/cli/pack_command.zig | 13 +- src/cli/pm_trusted_command.zig | 4 +- src/cli/publish_command.zig | 12 +- src/cli/run_command.zig | 24 +- src/cli/test_command.zig | 6 +- src/cli/upgrade_command.zig | 12 +- src/fmt.zig | 262 +++++++++++-- src/ini.zig | 268 +++++++------- src/install/install.zig | 64 ++-- src/install/lockfile.zig | 12 +- src/install/migration.zig | 2 +- src/install/npm.zig | 9 +- src/install/patch_install.zig | 33 +- src/js_parser.zig | 4 +- src/logger.zig | 346 ++++++++---------- src/main_wasm.zig | 2 +- src/router.zig | 4 +- src/string.zig | 2 +- src/string_immutable.zig | 136 ++++++- src/toml/toml_lexer.zig | 43 ++- src/toml/toml_parser.zig | 8 +- test/cli/install/redacted-config-logs.test.ts | 102 ++++++ .../registry/bun-install-registry.test.ts | 2 +- 39 files changed, 952 insertions(+), 612 deletions(-) create mode 100644 test/cli/install/redacted-config-logs.test.ts diff --git a/src/api/schema.zig b/src/api/schema.zig index bec43fbde7..fa85186280 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1,6 +1,7 @@ const std = @import("std"); const bun = @import("root").bun; const js_ast = bun.JSAst; +const OOM = bun.OOM; pub const Reader = struct { const Self = @This(); @@ -2825,11 +2826,11 @@ pub const Api = struct { } } - pub fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) !Api.NpmRegistry { + pub fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) OOM!Api.NpmRegistry { return try this.parseRegistryURLStringImpl(str.data); } - pub fn parseRegistryURLStringImpl(this: *Parser, str: []const u8) !Api.NpmRegistry { + pub fn parseRegistryURLStringImpl(this: *Parser, str: []const u8) OOM!Api.NpmRegistry { const url = bun.URL.parse(str); var registry = std.mem.zeroes(Api.NpmRegistry); diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index 471b28fb7c..c07599f638 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -777,14 +777,14 @@ fn bundle(dev: *DevServer, files: []const BakeEntryPoint) BundleError!void { const bundle_result = bv2.runFromBakeDevServer(files) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); - bv2.bundler.log.printForLogLevel(Output.errorWriter()) catch {}; + bv2.bundler.log.print(Output.errorWriter()) catch {}; Output.warn("BundleV2.runFromBakeDevServer returned error.{s}", .{@errorName(err)}); return; }; - bv2.bundler.log.printForLogLevel(Output.errorWriter()) catch {}; + bv2.bundler.log.print(Output.errorWriter()) catch {}; try dev.finalizeBundle(bv2, bundle_result); @@ -1256,7 +1256,7 @@ pub fn handleParseTaskFailure( bun.path.relative(dev.cwd, abs_path), }); Output.flush(); - log.printForLogLevel(Output.errorWriter()) catch {}; + log.print(Output.errorWriter()) catch {}; return switch (graph) { .server => dev.server_graph.insertFailure(abs_path, log, false), @@ -2967,7 +2967,7 @@ pub const SerializedFailure = struct { inline else => |k| @intFromEnum(@field(ErrorKind, "bundler_log_" ++ @tagName(k))), }); try writeLogData(msg.data, w); - const notes = msg.notes orelse &.{}; + const notes = msg.notes; try w.writeInt(u32, @intCast(notes.len), .little); for (notes) |note| { try writeLogData(note, w); diff --git a/src/bun.js/BuildMessage.zig b/src/bun.js/BuildMessage.zig index 652a11dc62..e3952df4c9 100644 --- a/src/bun.js/BuildMessage.zig +++ b/src/bun.js/BuildMessage.zig @@ -28,7 +28,7 @@ pub const BuildMessage = struct { } pub fn getNotes(this: *BuildMessage, globalThis: *JSC.JSGlobalObject) JSC.JSValue { - const notes: []const logger.Data = this.msg.notes orelse &[_]logger.Data{}; + const notes = this.msg.notes; const array = JSC.JSValue.createEmptyArray(globalThis, notes.len); for (notes, 0..) |note, i| { const cloned = note.clone(bun.default_allocator) catch { diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 05358336cd..bef890cb65 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -3909,7 +3909,7 @@ const TOMLObject = struct { var input_slice = arguments[0].toSlice(globalThis, bun.default_allocator); defer input_slice.deinit(); var source = logger.Source.initPathString("input.toml", input_slice.slice()); - const parse_result = TOMLParser.parse(&source, &log, allocator) catch { + const parse_result = TOMLParser.parse(&source, &log, allocator, false) catch { globalThis.throwValue(log.toJS(globalThis, default_allocator, "Failed to parse toml")); return .zero; }; @@ -5017,8 +5017,10 @@ const InternalTestingAPIs = struct { var buffer = MutableString.initEmpty(bun.default_allocator); defer buffer.deinit(); var writer = buffer.bufferedWriter(); - var formatter = bun.fmt.fmtJavaScript(code.slice(), true); - formatter.limited = false; + const formatter = bun.fmt.fmtJavaScript(code.slice(), .{ + .enable_colors = true, + .check_for_unhighlighted_write = false, + }); std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { globalThis.throwError(err, "Error formatting code"); return .zero; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 27ff465b7c..f12ae5bb41 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -2986,6 +2986,11 @@ pub const JSGlobalObject = opaque { JSGlobalObject__throwOutOfMemoryError(this); } + pub fn throwOutOfMemoryValue(this: *JSGlobalObject) JSValue { + JSGlobalObject__throwOutOfMemoryError(this); + return .zero; + } + pub fn throwTODO(this: *JSGlobalObject, msg: []const u8) void { const err = this.createErrorInstance("{s}", .{msg}); err.put(this, ZigString.static("name"), bun.String.static("TODOError").toJS(this)); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 20adca6a23..3b0ae31713 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -178,13 +178,14 @@ pub const SavedSourceMap = struct { try fail.toData(path).writeFormat( Output.errorWriter(), logger.Kind.warn, + false, true, ); } else { try fail.toData(path).writeFormat( Output.errorWriter(), logger.Kind.warn, - + false, false, ); } @@ -1513,11 +1514,7 @@ pub const VirtualMachine = struct { this.global.handleRejectedPromises(); if (this.log.msgs.items.len > 0) { - if (Output.enable_ansi_colors) { - this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; - } else { - this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; - } + this.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("\n", .{}); Output.flush(); } @@ -3538,7 +3535,7 @@ pub const VirtualMachine = struct { "{d} | {}" ++ fmt, allow_ansi_color, ), - .{ display_line, bun.fmt.fmtJavaScript(clamped, allow_ansi_color) }, + .{ display_line, bun.fmt.fmtJavaScript(clamped, .{ .enable_colors = allow_ansi_color }) }, ); } else { try writer.print( @@ -3546,7 +3543,7 @@ pub const VirtualMachine = struct { "{d} | {}\n", allow_ansi_color, ), - .{ display_line, bun.fmt.fmtJavaScript(clamped, allow_ansi_color) }, + .{ display_line, bun.fmt.fmtJavaScript(clamped, .{ .enable_colors = allow_ansi_color }) }, ); } } @@ -3583,7 +3580,7 @@ pub const VirtualMachine = struct { "- | {}" ++ fmt, allow_ansi_color, ), - .{bun.fmt.fmtJavaScript(text, allow_ansi_color)}, + .{bun.fmt.fmtJavaScript(text, .{ .enable_colors = allow_ansi_color })}, ); } else { try writer.print( @@ -3591,7 +3588,7 @@ pub const VirtualMachine = struct { "- | {}\n", allow_ansi_color, ), - .{bun.fmt.fmtJavaScript(text, allow_ansi_color)}, + .{bun.fmt.fmtJavaScript(text, .{ .enable_colors = allow_ansi_color })}, ); } @@ -3616,7 +3613,7 @@ pub const VirtualMachine = struct { "{d} | {}" ++ fmt, allow_ansi_color, ), - .{ display_line, bun.fmt.fmtJavaScript(clamped, allow_ansi_color) }, + .{ display_line, bun.fmt.fmtJavaScript(clamped, .{ .enable_colors = allow_ansi_color }) }, ); } else { try writer.print( @@ -3624,7 +3621,7 @@ pub const VirtualMachine = struct { "{d} | {}\n", allow_ansi_color, ), - .{ display_line, bun.fmt.fmtJavaScript(clamped, allow_ansi_color) }, + .{ display_line, bun.fmt.fmtJavaScript(clamped, .{ .enable_colors = allow_ansi_color }) }, ); if (clamped.len < max_line_length_with_divot or top.position.column.zeroBased() > max_line_length_with_divot) { diff --git a/src/bun.zig b/src/bun.zig index f7d9cda396..81803574a8 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -2357,7 +2357,7 @@ pub const win32 = struct { return original_mode; } - const watcherChildEnv: [:0]const u16 = strings.toUTF16LiteralZ("_BUN_WATCHER_CHILD"); + const watcherChildEnv: [:0]const u16 = strings.toUTF16Literal("_BUN_WATCHER_CHILD"); // magic exit code to indicate to the watcher manager that the child process should be re-spawned // this was randomly generated - we need to avoid using a common exit code that might be used by the script itself const watcher_reload_exit: w.DWORD = 3224497970; diff --git a/src/bun_js.zig b/src/bun_js.zig index 822bb43883..fb97c269cb 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -495,9 +495,7 @@ noinline fn dumpBuildError(vm: *JSC.VirtualMachine) void { const writer = buffered_writer.writer(); - switch (Output.enable_ansi_colors_stderr) { - inline else => |enable_colors| vm.log.printForLogLevelWithEnableAnsiColors(writer, enable_colors) catch {}, - } + vm.log.print(writer) catch {}; } pub noinline fn failWithBuildError(vm: *JSC.VirtualMachine) noreturn { diff --git a/src/bundler.zig b/src/bundler.zig index c6cd1eeb26..33b89a803b 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -15,7 +15,7 @@ const lex = bun.js_lexer; const logger = bun.logger; const options = @import("options.zig"); const js_parser = bun.js_parser; -const json_parser = bun.JSON; +const JSON = bun.JSON; const js_printer = bun.js_printer; const js_ast = bun.JSAst; const linker = @import("linker.zig"); @@ -1450,11 +1450,11 @@ pub const Bundler = struct { // We allow importing tsconfig.*.json or jsconfig.*.json with comments // These files implicitly become JSONC files, which aligns with the behavior of text editors. if (source.path.isJSONCFile()) - json_parser.parseTSConfig(&source, bundler.log, allocator, false) catch return null + JSON.parseTSConfig(&source, bundler.log, allocator, false) catch return null else - json_parser.parse(&source, bundler.log, allocator, false) catch return null + JSON.parse(&source, bundler.log, allocator, false) catch return null else if (kind == .toml) - TOML.parse(&source, bundler.log, allocator) catch return null + TOML.parse(&source, bundler.log, allocator, false) catch return null else @compileError("unreachable"); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 9239ed4d8c..b7cdfd295c 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1810,8 +1810,8 @@ pub const BundleV2 = struct { }, .err => |err| { log.msgs.append(err) catch unreachable; - log.errors += @as(usize, @intFromBool(err.kind == .err)); - log.warnings += @as(usize, @intFromBool(err.kind == .warn)); + log.errors += @as(u32, @intFromBool(err.kind == .err)); + log.warnings += @as(u32, @intFromBool(err.kind == .warn)); // An error occurred, prevent spinning the event loop forever _ = @atomicRmw(usize, &this.graph.parse_pending, .Sub, 1, .monotonic); @@ -1965,8 +1965,8 @@ pub const BundleV2 = struct { }, .err => |err| { log.msgs.append(err) catch unreachable; - log.errors += @as(usize, @intFromBool(err.kind == .err)); - log.warnings += @as(usize, @intFromBool(err.kind == .warn)); + log.errors += @as(u32, @intFromBool(err.kind == .err)); + log.warnings += @as(u32, @intFromBool(err.kind == .warn)); }, .pending, .consumed => unreachable, } @@ -3439,7 +3439,7 @@ pub const ParseTask = struct { .toml => { const trace = tracer(@src(), "ParseTOML"); defer trace.end(); - const root = try TOML.parse(&source, log, allocator); + const root = try TOML.parse(&source, log, allocator, false); return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); }, .text => { diff --git a/src/bunfig.zig b/src/bunfig.zig index 0ebfb9cb5d..5be26533cb 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -52,12 +52,20 @@ pub const Bunfig = struct { ctx: Command.Context, fn addError(this: *Parser, loc: logger.Loc, comptime text: string) !void { - this.log.addError(this.source, loc, text) catch unreachable; + this.log.addErrorOpts(text, .{ + .source = this.source, + .loc = loc, + .redact_sensitive_information = true, + }) catch unreachable; return error.@"Invalid Bunfig"; } fn addErrorFormat(this: *Parser, loc: logger.Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { - this.log.addErrorFmt(this.source, loc, allocator, text, args) catch unreachable; + this.log.addErrorFmtOpts(allocator, text, args, .{ + .source = this.source, + .loc = loc, + .redact_sensitive_information = true, + }) catch unreachable; return error.@"Invalid Bunfig"; } @@ -791,9 +799,18 @@ pub const Bunfig = struct { switch (expr.data) { .e_string, .e_utf8_string => {}, else => { - this.log.addErrorFmt(this.source, expr.loc, this.allocator, "expected string but received {}", .{ - @as(js_ast.Expr.Tag, expr.data), - }) catch unreachable; + this.log.addErrorFmtOpts( + this.allocator, + "expected string but received {}", + .{ + @as(js_ast.Expr.Tag, expr.data), + }, + .{ + .source = this.source, + .loc = expr.loc, + .redact_sensitive_information = true, + }, + ) catch unreachable; return error.@"Invalid Bunfig"; }, } @@ -801,10 +818,19 @@ pub const Bunfig = struct { pub fn expect(this: *Parser, expr: js_ast.Expr, token: js_ast.Expr.Tag) !void { if (@as(js_ast.Expr.Tag, expr.data) != token) { - this.log.addErrorFmt(this.source, expr.loc, this.allocator, "expected {} but received {}", .{ - token, - @as(js_ast.Expr.Tag, expr.data), - }) catch unreachable; + this.log.addErrorFmtOpts( + this.allocator, + "expected {} but received {}", + .{ + token, + @as(js_ast.Expr.Tag, expr.data), + }, + .{ + .source = this.source, + .loc = expr.loc, + .redact_sensitive_information = true, + }, + ) catch unreachable; return error.@"Invalid Bunfig"; } } @@ -813,14 +839,20 @@ pub const Bunfig = struct { pub fn parse(allocator: std.mem.Allocator, source: logger.Source, ctx: Command.Context, comptime cmd: Command.Tag) !void { const log_count = ctx.log.errors + ctx.log.warnings; - const expr = if (strings.eqlComptime(source.path.name.ext[1..], "toml")) TOML.parse(&source, ctx.log, allocator) catch |err| { + const expr = if (strings.eqlComptime(source.path.name.ext[1..], "toml")) TOML.parse(&source, ctx.log, allocator, true) catch |err| { if (ctx.log.errors + ctx.log.warnings == log_count) { - ctx.log.addErrorFmt(&source, logger.Loc.Empty, allocator, "Failed to parse", .{}) catch unreachable; + try ctx.log.addErrorOpts("Failed to parse", .{ + .source = &source, + .redact_sensitive_information = true, + }); } return err; } else JSONParser.parseTSConfig(&source, ctx.log, allocator, true) catch |err| { if (ctx.log.errors + ctx.log.warnings == log_count) { - ctx.log.addErrorFmt(&source, logger.Loc.Empty, allocator, "Failed to parse", .{}) catch unreachable; + try ctx.log.addErrorOpts("Failed to parse", .{ + .source = &source, + .redact_sensitive_information = true, + }); } return err; }; diff --git a/src/cli.zig b/src/cli.zig index 001f904d97..d6a863a97e 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -61,7 +61,7 @@ pub const Cli = struct { // var panicker = MainPanicHandler.init(log); // MainPanicHandler.Singleton = &panicker; Command.start(allocator, log) catch |err| { - log.printForLogLevel(Output.errorWriter()) catch {}; + log.print(Output.errorWriter()) catch {}; bun.crash_handler.handleRootError(err, @errorReturnTrace()); }; @@ -362,11 +362,7 @@ pub const Arguments = struct { if (getHomeConfigPath(&config_buf)) |path| { loadConfigPath(allocator, true, path, ctx, comptime cmd) catch |err| { if (ctx.log.hasAny()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + ctx.log.print(Output.errorWriter()) catch {}; } if (ctx.log.hasAny()) Output.printError("\n", .{}); Output.err(err, "failed to load bunfig", .{}); @@ -421,11 +417,7 @@ pub const Arguments = struct { loadConfigPath(allocator, auto_loaded, config_path, ctx, comptime cmd) catch |err| { if (ctx.log.hasAny()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + ctx.log.print(Output.errorWriter()) catch {}; } if (ctx.log.hasAny()) Output.printError("\n", .{}); Output.err(err, "failed to load bunfig", .{}); @@ -2277,11 +2269,7 @@ pub const Command = struct { ) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); - if (Output.enable_ansi_colors) { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; - } else { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; - } + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: Failed to run {s} due to error {s}", .{ std.fs.path.basename(file_path), diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 63f80063d3..a8ea3f0d67 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -284,7 +284,7 @@ pub const BuildCommand = struct { ); if (log.hasErrors()) { - try log.printForLogLevel(Output.errorWriter()); + try log.print(Output.errorWriter()); if (result.errors.len > 0 or result.output_files.len == 0) { Output.flush(); @@ -306,7 +306,7 @@ pub const BuildCommand = struct { &input_code_length, ) catch |err| { if (log.msgs.items.len > 0) { - try log.printForLogLevel(Output.errorWriter()); + try log.print(Output.errorWriter()); } else { try Output.errorWriter().print("error: {s}", .{@errorName(err)}); } @@ -474,7 +474,7 @@ pub const BuildCommand = struct { } } - try log.printForLogLevel(Output.errorWriter()); + try log.print(Output.errorWriter()); exitOrWatch(0, ctx.debug.hot_reload == .watch); } } diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 2d6577a4be..0c81ee3730 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -714,11 +714,7 @@ pub const CreateCommand = struct { const properties_list = std.ArrayList(js_ast.G.Property).fromOwnedSlice(default_allocator, package_json_expr.data.e_object.properties.slice()); if (ctx.log.errors > 0) { - if (Output.enable_ansi_colors) { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try ctx.log.print(Output.errorWriter()); package_json_file = null; break :process_package_json; @@ -2080,11 +2076,7 @@ pub const Example = struct { refresher.refresh(); if (ctx.log.errors > 0) { - if (Output.enable_ansi_colors) { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try ctx.log.print(Output.errorWriter()); Global.exit(1); } else { Output.prettyErrorln("Error parsing package: {s}", .{@errorName(err)}); @@ -2096,11 +2088,7 @@ pub const Example = struct { progress.end(); refresher.refresh(); - if (Output.enable_ansi_colors) { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try ctx.log.print(Output.errorWriter()); Global.exit(1); } @@ -2216,11 +2204,7 @@ pub const Example = struct { var source = logger.Source.initPathString("examples.json", mutable.list.items); const examples_object = JSON.parseUTF8(&source, ctx.log, ctx.allocator) catch |err| { if (ctx.log.errors > 0) { - if (Output.enable_ansi_colors) { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try ctx.log.print(Output.errorWriter()); Global.exit(1); } else { Output.prettyErrorln("Error parsing examples: {s}", .{@errorName(err)}); @@ -2229,11 +2213,7 @@ pub const Example = struct { }; if (ctx.log.errors > 0) { - if (Output.enable_ansi_colors) { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try ctx.log.print(Output.errorWriter()); Global.exit(1); } diff --git a/src/cli/install_command.zig b/src/cli/install_command.zig index 6183d43d86..ca69c32e46 100644 --- a/src/cli/install_command.zig +++ b/src/cli/install_command.zig @@ -9,7 +9,7 @@ pub const InstallCommand = struct { error.InvalidPackageJSON, => { const log = &bun.CLI.Cli.log_; - log.printForLogLevel(bun.Output.errorWriter()) catch {}; + log.print(bun.Output.errorWriter()) catch {}; bun.Global.exit(1); }, else => |e| return e, diff --git a/src/cli/outdated_command.zig b/src/cli/outdated_command.zig index 4c8745823e..7e51e94fef 100644 --- a/src/cli/outdated_command.zig +++ b/src/cli/outdated_command.zig @@ -77,12 +77,7 @@ pub const OutdatedCommand = struct { } if (ctx.log.hasErrors()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| try manager.log.printForLogLevelWithEnableAnsiColors( - Output.errorWriter(), - enable_ansi_colors, - ), - } + try manager.log.print(Output.errorWriter()); } } diff --git a/src/cli/pack_command.zig b/src/cli/pack_command.zig index 8ef1f9b0b9..7645912fef 100644 --- a/src/cli/pack_command.zig +++ b/src/cli/pack_command.zig @@ -136,12 +136,7 @@ pub const PackCommand = struct { } if (manager.log.hasErrors()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| try manager.log.printForLogLevelWithEnableAnsiColors( - Output.errorWriter(), - enable_ansi_colors, - ), - } + try manager.log.print(Output.errorWriter()); } Global.crash(); @@ -1090,11 +1085,7 @@ pub const PackCommand = struct { }, .parse_err => |err| { Output.err(err, "failed to parse package.json: {s}", .{abs_package_json_path}); - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Global.crash(); }, .entry => |entry| entry, diff --git a/src/cli/pm_trusted_command.zig b/src/cli/pm_trusted_command.zig index ba8a918523..b4a56684c9 100644 --- a/src/cli/pm_trusted_command.zig +++ b/src/cli/pm_trusted_command.zig @@ -378,9 +378,7 @@ pub const TrustCommand = struct { const package_json_source = logger.Source.initPathString(PackageManager.package_json_cwd, package_json_contents); var package_json = bun.JSON.parseUTF8(&package_json_source, ctx.log, ctx.allocator) catch |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}, - } + ctx.log.print(Output.errorWriter()) catch {}; Output.errGeneric("failed to parse package.json: {s}", .{@errorName(err)}); Global.crash(); diff --git a/src/cli/publish_command.zig b/src/cli/publish_command.zig index 03ec775d58..69c3e6d12f 100644 --- a/src/cli/publish_command.zig +++ b/src/cli/publish_command.zig @@ -314,11 +314,7 @@ pub const PublishCommand = struct { } if (manager.log.hasErrors()) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; } Global.crash(); @@ -367,11 +363,7 @@ pub const PublishCommand = struct { Output.errGeneric("failed to find package.json in tarball '{s}'", .{cli.positionals[1]}); }, error.InvalidPackageJSON => { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Output.errGeneric("failed to parse tarball package.json", .{}); }, error.PrivatePackage => { diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 0b0d083209..d24ef3600a 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -832,20 +832,12 @@ pub const RunCommand = struct { const root_dir_info = this_bundler.resolver.readDirInfo(this_bundler.fs.top_level_dir) catch |err| { if (!log_errors) return error.CouldntReadCurrentDirectory; - if (Output.enable_ansi_colors) { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; - } else { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; - } + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: {s} loading directory {}", .{ @errorName(err), bun.fmt.QuotedFormatter{ .text = this_bundler.fs.top_level_dir } }); Output.flush(); return err; } orelse { - if (Output.enable_ansi_colors) { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; - } else { - ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; - } + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error loading current directory", .{}); Output.flush(); return error.CouldntReadCurrentDirectory; @@ -1296,7 +1288,7 @@ pub const RunCommand = struct { Run.boot(ctx, ".") catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: Failed to run {s} due to error {s}", .{ script_name_to_search, @@ -1391,7 +1383,7 @@ pub const RunCommand = struct { Run.boot(ctx, out_path) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: Failed to run {s} due to error {s}", .{ std.fs.path.basename(file_path), @@ -1488,7 +1480,7 @@ pub const RunCommand = struct { Run.boot(ctx, ctx.allocator.dupe(u8, script_name_to_search) catch unreachable) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: Failed to run {s} due to error {s}", .{ std.fs.path.basename(script_name_to_search), @@ -1514,7 +1506,7 @@ pub const RunCommand = struct { const entry_path = entry_point_buf[0 .. cwd.len + trigger.len]; Run.boot(ctx, ctx.allocator.dupe(u8, entry_path) catch return false) catch |err| { - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("error: Failed to run {s} due to error {s}", .{ std.fs.path.basename(script_name_to_search), @@ -1631,7 +1623,7 @@ pub const RunCommand = struct { }; Run.boot(ctx, normalized_filename) catch |err| { - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.err(err, "Failed to run script \"{s}\"", .{std.fs.path.basename(normalized_filename)}); Global.exit(1); @@ -1704,7 +1696,7 @@ pub const BunXFastPath = struct { wpath, ) catch return; Run.boot(ctx, utf8) catch |err| { - ctx.log.printForLogLevel(Output.errorWriter()) catch {}; + ctx.log.print(Output.errorWriter()) catch {}; Output.err(err, "Failed to run bin \"{s}\"", .{std.fs.path.basename(utf8)}); Global.exit(1); }; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index b0f1000b5b..451f1e2a52 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -1161,11 +1161,7 @@ pub const TestCommand = struct { js_ast.Stmt.Data.Store.reset(); if (vm.log.errors > 0) { - if (Output.enable_ansi_colors) { - vm.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; - } else { - vm.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; - } + vm.log.print(Output.errorWriter()) catch {}; vm.log.msgs.clearRetainingCapacity(); vm.log.errors = 0; } diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index 8cdb5665c6..8b337f15f9 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -272,11 +272,7 @@ pub const UpgradeCommand = struct { refresher.?.refresh(); if (log.errors > 0) { - if (Output.enable_ansi_colors) { - try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try log.print(Output.errorWriter()); Global.exit(1); } else { @@ -293,11 +289,7 @@ pub const UpgradeCommand = struct { progress.?.end(); refresher.?.refresh(); - if (Output.enable_ansi_colors) { - try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true); - } else { - try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false); - } + try log.print(Output.errorWriter()); Global.exit(1); } diff --git a/src/fmt.zig b/src/fmt.zig index b91616e1de..309a278c8a 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -140,6 +140,32 @@ pub fn redactedNpmUrl(str: string) RedactedNpmUrlFormatter { }; } +pub const RedactedSourceFormatter = struct { + text: string, + + pub fn format(this: @This(), comptime _: string, _: std.fmt.FormatOptions, writer: anytype) !void { + var i: usize = 0; + while (i < this.text.len) { + if (strings.startsWithSecret(this.text[i..])) |secret| { + const offset, const len = secret; + try writer.writeAll(this.text[i..][0..offset]); + try writer.writeByteNTimes('*', len); + i += offset + len; + continue; + } + + try writer.writeByte(this.text[i]); + i += 1; + } + } +}; + +pub fn redactedSource(str: string) RedactedSourceFormatter { + return .{ + .text = str, + }; +} + // https://github.com/npm/cli/blob/63d6a732c3c0e9c19fd4d147eaa5cc27c29b168d/node_modules/npm-package-arg/lib/npa.js#L163 pub const DependencyUrlFormatter = struct { url: string, @@ -659,17 +685,29 @@ pub const QuotedFormatter = struct { } }; -pub fn fmtJavaScript(text: []const u8, enable_ansi_colors: bool) QuickAndDirtyJavaScriptSyntaxHighlighter { +pub fn fmtJavaScript(text: []const u8, opts: QuickAndDirtyJavaScriptSyntaxHighlighter.Options) QuickAndDirtyJavaScriptSyntaxHighlighter { return QuickAndDirtyJavaScriptSyntaxHighlighter{ .text = text, - .enable_colors = enable_ansi_colors, + .opts = opts, }; } pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { text: []const u8, - enable_colors: bool = false, - limited: bool = true, + opts: Options, + + pub const Options = struct { + enable_colors: bool, + check_for_unhighlighted_write: bool = true, + + redact_sensitive_information: bool = false, + + pub const default: Options = .{ + .enable_colors = Output.enable_ansi_colors, + .check_for_no_highlighting = true, + .redact_sensitive_information = false, + }; + }; const ColorCode = enum { magenta, @@ -824,18 +862,33 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { pub const Keywords = bun.ComptimeEnumMap(Keyword); + pub const RedactedKeyword = enum { + _auth, + _authToken, + token, + _password, + email, + }; + + pub const RedactedKeywords = bun.ComptimeEnumMap(RedactedKeyword); + pub fn format(this: @This(), comptime unused_fmt: []const u8, _: fmt.FormatOptions, writer: anytype) !void { comptime bun.assert(unused_fmt.len == 0); var text = this.text; - if (this.limited) { - if (!this.enable_colors or text.len > 2048 or text.len == 0 or !strings.isAllASCII(text)) { - try writer.writeAll(text); + if (this.opts.check_for_unhighlighted_write) { + if (!this.opts.enable_colors or text.len > 2048 or text.len == 0 or !strings.isAllASCII(text)) { + if (this.opts.redact_sensitive_information) { + try writer.print("{}", .{redactedSource(text)}); + } else { + try writer.writeAll(text); + } return; } } var prev_keyword: ?Keyword = null; + var should_redact_value = false; outer: while (text.len > 0) { if (js_lexer.isIdentifierStart(text[0])) { @@ -846,11 +899,13 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } if (Keywords.get(text[0..i])) |keyword| { + should_redact_value = false; if (keyword != .as) prev_keyword = keyword; const code = keyword.colorCode(); try writer.print(Output.prettyFmt("{s}{s}", true), .{ code.color(), text[0..i] }); } else { + should_redact_value = this.opts.redact_sensitive_information and RedactedKeywords.has(text[0..i]); write: { if (prev_keyword) |prev| { switch (prev) { @@ -885,11 +940,45 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } text = text[i..]; } else { + if (this.opts.redact_sensitive_information and should_redact_value) { + while (text.len > 0 and std.ascii.isWhitespace(text[0])) { + try writer.writeByte(text[0]); + text = text[1..]; + } + + if (text.len > 0 and (text[0] == '=' or text[0] == ':')) { + try writer.writeByte(text[0]); + text = text[1..]; + while (text.len > 0 and std.ascii.isWhitespace(text[0])) { + try writer.writeByte(text[0]); + text = text[1..]; + } + + if (text.len == 0) return; + } + } + switch (text[0]) { - '0'...'9' => { + '0'...'9' => |num| { + if (this.opts.redact_sensitive_information) { + if (should_redact_value) { + should_redact_value = false; + const end = strings.indexOfChar(text, '\n') orelse text.len; + text = text[end..]; + try writer.writeAll(Output.prettyFmt("***", true)); + continue; + } + + if (strings.startsWithUUID(text)) { + text = text[36..]; + try writer.writeAll(Output.prettyFmt("***", true)); + continue; + } + } + prev_keyword = null; var i: usize = 1; - if (text.len > 1 and text[0] == '0' and text[1] == 'x') { + if (text.len > 1 and num == '0' and text[1] == 'x') { i += 1; while (i < text.len and switch (text[i]) { '0'...'9', 'a'...'f', 'A'...'F' => true, @@ -914,7 +1003,8 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { var i: usize = 1; while (i < text.len and text[i] != char) { - if (char == '`') { + // if we're redacting, no need to syntax highlight contents + if (!should_redact_value and char == '`') { if (text[i] == '$' and i + 1 < text.len and text[i + 1] == '{') { const curly_start = i; i += 2; @@ -928,10 +1018,11 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { try writer.print(Output.prettyFmt("{s}", true), .{text[0..curly_start]}); try writer.writeAll("${"); + var opts = this.opts; + opts.check_for_unhighlighted_write = false; const curly_remain = QuickAndDirtyJavaScriptSyntaxHighlighter{ .text = text[curly_start + 2 .. i], - .enable_colors = this.enable_colors, - .limited = false, + .opts = opts, }; if (curly_remain.text.len > 0) { @@ -963,11 +1054,81 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { // Include the trailing quote, if any i += @intFromBool(i < text.len); + if (should_redact_value) { + should_redact_value = false; + if (i > 2 and text[i - 1] == char) { + const len = text[0..i].len - 2; + try writer.print(Output.prettyFmt("{c}", true), .{char}); + try writer.writeByteNTimes('*', len); + try writer.print(Output.prettyFmt("{c}", true), .{char}); + } else { + try writer.writeByteNTimes('*', text[0..i].len); + } + text = text[i..]; + continue; + } else if (this.opts.redact_sensitive_information) { + try_redact: { + var inner = text[1..i]; + if (inner.len > 0 and inner[inner.len - 1] == char) { + inner = inner[0 .. inner.len - 1]; + } + + if (inner.len == 0) { + break :try_redact; + } + + if (inner.len == 36 and strings.isUUID(inner)) { + try writer.print(Output.prettyFmt("{c}", true), .{char}); + try writer.writeByteNTimes('*', 36); + try writer.print(Output.prettyFmt("{c}", true), .{char}); + text = text[i..]; + continue; + } + + const npm_secret_len = strings.startsWithNpmSecret(inner); + if (npm_secret_len != 0) { + try writer.print(Output.prettyFmt("{c}", true), .{char}); + try writer.writeByteNTimes('*', npm_secret_len); + try writer.print(Output.prettyFmt("{c}", true), .{char}); + text = text[i..]; + continue; + } + + if (strings.findUrlPassword(inner)) |url_pass| { + const offset, const len = url_pass; + try writer.print(Output.prettyFmt("{c}{s}", true), .{ + char, + inner[0..offset], + }); + try writer.writeByteNTimes('*', len); + try writer.print(Output.prettyFmt("{s}{c}", true), .{ + inner[offset + len ..], + char, + }); + text = text[i..]; + continue; + } + } + + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + text = text[i..]; + continue; + } + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); text = text[i..]; }, '/' => { prev_keyword = null; + + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } + var i: usize = 1; // the start of a line comment @@ -985,7 +1146,11 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { i += 1; } - try writer.print(Output.prettyFmt("{s}", true), .{remain_to_print}); + if (this.opts.redact_sensitive_information) { + try writer.print(Output.prettyFmt("{}", true), .{redactedSource(remain_to_print)}); + } else { + try writer.print(Output.prettyFmt("{s}", true), .{remain_to_print}); + } text = text[i..]; continue; } @@ -1005,7 +1170,11 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { break :as_multiline_comment; } - try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + if (this.opts.redact_sensitive_information) { + try writer.print(Output.prettyFmt("{}", true), .{redactedSource(text[0..i])}); + } else { + try writer.print(Output.prettyFmt("{s}", true), .{text[0..i]}); + } text = text[i..]; continue; } @@ -1014,27 +1183,58 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { try writer.writeAll(text[0..i]); text = text[i..]; }, - '}', '{' => { + '}', '{' => |brace| { // support potentially highlighting "from" in an import statement if ((prev_keyword orelse Keyword.@"continue") != .import) { prev_keyword = null; } - try writer.writeAll(text[0..1]); + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } + + try writer.writeByte(brace); text = text[1..]; }, - '[', ']' => { + '[', ']' => |bracket| { prev_keyword = null; - try writer.writeAll(text[0..1]); + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } + try writer.writeByte(bracket); text = text[1..]; }, ';' => { prev_keyword = null; + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } try writer.print(Output.prettyFmt(";", true), .{}); text = text[1..]; }, '.' => { prev_keyword = null; + + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } + var i: usize = 1; if (text.len > 1 and (js_lexer.isIdentifierStart(text[1]) or text[1] == '#')) { i = 2; @@ -1051,11 +1251,18 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { i = 1; } - try writer.writeAll(text[0..1]); + try writer.writeByte(text[0]); text = text[1..]; }, '<' => { + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } var i: usize = 1; // JSX @@ -1095,8 +1302,15 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { text = text[i..]; }, - else => { - try writer.writeAll(text[0..1]); + else => |c| { + if (should_redact_value) { + should_redact_value = false; + const len = strings.indexOfChar(text, '\n') orelse text.len; + try writer.writeByteNTimes('*', len); + text = text[len..]; + continue; + } + try writer.writeByte(c); text = text[1..]; }, } @@ -1517,8 +1731,10 @@ pub const fmt_js_test_bindings = struct { const formatter_id: Formatter = @enumFromInt(args.ptr[1].toInt32()); switch (formatter_id) { .fmtJavaScript => { - var formatter = bun.fmt.fmtJavaScript(code.slice(), true); - formatter.limited = false; + const formatter = bun.fmt.fmtJavaScript(code.slice(), .{ + .enable_colors = true, + .check_for_unhighlighted_write = false, + }); std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { globalThis.throwError(err, "Error formatting"); return .zero; diff --git a/src/ini.zig b/src/ini.zig index 0a01c0263e..4db24c0b19 100644 --- a/src/ini.zig +++ b/src/ini.zig @@ -9,6 +9,7 @@ const Rope = js_ast.E.Object.Rope; const Output = bun.Output; const Global = bun.Global; const Registry = bun.install.Npm.Registry; +const OOM = bun.OOM; pub const Parser = struct { opts: Options = .{}, @@ -39,10 +40,6 @@ pub const Parser = struct { this.arena.deinit(); } - pub fn parse(this: *Parser, arena_allocator: Allocator) !void { - try this.parseImpl(arena_allocator); - } - inline fn shouldSkipLine(line: []const u8) bool { if (line.len == 0 or // comments @@ -60,7 +57,7 @@ pub const Parser = struct { return true; } - fn parseImpl(this: *Parser, arena_allocator: Allocator) !void { + fn parse(this: *Parser, arena_allocator: Allocator) OOM!void { var iter = std.mem.splitScalar(u8, this.src, '\n'); var head: *E.Object = this.out.data.e_object; @@ -91,7 +88,7 @@ pub const Parser = struct { const section: *Rope = try this.prepareStr(arena_allocator, ropealloc, line[1..close_bracket_idx], @as(i32, @intCast(@intFromPtr(line.ptr) - @intFromPtr(this.src.ptr))) + 1, .section); defer rope_stack.fixed_buffer_allocator.reset(); const parent_object = this.out.data.e_object.getOrPutObject(section, arena_allocator) catch |e| switch (e) { - error.OutOfMemory => bun.outOfMemory(), + error.OutOfMemory => |oom| return oom, error.Clobber => { // We're in here if key exists but it is not an object // @@ -161,7 +158,7 @@ pub const Parser = struct { else key_raw; - if (bun.strings.eql(key, "__proto__")) continue; + if (bun.strings.eqlComptime(key, "__proto__")) continue; const value_raw: Expr = brk: { if (maybe_eq_sign_idx) |eq_sign_idx| { @@ -193,11 +190,11 @@ pub const Parser = struct { if (head.get(key)) |val| { if (val.data != .e_array) { var arr = E.Array{}; - arr.push(arena_allocator, val) catch bun.outOfMemory(); - head.put(arena_allocator, key, Expr.init(E.Array, arr, Loc.Empty)) catch bun.outOfMemory(); + try arr.push(arena_allocator, val); + try head.put(arena_allocator, key, Expr.init(E.Array, arr, Loc.Empty)); } } else { - head.put(arena_allocator, key, Expr.init(E.Array, E.Array{}, Loc.Empty)) catch bun.outOfMemory(); + try head.put(arena_allocator, key, Expr.init(E.Array, E.Array{}, Loc.Empty)); } } @@ -207,12 +204,12 @@ pub const Parser = struct { if (head.get(key)) |val| { if (val.data == .e_array) { was_already_array = true; - val.data.e_array.push(arena_allocator, value) catch bun.outOfMemory(); - head.put(arena_allocator, key, val) catch bun.outOfMemory(); + try val.data.e_array.push(arena_allocator, value); + try head.put(arena_allocator, key, val); } } if (!was_already_array) { - head.put(arena_allocator, key, value) catch bun.outOfMemory(); + try head.put(arena_allocator, key, value); } } } @@ -224,7 +221,7 @@ pub const Parser = struct { val_: []const u8, offset_: i32, comptime usage: enum { section, key, value }, - ) !switch (usage) { + ) OOM!switch (usage) { .value => Expr, .section => *Rope, .key => []const u8, @@ -268,10 +265,7 @@ pub const Parser = struct { return "[Object object]"; }, else => { - const str = std.fmt.allocPrint(arena_allocator, "{}", .{ToStringFormatter{ .d = json_val.data }}) catch |e| { - this.logger.addErrorFmt(&this.source, Loc{ .start = offset }, arena_allocator, "failed to stringify value: {s}", .{@errorName(e)}) catch bun.outOfMemory(); - return error.ParserError; - }; + const str = try std.fmt.allocPrint(arena_allocator, "{}", .{ToStringFormatter{ .d = json_val.data }}); if (comptime usage == .section) return singleStrRope(ropealloc, str); return str; }, @@ -330,7 +324,7 @@ pub const Parser = struct { not_env_substitution: { if (comptime usage != .value) break :not_env_substitution; - if (this.parseEnvSubstitution(val, i, i, &unesc)) |new_i| { + if (try this.parseEnvSubstitution(val, i, i, &unesc)) |new_i| { // set to true so we heap alloc did_any_escape = true; i = new_i; @@ -348,7 +342,7 @@ pub const Parser = struct { }, '.' => { if (comptime usage == .section) { - this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); + try this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); } else { try unesc.append('.'); } @@ -380,7 +374,7 @@ pub const Parser = struct { switch (usage) { .section => { - this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); + try this.commitRopePart(arena_allocator, ropealloc, &unesc, &rope); return rope.?; }, .value => { @@ -412,7 +406,7 @@ pub const Parser = struct { /// - `i` must be an index into `val` that points to a '$' char /// /// npm/ini uses a regex pattern that will select the inner most ${...} - fn parseEnvSubstitution(this: *Parser, val: []const u8, start: usize, i: usize, unesc: *std.ArrayList(u8)) ?usize { + fn parseEnvSubstitution(this: *Parser, val: []const u8, start: usize, i: usize, unesc: *std.ArrayList(u8)) OOM!?usize { bun.debugAssert(val[i] == '$'); var esc = false; if (i + "{}".len < val.len and val[i + 1] == '{') { @@ -435,21 +429,21 @@ pub const Parser = struct { if (start != i) { const missed = val[start..i]; - unesc.appendSlice(missed) catch bun.outOfMemory(); + try unesc.appendSlice(missed); } const env_var = val[i + 2 .. j]; // https://github.com/npm/cli/blob/534ad7789e5c61f579f44d782bdd18ea3ff1ee20/workspaces/config/lib/env-replace.js#L6 const expanded = this.env.get(env_var) orelse return null; - unesc.appendSlice(expanded) catch bun.outOfMemory(); + try unesc.appendSlice(expanded); return j; } return null; } - fn singleStrRope(ropealloc: Allocator, str: []const u8) *Rope { - const rope = ropealloc.create(Rope) catch bun.outOfMemory(); + fn singleStrRope(ropealloc: Allocator, str: []const u8) OOM!*Rope { + const rope = try ropealloc.create(Rope); rope.* = .{ .head = Expr.init(E.String, E.String.init(str), Loc.Empty), }; @@ -460,15 +454,15 @@ pub const Parser = struct { return std.mem.indexOfScalar(u8, key, '.'); } - fn commitRopePart(this: *Parser, arena_allocator: Allocator, ropealloc: Allocator, unesc: *std.ArrayList(u8), existing_rope: *?*Rope) void { + fn commitRopePart(this: *Parser, arena_allocator: Allocator, ropealloc: Allocator, unesc: *std.ArrayList(u8), existing_rope: *?*Rope) OOM!void { _ = this; // autofix - const slice = arena_allocator.dupe(u8, unesc.items[0..]) catch bun.outOfMemory(); + const slice = try arena_allocator.dupe(u8, unesc.items[0..]); const expr = Expr.init(E.String, E.String{ .data = slice }, Loc.Empty); if (existing_rope.*) |_r| { const r: *Rope = _r; - _ = r.append(expr, ropealloc) catch bun.outOfMemory(); + _ = try r.append(expr, ropealloc); } else { - existing_rope.* = ropealloc.create(Rope) catch bun.outOfMemory(); + existing_rope.* = try ropealloc.create(Rope); existing_rope.*.?.* = Rope{ .head = expr, }; @@ -476,15 +470,15 @@ pub const Parser = struct { unesc.clearRetainingCapacity(); } - fn strToRope(ropealloc: Allocator, key: []const u8) *Rope { + fn strToRope(ropealloc: Allocator, key: []const u8) OOM!*Rope { var dot_idx = nextDot(key) orelse { - const rope = ropealloc.create(Rope) catch bun.outOfMemory(); + const rope = try ropealloc.create(Rope); rope.* = .{ .head = Expr.init(E.String, E.String.init(key), Loc.Empty), }; return rope; }; - var rope = ropealloc.create(Rope) catch bun.outOfMemory(); + var rope = try ropealloc.create(Rope); const head = rope; rope.* = .{ .head = Expr.init(E.String, E.String.init(key[0..dot_idx]), Loc.Empty), @@ -494,11 +488,11 @@ pub const Parser = struct { while (dot_idx + 1 < key.len) { const next_dot_idx = dot_idx + 1 + (nextDot(key[dot_idx + 1 ..]) orelse { const rest = key[dot_idx + 1 ..]; - rope = rope.append(Expr.init(E.String, E.String.init(rest), Loc.Empty), ropealloc) catch bun.outOfMemory(); + rope = try rope.append(Expr.init(E.String, E.String.init(rest), Loc.Empty), ropealloc); break; }); const part = key[dot_idx + 1 .. next_dot_idx]; - rope = rope.append(Expr.init(E.String, E.String.init(part), Loc.Empty), ropealloc) catch bun.outOfMemory(); + rope = try rope.append(Expr.init(E.String, E.String.init(part), Loc.Empty), ropealloc); dot_idx = next_dot_idx; } @@ -542,36 +536,36 @@ pub const IniTestingAPIs = struct { }).init(globalThis, envjs); defer object_iter.deinit(); - envmap.ensureTotalCapacity(object_iter.len) catch bun.outOfMemory(); + envmap.ensureTotalCapacity(object_iter.len) catch return globalThis.throwOutOfMemoryValue(); while (object_iter.next()) |key| { - const keyslice = key.toOwnedSlice(allocator) catch bun.outOfMemory(); + const keyslice = key.toOwnedSlice(allocator) catch return globalThis.throwOutOfMemoryValue(); var value = object_iter.value; if (value == .undefined) continue; const value_str = value.getZigString(globalThis); - const slice = value_str.toOwnedSlice(allocator) catch bun.outOfMemory(); + const slice = value_str.toOwnedSlice(allocator) catch return globalThis.throwOutOfMemoryValue(); envmap.put(keyslice, .{ .value = slice, .conditional = false, - }) catch bun.outOfMemory(); + }) catch return globalThis.throwOutOfMemoryValue(); } - const map = allocator.create(bun.DotEnv.Map) catch bun.outOfMemory(); + const map = allocator.create(bun.DotEnv.Map) catch return globalThis.throwOutOfMemoryValue(); map.* = .{ .map = envmap, }; const env = bun.DotEnv.Loader.init(map, allocator); - const envstable = allocator.create(bun.DotEnv.Loader) catch bun.outOfMemory(); + const envstable = allocator.create(bun.DotEnv.Loader) catch return globalThis.throwOutOfMemoryValue(); envstable.* = env; break :brk envstable; }; - const install = allocator.create(bun.Schema.Api.BunInstall) catch bun.outOfMemory(); + const install = allocator.create(bun.Schema.Api.BunInstall) catch return globalThis.throwOutOfMemoryValue(); install.* = std.mem.zeroes(bun.Schema.Api.BunInstall); - loadNpmrc(allocator, install, env, false, ".npmrc", &log, &source) catch { + loadNpmrc(allocator, install, env, ".npmrc", &log, &source) catch { return log.toJS(globalThis, allocator, "error"); }; @@ -636,11 +630,10 @@ pub const IniTestingAPIs = struct { var parser = Parser.init(bun.default_allocator, "", utf8str.slice(), globalThis.bunVM().bundler.env); defer parser.deinit(); - parser.parse(parser.arena.allocator()) catch |e| { - if (parser.logger.errors > 0) { - parser.logger.printForLogLevel(bun.Output.writer()) catch bun.outOfMemory(); - } else globalThis.throwError(e, "failed to parse"); - return .undefined; + parser.parse(parser.arena.allocator()) catch |err| { + switch (err) { + error.OutOfMemory => return globalThis.throwOutOfMemoryValue(), + } }; return parser.out.toJS(bun.default_allocator, globalThis, .{ .decode_escape_sequences = true }) catch |e| { @@ -738,25 +731,27 @@ pub const ConfigIterator = struct { allocator: Allocator, log: *bun.logger.Log, source: *const bun.logger.Source, - ) ?[]const u8 { + ) OOM!?[]const u8 { if (this.optname.isBase64Encoded()) { if (this.value.len == 0) return ""; const len = bun.base64.decodeLen(this.value); - var slice = allocator.alloc(u8, len) catch bun.outOfMemory(); + var slice = try allocator.alloc(u8, len); const result = bun.base64.decode(slice[0..], this.value); if (result.status != .success) { - log.addErrorFmt( - source, - this.loc, + try log.addErrorFmtOpts( allocator, "{s} is not valid base64", .{@tagName(this.optname)}, - ) catch bun.outOfMemory(); + .{ + .source = source, + .loc = this.loc, + }, + ); return null; } - return allocator.dupe(u8, slice[0..result.count]) catch bun.outOfMemory(); + return try allocator.dupe(u8, slice[0..result.count]); } - return allocator.dupe(u8, this.value) catch bun.outOfMemory(); + return try allocator.dupe(u8, this.value); } pub fn format(this: *const @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { @@ -764,7 +759,7 @@ pub const ConfigIterator = struct { } }; - pub fn next(this: *ConfigIterator) error{ParserError}!?Option(Item) { + pub fn next(this: *ConfigIterator) ?Option(Item) { if (this.prop_idx >= this.config.properties.len) return null; defer this.prop_idx += 1; @@ -830,7 +825,7 @@ pub const ScopeIterator = struct { const Item = struct { scope: []const u8, registry: bun.Schema.Api.NpmRegistry }; - pub fn next(this: *ScopeIterator) error{ParserError}!?Option(Item) { + pub fn next(this: *ScopeIterator) OOM!?Option(Item) { if (this.prop_idx >= this.config.properties.len) return null; defer this.prop_idx += 1; @@ -851,10 +846,7 @@ pub const ScopeIterator = struct { .source = this.source, .allocator = this.allocator, }; - break :brk parser.parseRegistryURLStringImpl(str) catch |e| { - if (e == error.OutOfMemory) bun.outOfMemory(); - return error.ParserError; - }; + break :brk try parser.parseRegistryURLStringImpl(str); } } return .none; @@ -887,44 +879,32 @@ pub fn loadNpmrcFromFile( }; defer allocator.free(source.contents); - loadNpmrc(allocator, install, env, auto_loaded, npmrc_path, &log, &source) catch { - if (log.errors == 1) - Output.warn("Encountered an error while reading .npmrc:\n", .{}) - else - Output.warn("Encountered errors while reading .npmrc:\n", .{}); + loadNpmrc(allocator, install, env, npmrc_path, &log, &source) catch |err| { + switch (err) { + error.OutOfMemory => bun.outOfMemory(), + } }; - log.printForLogLevel(Output.errorWriter()) catch bun.outOfMemory(); + if (log.hasErrors()) { + if (log.errors == 1) + Output.warn("Encountered an error while reading .npmrc:\n\n", .{}) + else + Output.warn("Encountered errors while reading .npmrc:\n\n", .{}); + Output.flush(); + } + log.print(Output.errorWriter()) catch {}; } pub fn loadNpmrc( allocator: std.mem.Allocator, install: *bun.Schema.Api.BunInstall, env: *bun.DotEnv.Loader, - auto_loaded: bool, npmrc_path: [:0]const u8, log: *bun.logger.Log, source: *const bun.logger.Source, -) !void { +) OOM!void { var parser = bun.ini.Parser.init(allocator, npmrc_path, source.contents, env); defer parser.deinit(); - parser.parse(parser.arena.allocator()) catch |e| { - if (e == error.ParserError) { - parser.logger.printForLogLevel(Output.errorWriter()) catch unreachable; - return e; - } - if (auto_loaded) { - Output.warn("{}\nwhile reading .npmrc \"{s}\"", .{ - e, - npmrc_path, - }); - return; - } - Output.prettyErrorln("{}\nwhile reading .npmrc \"{s}\"", .{ - e, - npmrc_path, - }); - Global.exit(1); - }; + try parser.parse(parser.arena.allocator()); // Need to be very, very careful here with strings. // They are allocated in the Parser's arena, which of course gets @@ -939,16 +919,13 @@ pub fn loadNpmrc( .log = log, .source = source, }; - install.default_registry = p.parseRegistryURLStringImpl(allocator.dupe(u8, str) catch bun.outOfMemory()) catch |e| { - if (e == error.OutOfMemory) bun.outOfMemory(); - return error.ParserError; - }; + install.default_registry = try p.parseRegistryURLStringImpl(try allocator.dupe(u8, str)); } } if (out.asProperty("cache")) |query| { if (query.expr.asUtf8StringLiteral()) |str| { - install.cache_directory = allocator.dupe(u8, str) catch bun.outOfMemory(); + install.cache_directory = try allocator.dupe(u8, str); } else if (query.expr.asBool()) |b| { install.disable_cache = !b; } @@ -1002,13 +979,7 @@ pub fn loadNpmrc( const scope_count = brk: { var count: usize = 0; - while (iter.next() catch { - const prop_idx = iter.prop_idx -| 1; - const prop = iter.config.properties.at(prop_idx); - const loc = prop.key.?.loc; - log.addErrorFmt(source, loc, parser.arena.allocator(), "Found an invalid registry option:", .{}) catch bun.outOfMemory(); - return error.ParserError; - }) |o| { + while (try iter.next()) |o| { if (o == .some) { count += 1; } @@ -1017,19 +988,19 @@ pub fn loadNpmrc( }; defer install.scoped = registry_map; - registry_map.scopes.ensureUnusedCapacity(allocator, scope_count) catch bun.outOfMemory(); + try registry_map.scopes.ensureUnusedCapacity(allocator, scope_count); iter.prop_idx = 0; iter.count = false; - while (iter.next() catch unreachable) |val| { + while (try iter.next()) |val| { if (val.get()) |result| { const registry = result.registry.dupe(allocator); - registry_map.scopes.put( + try registry_map.scopes.put( allocator, - allocator.dupe(u8, result.scope) catch bun.outOfMemory(), + try allocator.dupe(u8, result.scope), registry, - ) catch bun.outOfMemory(); + ); } } } @@ -1076,11 +1047,11 @@ pub fn loadNpmrc( // The line that sets the username would apply to both @myorg and @another var url_map = url_map: { var url_map = bun.StringArrayHashMap(bun.URL).init(parser.arena.allocator()); - url_map.ensureTotalCapacity(registry_map.scopes.keys().len) catch bun.outOfMemory(); + try url_map.ensureTotalCapacity(registry_map.scopes.keys().len); for (registry_map.scopes.keys(), registry_map.scopes.values()) |*k, *v| { const url = bun.URL.parse(v.url); - url_map.put(k.*, url) catch bun.outOfMemory(); + try url_map.put(k.*, url); } break :url_map url_map; @@ -1095,13 +1066,7 @@ pub fn loadNpmrc( .allocator = allocator, }; - while (iter.next() catch { - const prop_idx = iter.prop_idx -| 1; - const prop = iter.config.properties.at(prop_idx); - const loc = prop.key.?.loc; - log.addErrorFmt(source, loc, parser.arena.allocator(), "Found an invalid registry option:", .{}) catch bun.outOfMemory(); - return error.ParserError; - }) |val| { + while (iter.next()) |val| { if (val.get()) |conf_item_| { // `conf_item` will look like: // @@ -1113,7 +1078,7 @@ pub fn loadNpmrc( const conf_item: bun.ini.ConfigIterator.Item = conf_item_; switch (conf_item.optname) { .email, .certfile, .keyfile => { - log.addWarningFmt( + try log.addWarningFmt( source, iter.config.properties.at(iter.prop_idx - 1).key.?.loc, allocator, @@ -1122,7 +1087,7 @@ pub fn loadNpmrc( conf_item, @tagName(conf_item.optname), }, - ) catch bun.outOfMemory(); + ); continue; }, else => {}, @@ -1143,16 +1108,16 @@ pub fn loadNpmrc( switch (conf_item.optname) { ._authToken => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; }, .username => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; }, ._password => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; }, ._auth => { - _ = @"handle _auth"(allocator, v, &conf_item, log, source); + try @"handle _auth"(allocator, v, &conf_item, log, source); }, .email, .certfile, .keyfile => unreachable, } @@ -1170,16 +1135,16 @@ pub fn loadNpmrc( } switch (conf_item.optname) { ._authToken => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.token = x; }, .username => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.username = x; }, ._password => { - if (conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; + if (try conf_item.dupeValueDecoded(allocator, log, source)) |x| v.password = x; }, ._auth => { - _ = @"handle _auth"(allocator, v, &conf_item, log, source); + try @"handle _auth"(allocator, v, &conf_item, log, source); }, .email, .certfile, .keyfile => unreachable, } @@ -1190,11 +1155,6 @@ pub fn loadNpmrc( } } } - - const had_errors = log.hasErrors(); - if (had_errors) { - return error.ParserError; - } } fn @"handle _auth"( @@ -1203,35 +1163,57 @@ fn @"handle _auth"( conf_item: *const ConfigIterator.Item, log: *bun.logger.Log, source: *const bun.logger.Source, -) void { +) OOM!void { if (conf_item.value.len == 0) { - log.addErrorFmt( - source, - conf_item.loc, - allocator, - "invalid _auth value, expected it to be \"\\:\\\" encoded in base64, but got an empty string", - .{}, - ) catch bun.outOfMemory(); + try log.addErrorOpts( + "invalid _auth value, expected base64 encoded \":\", received an empty string", + .{ + .source = source, + .loc = conf_item.loc, + .redact_sensitive_information = true, + }, + ); return; } const decode_len = bun.base64.decodeLen(conf_item.value); - const decoded = allocator.alloc(u8, decode_len) catch bun.outOfMemory(); + const decoded = try allocator.alloc(u8, decode_len); const result = bun.base64.decode(decoded[0..], conf_item.value); if (!result.isSuccessful()) { defer allocator.free(decoded); - log.addErrorFmt(source, conf_item.loc, allocator, "invalid base64", .{}) catch bun.outOfMemory(); + try log.addErrorOpts( + "invalid _auth value, expected valid base64", + .{ + .source = source, + .loc = conf_item.loc, + .redact_sensitive_information = true, + }, + ); return; } const @"username:password" = decoded[0..result.count]; const colon_idx = std.mem.indexOfScalar(u8, @"username:password", ':') orelse { - defer allocator.free(decoded); - log.addErrorFmt(source, conf_item.loc, allocator, "invalid _auth value, expected it to be \"\\:\\\" encoded in base 64, but got:\n\n{s}", .{decoded}) catch bun.outOfMemory(); + defer allocator.free(@"username:password"); + try log.addErrorOpts( + "invalid _auth value, expected base64 encoded \":\"", + .{ + .source = source, + .loc = conf_item.loc, + .redact_sensitive_information = true, + }, + ); return; }; const username = @"username:password"[0..colon_idx]; if (colon_idx + 1 >= @"username:password".len) { - defer allocator.free(decoded); - log.addErrorFmt(source, conf_item.loc, allocator, "invalid _auth value, expected it to be \"\\:\\\" encoded in base64, but got:\n\n{s}", .{decoded}) catch bun.outOfMemory(); + defer allocator.free(@"username:password"); + try log.addErrorOpts( + "invalid _auth value, expected base64 encoded \":\"", + .{ + .source = source, + .loc = conf_item.loc, + .redact_sensitive_information = true, + }, + ); return; } const password = @"username:password"[colon_idx + 1 ..]; diff --git a/src/install/install.zig b/src/install/install.zig index 407b6779ab..7c457914f4 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -696,7 +696,7 @@ pub const Task = struct { if (pt.callback.apply.logger.errors > 0) { defer pt.callback.apply.logger.deinit(); // this.log.addErrorFmt(null, logger.Loc.Empty, bun.default_allocator, "failed to apply patch: {}", .{e}) catch unreachable; - pt.callback.apply.logger.printForLogLevel(Output.writer()) catch {}; + pt.callback.apply.logger.print(Output.writer()) catch {}; } } } @@ -2754,7 +2754,7 @@ pub const PackageManager = struct { pub fn crash(this: *PackageManager) noreturn { if (this.options.log_level != .silent) { - this.log.printForLogLevel(Output.errorWriter()) catch {}; + this.log.print(Output.errorWriter()) catch {}; } Global.crash(); } @@ -6580,11 +6580,7 @@ pub const PackageManager = struct { _ = manager.decrementPendingTasks(); if (task.log.msgs.items.len > 0) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - try task.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors); - }, - } + try task.log.print(Output.errorWriter()); } switch (task.tag) { @@ -8967,7 +8963,7 @@ pub const PackageManager = struct { error.InvalidPackageJSON, => { const log = &bun.CLI.Cli.log_; - log.printForLogLevel(bun.Output.errorWriter()) catch {}; + log.print(bun.Output.errorWriter()) catch {}; bun.Global.exit(1); return; }, @@ -10404,11 +10400,7 @@ pub const PackageManager = struct { ) !void { if (manager.log.errors > 0) { if (comptime log_level != .silent) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; } Global.crash(); } @@ -10423,11 +10415,7 @@ pub const PackageManager = struct { }, )) { .parse_err => |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Output.errGeneric("failed to parse package.json \"{s}\": {s}", .{ manager.original_package_json_path, @errorName(err), @@ -10627,11 +10615,7 @@ pub const PackageManager = struct { }, )) { .parse_err => |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Output.errGeneric("failed to parse package.json \"{s}\": {s}", .{ root_package_json_path, @errorName(err), @@ -11059,11 +11043,7 @@ pub const PackageManager = struct { initializeStore(); const json = JSON.parsePackageJSONUTF8AlwaysDecode(&package_json_source, manager.log, manager.allocator) catch |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("{s} parsing package.json in \"{s}\"", .{ @errorName(err), package_json_source.path.prettyDir() }); Global.crash(); }; @@ -11476,11 +11456,7 @@ pub const PackageManager = struct { initializeStore(); const json = JSON.parsePackageJSONUTF8AlwaysDecode(&package_json_source, manager.log, manager.allocator) catch |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + manager.log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("{s} parsing package.json in \"{s}\"", .{ @errorName(err), package_json_source.path.prettyDir() }); Global.crash(); }; @@ -11948,15 +11924,20 @@ pub const PackageManager = struct { pub fn install(ctx: Command.Context) !void { const cli = try CommandLineArguments.parse(ctx.allocator, .install); + + const subcommand: Subcommand = if (cli.positionals.len > 1) .add else .install; + + // TODO(dylan-conway): print `bun install ` or `bun add ` before logs from `init`. + // and cleanup install/add subcommand usage var manager, _ = try init(ctx, cli, .install); // switch to `bun add ` - if (manager.options.positionals.len > 1) { + if (subcommand == .add) { + manager.subcommand = .add; if (manager.options.shouldPrintCommandName()) { Output.prettyln("bun add v" ++ Global.package_json_version_with_sha ++ "\n", .{}); Output.flush(); } - manager.subcommand = .add; return try switch (manager.options.log_level) { inline else => |log_level| manager.updatePackageJSONAndInstallWithManager(ctx, log_level), }; @@ -12203,10 +12184,11 @@ pub const PackageManager = struct { if (bin_linker.err) |err| { if (log_level != .silent) { - this.manager.log.addErrorFmtNoLoc( + this.manager.log.addErrorFmtOpts( this.manager.allocator, "Failed to link {s}: {s}", .{ alias, @errorName(err) }, + .{}, ) catch bun.outOfMemory(); } @@ -14001,11 +13983,7 @@ pub const PackageManager = struct { } if (ctx.log.errors > 0) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - try manager.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors); - }, - } + try manager.log.print(Output.errorWriter()); } Output.flush(); } @@ -14434,7 +14412,7 @@ pub const PackageManager = struct { } const had_errors_before_cleaning_lockfile = manager.log.hasErrors(); - try manager.log.printForLogLevel(Output.errorWriter()); + try manager.log.print(Output.errorWriter()); manager.log.reset(); // This operation doesn't perform any I/O, so it should be relatively cheap. @@ -14551,7 +14529,7 @@ pub const PackageManager = struct { } if (comptime log_level != .silent) { - try manager.log.printForLogLevel(Output.errorWriter()); + try manager.log.print(Output.errorWriter()); } if (had_errors_before_cleaning_lockfile or manager.log.hasErrors()) Global.crash(); diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index c16356f72d..5648e2d5f1 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -1254,11 +1254,7 @@ pub const Printer = struct { }), } if (log.errors > 0) { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors); - }, - } + try log.print(Output.errorWriter()); } Global.crash(); }, @@ -3943,11 +3939,7 @@ pub const Package = extern struct { ) !void { initializeStore(); const json = JSON.parsePackageJSONUTF8AlwaysDecode(&source, log, allocator) catch |err| { - switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| { - log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), enable_ansi_colors) catch {}; - }, - } + log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("{s} parsing package.json in \"{s}\"", .{ @errorName(err), source.path.prettyDir() }); Global.crash(); }; diff --git a/src/install/migration.zig b/src/install/migration.zig index 6c44a9e59a..e0dd624826 100644 --- a/src/install/migration.zig +++ b/src/install/migration.zig @@ -77,7 +77,7 @@ pub fn detectAndLoadOtherLockfile( bun.handleErrorReturnTrace(err, @errorReturnTrace()); Output.prettyErrorln("Error: {s}", .{@errorName(err)}); - log.printForLogLevel(Output.errorWriter()) catch {}; + log.print(Output.errorWriter()) catch {}; Output.prettyErrorln("Invalid NPM package-lock.json\nIn a release build, this would ignore and do a fresh install.\nAborting", .{}); Global.exit(1); } diff --git a/src/install/npm.zig b/src/install/npm.zig index bc4fc4a637..69f5bf6f02 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -1204,7 +1204,14 @@ pub const PackageManifest = struct { const file_name = try manifestFileName(&file_path_buf, file_id, scope); const cache_file = File.openat(cache_dir, file_name, bun.O.RDONLY, 0).unwrap() catch return null; defer cache_file.close(); - return loadByFile(allocator, scope, cache_file); + + delete: { + return loadByFile(allocator, scope, cache_file) catch break :delete orelse break :delete; + } + + // delete the outdated/invalid manifest + try bun.sys.unlinkat(bun.toFD(cache_dir), file_name).unwrap(); + return null; } pub fn loadByFile(allocator: std.mem.Allocator, scope: *const Registry.Scope, manifest_file: File) !?PackageManifest { diff --git a/src/install/patch_install.zig b/src/install/patch_install.zig index 7b60e28319..3005a0c548 100644 --- a/src/install/patch_install.zig +++ b/src/install/patch_install.zig @@ -158,7 +158,7 @@ pub const PatchTask = struct { if (this.callback.apply.logger.errors > 0) { defer this.callback.apply.logger.deinit(); Output.errGeneric("failed to apply patchfile ({s})", .{this.callback.apply.patchfilepath}); - this.callback.apply.logger.printForLogLevel(Output.errorWriter()) catch {}; + this.callback.apply.logger.print(Output.errorWriter()) catch {}; } } @@ -183,7 +183,7 @@ pub const PatchTask = struct { } if (calc_hash.logger.errors > 0) { Output.prettyErrorln("\n\n", .{}); - calc_hash.logger.printForLogLevel(Output.errorWriter()) catch {}; + calc_hash.logger.print(Output.errorWriter()) catch {}; } Output.flush(); Global.crash(); @@ -281,20 +281,22 @@ pub const PatchTask = struct { )) { .result => |txt| txt, .err => |e| { - try log.addErrorFmtNoLoc( + try log.addErrorFmtOpts( this.manager.allocator, "failed to read patchfile: {}", .{e.toSystemError()}, + .{}, ); return; }, }; defer this.manager.allocator.free(patchfile_txt); var patchfile = bun.patch.parsePatchFile(patchfile_txt) catch |e| { - try log.addErrorFmtNoLoc( + try log.addErrorFmtOpts( this.manager.allocator, "failed to parse patchfile: {s}", .{@errorName(e)}, + .{}, ); return; }; @@ -333,27 +335,30 @@ pub const PatchTask = struct { switch (pkg_install.installImpl(true, system_tmpdir, .copyfile, this.callback.apply.resolution.tag)) { .success => {}, .fail => |reason| { - return try log.addErrorFmtNoLoc( + return try log.addErrorFmtOpts( this.manager.allocator, "{s} while executing step: {s}", .{ @errorName(reason.err), reason.step.name() }, + .{}, ); }, } - var patch_pkg_dir = system_tmpdir.openDir(tempdir_name, .{}) catch |e| return try log.addErrorFmtNoLoc( + var patch_pkg_dir = system_tmpdir.openDir(tempdir_name, .{}) catch |e| return try log.addErrorFmtOpts( this.manager.allocator, "failed trying to open temporary dir to apply patch to package: {s}", .{@errorName(e)}, + .{}, ); defer patch_pkg_dir.close(); // 4. apply patch if (patchfile.apply(this.manager.allocator, bun.toFD(patch_pkg_dir.fd))) |e| { - return try log.addErrorFmtNoLoc( + return try log.addErrorFmtOpts( this.manager.allocator, "failed applying patch file: {}", .{e}, + .{}, ); } @@ -371,7 +376,12 @@ pub const PatchTask = struct { )) { .result => |fd| fd, .err => |e| { - return try log.addErrorFmtNoLoc(this.manager.allocator, "failed adding bun tag: {}", .{e.withPath(buntagbuf[0 .. bun_tag_prefix.len + hashlen :0])}); + return try log.addErrorFmtOpts( + this.manager.allocator, + "failed adding bun tag: {}", + .{e.withPath(buntagbuf[0 .. bun_tag_prefix.len + hashlen :0])}, + .{}, + ); }, }; _ = bun.sys.close(buntagfd); @@ -391,7 +401,12 @@ pub const PatchTask = struct { bun.toFD(this.callback.apply.cache_dir.fd), this.callback.apply.cache_dir_subpath, .{ .move_fallback = true }, - ).asErr()) |e| return try log.addErrorFmtNoLoc(this.manager.allocator, "renaming changes to cache dir: {}", .{e.withPath(this.callback.apply.cache_dir_subpath)}); + ).asErr()) |e| return try log.addErrorFmtOpts( + this.manager.allocator, + "renaming changes to cache dir: {}", + .{e.withPath(this.callback.apply.cache_dir_subpath)}, + .{}, + ); } pub fn calcHash(this: *PatchTask) ?u64 { diff --git a/src/js_parser.zig b/src/js_parser.zig index 4f62865fcb..68153ae884 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -3113,7 +3113,7 @@ pub const Parser = struct { const stmts = p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts) catch |err| { if (comptime Environment.isWasm) { Output.print("JSParser.parse: caught error {s} at location: {d}\n", .{ @errorName(err), p.lexer.loc().start }); - p.log.printForLogLevel(Output.writer()) catch {}; + p.log.print(Output.writer()) catch {}; } return err; }; @@ -14700,7 +14700,7 @@ fn NewParser_( } p.log.level = .verbose; - p.log.printForLogLevel(panic_stream.writer()) catch unreachable; + p.log.print(panic_stream.writer()) catch unreachable; Output.panic(fmt ++ "\n{s}", args ++ .{panic_buffer[0..panic_stream.pos]}); } diff --git a/src/logger.zig b/src/logger.zig index 8aaf5f1880..c1b81145fe 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -18,9 +18,10 @@ const unicode = std.unicode; const Ref = @import("./ast/base.zig").Ref; const expect = std.testing.expect; const assert = bun.assert; -const ArrayList = std.ArrayList; const StringBuilder = @import("./string_builder.zig"); const Index = @import("./ast/base.zig").Index; +const OOM = bun.OOM; +const JSError = bun.JSError; pub const Kind = enum(u8) { err = 0, @@ -210,7 +211,7 @@ pub const Data = struct { allocator.free(d.text); } - pub fn cloneLineText(this: Data, should: bool, allocator: std.mem.Allocator) !Data { + pub fn cloneLineText(this: Data, should: bool, allocator: std.mem.Allocator) OOM!Data { if (!should or this.location == null or this.location.?.line_text == null) return this; @@ -223,7 +224,7 @@ pub const Data = struct { }; } - pub fn clone(this: Data, allocator: std.mem.Allocator) !Data { + pub fn clone(this: Data, allocator: std.mem.Allocator) OOM!Data { return Data{ .text = if (this.text.len > 0) try allocator.dupe(u8, this.text) else "", .location = if (this.location != null) try this.location.?.clone(allocator) else null, @@ -253,6 +254,7 @@ pub const Data = struct { this: *const Data, to: anytype, kind: Kind, + redact_sensitive_information: bool, comptime enable_ansi_colors: bool, ) !void { if (this.text.len == 0) return; @@ -293,7 +295,10 @@ pub const Data = struct { line_offset_for_second_line += std.fmt.count("{d} | ", .{location.line}); } - try to.print("{}\n", .{bun.fmt.fmtJavaScript(line_text, enable_ansi_colors)}); + try to.print("{}\n", .{bun.fmt.fmtJavaScript(line_text, .{ + .enable_colors = enable_ansi_colors, + .redact_sensitive_information = redact_sensitive_information, + })}); try to.writeByteNTimes(' ', line_offset_for_second_line); if ((comptime enable_ansi_colors) and message_color.len > 0) { @@ -379,11 +384,11 @@ pub const BabyString = packed struct { pub const Msg = struct { kind: Kind = Kind.err, data: Data, - metadata: Metadata = .{ .build = 0 }, - // TODO: make this non-optional, empty slice for no notes - notes: ?[]Data = null, + metadata: Metadata = .build, + notes: []Data = &.{}, + redact_sensitive_information: bool = false, - pub fn fromJS(allocator: std.mem.Allocator, globalObject: *bun.JSC.JSGlobalObject, file: string, err: bun.JSC.JSValue) !Msg { + pub fn fromJS(allocator: std.mem.Allocator, globalObject: *bun.JSC.JSGlobalObject, file: string, err: bun.JSC.JSValue) OOM!Msg { var zig_exception_holder: bun.JSC.ZigException.Holder = bun.JSC.ZigException.Holder.init(); if (err.toError()) |value| { value.toZigException(globalObject, zig_exception_holder.zigException()); @@ -410,22 +415,17 @@ pub const Msg = struct { pub fn count(this: *const Msg, builder: *StringBuilder) void { this.data.count(builder); - if (this.notes) |notes| { - for (notes) |note| { - note.count(builder); - } + for (this.notes) |note| { + note.count(builder); } } - pub fn clone(this: *const Msg, allocator: std.mem.Allocator) !Msg { + pub fn clone(this: *const Msg, allocator: std.mem.Allocator) OOM!Msg { return Msg{ .kind = this.kind, .data = try this.data.clone(allocator), .metadata = this.metadata, - .notes = if (this.notes != null and this.notes.?.len > 0) - try bun.clone(this.notes.?, allocator) - else - null, + .notes = try bun.clone(this.notes, allocator), }; } @@ -434,22 +434,18 @@ pub const Msg = struct { .kind = this.kind, .data = this.data.cloneWithBuilder(builder), .metadata = this.metadata, - .notes = if (this.notes != null and this.notes.?.len > 0) brk: { - for (this.notes.?, 0..) |note, i| { + .notes = if (this.notes.len > 0) brk: { + for (this.notes, 0..) |note, i| { notes[i] = note.cloneWithBuilder(builder); } - break :brk notes[0..this.notes.?.len]; - } else null, + break :brk notes[0..this.notes.len]; + } else &.{}, }; } - pub const Metadata = union(Tag) { - build: u0, + pub const Metadata = union(enum) { + build, resolve: Resolve, - pub const Tag = enum(u8) { - build = 1, - resolve = 2, - }; pub const Resolve = struct { specifier: BabyString, @@ -458,34 +454,29 @@ pub const Msg = struct { }; }; - pub fn toAPI(this: *const Msg, allocator: std.mem.Allocator) !Api.Message { - const notes_len = if (this.notes != null) this.notes.?.len else 0; - var _notes = try allocator.alloc( + pub fn toAPI(this: *const Msg, allocator: std.mem.Allocator) OOM!Api.Message { + var notes = try allocator.alloc( Api.MessageData, - notes_len, + this.notes.len, ); const msg = Api.Message{ .level = this.kind.toAPI(), .data = this.data.toAPI(), - .notes = _notes, + .notes = notes, .on = Api.MessageMeta{ .resolve = if (this.metadata == .resolve) this.metadata.resolve.specifier.slice(this.data.text) else "", .build = this.metadata == .build, }, }; - if (this.notes) |notes| { - if (notes.len > 0) { - for (notes, 0..) |note, i| { - _notes[i] = note.toAPI(); - } - } + for (this.notes, 0..) |note, i| { + notes[i] = note.toAPI(); } return msg; } - pub fn toAPIFromList(comptime ListType: type, list: ListType, allocator: std.mem.Allocator) ![]Api.Message { + pub fn toAPIFromList(comptime ListType: type, list: ListType, allocator: std.mem.Allocator) OOM![]Api.Message { var out_list = try allocator.alloc(Api.Message, list.items.len); for (list.items, 0..) |item, i| { out_list[i] = try item.toAPI(allocator); @@ -496,15 +487,13 @@ pub const Msg = struct { pub fn deinit(msg: *Msg, allocator: std.mem.Allocator) void { msg.data.deinit(allocator); - if (msg.notes) |notes| { - for (notes) |*note| { - note.deinit(allocator); - } - - allocator.free(notes); + for (msg.notes) |*note| { + note.deinit(allocator); } - msg.notes = null; + allocator.free(msg.notes); + + msg.notes = &.{}; } pub fn writeFormat( @@ -512,18 +501,16 @@ pub const Msg = struct { to: anytype, comptime enable_ansi_colors: bool, ) !void { - try msg.data.writeFormat(to, msg.kind, enable_ansi_colors); + try msg.data.writeFormat(to, msg.kind, msg.redact_sensitive_information, enable_ansi_colors); - if (msg.notes) |notes| { - if (notes.len > 0) { - try to.writeAll("\n"); - } + if (msg.notes.len > 0) { + try to.writeAll("\n"); + } - for (notes) |note| { - try to.writeAll("\n"); + for (msg.notes) |note| { + try to.writeAll("\n"); - try note.writeFormat(to, .note, enable_ansi_colors); - } + try note.writeFormat(to, .note, msg.redact_sensitive_information, enable_ansi_colors); } } @@ -599,12 +586,9 @@ pub const Range = struct { }; pub const Log = struct { - debug: bool = false, - // TODO: make u32 - warnings: usize = 0, - // TODO: make u32 - errors: usize = 0, - msgs: ArrayList(Msg), + warnings: u32 = 0, + errors: u32 = 0, + msgs: std.ArrayList(Msg), level: Level = if (Environment.isDebug) Level.info else Level.warn, clone_line_text: bool = false, @@ -668,7 +652,7 @@ pub const Log = struct { .{ "error", Level.err }, }); - pub fn fromJS(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue) !?Level { + pub fn fromJS(globalThis: *JSC.JSGlobalObject, value: JSC.JSValue) JSError!?Level { if (value == .zero or value == .undefined) { return null; } @@ -684,28 +668,28 @@ pub const Log = struct { pub fn init(allocator: std.mem.Allocator) Log { return Log{ - .msgs = ArrayList(Msg).init(allocator), + .msgs = std.ArrayList(Msg).init(allocator), .level = default_log_level, }; } pub fn initComptime(allocator: std.mem.Allocator) Log { return Log{ - .msgs = ArrayList(Msg).init(allocator), + .msgs = std.ArrayList(Msg).init(allocator), }; } - pub fn addDebugFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addDebugFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { if (!Kind.shouldPrint(.debug, log.level)) return; @setCold(true); try log.addMsg(.{ .kind = .debug, - .data = try rangeData(source, Range{ .loc = l }, allocPrint(allocator, text, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, Range{ .loc = l }, try allocPrint(allocator, text, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), }); } - pub fn addVerbose(log: *Log, source: ?*const Source, loc: Loc, text: string) !void { + pub fn addVerbose(log: *Log, source: ?*const Source, loc: Loc, text: string) OOM!void { if (!Kind.shouldPrint(.verbose, log.level)) return; @setCold(true); @@ -757,15 +741,13 @@ pub const Log = struct { return arr; } - pub fn cloneTo(self: *Log, other: *Log) !void { + pub fn cloneTo(self: *Log, other: *Log) OOM!void { var notes_count: usize = 0; for (self.msgs.items) |msg_| { const msg: Msg = msg_; - if (msg.notes) |notes| { - for (notes) |note| { - notes_count += @as(usize, @intCast(@intFromBool(note.text.len > 0))); - } + for (msg.notes) |note| { + notes_count += @as(usize, @intCast(@intFromBool(note.text.len > 0))); } } @@ -773,14 +755,12 @@ pub const Log = struct { var notes = try other.msgs.allocator.alloc(Data, notes_count); var note_i: usize = 0; for (self.msgs.items) |*msg| { - if (msg.notes) |current_notes| { - const start_note_i: usize = note_i; - for (current_notes) |note| { - notes[note_i] = note; - note_i += 1; - } - msg.notes = notes[start_note_i..note_i]; + const start_note_i: usize = note_i; + for (msg.notes) |note| { + notes[note_i] = note; + note_i += 1; } + msg.notes = notes[start_note_i..note_i]; } } @@ -789,12 +769,12 @@ pub const Log = struct { other.errors += self.errors; } - pub fn appendTo(self: *Log, other: *Log) !void { + pub fn appendTo(self: *Log, other: *Log) OOM!void { try self.cloneTo(other); self.msgs.clearAndFree(); } - pub fn cloneToWithRecycled(self: *Log, other: *Log, recycled: bool) !void { + pub fn cloneToWithRecycled(self: *Log, other: *Log, recycled: bool) OOM!void { try other.msgs.appendSlice(self.msgs.items); other.warnings += self.warnings; other.errors += self.errors; @@ -806,9 +786,7 @@ pub const Log = struct { for (self.msgs.items) |msg| { msg.count(&string_builder); - if (msg.notes) |notes| { - notes_count += notes.len; - } + notes_count += msg.notes.len; } } @@ -819,18 +797,18 @@ pub const Log = struct { { for (self.msgs.items, (other.msgs.items.len - self.msgs.items.len)..) |msg, j| { other.msgs.items[j] = msg.cloneWithBuilder(notes_buf[note_i..], &string_builder); - note_i += (msg.notes orelse &[_]Data{}).len; + note_i += msg.notes.len; } } } } - pub fn appendToWithRecycled(self: *Log, other: *Log, recycled: bool) !void { + pub fn appendToWithRecycled(self: *Log, other: *Log, recycled: bool) OOM!void { try self.cloneToWithRecycled(other, recycled); self.msgs.clearAndFree(); } - pub fn appendToMaybeRecycled(self: *Log, other: *Log, source: *const Source) !void { + pub fn appendToMaybeRecycled(self: *Log, other: *Log, source: *const Source) OOM!void { return self.appendToWithRecycled(other, source.contents_is_recycled); } @@ -838,7 +816,7 @@ pub const Log = struct { self.msgs.clearAndFree(); } - pub fn addVerboseWithNotes(log: *Log, source: ?*const Source, loc: Loc, text: string, notes: []Data) !void { + pub fn addVerboseWithNotes(log: *Log, source: ?*const Source, loc: Loc, text: string, notes: []Data) OOM!void { @setCold(true); if (!Kind.shouldPrint(.verbose, log.level)) return; @@ -849,13 +827,13 @@ pub const Log = struct { }); } - inline fn allocPrint(allocator: std.mem.Allocator, comptime fmt: string, args: anytype) !string { + inline fn allocPrint(allocator: std.mem.Allocator, comptime fmt: string, args: anytype) OOM!string { return try switch (Output.enable_ansi_colors) { inline else => |enable_ansi_colors| std.fmt.allocPrint(allocator, Output.prettyFmt(fmt, enable_ansi_colors), args), }; } - inline fn _addResolveErrorWithLevel( + inline fn addResolveErrorWithLevel( log: *Log, source: ?*const Source, r: Range, @@ -864,13 +842,13 @@ pub const Log = struct { args: anytype, import_kind: ImportKind, comptime dupe_text: bool, - comptime is_error: bool, + comptime kind: enum { err, warn }, err: anyerror, - ) !void { + ) OOM!void { const text = try allocPrint(allocator, fmt, args); // TODO: fix this. this is stupid, it should be returned in allocPrint. const specifier = BabyString.in(text, args.@"0"); - if (comptime is_error) { + if (comptime kind == .err) { log.errors += 1; } else { log.warnings += 1; @@ -884,7 +862,7 @@ pub const Log = struct { ); if (_data.location != null) { if (_data.location.?.line_text) |line| { - _data.location.?.line_text = allocator.dupe(u8, line) catch unreachable; + _data.location.?.line_text = try allocator.dupe(u8, line); } } break :brk _data; @@ -895,7 +873,8 @@ pub const Log = struct { ); const msg = Msg{ - .kind = if (comptime is_error) Kind.err else Kind.warn, + // .kind = if (comptime error_type == .err) Kind.err else Kind.warn, + .kind = @field(Kind, @tagName(kind)), .data = data, .metadata = .{ .resolve = Msg.Metadata.Resolve{ .specifier = specifier, @@ -907,34 +886,6 @@ pub const Log = struct { try log.addMsg(msg); } - inline fn _addResolveError( - log: *Log, - source: ?*const Source, - r: Range, - allocator: std.mem.Allocator, - comptime fmt: string, - args: anytype, - import_kind: ImportKind, - comptime dupe_text: bool, - err: anyerror, - ) !void { - return _addResolveErrorWithLevel(log, source, r, allocator, fmt, args, import_kind, dupe_text, true, err); - } - - inline fn _addResolveWarn( - log: *Log, - source: ?*const Source, - r: Range, - allocator: std.mem.Allocator, - comptime fmt: string, - args: anytype, - import_kind: ImportKind, - comptime dupe_text: bool, - err: anyerror, - ) !void { - return _addResolveErrorWithLevel(log, source, r, allocator, fmt, args, import_kind, dupe_text, false, err); - } - pub fn addResolveError( log: *Log, source: ?*const Source, @@ -944,9 +895,9 @@ pub const Log = struct { args: anytype, import_kind: ImportKind, err: anyerror, - ) !void { + ) OOM!void { @setCold(true); - return try _addResolveError(log, source, r, allocator, fmt, args, import_kind, false, err); + return try addResolveErrorWithLevel(log, source, r, allocator, fmt, args, import_kind, false, .err, err); } pub fn addResolveErrorWithTextDupe( @@ -957,30 +908,12 @@ pub const Log = struct { comptime fmt: string, args: anytype, import_kind: ImportKind, - ) !void { + ) OOM!void { @setCold(true); - return try _addResolveError(log, source, r, allocator, fmt, args, import_kind, true, error.ModuleNotFound); + return try addResolveErrorWithLevel(log, source, r, allocator, fmt, args, import_kind, true, .err, error.ModuleNotFound); } - pub fn addResolveErrorWithTextDupeMaybeWarn( - log: *Log, - source: ?*const Source, - r: Range, - allocator: std.mem.Allocator, - comptime fmt: string, - args: anytype, - import_kind: ImportKind, - warn: bool, - ) !void { - @setCold(true); - if (warn) { - return try _addResolveError(log, source, r, allocator, fmt, args, import_kind, true, error.ModuleNotFound); - } else { - return try _addResolveWarn(log, source, r, allocator, fmt, args, import_kind, true, error.ModuleNotFound); - } - } - - pub fn addRangeError(log: *Log, source: ?*const Source, r: Range, text: string) !void { + pub fn addRangeError(log: *Log, source: ?*const Source, r: Range, text: string) OOM!void { @setCold(true); log.errors += 1; try log.addMsg(.{ @@ -989,44 +922,55 @@ pub const Log = struct { }); } - pub fn addRangeErrorFmt(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addRangeErrorFmt(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { @setCold(true); log.errors += 1; try log.addMsg(.{ .kind = .err, - .data = try rangeData(source, r, allocPrint(allocator, text, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, r, try allocPrint(allocator, text, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), }); } - pub fn addRangeErrorFmtWithNotes(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, notes: []Data, comptime fmt: string, args: anytype) !void { + pub fn addRangeErrorFmtWithNotes(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, notes: []Data, comptime fmt: string, args: anytype) OOM!void { @setCold(true); log.errors += 1; try log.addMsg(.{ .kind = .err, - .data = try rangeData(source, r, allocPrint(allocator, fmt, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, r, try allocPrint(allocator, fmt, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), .notes = notes, }); } - pub fn addErrorFmtNoLoc(log: *Log, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { - try log.addErrorFmt(null, Loc.Empty, allocator, text, args); - } - - pub fn addErrorFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addErrorFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { @setCold(true); log.errors += 1; try log.addMsg(.{ .kind = .err, - .data = try rangeData(source, Range{ .loc = l }, allocPrint(allocator, text, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, .{ .loc = l }, try allocPrint(allocator, text, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), }); } - pub fn addZigErrorWithNote(log: *Log, allocator: std.mem.Allocator, err: anyerror, comptime noteFmt: string, args: anytype) !void { + // TODO(dylan-conway): rename and replace `addErrorFmt` + pub fn addErrorFmtOpts(log: *Log, allocator: std.mem.Allocator, comptime fmt: string, args: anytype, opts: AddErrorOptions) OOM!void { + @setCold(true); + log.errors += 1; + try log.addMsg(.{ + .kind = .err, + .data = try rangeData( + opts.source, + .{ .loc = opts.loc, .len = opts.len }, + try allocPrint(allocator, fmt, args), + ).cloneLineText(log.clone_line_text, log.msgs.allocator), + .redact_sensitive_information = opts.redact_sensitive_information, + }); + } + + pub fn addZigErrorWithNote(log: *Log, allocator: std.mem.Allocator, err: anyerror, comptime noteFmt: string, args: anytype) OOM!void { @setCold(true); log.errors += 1; var notes = try allocator.alloc(Data, 1); - notes[0] = rangeData(null, Range.None, allocPrint(allocator, noteFmt, args) catch unreachable); + notes[0] = rangeData(null, Range.None, try allocPrint(allocator, noteFmt, args)); try log.addMsg(.{ .kind = .err, @@ -1035,7 +979,7 @@ pub const Log = struct { }); } - pub fn addRangeWarning(log: *Log, source: ?*const Source, r: Range, text: string) !void { + pub fn addRangeWarning(log: *Log, source: ?*const Source, r: Range, text: string) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; @@ -1045,17 +989,17 @@ pub const Log = struct { }); } - pub fn addWarningFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addWarningFmt(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; try log.addMsg(.{ .kind = .warn, - .data = try rangeData(source, Range{ .loc = l }, allocPrint(allocator, text, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, Range{ .loc = l }, try allocPrint(allocator, text, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), }); } - pub fn addWarningFmtLineCol(log: *Log, filepath: []const u8, line: u32, col: u32, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addWarningFmtLineCol(log: *Log, filepath: []const u8, line: u32, col: u32, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; @@ -1064,24 +1008,24 @@ pub const Log = struct { try log.addMsg(.{ .kind = .warn, - .data = Data.cloneLineText(Data{ - .text = allocPrint(allocator, text, args) catch unreachable, + .data = try Data.cloneLineText(Data{ + .text = try allocPrint(allocator, text, args), .location = Location{ .file = filepath, .line = @intCast(line), .column = @intCast(col), }, - }, log.clone_line_text, allocator) catch unreachable, + }, log.clone_line_text, allocator), }); } - pub fn addRangeWarningFmt(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void { + pub fn addRangeWarningFmt(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, comptime text: string, args: anytype) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; try log.addMsg(.{ .kind = .warn, - .data = try rangeData(source, r, allocPrint(allocator, text, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, r, try allocPrint(allocator, text, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), }); } @@ -1095,27 +1039,27 @@ pub const Log = struct { comptime note_fmt: string, note_args: anytype, note_range: Range, - ) !void { + ) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; var notes = try allocator.alloc(Data, 1); - notes[0] = rangeData(source, note_range, allocPrint(allocator, note_fmt, note_args) catch unreachable); + notes[0] = rangeData(source, note_range, try allocPrint(allocator, note_fmt, note_args)); try log.addMsg(.{ .kind = .warn, - .data = rangeData(source, r, allocPrint(allocator, fmt, args) catch unreachable), + .data = rangeData(source, r, try allocPrint(allocator, fmt, args)), .notes = notes, }); } - pub fn addRangeWarningFmtWithNotes(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, notes: []Data, comptime fmt: string, args: anytype) !void { + pub fn addRangeWarningFmtWithNotes(log: *Log, source: ?*const Source, r: Range, allocator: std.mem.Allocator, notes: []Data, comptime fmt: string, args: anytype) OOM!void { @setCold(true); log.warnings += 1; try log.addMsg(.{ .kind = .warn, - .data = try rangeData(source, r, allocPrint(allocator, fmt, args) catch unreachable).cloneLineText(log.clone_line_text, log.msgs.allocator), + .data = try rangeData(source, r, try allocPrint(allocator, fmt, args)).cloneLineText(log.clone_line_text, log.msgs.allocator), .notes = notes, }); } @@ -1130,22 +1074,22 @@ pub const Log = struct { comptime note_fmt: string, note_args: anytype, note_range: Range, - ) !void { + ) OOM!void { @setCold(true); if (!Kind.shouldPrint(.err, log.level)) return; log.errors += 1; var notes = try allocator.alloc(Data, 1); - notes[0] = rangeData(source, note_range, allocPrint(allocator, note_fmt, note_args) catch unreachable); + notes[0] = rangeData(source, note_range, try allocPrint(allocator, note_fmt, note_args)); try log.addMsg(.{ .kind = .err, - .data = rangeData(source, r, allocPrint(allocator, fmt, args) catch unreachable), + .data = rangeData(source, r, try allocPrint(allocator, fmt, args)), .notes = notes, }); } - pub fn addWarning(log: *Log, source: ?*const Source, l: Loc, text: string) !void { + pub fn addWarning(log: *Log, source: ?*const Source, l: Loc, text: string) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; @@ -1155,13 +1099,13 @@ pub const Log = struct { }); } - pub fn addWarningWithNote(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, warn: string, comptime note_fmt: string, note_args: anytype) !void { + pub fn addWarningWithNote(log: *Log, source: ?*const Source, l: Loc, allocator: std.mem.Allocator, warn: string, comptime note_fmt: string, note_args: anytype) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; var notes = try allocator.alloc(Data, 1); - notes[0] = rangeData(source, Range{ .loc = l }, allocPrint(allocator, note_fmt, note_args) catch unreachable); + notes[0] = rangeData(source, Range{ .loc = l }, try allocPrint(allocator, note_fmt, note_args)); try log.addMsg(.{ .kind = .warn, @@ -1170,7 +1114,7 @@ pub const Log = struct { }); } - pub fn addRangeDebug(log: *Log, source: ?*const Source, r: Range, text: string) !void { + pub fn addRangeDebug(log: *Log, source: ?*const Source, r: Range, text: string) OOM!void { @setCold(true); if (!Kind.shouldPrint(.debug, log.level)) return; try log.addMsg(.{ @@ -1179,7 +1123,7 @@ pub const Log = struct { }); } - pub fn addRangeDebugWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) !void { + pub fn addRangeDebugWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) OOM!void { @setCold(true); if (!Kind.shouldPrint(.debug, log.level)) return; // log.de += 1; @@ -1190,7 +1134,7 @@ pub const Log = struct { }); } - pub fn addRangeErrorWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) !void { + pub fn addRangeErrorWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) OOM!void { @setCold(true); log.errors += 1; try log.addMsg(.{ @@ -1200,7 +1144,7 @@ pub const Log = struct { }); } - pub fn addRangeWarningWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) !void { + pub fn addRangeWarningWithNotes(log: *Log, source: ?*const Source, r: Range, text: string, notes: []Data) OOM!void { @setCold(true); if (!Kind.shouldPrint(.warn, log.level)) return; log.warnings += 1; @@ -1211,17 +1155,35 @@ pub const Log = struct { }); } - pub inline fn addMsg(self: *Log, msg: Msg) !void { + pub fn addMsg(self: *Log, msg: Msg) OOM!void { try self.msgs.append(msg); } - pub fn addError(self: *Log, _source: ?*const Source, loc: Loc, text: string) !void { + pub fn addError(self: *Log, _source: ?*const Source, loc: Loc, text: string) OOM!void { @setCold(true); self.errors += 1; try self.addMsg(.{ .kind = .err, .data = rangeData(_source, Range{ .loc = loc }, text) }); } - pub fn addSymbolAlreadyDeclaredError(self: *Log, allocator: std.mem.Allocator, source: *const Source, name: string, new_loc: Loc, old_loc: Loc) !void { + const AddErrorOptions = struct { + source: ?*const Source = null, + loc: Loc = Loc.Empty, + len: i32 = 0, + redact_sensitive_information: bool = false, + }; + + // TODO(dylan-conway): rename and replace `addError` + pub fn addErrorOpts(self: *Log, text: string, opts: AddErrorOptions) OOM!void { + @setCold(true); + self.errors += 1; + try self.addMsg(.{ + .kind = .err, + .data = rangeData(opts.source, .{ .loc = opts.loc, .len = opts.len }, text), + .redact_sensitive_information = opts.redact_sensitive_information, + }); + } + + pub fn addSymbolAlreadyDeclaredError(self: *Log, allocator: std.mem.Allocator, source: *const Source, name: string, new_loc: Loc, old_loc: Loc) OOM!void { var notes = try allocator.alloc(Data, 1); notes[0] = rangeData( source, @@ -1239,13 +1201,13 @@ pub const Log = struct { ); } - pub fn printForLogLevel(self: *Log, to: anytype) !void { + pub fn print(self: *Log, to: anytype) !void { return switch (Output.enable_ansi_colors) { - inline else => |enable_ansi_colors| self.printForLogLevelWithEnableAnsiColors(to, enable_ansi_colors), + inline else => |enable_ansi_colors| self.printWithEnableAnsiColors(to, enable_ansi_colors), }; } - pub fn printForLogLevelWithEnableAnsiColors(self: *const Log, to: anytype, comptime enable_ansi_colors: bool) !void { + pub fn printWithEnableAnsiColors(self: *const Log, to: anytype, comptime enable_ansi_colors: bool) !void { var needs_newline = false; if (self.warnings > 0 and self.errors > 0) { // Print warnings at the top @@ -1285,14 +1247,6 @@ pub const Log = struct { if (needs_newline) _ = try to.write("\n"); } - pub fn printForLogLevelColorsRuntime(self: *Log, to: anytype, enable_ansi_colors: bool) !void { - if (enable_ansi_colors) { - return self.printForLogLevelWithEnableAnsiColors(to, true); - } else { - return self.printForLogLevelWithEnableAnsiColors(to, false); - } - } - pub fn toZigException(this: *const Log, allocator: std.mem.Allocator) *js.ZigException.Holder { var holder = try allocator.create(js.ZigException.Holder); holder.* = js.ZigException.Holder.init(); diff --git a/src/main_wasm.zig b/src/main_wasm.zig index d4d0c43dca..3a1cfc5c83 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -483,7 +483,7 @@ export fn getTests(opts_array: u64) u64 { Output.print("Error: {s}\n", .{@errorName(err)}); - log_.printForLogLevel(Output.writer()) catch unreachable; + log_.print(Output.writer()) catch unreachable; return 0; }; diff --git a/src/router.zig b/src/router.zig index f5316982ed..7fcf314411 100644 --- a/src/router.zig +++ b/src/router.zig @@ -993,7 +993,7 @@ pub const Test = struct { const Resolver = @import("./resolver/resolver.zig").Resolver; var logger = Logger.Log.init(default_allocator); errdefer { - logger.printForLogLevel(Output.errorWriter()) catch {}; + logger.print(Output.errorWriter()) catch {}; } const opts = Options.BundleOptions{ @@ -1049,7 +1049,7 @@ pub const Test = struct { const Resolver = @import("./resolver/resolver.zig").Resolver; var logger = Logger.Log.init(default_allocator); errdefer { - logger.printForLogLevel(Output.errorWriter()) catch {}; + logger.print(Output.errorWriter()) catch {}; } const opts = Options.BundleOptions{ diff --git a/src/string.zig b/src/string.zig index 930ee12dd5..2c694efbdd 100644 --- a/src/string.zig +++ b/src/string.zig @@ -341,7 +341,7 @@ pub const String = extern struct { return bytes; } - pub fn toOwnedSliceReturningAllASCII(this: String, allocator: std.mem.Allocator) !struct { []u8, bool } { + pub fn toOwnedSliceReturningAllASCII(this: String, allocator: std.mem.Allocator) OOM!struct { []u8, bool } { switch (this.tag) { .ZigString => return .{ try this.value.ZigString.toOwnedSlice(allocator), true }, .WTFStringImpl => { diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 5745bd2b91..d62af2b977 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -85,9 +85,6 @@ fn literalLength(comptime T: type, comptime str: string) usize { }; } -// TODO: remove this -pub const toUTF16LiteralZ = toUTF16Literal; - pub const OptionalUsize = std.meta.Int(.unsigned, @bitSizeOf(usize) - 1); pub fn indexOfAny(slice: string, comptime str: []const u8) ?OptionalUsize { switch (comptime str.len) { @@ -217,9 +214,8 @@ pub fn isNPMPackageName(target: string) bool { return !scoped or slash_index > 0 and slash_index + 1 < target.len; } -pub fn startsWithUUID(str: string) bool { - const uuid_len = 36; - if (str.len < uuid_len) return false; +pub fn isUUID(str: string) bool { + if (str.len != uuid_len) return false; for (0..8) |i| { switch (str[i]) { '0'...'9', 'a'...'f', 'A'...'F' => {}, @@ -257,6 +253,12 @@ pub fn startsWithUUID(str: string) bool { return true; } +pub const uuid_len = 36; + +pub fn startsWithUUID(str: string) bool { + return isUUID(str[0..@min(str.len, uuid_len)]); +} + /// https://github.com/npm/cli/blob/63d6a732c3c0e9c19fd4d147eaa5cc27c29b168d/node_modules/%40npmcli/redact/lib/matchers.js#L7 /// /\b(npms?_)[a-zA-Z0-9]{36,48}\b/gi /// Returns the length of the secret if one exist. @@ -294,6 +296,128 @@ pub fn startsWithNpmSecret(str: string) u8 { return i; } +fn startsWithRedactedItem(text: string, comptime item: string) ?struct { usize, usize } { + if (!strings.hasPrefixComptime(text, item)) return null; + + var whitespace = false; + var offset: usize = item.len; + while (offset < text.len and std.ascii.isWhitespace(text[offset])) { + offset += 1; + whitespace = true; + } + if (offset == text.len) return null; + const cont = js_lexer.isIdentifierContinue(text[offset]); + + // must be another identifier + if (!whitespace and cont) return null; + + // `null` is not returned after this point. Redact to the next + // newline if anything is unexpected + if (cont) return .{ offset, indexOfChar(text[offset..], '\n') orelse text[offset..].len }; + offset += 1; + + var end = offset; + while (end < text.len and std.ascii.isWhitespace(text[end])) { + end += 1; + } + + if (end == text.len) { + return .{ offset, text[offset..].len }; + } + + switch (text[end]) { + inline '\'', '"', '`' => |q| { + // attempt to find closing + const opening = end; + end += 1; + while (end < text.len) { + switch (text[end]) { + '\\' => { + // skip + end += 1; + end += 1; + }, + q => { + // closing + return .{ opening + 1, (end - 1) - opening }; + }, + else => { + end += 1; + }, + } + } + + const len = strings.indexOfChar(text[offset..], '\n') orelse text[offset..].len; + return .{ offset, len }; + }, + else => { + const len = strings.indexOfChar(text[offset..], '\n') orelse text[offset..].len; + return .{ offset, len }; + }, + } +} + +/// Returns offset and length of first secret found. +pub fn startsWithSecret(str: string) ?struct { usize, usize } { + if (startsWithRedactedItem(str, "_auth")) |auth| { + const offset, const len = auth; + return .{ offset, len }; + } + if (startsWithRedactedItem(str, "_authToken")) |auth_token| { + const offset, const len = auth_token; + return .{ offset, len }; + } + if (startsWithRedactedItem(str, "email")) |email| { + const offset, const len = email; + return .{ offset, len }; + } + if (startsWithRedactedItem(str, "_password")) |password| { + const offset, const len = password; + return .{ offset, len }; + } + if (startsWithRedactedItem(str, "token")) |token| { + const offset, const len = token; + return .{ offset, len }; + } + + if (startsWithUUID(str)) { + return .{ 0, 36 }; + } + + const npm_secret_len = startsWithNpmSecret(str); + if (npm_secret_len > 0) { + return .{ 0, npm_secret_len }; + } + + if (findUrlPassword(str)) |url_pass| { + const offset, const len = url_pass; + return .{ offset, len }; + } + + return null; +} + +pub fn findUrlPassword(text: string) ?struct { usize, usize } { + if (!strings.hasPrefixComptime(text, "http")) return null; + var offset: usize = "http".len; + if (hasPrefixComptime(text[offset..], "://")) { + offset += "://".len; + } else if (hasPrefixComptime(text[offset..], "s://")) { + offset += "s://".len; + } else { + return null; + } + var remain = text[offset..]; + const end = indexOfChar(remain, '\n') orelse remain.len; + remain = remain[0..end]; + const at = indexOfChar(remain, '@') orelse return null; + const colon = indexOfCharNeg(remain[0..at], ':'); + if (colon == -1 or colon == at - 1) return null; + offset += @intCast(colon + 1); + const len: usize = at - @as(usize, @intCast(colon + 1)); + return .{ offset, len }; +} + pub fn indexAnyComptime(target: string, comptime chars: string) ?usize { for (target, 0..) |parent, i| { inline for (chars) |char| { diff --git a/src/toml/toml_lexer.zig b/src/toml/toml_lexer.zig index 4e53e1a2b4..34f3646de1 100644 --- a/src/toml/toml_lexer.zig +++ b/src/toml/toml_lexer.zig @@ -70,6 +70,8 @@ pub const Lexer = struct { has_newline_before: bool = false, + should_redact_logs: bool, + pub inline fn loc(self: *const Lexer) logger.Loc { return logger.usize2Loc(self.start); } @@ -80,12 +82,12 @@ pub const Lexer = struct { // Only add this if there is not already an error. // It is possible that there is a more descriptive error already emitted. if (!self.log.hasErrors()) - self.addError(self.start, "Syntax Error", .{}, true); + self.addError(self.start, "Syntax Error", .{}); return Error.SyntaxError; } - pub fn addError(self: *Lexer, _loc: usize, comptime format: []const u8, args: anytype, _: bool) void { + pub fn addError(self: *Lexer, _loc: usize, comptime format: []const u8, args: anytype) void { @setCold(true); var __loc = logger.usize2Loc(_loc); @@ -93,24 +95,33 @@ pub const Lexer = struct { return; } - self.log.addErrorFmt(&self.source, __loc, self.log.msgs.allocator, format, args) catch unreachable; + self.log.addErrorFmtOpts( + self.log.msgs.allocator, + format, + args, + .{ + .source = &self.source, + .loc = __loc, + .redact_sensitive_information = self.should_redact_logs, + }, + ) catch unreachable; self.prev_error_loc = __loc; } pub fn addDefaultError(self: *Lexer, msg: []const u8) !void { @setCold(true); - self.addError(self.start, "{s}", .{msg}, true); + self.addError(self.start, "{s}", .{msg}); return Error.SyntaxError; } pub fn addSyntaxError(self: *Lexer, _loc: usize, comptime fmt: []const u8, args: anytype) !void { @setCold(true); - self.addError(_loc, fmt, args, false); + self.addError(_loc, fmt, args); return Error.SyntaxError; } - pub fn addRangeError(self: *Lexer, r: logger.Range, comptime format: []const u8, args: anytype, _: bool) !void { + pub fn addRangeError(self: *Lexer, r: logger.Range, comptime format: []const u8, args: anytype) !void { @setCold(true); if (self.prev_error_loc.eql(r.loc)) { @@ -118,12 +129,13 @@ pub const Lexer = struct { } const errorMessage = std.fmt.allocPrint(self.log.msgs.allocator, format, args) catch unreachable; - try self.log.addRangeError(&self.source, r, errorMessage); + try self.log.addErrorOpts(errorMessage, .{ + .source = &self.source, + .loc = r.loc, + .len = r.len, + .redact_sensitive_information = self.should_redact_logs, + }); self.prev_error_loc = r.loc; - - // if (panic) { - // return Error.ParserError; - // } } /// Look ahead at the next n codepoints without advancing the iterator. @@ -924,7 +936,6 @@ pub const Lexer = struct { logger.Range{ .loc = .{ .start = @as(i32, @intCast(octal_start)) }, .len = @as(i32, @intCast(iter.i - octal_start)) }, "Invalid legacy octal literal", .{}, - false, ) catch unreachable; } }, @@ -1036,7 +1047,6 @@ pub const Lexer = struct { .{ .loc = .{ .start = @as(i32, @intCast(start + hex_start)) }, .len = @as(i32, @intCast((iter.i - hex_start))) }, "Unicode escape sequence is out of range", .{}, - true, ); return; } @@ -1132,7 +1142,7 @@ pub const Lexer = struct { } }; - try lexer.addRangeError(lexer.range(), "Unexpected {s}", .{found}, true); + try lexer.addRangeError(lexer.range(), "Unexpected {s}", .{found}); } pub fn expectedString(self: *Lexer, text: string) !void { @@ -1144,7 +1154,7 @@ pub const Lexer = struct { } }; - try self.addRangeError(self.range(), "Expected {s} but found {s}", .{ text, found }, true); + try self.addRangeError(self.range(), "Expected {s} but found {s}", .{ text, found }); } pub fn range(self: *Lexer) logger.Range { @@ -1154,12 +1164,13 @@ pub const Lexer = struct { }; } - pub fn init(log: *logger.Log, source: logger.Source, allocator: std.mem.Allocator) !Lexer { + pub fn init(log: *logger.Log, source: logger.Source, allocator: std.mem.Allocator, redact_logs: bool) !Lexer { var lex = Lexer{ .log = log, .source = source, .prev_error_loc = logger.Loc.Empty, .allocator = allocator, + .should_redact_logs = redact_logs, }; lex.step(); try lex.next(); diff --git a/src/toml/toml_parser.zig b/src/toml/toml_parser.zig index 25d760526a..b569be7495 100644 --- a/src/toml/toml_parser.zig +++ b/src/toml/toml_parser.zig @@ -81,9 +81,9 @@ pub const TOML = struct { log: *logger.Log, allocator: std.mem.Allocator, - pub fn init(allocator: std.mem.Allocator, source_: logger.Source, log: *logger.Log) !TOML { + pub fn init(allocator: std.mem.Allocator, source_: logger.Source, log: *logger.Log, redact_logs: bool) !TOML { return TOML{ - .lexer = try Lexer.init(log, source_, allocator), + .lexer = try Lexer.init(log, source_, allocator, redact_logs), .allocator = allocator, .log = log, }; @@ -166,7 +166,7 @@ pub const TOML = struct { return head; } - pub fn parse(source_: *const logger.Source, log: *logger.Log, allocator: std.mem.Allocator) !Expr { + pub fn parse(source_: *const logger.Source, log: *logger.Log, allocator: std.mem.Allocator, redact_logs: bool) !Expr { switch (source_.contents.len) { // This is to be consisntent with how disabled JS files are handled 0 => { @@ -175,7 +175,7 @@ pub const TOML = struct { else => {}, } - var parser = try TOML.init(allocator, source_.*, log); + var parser = try TOML.init(allocator, source_.*, log, redact_logs); return try parser.runParser(); } diff --git a/test/cli/install/redacted-config-logs.test.ts b/test/cli/install/redacted-config-logs.test.ts new file mode 100644 index 0000000000..68d9a70c5b --- /dev/null +++ b/test/cli/install/redacted-config-logs.test.ts @@ -0,0 +1,102 @@ +import { bunExe, bunEnv, tmpdirSync } from "harness"; +import { write, spawnSync } from "bun"; +import { describe, test, expect } from "bun:test"; +import { join } from "path"; + +describe("redact", async () => { + const tests = [ + { + title: "url password", + bunfig: `install.registry = "https://user:pass@registry.org`, + expected: `"https://user:****@registry.org`, + }, + { + title: "empty url password", + bunfig: `install.registry = "https://user:@registry.org`, + expected: `"https://user:@registry.org`, + }, + { + title: "small string", + bunfig: `l;token = "1"`, + expected: `"*"`, + }, + { + title: "random UUID", + bunfig: 'unre;lated = "f1b0b6b4-4b1b-4b1b-8b1b-4b1b4b1b4b1b"', + expected: '"************************************"', + }, + { + title: "random npm_ secret", + bunfig: 'the;secret = "npm_1234567890abcdefghijklmnopqrstuvwxyz"', + expected: '"****************************************"', + }, + { + title: "random npms_ secret", + bunfig: 'the;secret = "npms_1234567890abcdefghijklmnopqrstuvwxyz"', + expected: "*****************************************", + }, + { + title: "zero length unterminated string", + bunfig: '_authToken = "', + expected: "*", + }, + { + title: "invalid _auth", + npmrc: "//registry.npmjs.org/:_auth = does-not-decode", + expected: "****************", + }, + { + title: "unexpected _auth", + npmrc: "//registry.npmjs.org/:_auth=:secret", + expected: "*******", + }, + { + title: "_auth zero length", + npmrc: "//registry.npmjs.org/:_auth=", + expected: "received an empty string", + }, + { + title: "_auth one length", + npmrc: "//registry.npmjs.org/:_auth=1", + expected: "*", + }, + ]; + + for (const { title, bunfig, npmrc, expected } of tests) { + test(title + (bunfig ? " (bunfig)" : " (npmrc)"), async () => { + const testDir = tmpdirSync(); + await Promise.all([ + write(join(testDir, bunfig ? "bunfig.toml" : ".npmrc"), (bunfig || npmrc)!), + write(join(testDir, "package.json"), "{}"), + ]); + + // once without color + let proc = spawnSync({ + cmd: [bunExe(), "install"], + cwd: testDir, + env: { ...bunEnv, NO_COLOR: "1" }, + stdout: "pipe", + stderr: "pipe", + }); + + let out = proc.stdout.toString(); + let err = proc.stderr.toString(); + expect(proc.exitCode).toBe(+!!bunfig); + expect(err).toContain(expected || "*"); + + // once with color + proc = spawnSync({ + cmd: [bunExe(), "install"], + cwd: testDir, + env: { ...bunEnv, NO_COLOR: undefined, FORCE_COLOR: "1" }, + stdout: "pipe", + stderr: "pipe", + }); + + out = proc.stdout.toString(); + err = proc.stderr.toString(); + expect(proc.exitCode).toBe(+!!bunfig); + expect(err).toContain(expected || "*"); + }); + } +}); diff --git a/test/cli/install/registry/bun-install-registry.test.ts b/test/cli/install/registry/bun-install-registry.test.ts index 4e5f931221..1321410be3 100644 --- a/test/cli/install/registry/bun-install-registry.test.ts +++ b/test/cli/install/registry/bun-install-registry.test.ts @@ -515,7 +515,7 @@ ${Object.keys(opts) dotEnv: { SECRET_AUTH: "" }, }, (stdout: string, stderr: string) => { - expect(stderr).toContain("got an empty string"); + expect(stderr).toContain("received an empty string"); }, ); });