package checker import ( "context" "fmt" sdk "git.happydns.org/checker-sdk-go/checker" ) // Rule returns the single aggregation rule for this checker. It folds // every finding produced by Collect into a CheckState whose status is // the worst severity seen. func Rule() sdk.CheckRule { return &emailKeyRule{} } type emailKeyRule struct{} func (r *emailKeyRule) Name() string { return "openpgpkey_smimea_check" } func (r *emailKeyRule) Description() string { return "Validates a DNS-published OpenPGP key (RFC 7929) or S/MIME certificate (RFC 8162), running DNSSEC, record-hash, parse, expiration, algorithm-strength, and S/MIME EKU checks." } func (r *emailKeyRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { var data EmailKeyData if err := obs.Get(ctx, ObservationKey, &data); err != nil { return []sdk.CheckState{{ Status: sdk.StatusError, Message: fmt.Sprintf("Failed to read observation %q: %v", ObservationKey, err), Code: "openpgpkey_observation_error", }} } return []sdk.CheckState{evaluate(&data)} } // evaluate folds findings into a CheckState. The status is the highest // severity observed: any Crit makes the whole result Crit, any Warn // makes it Warn, otherwise Info/OK. func evaluate(data *EmailKeyData) sdk.CheckState { var crit, warn, info int var firstCrit, firstWarn, firstInfo string for _, f := range data.Findings { switch f.Severity { case SeverityCrit: crit++ if firstCrit == "" { firstCrit = f.Message } case SeverityWarn: warn++ if firstWarn == "" { firstWarn = f.Message } case SeverityInfo: info++ if firstInfo == "" { firstInfo = f.Message } } } status := sdk.StatusOK msg := summariseHealthy(data) code := "openpgpkey_ok" switch { case crit > 0: status = sdk.StatusCrit msg = firstCrit code = "openpgpkey_crit" case warn > 0: status = sdk.StatusWarn msg = firstWarn code = "openpgpkey_warn" case info > 0: status = sdk.StatusInfo msg = firstInfo code = "openpgpkey_info" } meta := map[string]any{ "kind": data.Kind, "queried": data.QueriedOwner, "record_count": data.RecordCount, "findings": data.Findings, } if data.DNSSECSecure != nil { meta["dnssec_secure"] = *data.DNSSECSecure } return sdk.CheckState{ Status: status, Message: msg, Code: code, Subject: data.QueriedOwner, Meta: meta, } } func summariseHealthy(data *EmailKeyData) string { switch data.Kind { case KindOpenPGPKey: if data.OpenPGP != nil && data.OpenPGP.Fingerprint != "" { return fmt.Sprintf("OPENPGPKEY %s published and valid (fingerprint %s)", data.QueriedOwner, data.OpenPGP.Fingerprint) } return fmt.Sprintf("OPENPGPKEY %s published and valid", data.QueriedOwner) case KindSMIMEA: if data.SMIMEA != nil && data.SMIMEA.Certificate != nil { return fmt.Sprintf("SMIMEA %s valid (subject %s)", data.QueriedOwner, data.SMIMEA.Certificate.Subject) } return fmt.Sprintf("SMIMEA %s published and valid", data.QueriedOwner) } return "Record validated" }