64 lines
2.2 KiB
Go
64 lines
2.2 KiB
Go
// 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:, …)."},
|
|
}}
|
|
}
|
|
}
|