/* ·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· : : : █▀ █ █▀▀ · Blazing-fast pentesting suite : : ▄█ █ █▀ · BSD 3-Clause License : : : : (c) 2022-2026 vmfunc, xyzeva, : : lunchcat alumni & contributors : : : ·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· */ package scan import ( "encoding/json" "net/http" "net/http/httptest" "testing" "time" "unicode/utf8" ) func TestResolveHostname_IP(t *testing.T) { ip, err := resolveHostname("8.8.8.8") if err != nil { t.Fatalf("unexpected error: %v", err) } if ip != "8.8.8.8" { t.Errorf("expected '8.8.8.8', got '%s'", ip) } } func TestResolveHostname_Hostname(t *testing.T) { ip, err := resolveHostname("localhost") if err != nil { t.Fatalf("unexpected error: %v", err) } if ip != "127.0.0.1" && ip != "::1" { t.Errorf("expected localhost to resolve to 127.0.0.1 or ::1, got '%s'", ip) } } func TestTruncateBanner(t *testing.T) { tests := []struct { input string maxLen int expected string }{ {"short", 10, "short"}, {"this is a long banner", 10, "this is a ..."}, {"with\nnewlines\r\n", 50, "with newlines"}, {" trimmed ", 50, "trimmed"}, // truncation must land on a rune boundary, not split a multibyte rune {"aaaé", 4, "aaaé"}, {"日本語テスト", 3, "日本語..."}, } for _, tt := range tests { result := truncateBanner(tt.input, tt.maxLen) if result != tt.expected { t.Errorf("truncateBanner(%q, %d) = %q, want %q", tt.input, tt.maxLen, result, tt.expected) } if !utf8.ValidString(result) { t.Errorf("truncateBanner(%q, %d) produced invalid UTF-8: %q", tt.input, tt.maxLen, result) } } } func TestQueryShodanHost_NotFound(t *testing.T) { // this test verifies that a mock server returning 404 is handled correctly // note: we can't easily override the const shodanBaseURL for testing // so this is more of a documentation of expected behavior server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer server.Close() // the actual API query would return a partial result with just the IP // when Shodan has no data for a host } func TestQueryShodanHost_Success(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { response := shodanHostResponse{ IP: "93.184.216.34", Hostnames: []string{"example.com"}, Org: "EDGECAST", ASN: "AS15133", ISP: "Edgecast Inc.", CountryName: "United States", City: "Los Angeles", Ports: []int{80, 443}, Data: []shodanData{ { Port: 80, Transport: "tcp", Product: "nginx", Version: "1.18.0", Data: "HTTP/1.1 200 OK\r\nServer: nginx", }, }, } json.NewEncoder(w).Encode(response) })) defer server.Close() // Note: This test would need the actual API endpoint to be overridable // For now, we just verify the response parsing } func TestShodanResult_Fields(t *testing.T) { result := ShodanResult{ IP: "93.184.216.34", Hostnames: []string{"example.com"}, Organization: "EDGECAST", ASN: "AS15133", ISP: "Edgecast Inc.", Country: "United States", City: "Los Angeles", Ports: []int{80, 443}, Services: []ShodanService{ { Port: 80, Protocol: "tcp", Product: "nginx", Version: "1.18.0", }, }, } if result.IP != "93.184.216.34" { t.Errorf("expected IP '93.184.216.34', got '%s'", result.IP) } if len(result.Hostnames) != 1 || result.Hostnames[0] != "example.com" { t.Errorf("expected hostnames ['example.com'], got %v", result.Hostnames) } if result.Organization != "EDGECAST" { t.Errorf("expected org 'EDGECAST', got '%s'", result.Organization) } if len(result.Ports) != 2 { t.Errorf("expected 2 ports, got %d", len(result.Ports)) } if len(result.Services) != 1 { t.Errorf("expected 1 service, got %d", len(result.Services)) } } func TestShodanService_Fields(t *testing.T) { service := ShodanService{ Port: 443, Protocol: "tcp", Product: "OpenSSL", Version: "1.1.1", Banner: "TLS handshake", Module: "https", } if service.Port != 443 { t.Errorf("expected port 443, got %d", service.Port) } if service.Protocol != "tcp" { t.Errorf("expected protocol 'tcp', got '%s'", service.Protocol) } if service.Product != "OpenSSL" { t.Errorf("expected product 'OpenSSL', got '%s'", service.Product) } } func TestShodan_NoAPIKey(t *testing.T) { // ensure no API key is set originalKey := "" // Note: we can't easily test this without setting/unsetting env vars // which could affect other tests. This is just a placeholder. _ = originalKey } func TestShodanIntegration(t *testing.T) { // This would be an integration test with the real Shodan API // Skipping in unit tests t.Skip("Integration test - requires valid SHODAN_API_KEY") _, err := Shodan("https://example.com", 10*time.Second, "") if err != nil { t.Logf("Shodan lookup failed (expected without API key): %v", err) } }