Skip to content

fix: honor request init and https transport policy - #12

Merged
TheGreatAxios merged 3 commits into
mainfrom
cl-11480-preserve-requestinit-and-transport-policy
Oct 26, 2026
Merged

TheGreatAxios merged 3 commits into
mainfrom
cl-11480-preserve-requestinit-and-transport-policy

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Request inputs to origin-pinned fetch now apply RequestInit, including abort signals, while the credential header and manual redirects stay forced. Host extra origins and pinned origins must be https, with http only on loopback, and a refused target never receives the secret.

Peer ranges for @intx/harness and @intx/types accept ^0.4.0 and ^0.5.0. The credential provider and FetchLike contracts match between those releases, and this package typechecks against both.

Closes CL-11480

@linear-code

linear-code Bot commented Oct 25, 2026

Copy link
Copy Markdown

CL-11480

@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Review · Comment

Security lens. PR #12 is blocked. The origin allowlist is not bound to the URL fetch actually sends, so a crafted URL or Request delivers the credential to an attacker host. The scheme gate also misses blob: URLs.

Findings

  • src/origin-pinned-fetch.ts:91-126 and :142-145 — The allowlist checks target.origin or the first read of Request.url, then calls fetch with the same live object. Reproduced on Bun 1.4.2 with the secret received by an attacker listener:
    • A URL subclass whose origin getter returns the pinned origin and whose href is another host.
    • A pinned loopback URL whose init.headers getter changes url.port before new Headers(init.headers).
    • A Request whose url getter returns the pin on the first read and the attacker URL on the second.
      String URLs do not hit this. Any in-process caller that can pass a URL or Request exfiltrates the secret without calling readValue. Snapshot the URL string that will be fetched before reading headers or attaching the credential, and pass that snapshot to fetch.
  • src/origin-pinned-fetch.ts:35-43 and :99 — The scheme gate checks target.origin. In Bun, new URL("blob:https://api.example.com/<id>").origin is https://api.example.com while .protocol is blob:. A blob: string for the pinned origin passes the allowlist and the secret is attached. Default Bun fetch then rejects blob:, but an injected fetch receives the header. Refuse blob: and every non-http(s) protocol before readValue.

Checked

redirect: "manual" keeps the secret on the first hop for 302 and 307. Abort signals reject with AbortError and the server sees no request. Loopback is only localhost, 127.0.0.1, and [::1] after WHATWG normalization. Integer and hex forms of 127.0.0.1 connect there. 127.0.0.2, 0.0.0.0, [::], mapped IPv4, RFC1918, link-local, and lookalike hostnames are refused. A forged credential header is overwritten. Cross-origin strings throw before readValue.

Not covered: Node/undici, DNS poisoning, and @intx/harness internals. The peer-range bump was not a trust review. Probes were removed.

@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Review · Comment

Code lens. The diff at 77428c3 and e604aea passes the checks this lane ran. This does not approve the pull request. The security review already blocked it.

Checks

  • bun run check exits 0: typecheck, lint, format, and 9 unit tests.
  • bun run test:e2e exits 0: 12 tests.
  • RequestInit body, content type, and abort-signal behavior match native fetch on Bun and Node 26, including a pre-aborted request and an init signal that overrides the request signal.
  • Loopback HTTP is limited to localhost, 127.0.0.1, and [::1] after normalization. Public HTTP hosts are refused.
  • The @intx/harness and @intx/types 0.4.0 and 0.5.0 declarations used here match.

Still blocking

The security review reproduced credential delivery through a live URL or Request whose getters change the host after the allowlist check, and through blob: URLs whose origin is the pin while protocol is blob:. Those are not covered by the tests above. A fix is in progress on cl-11480-bind-fetched-url.

@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Security follow-up

The live URL/Request and blob: findings are fixed on this branch at 37f2852.

The fetch now authorizes an immutable URL snapshot and passes that string to fetch. blob: and other non-HTTP(S) protocols are refused before the credential is read. HTTP remains limited to localhost, 127.0.0.1, and [::1].

Independent retest on Bun 1.4.2: the three live-object attacks and both blob: URLs throw before readValue, and the attacker listener stays empty. Redirects stay manual, forged credential headers are overwritten, and abort signals still reject with AbortError. bun test src passed 13 tests. Node/undici was not re-run.

This closes the security block from the earlier review. It is not a deployed-network certification.

@TheGreatAxios
TheGreatAxios merged commit 6b32fd4 into main Oct 26, 2026
2 checks passed
@TheGreatAxios
TheGreatAxios deleted the cl-11480-preserve-requestinit-and-transport-policy branch October 26, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant