Harden 36 reachable security findings across runtime, install, parsers, http
Merged
JA
6mo ago+739+739+012345678901234567890123456789-123-123-012345678901234567890123456789
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.envproxy href to an ownedVecbefore reentrant getters can free the env map (Blob.rs) - #15 — Rollback restores dangling editor name pointer — preserve and restore
name_storageondetect_editorfailure (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::slicewhich tolerates(null, 0)(Crypto.rs) - #67 — Raw serialization reads struct padding bytes — add explicit
_padding_*fields withoffset_of!proof asserts (npm.rs) - #74 — TLS rejection path leaks websocket refcount — route SSL/auth failures through
self.fail()which clearsoutgoing_websocket(websocket_client.rs) - #108 — FD-backed fetch body leaks duplicated descriptor — close
opened_fdunconditionally afterread_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
Stringpointers againstctx.buffer(dependency.rs) - #91 — Panic on unvalidated resolution tag — validate
ResolutionTagdiscriminants on lockfile load (Package.rs) - #24 — Unwrap panic on unexpected 304 response — return
UnexpectedNotModifiedwhen no cached manifest exists (npm.rs) - #44 — UDP port getter unwrap panic on transient state — return
undefinedwhensocketisNone(udp_socket.rs) - #36 — Close reason length mismatch causes panic — clamp
body_lento 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_LENbeforecopy_nonoverlapping(bun_shim_impl.rs) - #76 / #101 — Unchecked bin name/entry name copies — bounds-check before slicing into
abs_dest_buf(bin.rs) - #79 —
ifkeyword 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.$andBun.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 justssl_mode != Disable(MySQLConnection.rs) - #83 — Strict TLS request reuses lax-verified pooled socket — track
established_with_reject_unauthorizedand refuse pool reuse for strict callers (HTTPContext.rs,lib.rs,ClientSession.rs) - #73 — IPv6 loopback prefix auth bypass — exact-match
::1instead ofstarts_with(server_body.rs) - #56 — Unsanitized filename injects response headers — reject
\r/\n/NUL/"incontent-dispositionfilenames (RequestContext.rs) - #43 — Missing CRLF checks for signed host/auth headers — also validate
region,access_key_id, andhost(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 —
lstatcached binary; refuse symlinks and other-uid files (bunx_command.rs)
Permission hygiene
- #6 — Bin target chmod always sets mode 0777 —
0o777 & !umaskinstead ofumask | 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)