Initial commit
This commit is contained in:
commit
542ebdea34
40 changed files with 4592 additions and 0 deletions
111
checker/header_rule.go
Normal file
111
checker/header_rule.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2026 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
)
|
||||
|
||||
// HeaderRuleSpec declares a "presence + value validation" rule for one
|
||||
// HTTP response header. It covers the most common shape of security
|
||||
// header rule (one of Referrer-Policy, Permissions-Policy, COOP, COEP,
|
||||
// CORP, X-Content-Type-Options, …) without forcing the author to write
|
||||
// the load/iterate/build-state scaffolding.
|
||||
//
|
||||
// The DSL emits three CheckState codes derived from Code:
|
||||
// - Code+".missing" when the header is absent
|
||||
// - Code+".invalid" when Validate returns a non-OK status
|
||||
// - Code+".ok" when Validate accepts the value
|
||||
//
|
||||
// Rules with richer semantics (HSTS quality thresholds, CSP directive
|
||||
// inspection, cookie flag aggregation, legacy headers with reversed
|
||||
// "absent is fine" semantics) keep implementing sdk.CheckRule directly.
|
||||
type HeaderRuleSpec struct {
|
||||
// Code is the rule's Name() and the prefix for every CheckState
|
||||
// code it emits.
|
||||
Code string
|
||||
|
||||
// Description is returned by Description().
|
||||
Description string
|
||||
|
||||
// Header is the response header to inspect. Lookups go through the
|
||||
// lowercased map populated by the collector, so casing is flexible.
|
||||
Header string
|
||||
|
||||
// Required toggles the severity of an absent header: Warn when true,
|
||||
// Info when false.
|
||||
Required bool
|
||||
|
||||
// Validate, when set, inspects the trimmed header value. Return
|
||||
// (StatusOK, msg) to accept the value (emits ".ok" with msg) or any
|
||||
// other status to flag it (emits ".invalid" with msg). When nil,
|
||||
// presence alone is treated as OK with a generic message.
|
||||
Validate func(value string) (sdk.Status, string)
|
||||
|
||||
// FixHint, when set, is attached as Meta.fix on the ".missing"
|
||||
// state.
|
||||
FixHint string
|
||||
}
|
||||
|
||||
// HeaderRule constructs a self-contained sdk.CheckRule from a spec.
|
||||
// Intended to be wired in init() via RegisterRule.
|
||||
func HeaderRule(spec HeaderRuleSpec) sdk.CheckRule {
|
||||
return &headerRule{spec: spec}
|
||||
}
|
||||
|
||||
type headerRule struct{ spec HeaderRuleSpec }
|
||||
|
||||
func (r *headerRule) Name() string { return r.spec.Code }
|
||||
func (r *headerRule) Description() string { return r.spec.Description }
|
||||
|
||||
func (r *headerRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
||||
data, errSt := loadHTTPData(ctx, obs)
|
||||
if errSt != nil {
|
||||
return []sdk.CheckState{*errSt}
|
||||
}
|
||||
headerKey := strings.ToLower(r.spec.Header)
|
||||
|
||||
return EvalPerHTTPS(data, r.spec.Code, func(p HTTPProbe) sdk.CheckState {
|
||||
v := strings.TrimSpace(p.Headers[headerKey])
|
||||
if v == "" {
|
||||
status := sdk.StatusWarn
|
||||
if !r.spec.Required {
|
||||
status = sdk.StatusInfo
|
||||
}
|
||||
st := sdk.CheckState{
|
||||
Status: status,
|
||||
Code: r.spec.Code + ".missing",
|
||||
Subject: p.Address,
|
||||
Message: r.spec.Header + " is not set.",
|
||||
}
|
||||
if r.spec.FixHint != "" {
|
||||
st.Meta = map[string]any{"fix": r.spec.FixHint}
|
||||
}
|
||||
return st
|
||||
}
|
||||
if r.spec.Validate == nil {
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusOK,
|
||||
Code: r.spec.Code + ".ok",
|
||||
Subject: p.Address,
|
||||
Message: r.spec.Header + " is set.",
|
||||
}
|
||||
}
|
||||
status, msg := r.spec.Validate(v)
|
||||
suffix := ".invalid"
|
||||
if status == sdk.StatusOK {
|
||||
suffix = ".ok"
|
||||
}
|
||||
return sdk.CheckState{
|
||||
Status: status,
|
||||
Code: r.spec.Code + suffix,
|
||||
Subject: p.Address,
|
||||
Message: msg,
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue