| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Security
Description
Outbound-request and response-bound guards for the proxy's data plane.
Écluse builds outbound HTTP requests from two untrusted sources -- __client-supplied
package identifiers (the request path) and upstream-supplied artifact
locations__ (a packument's dist.tarball) -- and then parses whatever an upstream
returns. This module is the pure guard layer that keeps those steps from being
steered or exhausted by hostile input. It defends three boundaries:
- Where the proxy fetches.
isAllowedUpstreamHostrestricts outbound fetches to the configured upstream hosts, andisBlockedTargetrejects internal address ranges (cloud instance metadata, loopback, RFC1918) that the proxy's network position can otherwise reach. Together they are the SSRF gate: a target must be both on the allowlist and not an internal address. - How an upstream URL is derived.
upstreamUrlForbuilds an artifact/metadata URL from a configured base URL and an already-parsedPackageName, never from raw client path segments, re-checking each name component with the router's own safety rule so traversal, encoded slashes, or an absolute URL cannot change the target. - How much an upstream may cost. A
Limitsbudget plusboundedRead(abort a streamed body pastmaxBodyBytes) andcheckVersionCount/checkNestingDepth(reject an oversized or deeply-nested parsed document) bound algorithmic-complexity DoS from a hostile or compromised upstream. Every limit fails closed: exceeding one yieldsLeft, never a truncated or partial result.
The functions are pure and total; the streamed-body guard (boundedRead) is
polymorphic over the producing monad so the streaming data plane can run it in
IO while tests drive it purely. They are primitives: the fetch and serve
layers compose them at the boundary (see docs/architecture/registry-model.md
→ "Registry Abstraction", docs/architecture/web-layer.md, and
docs/architecture/hosting.md → "URL rewriting"). Path-component safety is
shared with the router's Ecluse.Core.Server.Route (isSafeComponent); the threat
model these guards answer is recorded there too.
Documentation
module Ecluse.Core.Security.Host
module Ecluse.Core.Security.Url
module Ecluse.Core.Security.Limits