Files
god-eye/internal/scanner/javascript.go
Vyntral 14718dd75f 🚀 God's Eye v0.1 - Initial Release
God's Eye is an ultra-fast subdomain enumeration and reconnaissance tool with AI-powered security analysis.

##  Key Features

### 🔍 Comprehensive Enumeration
- 20+ passive sources (crt.sh, Censys, URLScan, etc.)
- DNS brute-force with smart wordlists
- Wildcard detection and filtering
- 1000 concurrent workers for maximum speed

### 🌐 Deep Reconnaissance
- HTTP probing with 13+ security checks
- Port scanning (configurable)
- TLS/SSL fingerprinting
- Technology detection (Wappalyzer-style)
- WAF detection (Cloudflare, Akamai, etc.)
- Security header analysis
- JavaScript secrets extraction
- Admin panel & API discovery
- Backup file detection
- robots.txt & sitemap.xml checks

### 🎯 Subdomain Takeover Detection
- 110+ fingerprints (AWS, Azure, GitHub Pages, Heroku, etc.)
- CNAME validation
- Dead DNS detection

### 🤖 AI-Powered Analysis (NEW!)
- Local AI using Ollama - No API costs, complete privacy
- Real-time CVE detection via function calling (queries NVD database)
- Cascade architecture: phi3.5 (fast triage) + qwen2.5-coder (deep analysis)
- JavaScript security analysis
- HTTP response anomaly detection
- Executive summary reports

### 📊 Output Formats
- Pretty terminal output with colors
- JSON export
- CSV export
- TXT (simple subdomain list)
- Silent mode for piping

## 🚀 Installation

bash
go install github.com/Vyntral/god-eye@latest

## 📖 Quick Start

bash
# Basic scan
god-eye -d example.com

# With AI analysis
god-eye -d example.com --enable-ai

# Only active hosts
god-eye -d example.com --active

# Export to JSON
god-eye -d example.com -o results.json -f json

## 🎯 Use Cases
- Bug bounty reconnaissance
- Penetration testing
- Security audits
- Attack surface mapping
- Red team operations

## ⚠️ Legal Notice
This tool is for authorized security testing only. Users must obtain explicit permission before scanning any targets. Unauthorized access is illegal.

## 📄 License
MIT License with additional security tool terms - see LICENSE file

## 🙏 Credits
Built with ❤️ by Vyntral for Orizon
Powered by Go, Ollama, and the security community

---

🤖 Generated with Claude Code
https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 10:41:05 +01:00

165 lines
4.5 KiB
Go

package scanner
import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
)
// AnalyzeJSFiles finds JavaScript files and extracts potential secrets
func AnalyzeJSFiles(subdomain string, client *http.Client) ([]string, []string) {
var jsFiles []string
var secrets []string
urls := []string{
fmt.Sprintf("https://%s", subdomain),
fmt.Sprintf("http://%s", subdomain),
}
// Common JS file paths
jsPaths := []string{
"/main.js", "/app.js", "/bundle.js", "/vendor.js",
"/static/js/main.js", "/static/js/app.js",
"/assets/js/app.js", "/js/main.js", "/js/app.js",
"/dist/main.js", "/dist/bundle.js",
"/_next/static/chunks/main.js",
"/build/static/js/main.js",
}
// Secret patterns to search for
secretPatterns := []*regexp.Regexp{
regexp.MustCompile(`(?i)['"]?api[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9_\-]{20,})['"]`),
regexp.MustCompile(`(?i)['"]?aws[_-]?access[_-]?key[_-]?id['"]?\s*[:=]\s*['"]([A-Z0-9]{20})['"]`),
regexp.MustCompile(`(?i)['"]?aws[_-]?secret[_-]?access[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9/+=]{40})['"]`),
regexp.MustCompile(`(?i)['"]?google[_-]?api[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9_\-]{39})['"]`),
regexp.MustCompile(`(?i)['"]?firebase[_-]?api[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9_\-]{39})['"]`),
regexp.MustCompile(`(?i)['"]?stripe[_-]?(publishable|secret)[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9_\-]{20,})['"]`),
regexp.MustCompile(`(?i)['"]?github[_-]?token['"]?\s*[:=]\s*['"]([a-zA-Z0-9_]{36,})['"]`),
regexp.MustCompile(`(?i)['"]?slack[_-]?token['"]?\s*[:=]\s*['"]([a-zA-Z0-9\-]{30,})['"]`),
regexp.MustCompile(`(?i)['"]?private[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9/+=]{50,})['"]`),
regexp.MustCompile(`(?i)['"]?secret['"]?\s*[:=]\s*['"]([a-zA-Z0-9_\-]{20,})['"]`),
regexp.MustCompile(`(?i)['"]?password['"]?\s*[:=]\s*['"]([^'"]{8,})['"]`),
regexp.MustCompile(`(?i)['"]?authorization['"]?\s*[:=]\s*['"]Bearer\s+([a-zA-Z0-9_\-\.]+)['"]`),
}
// Also search for API endpoints in JS
endpointPatterns := []*regexp.Regexp{
regexp.MustCompile(`(?i)['"]https?://[a-zA-Z0-9\-\.]+/api/[a-zA-Z0-9/\-_]+['"]`),
regexp.MustCompile(`(?i)['"]https?://api\.[a-zA-Z0-9\-\.]+[a-zA-Z0-9/\-_]*['"]`),
}
for _, baseURL := range urls {
// First, get the main page and extract JS file references
resp, err := client.Get(baseURL)
if err != nil {
continue
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 500000))
resp.Body.Close()
if err != nil {
continue
}
// Find JS files referenced in HTML
jsRe := regexp.MustCompile(`src=["']([^"']*\.js[^"']*)["']`)
matches := jsRe.FindAllStringSubmatch(string(body), -1)
for _, match := range matches {
if len(match) > 1 {
jsURL := match[1]
if !strings.HasPrefix(jsURL, "http") {
if strings.HasPrefix(jsURL, "/") {
jsURL = baseURL + jsURL
} else {
jsURL = baseURL + "/" + jsURL
}
}
jsFiles = append(jsFiles, jsURL)
}
}
// Also check common JS paths
for _, path := range jsPaths {
testURL := baseURL + path
resp, err := client.Get(testURL)
if err != nil {
continue
}
if resp.StatusCode == 200 {
jsFiles = append(jsFiles, path)
// Read JS content and search for secrets
jsBody, err := io.ReadAll(io.LimitReader(resp.Body, 500000))
resp.Body.Close()
if err != nil {
continue
}
jsContent := string(jsBody)
// Search for secrets
for _, pattern := range secretPatterns {
if matches := pattern.FindAllStringSubmatch(jsContent, 3); len(matches) > 0 {
for _, m := range matches {
if len(m) > 1 {
secret := m[0]
if len(secret) > 60 {
secret = secret[:57] + "..."
}
secrets = append(secrets, secret)
}
}
}
}
// Search for API endpoints
for _, pattern := range endpointPatterns {
if matches := pattern.FindAllString(jsContent, 5); len(matches) > 0 {
for _, m := range matches {
if len(m) > 60 {
m = m[:57] + "..."
}
secrets = append(secrets, "endpoint: "+m)
}
}
}
} else {
resp.Body.Close()
}
}
if len(jsFiles) > 0 || len(secrets) > 0 {
break
}
}
// Deduplicate and limit
jsFiles = UniqueStrings(jsFiles)
secrets = UniqueStrings(secrets)
if len(jsFiles) > 10 {
jsFiles = jsFiles[:10]
}
if len(secrets) > 10 {
secrets = secrets[:10]
}
return jsFiles, secrets
}
// UniqueStrings returns unique strings from a slice
func UniqueStrings(input []string) []string {
seen := make(map[string]bool)
var result []string
for _, s := range input {
if !seen[s] {
seen[s] = true
result = append(result, s)
}
}
return result
}