559 lines
18 KiB
Go
559 lines
18 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// stubObsGetter is a minimal ObservationGetter for tests: it serves a
|
|
// canned CAAData under ObservationKeyCAA and a canned list of related
|
|
// observations under TLSRelatedKey.
|
|
type stubObsGetter struct {
|
|
data CAAData
|
|
related []sdk.RelatedObservation
|
|
}
|
|
|
|
func (s *stubObsGetter) Get(_ context.Context, key sdk.ObservationKey, dest any) error {
|
|
if key != ObservationKeyCAA {
|
|
return nil
|
|
}
|
|
b, _ := json.Marshal(s.data)
|
|
return json.Unmarshal(b, dest)
|
|
}
|
|
|
|
func (s *stubObsGetter) GetRelated(_ context.Context, _ sdk.ObservationKey) ([]sdk.RelatedObservation, error) {
|
|
return s.related, nil
|
|
}
|
|
|
|
// mkTLSObs wraps a single probe into the {"probes": {<ref>: …}} shape
|
|
// checker-tls actually emits.
|
|
func mkTLSObs(t *testing.T, ref string, probe map[string]any) sdk.RelatedObservation {
|
|
t.Helper()
|
|
payload := map[string]any{
|
|
"probes": map[string]any{ref: probe},
|
|
}
|
|
b, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal tls payload: %v", err)
|
|
}
|
|
return sdk.RelatedObservation{
|
|
CheckerID: "tls",
|
|
Key: TLSRelatedKey,
|
|
Data: b,
|
|
CollectedAt: time.Now(),
|
|
Ref: ref,
|
|
}
|
|
}
|
|
|
|
// TestRule_OK: CAA allows letsencrypt.org and the probe is from a
|
|
// Let's Encrypt intermediate. Expect StatusOK.
|
|
func TestRule_OK(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: "letsencrypt.org"}},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_dn": "CN=R10,O=Let's Encrypt,C=US",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusOK {
|
|
t.Fatalf("expected StatusOK, got %s: %s", state.Status, state.Message)
|
|
}
|
|
if state.Code != CodeOK {
|
|
t.Errorf("expected code %q, got %q", CodeOK, state.Code)
|
|
}
|
|
}
|
|
|
|
// TestRule_NotAuthorized: CAA only allows digicert.com but the probe
|
|
// shows a Let's Encrypt cert. Expect StatusCrit / caa_not_authorized.
|
|
func TestRule_NotAuthorized(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: "digicert.com"}},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusCrit {
|
|
t.Fatalf("expected StatusCrit, got %s: %s", state.Status, state.Message)
|
|
}
|
|
if state.Code != CodeNotAuthorized {
|
|
t.Errorf("expected code %q, got %q", CodeNotAuthorized, state.Code)
|
|
}
|
|
if !strings.Contains(state.Message, "letsencrypt.org") {
|
|
t.Errorf("expected message to mention letsencrypt.org, got %q", state.Message)
|
|
}
|
|
}
|
|
|
|
// TestRule_IssuanceDisallowed: CAA says `issue ";"` but a cert was
|
|
// observed. Expect StatusCrit / caa_issuance_disallowed regardless of
|
|
// the issuer.
|
|
func TestRule_IssuanceDisallowed(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: ";"}},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusCrit {
|
|
t.Fatalf("expected StatusCrit, got %s: %s", state.Status, state.Message)
|
|
}
|
|
if state.Code != CodeIssuanceDisallowed {
|
|
t.Errorf("expected code %q, got %q", CodeIssuanceDisallowed, state.Code)
|
|
}
|
|
}
|
|
|
|
// TestRule_IssuerUnknown: the observed AKI is not in CCADB. Expect
|
|
// StatusInfo / caa_issuer_unknown.
|
|
func TestRule_IssuerUnknown(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: "letsencrypt.org"}},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
|
|
"issuer_dn": "CN=Totally Made Up CA,O=Nope,C=XX",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusInfo {
|
|
t.Fatalf("expected StatusInfo, got %s: %s", state.Status, state.Message)
|
|
}
|
|
if state.Code != CodeIssuerUnknown {
|
|
t.Errorf("expected code %q, got %q", CodeIssuerUnknown, state.Code)
|
|
}
|
|
}
|
|
|
|
// TestRule_NoTLS: no related TLS observations yet. Steady state during
|
|
// the eventual-consistency window before checker-tls has produced data.
|
|
func TestRule_NoTLS(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: "letsencrypt.org"}},
|
|
},
|
|
related: nil,
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusUnknown {
|
|
t.Fatalf("expected StatusUnknown, got %s: %s", state.Status, state.Message)
|
|
}
|
|
if state.Code != CodeNoTLS {
|
|
t.Errorf("expected code %q, got %q", CodeNoTLS, state.Code)
|
|
}
|
|
}
|
|
|
|
// TestRule_NoCAAPublished: valid TLS cert, but the zone has no CAA
|
|
// records. Rule should nudge the user (StatusInfo) with a suggestion
|
|
// to publish CAA.
|
|
func TestRule_NoCAAPublished(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{Domain: "example.com", Records: nil},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
state := states[0]
|
|
if state.Status != sdk.StatusInfo {
|
|
t.Fatalf("expected StatusInfo (no policy), got %s: %s", state.Status, state.Message)
|
|
}
|
|
if !strings.Contains(state.Message, "letsencrypt.org") {
|
|
t.Errorf("expected suggestion to mention letsencrypt.org, got %q", state.Message)
|
|
}
|
|
}
|
|
|
|
// findState returns the first state matching code, or nil.
|
|
func findState(states []sdk.CheckState, code string) *sdk.CheckState {
|
|
for i := range states {
|
|
if states[i].Code == code {
|
|
return &states[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TestRule_UnknownCriticalTag: an unknown tag with the Issuer Critical
|
|
// bit (0x80) must surface a Warn / caa_unknown_critical state.
|
|
func TestRule_UnknownCriticalTag(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 128, Tag: "frobnicate", Value: "yes"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
st := findState(states, CodeUnknownCritical)
|
|
if st == nil {
|
|
t.Fatalf("expected %s state, got %+v", CodeUnknownCritical, states)
|
|
}
|
|
if st.Status != sdk.StatusWarn {
|
|
t.Errorf("expected StatusWarn, got %s", st.Status)
|
|
}
|
|
if !strings.Contains(st.Message, "frobnicate") {
|
|
t.Errorf("expected unknown tag name in message, got %q", st.Message)
|
|
}
|
|
}
|
|
|
|
// TestRule_UnknownCritical_NoTLS: the policy-level warning must fire
|
|
// even when checker-tls has not yet produced any probes (issue #1: the
|
|
// warning was previously gated on probe presence).
|
|
func TestRule_UnknownCritical_NoTLS(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 128, Tag: "frobnicate", Value: "yes"},
|
|
},
|
|
},
|
|
related: nil,
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if findState(states, CodeUnknownCritical) == nil {
|
|
t.Errorf("expected %s state with no TLS probes, got %+v", CodeUnknownCritical, states)
|
|
}
|
|
if findState(states, CodeNoTLS) == nil {
|
|
t.Errorf("expected %s state alongside the warning, got %+v", CodeNoTLS, states)
|
|
}
|
|
}
|
|
|
|
// TestRule_CriticalIodef: iodef is a recognized tag, so the critical
|
|
// bit on it must not produce an unknown-critical warning.
|
|
func TestRule_CriticalIodef(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 128, Tag: "iodef", Value: "mailto:sec@example.com"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if st := findState(states, CodeUnknownCritical); st != nil {
|
|
t.Errorf("did not expect unknown-critical for iodef, got %+v", st)
|
|
}
|
|
}
|
|
|
|
// TestRule_CriticalIssue: critical bit on the well-known "issue" tag
|
|
// is normal (CAs always understand it) and must not warn.
|
|
func TestRule_CriticalIssue(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 128, Tag: "issue", Value: "letsencrypt.org"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if st := findState(states, CodeUnknownCritical); st != nil {
|
|
t.Errorf("did not expect unknown-critical for issue, got %+v", st)
|
|
}
|
|
}
|
|
|
|
// TestRule_CriticalEmptyTag: a malformed record with the critical bit
|
|
// set and an empty tag is still surfaced (issue #3, previously
|
|
// silently dropped).
|
|
func TestRule_CriticalEmptyTag(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 128, Tag: "", Value: "garbage"},
|
|
},
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if findState(states, CodeUnknownCritical) == nil {
|
|
t.Errorf("expected %s for critical empty tag, got %+v", CodeUnknownCritical, states)
|
|
}
|
|
}
|
|
|
|
// TestRule_KnownExtraTagsCritical: tags registered outside the v1
|
|
// vocabulary (contactemail, contactphone, issuemail, issuevmc) should
|
|
// not trigger unknown-critical warnings even when marked critical.
|
|
func TestRule_KnownExtraTagsCritical(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 128, Tag: "contactemail", Value: "sec@example.com"},
|
|
{Flag: 128, Tag: "contactphone", Value: "+1-555-0100"},
|
|
{Flag: 128, Tag: "issuemail", Value: "letsencrypt.org"},
|
|
{Flag: 128, Tag: "issuevmc", Value: "letsencrypt.org"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if st := findState(states, CodeUnknownCritical); st != nil {
|
|
t.Errorf("did not expect unknown-critical for known extra tags, got %+v", st)
|
|
}
|
|
}
|
|
|
|
// TestBuildAllowList is a unit test for the policy parser. The ';'
|
|
// sentinel and parameter stripping are the two subtle bits worth
|
|
// covering directly.
|
|
func TestBuildAllowList(t *testing.T) {
|
|
al := buildAllowList([]CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 0, Tag: "issue", Value: "sectigo.com; account=12345"},
|
|
{Flag: 0, Tag: "issuewild", Value: ";"},
|
|
})
|
|
if !al.issueAll["letsencrypt.org"] {
|
|
t.Error("expected letsencrypt.org in issueAll")
|
|
}
|
|
if !al.issueAll["sectigo.com"] {
|
|
t.Errorf("expected sectigo.com (stripped) in issueAll, got %v", al.issueAll)
|
|
}
|
|
if al.disallowIssue {
|
|
t.Error("disallowIssue should be false; only issuewild was ';'")
|
|
}
|
|
if !al.disallowWildcardIssue {
|
|
t.Error("expected disallowWildcardIssue=true")
|
|
}
|
|
if !al.hasIssueWild {
|
|
t.Error("expected hasIssueWild=true")
|
|
}
|
|
}
|
|
|
|
// TestRule_WildcardDisallowed: zone allows letsencrypt.org via "issue"
|
|
// but explicitly forbids wildcard issuance via `issuewild ";"`. A
|
|
// wildcard cert should trip caa_issuance_disallowed even though the
|
|
// CA is otherwise authorized.
|
|
func TestRule_WildcardDisallowed(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 0, Tag: "issuewild", Value: ";"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
"dns_names": []string{"*.example.com", "example.com"},
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
if states[0].Status != sdk.StatusCrit {
|
|
t.Fatalf("expected StatusCrit, got %s: %s", states[0].Status, states[0].Message)
|
|
}
|
|
if states[0].Code != CodeIssuanceDisallowed {
|
|
t.Errorf("expected %q, got %q", CodeIssuanceDisallowed, states[0].Code)
|
|
}
|
|
if !strings.Contains(states[0].Message, "issuewild") {
|
|
t.Errorf("expected message to mention issuewild, got %q", states[0].Message)
|
|
}
|
|
}
|
|
|
|
// TestRule_WildcardOverridesIssue: when "issuewild" is present, it
|
|
// fully overrides "issue" for wildcard certs (RFC 8659 §4.3). The
|
|
// wildcard probe must be checked against issuewild only, even if the
|
|
// CA is allowed by "issue".
|
|
func TestRule_WildcardOverridesIssue(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 0, Tag: "issuewild", Value: "digicert.com"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
"dns_names": []string{"*.example.com"},
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
if states[0].Status != sdk.StatusCrit {
|
|
t.Fatalf("expected StatusCrit (LE not in issuewild), got %s: %s", states[0].Status, states[0].Message)
|
|
}
|
|
if states[0].Code != CodeNotAuthorized {
|
|
t.Errorf("expected %q, got %q", CodeNotAuthorized, states[0].Code)
|
|
}
|
|
if !strings.Contains(states[0].Message, "issuewild") {
|
|
t.Errorf("expected message to mention issuewild, got %q", states[0].Message)
|
|
}
|
|
}
|
|
|
|
// TestRule_WildcardFallsBackToIssue: with no "issuewild" records, a
|
|
// wildcard cert is governed by the "issue" allow list as if it were a
|
|
// regular cert.
|
|
func TestRule_WildcardFallsBackToIssue(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{{Flag: 0, Tag: "issue", Value: "letsencrypt.org"}},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
"dns_names": []string{"*.example.com"},
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
if states[0].Status != sdk.StatusOK {
|
|
t.Fatalf("expected StatusOK, got %s: %s", states[0].Status, states[0].Message)
|
|
}
|
|
}
|
|
|
|
// TestRule_NonWildcardIgnoresIssueWild: a non-wildcard cert must be
|
|
// checked against "issue" even when "issuewild" is present and would
|
|
// disallow issuance.
|
|
func TestRule_NonWildcardIgnoresIssueWild(t *testing.T) {
|
|
obs := &stubObsGetter{
|
|
data: CAAData{
|
|
Domain: "example.com",
|
|
Records: []CAARecord{
|
|
{Flag: 0, Tag: "issue", Value: "letsencrypt.org"},
|
|
{Flag: 0, Tag: "issuewild", Value: ";"},
|
|
},
|
|
},
|
|
related: []sdk.RelatedObservation{
|
|
mkTLSObs(t, "ep-1", map[string]any{
|
|
"host": "www.example.com",
|
|
"port": 443,
|
|
"endpoint": "www.example.com:443",
|
|
"issuer": "R10",
|
|
"issuer_aki": "BBBCC347A5E4BCA9C6C3A4720C108DA235E1C8E8",
|
|
"dns_names": []string{"www.example.com"},
|
|
}),
|
|
},
|
|
}
|
|
states := Rule().Evaluate(context.Background(), obs, nil)
|
|
if len(states) != 1 {
|
|
t.Fatalf("expected 1 state, got %d", len(states))
|
|
}
|
|
if states[0].Status != sdk.StatusOK {
|
|
t.Fatalf("expected StatusOK, got %s: %s", states[0].Status, states[0].Message)
|
|
}
|
|
}
|