// This file is part of the happyDomain (R) project. // Copyright (c) 2020-2026 happyDomain // Authors: Pierre-Olivier Mercier, et al. // // This program is offered under a commercial and under the AGPL license. // For commercial licensing, contact us at . // Package checker implements an HTTP/HTTPS server checker for happyDomain. // It probes the abstract.Server it is attached to over HTTP (80) and HTTPS // (443), captures response headers, cookies and the (parsed) HTML body, then // evaluates a set of independent rules covering reachability, the HTTP→HTTPS // upgrade path, modern transport-security headers (HSTS, CSP), application // security headers (X-Frame-Options, X-Content-Type-Options, X-XSS-Protection) // and Subresource Integrity. Deep TLS / certificate analysis is intentionally // delegated to checker-tls. package checker import ( "encoding/json" "time" ) const ObservationKeyHTTP = "http" const ( OptionService = "service" OptionDomainName = "domain_name" OptionProbeTimeoutMs = "probeTimeoutMs" OptionMaxRedirects = "maxRedirects" OptionUserAgent = "userAgent" OptionRequireHTTPS = "requireHTTPS" OptionRequireHSTS = "requireHSTS" OptionMinHSTSMaxAgeDays = "minHSTSMaxAgeDays" OptionRequireCSP = "requireCSP" ) const ( DefaultHTTPPort uint16 = 80 DefaultHTTPSPort uint16 = 443 DefaultProbeTimeoutMs = 10000 DefaultMaxRedirects = 5 DefaultUserAgent = "happyDomain-checker-http/1.0" DefaultMinHSTSMaxAge = 180 // days; 180d ≈ 15552000s, the commonly recommended minimum MaxConcurrentProbes = 8 MaxBodyBytes = 1 << 20 // 1 MiB cap on HTML body to keep memory bounded ) // HTTPData is the full collected payload written under ObservationKeyHTTP. // // Probes/Domain/CollectedAt come from the root collector and are kept at // the top level for backward compatibility with the rules that have // always read them directly. // // Extensions holds the JSON-encoded outputs of every additional Collector // registered via RegisterCollector, keyed by Collector.Key(). Rules // access them via LoadExtension[T] to get a typed view. type HTTPData struct { Domain string `json:"domain,omitempty"` Probes []HTTPProbe `json:"probes"` CollectedAt time.Time `json:"collected_at"` Extensions map[string]json.RawMessage `json:"extensions,omitempty"` } // HTTPProbe is the outcome of a single (scheme, ip, port) probe. type HTTPProbe struct { Scheme string `json:"scheme"` // "http" or "https" Host string `json:"host"` IP string `json:"ip,omitempty"` Port uint16 `json:"port"` Address string `json:"address"` IsIPv6 bool `json:"ipv6,omitempty"` TCPConnected bool `json:"tcp_connected"` ElapsedMS int64 `json:"elapsed_ms,omitempty"` Error string `json:"error,omitempty"` // Final response after following redirects (if any). StatusCode int `json:"status_code,omitempty"` FinalURL string `json:"final_url,omitempty"` Headers map[string]string `json:"headers,omitempty"` Cookies []CookieInfo `json:"cookies,omitempty"` RedirectChain []RedirectStep `json:"redirect_chain,omitempty"` // Parsed HTML resource references (only populated for the primary // HTTPS probe, to keep payloads small). Resources []HTMLResource `json:"resources,omitempty"` HTMLBytes int `json:"html_bytes,omitempty"` BodyTruncated bool `json:"body_truncated,omitempty"` } // RedirectStep records one hop of a redirect chain. type RedirectStep struct { From string `json:"from"` To string `json:"to"` Status int `json:"status"` } // CookieInfo summarises one Set-Cookie header. type CookieInfo struct { Name string `json:"name"` Domain string `json:"domain,omitempty"` Path string `json:"path,omitempty"` Secure bool `json:"secure"` HttpOnly bool `json:"http_only"` SameSite string `json:"same_site,omitempty"` // "Strict", "Lax", "None", or "" HasExpiry bool `json:"has_expiry,omitempty"` // Size is the byte length of the raw Set-Cookie header value // (everything after "Set-Cookie: "), used to evaluate the // per-cookie 4096-byte budget RFC 6265 §6.1 says browsers SHOULD // support. Size int `json:"size,omitempty"` } // MaxCookieSize is the per-cookie size browsers are required to // support per RFC 6265 §6.1. Cookies above this are likely to be // silently dropped by some user agents. const MaxCookieSize = 4096 // HTMLResource is a