// This file is part of the happyDomain (R) project. // Copyright (c) 2020-2026 happyDomain // Authors: Pierre-Olivier Mercier, et al. package checker import ( "context" "fmt" sdk "git.happydns.org/checker-sdk-go/checker" ) func init() { RegisterRule(&securityTxtRule{}) } // securityTxtRule reports whether /.well-known/security.txt is published // (RFC 9116). Absence is an Info, not a Warn: many sites legitimately // have no security disclosure pipeline, but it is now the expected place // for researchers to look first. type securityTxtRule struct{} func (r *securityTxtRule) Name() string { return "http.security_txt" } func (r *securityTxtRule) Description() string { return "Reports whether /.well-known/security.txt (RFC 9116) is published." } func (r *securityTxtRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState { data, errSt := loadHTTPData(ctx, obs) if errSt != nil { return []sdk.CheckState{*errSt} } wk, ok, err := LoadExtension[WellKnownData](data, ObservationKeyWellKnown) if err != nil { return []sdk.CheckState{{Status: sdk.StatusError, Code: "http.security_txt.decode_error", Message: err.Error()}} } if !ok { return []sdk.CheckState{unknownState("http.security_txt.no_data", "Well-known collector did not run.")} } probe := wk.URIs["/.well-known/security.txt"] switch { case probe.StatusCode == 200 && probe.Bytes > 0: return []sdk.CheckState{{ Status: sdk.StatusOK, Code: "http.security_txt.ok", Subject: data.Domain, Message: fmt.Sprintf("/.well-known/security.txt is published (%d bytes).", probe.Bytes), }} case probe.StatusCode == 200: return []sdk.CheckState{{ Status: sdk.StatusWarn, Code: "http.security_txt.empty", Subject: data.Domain, Message: "/.well-known/security.txt responded 200 but is empty.", }} default: return []sdk.CheckState{{ Status: sdk.StatusInfo, Code: "http.security_txt.missing", Subject: data.Domain, Message: fmt.Sprintf("/.well-known/security.txt is not published (status %d).", probe.StatusCode), Meta: map[string]any{"fix": "Publish /.well-known/security.txt per RFC 9116 (Contact:, Expires:, …)."}, }} } }