mirror of
https://github.com/Vyntral/god-eye.git
synced 2026-05-16 21:43:34 +02:00
3a4c230aa7
Complete architectural overhaul. Replaces the v0.1 monolithic scanner with an event-driven pipeline of auto-registered modules. Foundation (internal/): - eventbus: typed pub/sub, 20 event types, race-safe, drop counter - module: registry with phase-based selection - store: thread-safe host store with per-host locks + deep-copy reads - pipeline: coordinator with phase barriers + panic recovery - config: 5 scan profiles + 3 AI tiers + YAML loader + auto-discovery Modules (26 auto-registered across 6 phases): - Discovery: passive (26 sources), bruteforce, recursive, AXFR, GitHub dorks, CT streaming, permutation, reverse DNS, vhost, ASN, supply chain (npm + PyPI) - Enrichment: HTTP probe + tech fingerprint + TLS appliance ID, ports - Analysis: security checks, takeover (110+ sigs), cloud, JavaScript, GraphQL, JWT, headers (OWASP), HTTP smuggling, AI cascade, Nuclei - Reporting: TXT/JSON/CSV writer + AI scan brief AI layer (internal/ai/ + internal/modules/ai/): - Three profiles: lean (16 GB), balanced (32 GB MoE), heavy (64 GB) - Six event-driven handlers: CVE, JS file, HTTP response, secret filter, multi-agent vuln enrichment, anomaly + executive report - Content-hash cache dedups Ollama calls across hosts - Auto-pull of missing models via /api/pull with streaming progress - End-of-scan AI SCAN BRIEF in terminal with top chains + next actions Nuclei compat layer (internal/nucleitpl/): - Executes ~13k community templates (HTTP subset) - Auto-download of nuclei-templates ZIP to ~/.god-eye/nuclei-templates - Scope filter rejects off-host templates (eliminates OSINT FPs) Operations: - Interactive wizard (internal/wizard/) — zero-flag launch - LivePrinter (internal/tui/) — colorized event stream - Diff engine + scheduler (internal/diff, internal/scheduler) for continuous ASM monitoring with webhook alerts - Proxy support (internal/proxyconf/): http / https / socks5 / socks5h + basic auth Fixes #1 — native SOCKS5 / Tor compatibility via --proxy flag. 185 unit tests across 15 packages, all race-detector clean.
157 lines
3.6 KiB
Go
157 lines
3.6 KiB
Go
package config
|
|
|
|
import "testing"
|
|
|
|
func TestView_NilSafe(t *testing.T) {
|
|
var v *View
|
|
if v.Profile() != "" {
|
|
t.Error("nil view Profile should be empty")
|
|
}
|
|
if v.Bool("ai.enabled", true) != true {
|
|
t.Error("nil view Bool should return fallback")
|
|
}
|
|
if v.Int("concurrency", 99) != 99 {
|
|
t.Error("nil view Int should return fallback")
|
|
}
|
|
if v.String("domain", "fb") != "fb" {
|
|
t.Error("nil view String should return fallback")
|
|
}
|
|
if v.ModuleEnabled("x") {
|
|
t.Error("nil view ModuleEnabled should be false")
|
|
}
|
|
}
|
|
|
|
func TestView_Profile(t *testing.T) {
|
|
v := NewView(&Config{Profile: "bugbounty"})
|
|
if v.Profile() != "bugbounty" {
|
|
t.Errorf("Profile = %q", v.Profile())
|
|
}
|
|
}
|
|
|
|
func TestView_Bool(t *testing.T) {
|
|
cfg := &Config{
|
|
EnableAI: true,
|
|
AICascade: true,
|
|
AIDeepAnalysis: false,
|
|
MultiAgent: true,
|
|
Silent: true,
|
|
Verbose: false,
|
|
JsonOutput: true,
|
|
NoBrute: true,
|
|
OnlyActive: true,
|
|
Recursive: true,
|
|
CloudScan: true,
|
|
APIScan: false,
|
|
}
|
|
v := NewView(cfg)
|
|
|
|
tests := []struct {
|
|
key string
|
|
fb bool
|
|
want bool
|
|
}{
|
|
{"ai.enabled", false, true},
|
|
{"ai.cascade", false, true},
|
|
{"ai.deep", true, false},
|
|
{"ai.multi_agent", false, true},
|
|
{"silent", false, true},
|
|
{"verbose", true, false},
|
|
{"json", false, true},
|
|
{"no_brute", false, true},
|
|
{"only_active", false, true},
|
|
{"recursive", false, true},
|
|
{"cloud_scan", false, true},
|
|
{"api_scan", true, false},
|
|
{"unknown_key", true, true}, // fallback
|
|
{"unknown_key", false, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if got := v.Bool(tt.key, tt.fb); got != tt.want {
|
|
t.Errorf("Bool(%q, %v) = %v, want %v", tt.key, tt.fb, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestView_Int(t *testing.T) {
|
|
v := NewView(&Config{Concurrency: 500, Timeout: 10, RecursiveDepth: 4})
|
|
if v.Int("concurrency", 1) != 500 {
|
|
t.Errorf("concurrency wrong")
|
|
}
|
|
if v.Int("timeout", 1) != 10 {
|
|
t.Errorf("timeout wrong")
|
|
}
|
|
if v.Int("recursive.depth", 1) != 4 {
|
|
t.Errorf("recursive.depth wrong")
|
|
}
|
|
if v.Int("unknown", 99) != 99 {
|
|
t.Errorf("unknown key should return fallback")
|
|
}
|
|
}
|
|
|
|
func TestView_String(t *testing.T) {
|
|
v := NewView(&Config{
|
|
Domain: "example.com",
|
|
Wordlist: "/wl",
|
|
Output: "/out",
|
|
Format: "json",
|
|
Ports: "80,443",
|
|
Resolvers: "8.8.8.8",
|
|
StealthMode: "light",
|
|
AIUrl: "http://x",
|
|
AIFastModel: "f",
|
|
AIDeepModel: "d",
|
|
})
|
|
|
|
cases := map[string]string{
|
|
"domain": "example.com",
|
|
"wordlist": "/wl",
|
|
"output": "/out",
|
|
"format": "json",
|
|
"ports": "80,443",
|
|
"resolvers": "8.8.8.8",
|
|
"stealth": "light",
|
|
"ai.url": "http://x",
|
|
"ai.fast_model": "f",
|
|
"ai.deep_model": "d",
|
|
}
|
|
for k, want := range cases {
|
|
if got := v.String(k, "fb"); got != want {
|
|
t.Errorf("String(%q) = %q, want %q", k, got, want)
|
|
}
|
|
}
|
|
|
|
if v.String("unknown", "fb") != "fb" {
|
|
t.Error("unknown key should return fallback")
|
|
}
|
|
}
|
|
|
|
func TestView_Strings(t *testing.T) {
|
|
// Placeholder — no multi-value keys defined yet
|
|
v := NewView(&Config{})
|
|
if got := v.Strings("anything"); got != nil {
|
|
t.Errorf("expected nil, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestView_ModuleEnabled(t *testing.T) {
|
|
cfg := &Config{ModuleSettings: map[string]bool{"m1": true, "m2": false}}
|
|
v := NewView(cfg)
|
|
if !v.ModuleEnabled("m1") {
|
|
t.Error("m1 should be enabled")
|
|
}
|
|
if v.ModuleEnabled("m2") {
|
|
t.Error("m2 should be disabled (false in map)")
|
|
}
|
|
if v.ModuleEnabled("unset") {
|
|
t.Error("unset module should be false")
|
|
}
|
|
}
|
|
|
|
func TestView_ModuleEnabled_NilMap(t *testing.T) {
|
|
v := NewView(&Config{})
|
|
if v.ModuleEnabled("anything") {
|
|
t.Error("nil map should result in false")
|
|
}
|
|
}
|