Sources that always work (botvrij, disconnect, oisd, openphish, phishtank, quad9) drop their user-facing enable_* option; the rule's enabled/disabled state is now solely controlled by the SDK rule toggle. Sources that require credentials (criminalip, malwarebazaar, otx, pulsedive, safebrowsing, threatfox, urlhaus, virustotal) instead implement the new SourcePrecheck interface so the host UI can surface "not configured" before attempting a query.
117 lines
3.1 KiB
Go
117 lines
3.1 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const disconnectFakeFeed = `{
|
|
"categories": {
|
|
"Advertising": [
|
|
{
|
|
"Evil Corp": {
|
|
"https://evilcorp.com": ["tracker.com", "sub.example.org"]
|
|
}
|
|
}
|
|
],
|
|
"Analytics": [
|
|
{
|
|
"Metrics Inc": {
|
|
"https://metrics.io": ["analytics.net"]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}`
|
|
|
|
func newDisconnectTestSource(srv *httptest.Server) *disconnectSource {
|
|
return &disconnectSource{
|
|
cache: newFeedCache(time.Hour, disconnectFetch(srv.URL)),
|
|
}
|
|
}
|
|
|
|
func TestDisconnectSource_Listed(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = w.Write([]byte(disconnectFakeFeed))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
s := newDisconnectTestSource(srv)
|
|
r := s.Query(context.Background(), "tracker.com", "tracker.com", nil)[0]
|
|
|
|
if !r.Enabled || r.Error != "" {
|
|
t.Fatalf("expected enabled with no error, got %+v", r)
|
|
}
|
|
if len(r.Evidence) == 0 {
|
|
t.Fatalf("expected evidence, got none")
|
|
}
|
|
found := false
|
|
for _, e := range r.Evidence {
|
|
if e.Value == "Advertising" {
|
|
found = true
|
|
if e.Extra["company"] != "Evil Corp" {
|
|
t.Errorf("expected company 'Evil Corp', got %q", e.Extra["company"])
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected Advertising evidence, got %+v", r.Evidence)
|
|
}
|
|
if listed, sev := s.Evaluate(r); !listed || sev != SeverityWarn {
|
|
t.Errorf("expected (true, warn), got (%v, %q)", listed, sev)
|
|
}
|
|
}
|
|
|
|
func TestDisconnectSource_SubdomainInFeed(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = w.Write([]byte(disconnectFakeFeed))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
// Feed has "sub.example.org"; querying registered domain "example.org" should match.
|
|
s := newDisconnectTestSource(srv)
|
|
r := s.Query(context.Background(), "example.org", "example.org", nil)[0]
|
|
|
|
if !r.Enabled || r.Error != "" {
|
|
t.Fatalf("expected enabled with no error, got %+v", r)
|
|
}
|
|
if len(r.Evidence) == 0 {
|
|
t.Errorf("expected subdomain match evidence, got none")
|
|
}
|
|
}
|
|
|
|
func TestDisconnectSource_NotListed(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = w.Write([]byte(disconnectFakeFeed))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
s := newDisconnectTestSource(srv)
|
|
r := s.Query(context.Background(), "clean.example.com", "clean.example.com", nil)[0]
|
|
|
|
if !r.Enabled || r.Error != "" {
|
|
t.Fatalf("expected enabled with no error, got %+v", r)
|
|
}
|
|
if len(r.Evidence) != 0 {
|
|
t.Errorf("expected no evidence for clean domain, got %+v", r.Evidence)
|
|
}
|
|
if listed, _ := s.Evaluate(r); listed {
|
|
t.Errorf("expected not listed for clean domain")
|
|
}
|
|
}
|
|
|
|
func TestDisconnectSource_HTTPError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
s := newDisconnectTestSource(srv)
|
|
r := s.Query(context.Background(), "tracker.com", "tracker.com", nil)[0]
|
|
if r.Error == "" {
|
|
t.Errorf("expected error on HTTP 500, got empty error")
|
|
}
|
|
}
|