Initial commit
This commit is contained in:
commit
53626dd36a
29 changed files with 3940 additions and 0 deletions
164
checker/rules_status.go
Normal file
164
checker/rules_status.go
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue