// SPDX-License-Identifier: MIT package checker import ( "context" "fmt" sdk "git.happydns.org/checker-sdk-go/checker" ) type overallStatusRule struct{} func (r *overallStatusRule) Name() string { return "dnsviz_overall_status" } func (r *overallStatusRule) Description() string { return "Reports the DNSViz status of the queried domain (SECURE, INSECURE, BOGUS, INDETERMINATE)." } func (r *overallStatusRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { data, errState := loadData(ctx, obs, "dnsviz_overall_status") if errState != nil { return errState } leaf := data.Domain + "." z, ok := data.Zones[leaf] if !ok { // Fall back to the most-specific zone DNSViz reported. zones := orderedZones(data) if len(zones) == 0 { return []sdk.CheckState{{ Status: sdk.StatusUnknown, Code: "dnsviz_overall_status", Message: "DNSViz returned no zones for this domain", }} } leaf = zones[0] z = data.Zones[leaf] } st := sdk.CheckState{ Code: "dnsviz_overall_status", Subject: leaf, Status: statusFromGrok(z.Status), Message: fmt.Sprintf("DNSViz status: %s", emptyAsUnknown(z.Status)), Meta: map[string]any{ "status": z.Status, "errors": len(z.Errors), "warnings": len(z.Warnings), }, } return []sdk.CheckState{st} } // Subject is set to the zone name so each delegation level gets its own report block. type perZoneStatusRule struct{} func (r *perZoneStatusRule) Name() string { return "dnsviz_per_zone_status" } func (r *perZoneStatusRule) Description() string { return "Reports the DNSViz status of every zone in the chain (root, TLD, intermediates, leaf)." } func (r *perZoneStatusRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { data, errState := loadData(ctx, obs, "dnsviz_per_zone_status") if errState != nil { return errState } zones := orderedZones(data) if len(zones) == 0 { return []sdk.CheckState{{ Status: sdk.StatusUnknown, Code: "dnsviz_per_zone_status", Message: "DNSViz returned no zones for this domain", }} } out := make([]sdk.CheckState, 0, len(zones)) for _, name := range zones { z := data.Zones[name] out = append(out, sdk.CheckState{ Code: "dnsviz_per_zone_status", Subject: name, Status: statusFromGrok(z.Status), Message: fmt.Sprintf("%s: errors=%d warnings=%d", emptyAsUnknown(z.Status), len(z.Errors), len(z.Warnings)), }) } return out } // One state per (zone, finding) pair so the UI can show a precise list. type zoneErrorsRule struct{} func (r *zoneErrorsRule) Name() string { return "dnsviz_zone_errors" } func (r *zoneErrorsRule) Description() string { return "Surfaces every error reported by DNSViz, scoped to the zone where it was found." } func (r *zoneErrorsRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { data, errState := loadData(ctx, obs, "dnsviz_zone_errors") if errState != nil { return errState } return zoneFindingStates(data, "dnsviz_zone_errors", sdk.StatusCrit, "errors", func(z ZoneAnalysis) []Finding { return z.Errors }) } type zoneWarningsRule struct{} func (r *zoneWarningsRule) Name() string { return "dnsviz_zone_warnings" } func (r *zoneWarningsRule) Description() string { return "Surfaces every warning reported by DNSViz, scoped to the zone where it was found." } func (r *zoneWarningsRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { data, errState := loadData(ctx, obs, "dnsviz_zone_warnings") if errState != nil { return errState } return zoneFindingStates(data, "dnsviz_zone_warnings", sdk.StatusWarn, "warnings", func(z ZoneAnalysis) []Finding { return z.Warnings }) } // zoneFindingStates emits a single OK state when nothing matches so the rule outcome is always observable. func zoneFindingStates(data *DNSVizData, ruleCode string, status sdk.Status, kindLabel string, pick func(ZoneAnalysis) []Finding) []sdk.CheckState { var out []sdk.CheckState for _, name := range orderedZones(data) { for _, f := range pick(data.Zones[name]) { out = append(out, sdk.CheckState{ Status: status, Code: nonEmpty(f.Code, ruleCode), Subject: name, Message: f.Description, Meta: findingMeta(f), }) } } if len(out) == 0 { return []sdk.CheckState{{ Status: sdk.StatusOK, Code: ruleCode, Message: fmt.Sprintf("DNSViz reported no %s in any zone", kindLabel), }} } return out } func emptyAsUnknown(s string) string { if s == "" { return "UNKNOWN" } return s } func nonEmpty(a, b string) string { if a != "" { return a } return b } func findingMeta(f Finding) map[string]any { m := map[string]any{} if f.Code != "" { m["code"] = f.Code } if len(f.Servers) > 0 { m["servers"] = f.Servers } if len(m) == 0 { return nil } return m }