Skip to content

Harden HTTP streaming proxy surface - #436

Merged
Laveesh Rohra (larohra) merged 13 commits into
Azure:larohra/harden-authfrom
larohra:swarm/9e1d1fce/integration
May 13, 2026
Merged

Laveesh Rohra (larohra) merged 13 commits into
Azure:larohra/harden-authfrom
larohra:swarm/9e1d1fce/integration

Conversation

@larohra

Copy link
Copy Markdown

Context

  • Round-1 scope still stands: src/http/httpProxy.ts is the internal HTTP streaming bridge behind app.setup({ enableHttpStream: true }).
  • The current library implementation calls server.listen() with no host, so Node binds the proxy on a wildcard interface even though the worker advertises http://localhost:<port>/ back to the host.
  • In azure-functions-host, src/WebJobs.Script.Grpc/Channel/WorkerChannel.cs only parses HttpUri with new Uri(httpUri) and uses it for proxying, while src/WebJobs.Script/Http/DefaultHttpProxyService.cs overwrites x-ms-invocation-id before forwarding.

Assessment

  • Finding relevance: Actionable, but narrower than a generic “public unauthenticated endpoint” finding. Normal front-door callers cannot pick the correlation header because the host overwrites it, and the host does not expose HttpUri as a customer contract. The real gap is the worker’s wildcard listener plus permissive handling of direct requests from anything that can reach the sandbox/pod namespace. Even if the host never leaks the URI, the wildcard bind (and the fixed 55000-55025 fallback range) still creates discoverable local surface.
  • Will repo-only fixes satisfy the host as-is? Yes for the repo-local reachability issue: the host already assumes same-host reachability because the worker reports localhost today, and it accepts any absolute HttpUri. No for full channel authentication: the host does not send any shared secret today, and WorkerChannel starts HTTP forwarding before the worker receives the gRPC invocation, so the library cannot repo-only pre-whitelist expected invocation IDs.
  • Are fixes necessary? Yes. Binding to concrete loopback and promptly rejecting malformed proxy traffic are necessary hardening steps. Adding a new repo-only auth header/token is not viable without a coordinated host change.
  • Historical context: PR Http stream support #212 (“Http stream support”) prioritized minimal user changes and introduced the default server.listen() behavior with no evidence that wildcard binding was an intentional security posture. PR Add port validation checks for HTTP streaming #315 (“Add port validation checks for HTTP streaming”) later fixed Windows/VNET port-0 reliability but still returned localhost, which reinforces that loopback was the intended contract and wildcard binding was an implementation gap.
  • Regression / breaking-change risk: Low if the worker binds and returns the exact loopback URI and preserves PR Add port validation checks for HTTP streaming #315 rebinding behavior. The host already consumes HttpUri as an opaque absolute URI string, so returning 127.0.0.1 or [::1] is not a customer API break. The real risks are IPv4/IPv6 family selection, URI formatting, and port-0 retry behavior.
  • Current test posture: Existing round-1 evidence had the full suite green after npm ci (357 passing), but this repo still lacks direct coverage for src/http/httpProxy.ts and ProgrammingModel.getCapabilities/streamed invocation setup. The host repo does have contract-oriented coverage for correlation-header override (DefaultHttpProxyServiceTests), forwarded-header behavior (ScriptInvocationRequestTransformerTests), and the HttpUri proxying path (ScriptInvocationContextExtensionsTests).

Implementation direction

  1. Harden src/http/httpProxy.ts to bind one concrete loopback address, use it consistently for the listener and port-probe/rebind path, and fail malformed proxy requests fast.
  2. Preserve the current worker-host contract: keep HttpUri as an internal absolute URI capability and keep x-ms-invocation-id correlation semantics; do not add new repo-only auth signals or public API changes.
  3. Add proxy-focused worker tests for loopback reachability, malformed requests, correlation, response propagation, and port-0 fallback.
  4. Add capability/integration tests around ProgrammingModel and streamed invocation behavior so the hardened worker stays compatible with the current host contract and avoids address-family regressions.

Non-goal / follow-up

  • If SFI requires protection against malicious same-host peers that can reach loopback and somehow learn the correlation material, that needs a coordinated azure-functions-host + library handshake change rather than a library-only patch.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the internal HTTP streaming proxy used when enableHttpStream is enabled by binding it to loopback and rejecting malformed proxy traffic.

Changes:

  • Binds the HTTP proxy to a concrete loopback address and serializes the advertised HttpUri accordingly.
  • Adds validation for malformed or duplicate invocation ID headers.
  • Adds tests for proxy behavior, streaming invocation handling, and capability negotiation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/http/httpProxy.ts Implements loopback binding, loopback URI serialization, invocation ID validation, and async port probing.
test/http/httpProxy.test.ts Adds proxy behavior coverage for loopback binding, malformed requests, buffering, responses, cookies, and port fallback.
test/InvocationModel.test.ts Adds streamed HTTP invocation coverage using the proxy request/response path.
test/ProgrammingModel.test.ts Adds capability negotiation coverage for advertised streaming HttpUri and setup locking.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/ProgrammingModel.test.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the package version to 4.15.0

@larohra
Laveesh Rohra (larohra) changed the base branch from v4.x to larohra/harden-auth May 13, 2026 20:51
@larohra
Laveesh Rohra (larohra) merged commit 18b57a5 into Azure:larohra/harden-auth May 13, 2026
7 of 13 checks passed
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.

3 participants