// SPDX-License-Identifier: MIT // Package checker implements a happyDomain checker that wraps DNSViz // (https://github.com/dnsviz/dnsviz). It runs `dnsviz probe` followed by // `dnsviz grok` against a domain, stores the structured analysis as the // observation, and turns the per-zone errors/warnings into CheckStates. // // The container ships dnsviz alongside this binary, so the checker has no // external dependency at runtime besides the network. package checker import ( sdk "git.happydns.org/checker-sdk-go/checker" ) // ObservationKeyDNSViz is the observation key for DNSViz analysis output. const ObservationKeyDNSViz sdk.ObservationKey = "dnsviz" // DNSVizData is what Collect stores. It carries the full grok output // (parsed into a permissive structure) plus the raw bytes for the report. // // DNSViz emits a single top-level object whose keys are zone FQDNs (with // trailing dot), one per level of the chain. Inside each zone object the // shape is permissive: many fields are conditional, so we keep most of them // as map[string]any and only pluck out what the rules need. type DNSVizData struct { // Domain is the queried FQDN, with trailing dot stripped. Domain string `json:"domain"` // Zones is the per-zone analysis, keyed by zone FQDN (with trailing dot // preserved, matching DNSViz's output). Zones map[string]ZoneAnalysis `json:"zones"` // Order is Zones' keys, sorted from the queried name up to the root. // We surface it explicitly so the report can render in a stable order // without having to re-sort on every render. Order []string `json:"order,omitempty"` // Raw is the unmodified `dnsviz grok` JSON. Kept around so the report // can fall back on it for fields the typed view does not capture. Raw []byte `json:"raw,omitempty"` // ProbeStderr / GrokStderr capture the diagnostics dnsviz prints to // stderr. Useful when collection succeeds but the analysis is partial. ProbeStderr string `json:"probe_stderr,omitempty"` GrokStderr string `json:"grok_stderr,omitempty"` } // ZoneAnalysis is a permissive view over one zone's grok block. // // DNSViz uses a small set of statuses ("SECURE", "BOGUS", "INSECURE", // "INDETERMINATE", "NON_EXISTENT") and groups problems into "errors" and // "warnings" arrays. Each finding has a "description" and may carry a // "code" plus a list of servers it was observed on. We expose those as a // stable Finding type and keep everything else under Extra for the report. type ZoneAnalysis struct { Status string `json:"status,omitempty"` Errors []Finding `json:"errors,omitempty"` Warnings []Finding `json:"warnings,omitempty"` Extra map[string]any `json:"extra,omitempty"` } // Finding mirrors the shape DNSViz uses for entries in errors/warnings. // Producers occasionally use slightly different field names across versions // of dnsviz; we accept both `description`/`message` for the human text and // fall back to a generic stringification at parse time. type Finding struct { Code string `json:"code,omitempty"` Description string `json:"description"` Servers []string `json:"servers,omitempty"` }