Files
god-eye/internal/output/print.go
T
Vyntral 3a4c230aa7 feat: v2.0 full rewrite — event-driven pipeline, AI + Nuclei + proxy
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.
2026-04-18 16:48:41 +02:00

147 lines
5.0 KiB
Go

package output
import (
"encoding/csv"
"encoding/json"
"fmt"
"os"
"sort"
"strconv"
"strings"
"github.com/fatih/color"
"god-eye/internal/config"
)
var (
// Basic colors
Green = color.New(color.FgGreen).SprintFunc()
Red = color.New(color.FgRed).SprintFunc()
Blue = color.New(color.FgBlue).SprintFunc()
Yellow = color.New(color.FgYellow).SprintFunc()
Cyan = color.New(color.FgCyan).SprintFunc()
Magenta = color.New(color.FgMagenta).SprintFunc()
White = color.New(color.FgWhite).SprintFunc()
// Bold variants
BoldGreen = color.New(color.FgGreen, color.Bold).SprintFunc()
BoldRed = color.New(color.FgRed, color.Bold).SprintFunc()
BoldCyan = color.New(color.FgCyan, color.Bold).SprintFunc()
BoldYellow = color.New(color.FgYellow, color.Bold).SprintFunc()
BoldMagenta = color.New(color.FgMagenta, color.Bold).SprintFunc()
BoldWhite = color.New(color.FgWhite, color.Bold).SprintFunc()
// Dim/faint
Dim = color.New(color.Faint).SprintFunc()
// Background highlights
BgRed = color.New(color.BgRed, color.FgWhite, color.Bold).SprintFunc()
BgGreen = color.New(color.BgGreen, color.FgBlack, color.Bold).SprintFunc()
BgYellow = color.New(color.BgYellow, color.FgBlack, color.Bold).SprintFunc()
)
func PrintBanner() {
fmt.Println()
fmt.Println(BoldWhite(" ██████╗ ██████╗ ██████╗ ") + BoldGreen("███████╗") + BoldWhite(" ███████╗██╗ ██╗███████╗"))
fmt.Println(BoldWhite(" ██╔════╝ ██╔═══██╗██╔══██╗") + BoldGreen("██╔════╝") + BoldWhite(" ██╔════╝╚██╗ ██╔╝██╔════╝"))
fmt.Println(BoldWhite(" ██║ ███╗██║ ██║██║ ██║") + BoldGreen("███████╗") + BoldWhite(" █████╗ ╚████╔╝ █████╗ "))
fmt.Println(BoldWhite(" ██║ ██║██║ ██║██║ ██║") + BoldGreen("╚════██║") + BoldWhite(" ██╔══╝ ╚██╔╝ ██╔══╝ "))
fmt.Println(BoldWhite(" ╚██████╔╝╚██████╔╝██████╔╝") + BoldGreen("███████║") + BoldWhite(" ███████╗ ██║ ███████╗"))
fmt.Println(BoldWhite(" ╚═════╝ ╚═════╝ ╚═════╝ ") + BoldGreen("╚══════╝") + BoldWhite(" ╚══════╝ ╚═╝ ╚══════╝"))
fmt.Println()
fmt.Printf(" %s %s\n", BoldGreen("⚡"), Dim("AI-powered attack surface discovery & offensive security analysis"))
fmt.Printf(" %s %s %s %s\n",
Dim("Version:"), BoldGreen("2.0.0-rc1"),
Dim("By:"), White("github.com/Vyntral"))
fmt.Println()
}
func PrintSection(icon, title string) {
fmt.Println()
fmt.Printf(" %s %s\n", icon, BoldWhite(title))
fmt.Printf(" %s\n", Dim(strings.Repeat("─", 50)))
}
func PrintSubSection(text string) {
fmt.Printf(" %s\n", text)
}
func PrintEndSection() {
// No more lines, just spacing
}
func PrintProgress(current, total int, label string) {
width := 30
filled := int(float64(current) / float64(total) * float64(width))
if filled > width {
filled = width
}
bar := strings.Repeat("█", filled) + strings.Repeat("░", width-filled)
percent := float64(current) / float64(total) * 100
fmt.Printf("\r %s %s %s %.0f%% ", label, Green(bar), Dim(fmt.Sprintf("(%d/%d)", current, total)), percent)
}
func ClearLine() {
fmt.Print("\r\033[K")
}
func SaveOutput(path string, format string, results map[string]*config.SubdomainResult) {
file, err := os.Create(path)
if err != nil {
fmt.Printf("%s Failed to create output file: %v\n", Red("[-]"), err)
return
}
defer file.Close()
// Sort subdomains for consistent output
var sortedSubs []string
for sub := range results {
sortedSubs = append(sortedSubs, sub)
}
sort.Strings(sortedSubs)
switch format {
case "json":
var resultList []*config.SubdomainResult
for _, sub := range sortedSubs {
resultList = append(resultList, results[sub])
}
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(resultList)
case "csv":
writer := csv.NewWriter(file)
// Header
writer.Write([]string{"subdomain", "ips", "status_code", "title", "server", "technologies", "ports", "takeover", "response_ms"})
for _, sub := range sortedSubs {
r := results[sub]
var portStrs []string
for _, p := range r.Ports {
portStrs = append(portStrs, strconv.Itoa(p))
}
writer.Write([]string{
r.Subdomain,
strings.Join(r.IPs, ";"),
strconv.Itoa(r.StatusCode),
r.Title,
r.Server,
strings.Join(r.Tech, ";"),
strings.Join(portStrs, ";"),
r.Takeover,
strconv.FormatInt(r.ResponseMs, 10),
})
}
writer.Flush()
default: // txt
for _, sub := range sortedSubs {
file.WriteString(sub + "\n")
}
}
fmt.Printf("\n %s Results saved to %s\n", Green("✓"), path)
}