httpsec

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: GPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package httpsec provides SSRF-safe URL validation and HTTP client construction for the OpenCTEM SDK.

This is a lifted copy of api/pkg/httpsec. Originally the SDK lived in its own Go module and could not import from api/, so the 9+ outbound-client sites across sdk-go/pkg/* each carried a bare &http.Client{Timeout: …} with no dialer-level blocklist. That was fine when the SDK only talked to the pinned OpenCTEM API over a trusted operator-provided URL, but third-party users of the SDK (custom scanners, integration shims) can point it at arbitrary hostnames. Any of those callers inherit the same SSRF gap as the API did before the audit.

This package mirrors api/pkg/httpsec verbatim — when you change one, change the other. A follow-up task tracks hoisting the single canonical copy into a top-level shared Go module; until then, the drift is caught by scripts/security-lint.sh which grep-checks both copies for the same CIDR blocklist.

Any outbound HTTP call in sdk-go/pkg/* that reaches a hostname chosen at runtime (API URL from config, KEV/EPSS feed, bootstrap token endpoint, command poller, lease endpoint) MUST use SafeHTTPClient + ValidateURL, not &http.Client{} directly.

Index

Constants

This section is empty.

Variables

View Source
var AllowLoopback = os.Getenv(allowLoopbackEnvVar) == "1"

AllowLoopback, when set to true, skips the loopback (127.0.0.0/8, ::1/128) branch of the blocklist. All other CIDRs (RFC1918, link-local IMDS, CGNAT, …) remain enforced. Prefer the env var for external test harnesses; this variable is for in-process overrides.

Functions

func AllowPrivate

func AllowPrivate() bool

AllowPrivate reports the current runtime posture for startup-logging; do not consult it per-call (that happens inside IsIPBlocked).

func IsIPBlocked

func IsIPBlocked(ip net.IP) bool

IsIPBlocked reports whether the given IP falls in a blocked CIDR. The hard-blocked set always rejects. The RFC1918 / ULA set is conditional on allowPrivate (set via env).

AllowLoopback (test-only) still overrides loopback for httptest servers; it does NOT touch IMDS or RFC1918 — those remain on their own toggle chains.

func SafeHTTPClient

func SafeHTTPClient(timeout time.Duration) *http.Client

SafeHTTPClient returns an *http.Client whose dialer rejects any connection attempt to a blocked CIDR at the transport layer. This is the belt to ValidateURL's braces: even if a caller forgets to validate up front, the dial fails closed.

Callers should still ValidateURL themselves because the dialer-only check happens AFTER DNS resolution, so a request to an attacker- supplied URL will have burned a DNS lookup (possibly against an attacker-controlled resolver). ValidateURL rejects before the lookup leaves the host process.

Types

type ValidationResult

type ValidationResult struct {
	URL         *url.URL
	ResolvedIPs []net.IP
}

ValidationResult carries the parsed URL + the DNS-pinned IP set so callers that want to prevent DNS rebinding can dial one of the resolved IPs rather than re-resolve at dial time.

func ValidateURL

func ValidateURL(rawURL string) (*ValidationResult, error)

ValidateURL parses rawURL, confirms scheme is http/https, blocks common dangerous hostnames, resolves DNS, and rejects if any A/AAAA hits a blocked CIDR. On any failure returns an error — callers MUST NOT proceed to dial.

Fail-closed on DNS lookup failure: if we cannot resolve, we cannot verify the target is safe, so the request is rejected.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL