Harden 36 reachable security findings across runtime, install, parsers, http

Merged
JA
6mo ago
+739-123

Hardens 36 reachable security findings across the runtime, package manager, parsers, HTTP client/server, and SQL drivers. Three auto-applied fixes (#61 SSL exception leak, #68 YAML merge dedup, #104 archive overwrite precheck) were dropped: #61 introduced a use-after-free, #68 stored a non-'static byte view in a 'static field, and #104 added dead gating that did not close the traversal.

Memory safety / lifetime

  • #2 — Dangling proxy slice across reentrant JS getter — copy process.env proxy href to an owned Vec before reentrant getters can free the env map (Blob.rs)
  • #15 — Rollback restores dangling editor name pointer — preserve and restore name_storage on detect_editor failure (BunObject.rs)
  • #81 — Reentrant reconnect frees live handlers — only free previous handlers when active_connections == 0 (Listener.rs)
  • #110 — Async randomFill uses stale resizable buffer pointer — fill a worker-owned scratch buffer; copy back on the JS thread after re-validating bounds (node_crypto_binding.rs)
  • #119 — Null zero-length slice UB in DOMJIT fast path — use ffi::slice which tolerates (null, 0) (Crypto.rs)
  • #67 — Raw serialization reads struct padding bytes — add explicit _padding_* fields with offset_of! proof asserts (npm.rs)
  • #74 — TLS rejection path leaks websocket refcount — route SSL/auth failures through self.fail() which clears outgoing_websocket (websocket_client.rs)
  • #108 — FD-backed fetch body leaks duplicated descriptor — close opened_fd unconditionally after read_file (fetch.rs)

Untrusted-input bounds / panics

  • #10 — Invalid lockfile tag causes panic DoS — replace unreachable!() with logged error + Tag::Uninitialized (dependency.rs)
  • #20 — Unchecked lockfile string offsets cause OOB slice — bounds-check non-inline String pointers against ctx.buffer (dependency.rs)
  • #91 — Panic on unvalidated resolution tag — validate ResolutionTag discriminants on lockfile load (Package.rs)
  • #24 — Unwrap panic on unexpected 304 response — return UnexpectedNotModified when no cached manifest exists (npm.rs)
  • #44 — UDP port getter unwrap panic on transient state — return undefined when socket is None (udp_socket.rs)
  • #36 — Close reason length mismatch causes panic — clamp body_len to 125 and bail on overlong UTF-8 transcode (websocket_client.rs)
  • #100 — Windows pipe name length panic DoS — debug_assert → real bounds check (Listener.rs)
  • #60 / #111 — Windows shim stack buffer overflows — bounds-check argument and filename writes against BUF1_LEN/BUF2_U16_LEN before copy_nonoverlapping (bun_shim_impl.rs)
  • #76 / #101 — Unchecked bin name/entry name copies — bounds-check before slicing into abs_dest_buf (bin.rs)
  • #79 — if keyword misclassification causes parser panic — require a delimiter token before classifying (shell_parser/parse.rs)
  • #32 — Bounds check occurs after UTF-16 write — pre-flight key/value lengths before convert_utf8_to_utf16_in_buffer (env_loader.rs)
  • #95 — PBKDF2 digest validation allows panic-only algorithm — reject digests with no EVP_MD (PBKDF2.rs)

DoS / resource caps

  • #17 — Unbounded recursion on deep TOML dotted keys — cap dotted-key segments at 512 (toml.rs)
  • #39 — Unbounded brace expansion preallocation — cap expansion count at 65536 in Bun.$ and Bun.braces (BunObject.rs, Expansion.rs)
  • #31 — SCRAM PBKDF2 parameters accepted from server — clamp iteration count to [4096, 10M], salt length to [1, 1024] (PostgresSQLConnection.rs)

Auth / injection / traversal

  • #19 — Cleartext password sent after TLS downgrade — require TLSStatus::SslOk, not just ssl_mode != Disable (MySQLConnection.rs)
  • #83 — Strict TLS request reuses lax-verified pooled socket — track established_with_reject_unauthorized and refuse pool reuse for strict callers (HTTPContext.rs, lib.rs, ClientSession.rs)
  • #73 — IPv6 loopback prefix auth bypass — exact-match ::1 instead of starts_with (server_body.rs)
  • #56 — Unsanitized filename injects response headers — reject \r/\n/NUL/" in content-disposition filenames (RequestContext.rs)
  • #43 — Missing CRLF checks for signed host/auth headers — also validate region, access_key_id, and host (s3_signing/credentials.rs)
  • #34 — Bucket slash enables S3 host confusion — reject buckets containing / (s3_signing/credentials.rs)
  • #25 — Lexical symlink check permits extraction escape — track created symlinks during extraction and refuse paths that traverse them (libarchive/lib.rs)
  • #71 — bunx executes untrusted temp-cache binary — lstat cached binary; refuse symlinks and other-uid files (bunx_command.rs)

Permission hygiene

  • #6 — Bin target chmod always sets mode 0777 — 0o777 & !umask instead of umask | 0o777 (bin.rs)
  • #23 — Process umask cleared and never restored — restore umask after probing it in ensure_umask (bin.rs)

Parser correctness

  • #22 — Sign-prefixed scalar misparsed as infinity — fix Zig→Rust &&/|| precedence transliteration (yaml.rs)