85 lines
3.0 KiB
Go
85 lines
3.0 KiB
Go
package honeypot
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPromotesAfterThresholdAndExpires(t *testing.T) {
|
|
tracker := New(Config{Threshold: 2, Window: time.Minute, TTL: time.Millisecond, Token: "secret"})
|
|
for _, path := range []string{"/.env", "/.git/config", "/wp-login.php"} {
|
|
req := httptest.NewRequest(http.MethodGet, "https://example.test"+path, nil)
|
|
req.RemoteAddr = "192.0.2.10:1234"
|
|
tracker.Middleware(http.HandlerFunc(http.NotFound)).ServeHTTP(httptest.NewRecorder(), req)
|
|
}
|
|
if got := len(tracker.PromotedIPs()); got != 1 {
|
|
t.Fatalf("got %d promoted IPs, want 1", got)
|
|
}
|
|
time.Sleep(2 * time.Millisecond)
|
|
if got := len(tracker.PromotedIPs()); got != 0 {
|
|
t.Fatalf("got %d expired IPs, want 0", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleIPsRequiresToken(t *testing.T) {
|
|
tracker := New(Config{Threshold: 1, Token: "secret"})
|
|
recorder := httptest.NewRecorder()
|
|
tracker.HandleIPs(recorder, httptest.NewRequest(http.MethodGet, "https://example.test/internal/honeypot/ips", nil))
|
|
if recorder.Code != http.StatusUnauthorized {
|
|
t.Fatalf("got %d, want 401", recorder.Code)
|
|
}
|
|
recorder = httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "https://example.test/internal/honeypot/ips", nil)
|
|
req.Header.Set("Authorization", "Bearer secret")
|
|
tracker.HandleIPs(recorder, req)
|
|
if recorder.Code != http.StatusOK || !strings.HasPrefix(recorder.Body.String(), "[]") {
|
|
t.Fatalf("got %d and %q", recorder.Code, recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRecordTelemetryPromotesTheObservedSourceIP(t *testing.T) {
|
|
tracker := New(Config{Threshold: 2, TTL: time.Minute})
|
|
if tracker.RecordTelemetry("203.0.113.20", "/mail.php") {
|
|
t.Fatal("must not promote before threshold")
|
|
}
|
|
if !tracker.RecordTelemetry("203.0.113.20", "/settings.php") {
|
|
t.Fatal("must promote at threshold")
|
|
}
|
|
if tracker.RecordTelemetry("203.0.113.20", "/index.html") {
|
|
t.Fatal("normal paths must not be tracked")
|
|
}
|
|
ips := tracker.PromotedIPs()
|
|
if len(ips) != 1 || ips[0].Address != "203.0.113.20" {
|
|
t.Fatalf("unexpected promoted IPs: %+v", ips)
|
|
}
|
|
}
|
|
|
|
func TestNeverTracksInternalAddresses(t *testing.T) {
|
|
tracker := New(Config{Threshold: 1, TTL: time.Minute})
|
|
for _, ip := range []string{"127.0.0.1", "::1", "10.244.4.1", "192.168.1.5", "169.254.1.1", "100.64.0.1", "100.127.255.254", "198.18.0.1", "240.0.0.1", "255.255.255.255", "::ffff:10.1.2.3", "not-an-ip", ""} {
|
|
if tracker.RecordTelemetry(ip, "/.env") {
|
|
t.Fatalf("%q must never be promoted", ip)
|
|
}
|
|
}
|
|
if got := len(tracker.PromotedIPs()); got != 0 {
|
|
t.Fatalf("promoted %d internal addresses", got)
|
|
}
|
|
}
|
|
|
|
func TestMinPathsRequiresBreadth(t *testing.T) {
|
|
tracker := New(Config{Threshold: 2, TTL: time.Minute, MinPaths: 3})
|
|
for i := 0; i < 5; i++ {
|
|
tracker.RecordTelemetry("203.0.113.9", "/manifest.json")
|
|
}
|
|
if len(tracker.PromotedIPs()) != 0 {
|
|
t.Fatal("one repeated path must not promote")
|
|
}
|
|
tracker.RecordTelemetry("203.0.113.9", "/.env")
|
|
if !tracker.RecordTelemetry("203.0.113.9", "/wp-login.php") {
|
|
t.Fatal("three distinct paths must promote")
|
|
}
|
|
}
|