// This file is part of the happyDomain (R) project. // Copyright (c) 2020-2026 happyDomain // Authors: Pierre-Olivier Mercier, et al. package checker import ( "encoding/json" "testing" sdk "git.happydns.org/checker-sdk-go/checker" ) func honeypotData(t *testing.T, probes map[string]HoneypotProbe) map[string]json.RawMessage { t.Helper() raw, err := json.Marshal(HoneypotData{Probes: probes}) if err != nil { t.Fatalf("marshal: %v", err) } return map[string]json.RawMessage{ObservationKeyHoneypot: raw} } func TestHoneypotRule_Clean(t *testing.T) { probes := map[string]HoneypotProbe{ "/.env": {PathProbe: PathProbe{StatusCode: 404}}, "/.git/config": {PathProbe: PathProbe{StatusCode: 404}}, "/actuator/env": {PathProbe: PathProbe{StatusCode: 404}}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusOK) if !hasCode(states, "http.honeypot.clean") { t.Errorf("expected clean, got %+v", states) } } func TestHoneypotRule_CriticalExposed(t *testing.T) { probes := map[string]HoneypotProbe{ "/.env": {PathProbe: PathProbe{StatusCode: 200, Bytes: 512}, Critical: true}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusCrit) if !hasCode(states, "http.honeypot.critical_exposed") { t.Errorf("expected critical_exposed, got %+v", states) } } func TestHoneypotRule_WarnExposed(t *testing.T) { probes := map[string]HoneypotProbe{ "/server-status": {PathProbe: PathProbe{StatusCode: 200, Bytes: 1024}}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusWarn) if !hasCode(states, "http.honeypot.exposed") { t.Errorf("expected exposed, got %+v", states) } } func TestHoneypotRule_Redirect(t *testing.T) { probes := map[string]HoneypotProbe{ "/console/": {PathProbe: PathProbe{StatusCode: 301}}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusWarn) if !hasCode(states, "http.honeypot.exposed") { t.Errorf("expected exposed for redirect, got %+v", states) } } func TestHoneypotRule_Protected(t *testing.T) { probes := map[string]HoneypotProbe{ "/.git/config": {PathProbe: PathProbe{StatusCode: 403}, Critical: true}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusInfo) if !hasCode(states, "http.honeypot.protected") { t.Errorf("expected protected, got %+v", states) } } func TestHoneypotRule_NoCollectorData(t *testing.T) { data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusUnknown) if !hasCode(states, "http.honeypot.no_data") { t.Errorf("expected no_data, got %+v", states) } } func TestHoneypotRule_DecodeError(t *testing.T) { data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: map[string]json.RawMessage{ ObservationKeyHoneypot: json.RawMessage(`"not an object"`), }, } states := runRule(t, &honeypotRule{}, data, nil) if states[0].Status != sdk.StatusError || states[0].Code != "http.honeypot.decode_error" { t.Errorf("expected decode_error, got %+v", states) } } func TestHoneypotRule_IgnoresErrors(t *testing.T) { // All-error probes must return Unknown, not OK, to avoid false-green. probes := map[string]HoneypotProbe{ "/.env": {PathProbe: PathProbe{Error: "connection refused"}}, "/wp-json/": {}, } data := &HTTPData{ Domain: "example.test", Probes: []HTTPProbe{httpsProbe("a:443")}, Extensions: honeypotData(t, probes), } states := runRule(t, &honeypotRule{}, data, nil) mustStatus(t, states, sdk.StatusUnknown) if !hasCode(states, "http.honeypot.no_response") { t.Errorf("expected no_response when all probes errored, got %+v", states) } }