Initial commit
This commit is contained in:
commit
257c7e494f
21 changed files with 1891 additions and 0 deletions
185
checker/rules_status.go
Normal file
185
checker/rules_status.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
)
|
||||
|
||||
// overallStatusRule reports on the leaf zone's DNSViz status. A SECURE leaf
|
||||
// means the entire chain validates from the root; BOGUS means at least one
|
||||
// link of the chain is broken.
|
||||
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}
|
||||
}
|
||||
|
||||
// perZoneStatusRule emits one CheckState per zone in the chain. This is what
|
||||
// powers the "every authoritative/parent in a dedicated block" requirement
|
||||
// of the report: each entry has Subject set to the zone name.
|
||||
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
|
||||
}
|
||||
|
||||
// zoneErrorsRule turns every DNSViz "error" entry into a Crit CheckState.
|
||||
// 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
|
||||
}
|
||||
var out []sdk.CheckState
|
||||
for _, name := range orderedZones(data) {
|
||||
for _, f := range data.Zones[name].Errors {
|
||||
out = append(out, sdk.CheckState{
|
||||
Status: sdk.StatusCrit,
|
||||
Code: nonEmpty(f.Code, "dnsviz_zone_errors"),
|
||||
Subject: name,
|
||||
Message: f.Description,
|
||||
Meta: findingMeta(f),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusOK,
|
||||
Code: "dnsviz_zone_errors",
|
||||
Message: "DNSViz reported no errors in any zone",
|
||||
}}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// zoneWarningsRule mirrors zoneErrorsRule for warnings (StatusWarn).
|
||||
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
|
||||
}
|
||||
var out []sdk.CheckState
|
||||
for _, name := range orderedZones(data) {
|
||||
for _, f := range data.Zones[name].Warnings {
|
||||
out = append(out, sdk.CheckState{
|
||||
Status: sdk.StatusWarn,
|
||||
Code: nonEmpty(f.Code, "dnsviz_zone_warnings"),
|
||||
Subject: name,
|
||||
Message: f.Description,
|
||||
Meta: findingMeta(f),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusOK,
|
||||
Code: "dnsviz_zone_warnings",
|
||||
Message: "DNSViz reported no warnings in any zone",
|
||||
}}
|
||||
}
|
||||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue