Hardening: input validation and protocol tightening across 24 subsystems (round 7) (#31495)
Tightens input validation, bounds checking, protocol framing, and state handling across the package manager, HTTP stack, sockets/TLS, SQL clients, and parsers. Continuation of #31417 (round 6); same structure — small per-area commits with regression tests included. ### Package manager / CLI - install: validate extract folder names and dependency aliases for the remaining resolution shapes; bounds-check resolution lookups during lockfile loads - install: trusted-dependency entries for non-npm resolutions only apply when the dependency is declared by the root or a workspace; the security scanner is also told about packages reached through non-npm parents - install: registry auth tokens stay scoped to their configured host (and are not carried across an https→http redirect) - pm: `bun pm cache rm` resolves the cache directory from the process environment, ignoring project-local overrides for the destructive delete (`bun pm cache` prints the same path it would delete) - pack: validate the name/version used to form the tarball filename - compile: build artifacts are created exclusively with owner-only modes; repl: the history file is created 0600 (and an existing one is tightened) - yarn.lock migration behavior is unchanged; a comment now documents the intentionally lenient handling of entries without integrity ### HTTP / HTTP2 / WebSocket / S3 - fetch: never forwards a caller-supplied Content-Length that disagrees with the actual body (node:http behavior unchanged); drains interim 1xx bytes as they are parsed; never returns a connection with a framing violation to the keep-alive pool - node:http2: rejects requests carrying connection-specific headers, repeated single-value/pseudo headers, or content-length/DATA mismatches (verified against Node/nghttp2 behavior); copies queued DATA frames before re-entrant writes; keeps parsing safe when a socket chunk's buffer is transferred mid-frame - Bun.serve: absolute-form request targets are routed by path with `request.url` derived from Host; no message-body bytes are written for null-body statuses (101/103/204/205/304) — Response construction is unchanged - websocket: handshake buffering survives client teardown from the open handler - S3: object keys keep `?`/`#` characters end-to-end; list-response parsing is linear; the remote-address accessor copies into caller-owned storage ### Sockets / TLS / SQL / runtime - sql: an explicit `tls`/`ssl` option aborts the connection before credentials are sent if the server declines TLS; certificate-verification behavior is unchanged. Row construction is bounded by the declared structure; MySQL rebuilds cached row shapes when result columns change; postgres protocol counts are validated against the received bytes - node:tls: server sockets only report `authorized: true` after a client certificate was requested and verified (matches observed Node behavior) - sockets: handler reload and accepted-socket reuse re-validate ownership before freeing; the TLS read path restores shared dispatch state after re-entrant callbacks; pipe reads re-acquire the scratch buffer per use - sqlite: the cross-worker database registry and lazy library load are lock-protected; spawned children close descriptors across the full fd range - crypto: encoded-hash cost parameters are bounded before verification; errors: stack-frame callees stay GC-reachable while formatting ### Parsers / shell / node - markdown: reference-definition lookup, table column counts, and autolink scanning are linear/bounded - YAML: alias expansion is budgeted in the parser used by imports and the bundler - node:fs: watcher path joins surface ENAMETOOLONG instead of aborting; assert: diff output is bounded for large operands ### Tests 33 new regression tests across the touched areas (validated to fail on the released build and pass here), plus a handful of regression-only cases where the released build does not exhibit the behavior deterministically. A few fixes have no standalone test where the change is not observable from JS. `cargo check` and the cross-target checks pass on all 10 CI targets; the touched suites pass locally with no new failures versus main. The branch is rebased on current main. ### Changes tried and reverted during review (rationale) - **Lockfile lifecycle scripts** — an earlier commit re-derived dependency lifecycle script text from each installed package's `package.json` instead of the deserialized lockfile data; reverted because `bun.lockb` intentionally stores and uses the script text, and that design stays. - **yarn.lock migration** — an earlier commit rejected off-registry tarball URLs without integrity during migration; reverted because yarn v1 lockfiles legitimately contain such entries (workspace, `file:`, codeload) and migration must not fail closed. The lenient handling is now documented with a comment at the site. - **`Response` and null-body statuses** — both the body-dropping and the later spec-style `TypeError` for non-null bodies with 101/103/204/205/304 were reverted: existing code and frameworks (including Elysia) construct such responses and expect the body to be ignored, so changing construction is a breaking change nobody asked for. Only the wire-level behavior (no body bytes emitted) is kept. - **SQL client TLS** — an earlier commit made an explicit `tls`/`ssl` option imply certificate verification; narrowed to abort-on-decline only because the unverified default for these options is intentional and matches other JavaScript database clients. - **node:http `path` with an absolute URI** — an earlier commit rewrote such requests to a path-only target; removed because measured Node behavior does not do that, and Node compatibility takes precedence. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
J
Jarred Sumner committed
002dd41eb37926407b43c8456b0d895ad97b7853
Parent: 843549b
Committed by GitHub <noreply@github.com>
on 5/28/2026, 9:38:12 PM