Repository navigation
undici transport: native header rejections, fileBody short-write detection, SOCKS proxy type #81
Copy link
Copy link
Closed
Labels
area:transportTransport, async model, seams: TRANSPORT-* ASYNC-* SEAM-*Transport, async model, seams: TRANSPORT-* ASYNC-* SEAM-*spec:conformanceConformance-suite behavior, or a gap in the suite itselfConformance-suite behavior, or a gap in the suite itselftype:bugDefect in shipped behaviorDefect in shipped behavior
Milestone
Description
Part of the audit remediation umbrella #67. Milestone 5. Severity: HIGH (first item), MEDIUM (rest). Requirements: TRANSPORT-11, TRANSPORT-12, TRANSPORT-30, BODY-13.
Current SDK behavior
- Native header rejections escape
send().UNDICI_FORBIDDEN_HEADERS
(packages/transport-undici/src/undici-transport.ts:58-62) dropscontent-length,host,
transfer-encodingonly. undici 6.28.0 (lib/core/request.js:397-409) throws
InvalidArgumentErrorforkeep-aliveandupgrade,NotSupportedErrorforexpect, and
InvalidArgumentErrorforconnectionwith a value other thanclose/keep-alive.
toDispatchError(:221-231) maps these to a bare non-retryableTypeError; nothing is
sent.toUndiciHeaders(:150-160) has no per-header guard, so a model-valid non-token name
such asX Custom(core admits printable non-token bytes,ascii-validation.ts:29-33) is
rejected by undici'sisValidHTTPTokenand the send fails.fetch-transport.ts:146-152wraps
eachnative.appendin atry/catchand degrades to a drop; undici has no equivalent. No
test exercises a non-token header name. fileBodybypasses short-write detection. The file-body branch (:259-273) hands
createReadStream(path, {start, end})to undici directly.writeToand its
transferred === countinvariant (packages/body-file/src/file-body.ts:75-78) never run.
content-lengthis dropped, so undici frames the body chunked and the wire cannot detect a
short write either. A 1 MBfileBodytruncated to 10 bytes beforesend(): undici POSTs 10
bytes and resolves 200; fetch raisesTransportFailureError. The Phase 8a checklist (:80)
marks BODY-13 done while recording the bypass.- A SOCKS proxy type throws undici's raw error.
toProxyAgentOptions(:115-122) uses
proxy.typeas the URI scheme with no check. Core resolvesALL_PROXY=socks5://host:1080to
type: 'socks5'(config/proxy.ts:34,372-380).new ProxyAgent({uri: 'socks5://…'})throws
InvalidArgumentError('Invalid URL protocol: …')out of the public factory, untyped and
undocumented.
Expected behavior
undiciTransport().send() does not throw a native error for any model-valid request. A
header the native layer cannot carry is dropped and logged, the same as in the fetch
transport. A file body that ends short of its declared length fails the send with a
TransportFailureError naming transferred-of-total on both transports. undiciTransport()
rejects an unsupported proxy type with a typed, documented error.
Notes and leads
- Drop set: add
expect,keep-alive,upgrade; dropconnectionvalues other than
close/keep-alive. Validate each name against RFC 9110tokenintoUndiciHeadersand
degrade to a logged drop. CheckFETCH_FORBIDDEN_HEADERS(fetch-transport.ts:45-50) too:
on Node the global fetch is undici-backed and itsHeadersdoes not implement forbidden
names, so the same rejections likely surface there as a retryableTransportFailureError. - File body: route through
writeTovia thepumpBodypath the streamed case uses, or wrap
the read stream to count bytes and destroy withTransportFailureErrorwhen it ends short. - Proxy: reject
proxy.type !== 'http'with a typedDexpaceError(or the documented
TypeError), add@throws, and either record "SOCKS resolved by core, supported by neither
transport" indocs/deviations.mdor dropsocks*fromProxyTypebefore the first bump. - Every item gets a row in
packages/transport-conformancethat both transports run:
Expect: 100-continue,Upgrade: websocket, a non-token name, truncate-after-stat. - Patch changesets for
@dexpace/transport-undici,@dexpace/transport-fetch, and
@dexpace/coreifProxyTypechanges.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:transportTransport, async model, seams: TRANSPORT-* ASYNC-* SEAM-*Transport, async model, seams: TRANSPORT-* ASYNC-* SEAM-*spec:conformanceConformance-suite behavior, or a gap in the suite itselfConformance-suite behavior, or a gap in the suite itselftype:bugDefect in shipped behaviorDefect in shipped behavior
Activity
docs(transport-fetch): the README's drop set was three names short
docs: ledger — wave 6a landed (#81)
docs: ledger — wave 6b landed (#82), run complete; re-anchor item 13 …
Wahbeh-Mohammad commentedon Sep 5, 2026
Addressed in PR #96, merged into
audit/remediation-67; reachesmvpthrough PR #98. Decision record:docs/audit-67-decisions.md.