-- SPDX-FileCopyrightText: 2026 Alexandra de Wit -- -- SPDX-License-Identifier: MIT {- | 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.__ 'isAllowedUpstreamHost' restricts outbound fetches to the configured upstream @host:port@ pairs, and 'isBlockedTarget' rejects 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 much an upstream may cost.__ A 'Limits' budget plus 'boundedRead' (abort a streamed body past 'maxBodyBytes') and 'checkVersionCount' \/ '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 yields 'Left', 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" and @docs\/architecture\/web-layer.md@ → "Multi-ecosystem mounts"). Path-component safety is shared with the router's "Ecluse.Core.Server.Route" ('isSafeComponent'); the threat model these guards answer is recorded there too. -} module Ecluse.Core.Security ( module Ecluse.Core.Security.Host, module Ecluse.Core.Security.Limits, -- The host authority extractors ("Ecluse.Core.Security.Authority"). module Ecluse.Core.Security.Authority, -- The IP-literal recogniser: only the surface consumers reach today. The -- 'IpAddr' constructors stay internal to the parser and the policy layer. parseIpLiteral, isDecimal, isHex, ) where import Ecluse.Core.Security.Authority import Ecluse.Core.Security.Host import Ecluse.Core.Security.IpLiteral (isDecimal, isHex, parseIpLiteral) import Ecluse.Core.Security.Limits