144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
func newOTXServer(t *testing.T, status int, body string) (string, func()) {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("X-OTX-API-KEY") == "" {
|
|
t.Errorf("missing X-OTX-API-KEY header")
|
|
}
|
|
w.WriteHeader(status)
|
|
_, _ = w.Write([]byte(body))
|
|
}))
|
|
return srv.URL + "/", srv.Close
|
|
}
|
|
|
|
func TestOTXSource_NoKey(t *testing.T) {
|
|
s := &otxSource{endpoint: otxEndpoint}
|
|
r := s.Query(context.Background(), "example.com", "example.com", sdk.CheckerOptions{})[0]
|
|
if r.Enabled {
|
|
t.Errorf("expected disabled without API key, got %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestOTXSource_Listed(t *testing.T) {
|
|
body := `{
|
|
"reputation": 0,
|
|
"pulse_info": {
|
|
"count": 1,
|
|
"pulses": [{
|
|
"name": "Test Pulse",
|
|
"tags": ["phishing"],
|
|
"malware_families": [{"display_name": "Emotet"}],
|
|
"adversary": "",
|
|
"created": "2024-01-01T00:00:00.000Z"
|
|
}]
|
|
}
|
|
}`
|
|
endpoint, stop := newOTXServer(t, http.StatusOK, body)
|
|
defer stop()
|
|
|
|
s := &otxSource{endpoint: endpoint}
|
|
r := s.Query(context.Background(), "example.com", "example.com", sdk.CheckerOptions{"otx_api_key": "k"})[0]
|
|
|
|
if len(r.Evidence) != 1 {
|
|
t.Fatalf("expected 1 evidence entry, got %d", len(r.Evidence))
|
|
}
|
|
if listed, severity := s.Evaluate(r); !listed || severity != SeverityWarn {
|
|
t.Errorf("expected Evaluate()=(true, warn), got (%v, %q)", listed, severity)
|
|
}
|
|
|
|
var d otxDetails
|
|
if err := json.Unmarshal(r.Details, &d); err != nil {
|
|
t.Fatalf("details decode: %v", err)
|
|
}
|
|
if d.PulseCount != 1 || d.Reputation != 0 {
|
|
t.Errorf("details wrong: %+v", d)
|
|
}
|
|
if len(d.Pulses) != 1 || len(d.Pulses[0].MalwareFamilies) != 1 || d.Pulses[0].MalwareFamilies[0] != "Emotet" {
|
|
t.Errorf("pulse details wrong: %+v", d.Pulses)
|
|
}
|
|
|
|
html, err := s.RenderDetail(r)
|
|
if err != nil || !strings.Contains(string(html), "Emotet") {
|
|
t.Errorf("RenderDetail html=%q err=%v", html, err)
|
|
}
|
|
}
|
|
|
|
func TestOTXSource_ListedCrit(t *testing.T) {
|
|
body := `{
|
|
"reputation": -2,
|
|
"pulse_info": {
|
|
"count": 5,
|
|
"pulses": [{"name": "APT Pulse", "tags": [], "malware_families": [], "adversary": "APT28", "created": ""}]
|
|
}
|
|
}`
|
|
endpoint, stop := newOTXServer(t, http.StatusOK, body)
|
|
defer stop()
|
|
|
|
s := &otxSource{endpoint: endpoint}
|
|
r := s.Query(context.Background(), "evil.com", "evil.com", sdk.CheckerOptions{"otx_api_key": "k"})[0]
|
|
|
|
if listed, severity := s.Evaluate(r); !listed || severity != SeverityCrit {
|
|
t.Errorf("expected Evaluate()=(true, crit) for reputation -2, got (%v, %q)", listed, severity)
|
|
}
|
|
}
|
|
|
|
func TestOTXSource_NotFound(t *testing.T) {
|
|
endpoint, stop := newOTXServer(t, http.StatusNotFound, `{"detail":"Not found"}`)
|
|
defer stop()
|
|
|
|
s := &otxSource{endpoint: endpoint}
|
|
r := s.Query(context.Background(), "example.com", "example.com", sdk.CheckerOptions{"otx_api_key": "k"})[0]
|
|
|
|
if r.Error != "" {
|
|
t.Errorf("404 should be quiet not-listed, got Error=%q", r.Error)
|
|
}
|
|
if len(r.Evidence) != 0 {
|
|
t.Errorf("expected no evidence for 404, got %+v", r.Evidence)
|
|
}
|
|
if listed, _ := s.Evaluate(r); listed {
|
|
t.Errorf("Evaluate() on clean result should return false")
|
|
}
|
|
if !strings.Contains(r.Reference, "example.com") {
|
|
t.Errorf("reference URL missing domain: %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestOTXSource_HTTPError(t *testing.T) {
|
|
endpoint, stop := newOTXServer(t, http.StatusInternalServerError, `{"error":"internal"}`)
|
|
defer stop()
|
|
|
|
s := &otxSource{endpoint: endpoint}
|
|
r := s.Query(context.Background(), "example.com", "example.com", sdk.CheckerOptions{"otx_api_key": "k"})[0]
|
|
|
|
if r.Error == "" {
|
|
t.Errorf("expected non-empty Error for HTTP 500, got %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestOTXSource_NoResults(t *testing.T) {
|
|
body := `{"reputation": 0, "pulse_info": {"count": 0, "pulses": []}}`
|
|
endpoint, stop := newOTXServer(t, http.StatusOK, body)
|
|
defer stop()
|
|
|
|
s := &otxSource{endpoint: endpoint}
|
|
r := s.Query(context.Background(), "clean.com", "clean.com", sdk.CheckerOptions{"otx_api_key": "k"})[0]
|
|
|
|
if len(r.Evidence) != 0 {
|
|
t.Errorf("expected no evidence for clean domain, got %+v", r.Evidence)
|
|
}
|
|
if listed, severity := s.Evaluate(r); listed || severity != "" {
|
|
t.Errorf("Evaluate() on clean domain = (%v, %q), want (false, \"\")", listed, severity)
|
|
}
|
|
}
|