mirror of
https://github.com/Vyntral/god-eye.git
synced 2026-02-12 16:52:45 +00:00
- Implement 8 specialized AI agents (XSS, SQLi, Auth, API, Crypto, Secrets, Headers, General) - Add fast type-based routing for finding classification - Include OWASP-aligned knowledge bases per agent - Add agent handoff logic for cross-vulnerability detection - Optimize timeouts and parallelism for local LLM - Add new modules: cache, network, fingerprint, secrets, cloud, API, discovery - Update documentation with multi-agent feature
174 lines
4.2 KiB
Go
174 lines
4.2 KiB
Go
package sources
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// ErrorType categorizes source errors
|
|
type ErrorType string
|
|
|
|
const (
|
|
ErrTypeTimeout ErrorType = "timeout"
|
|
ErrTypeHTTP ErrorType = "http_error"
|
|
ErrTypeParse ErrorType = "parse_error"
|
|
ErrTypeRateLimit ErrorType = "rate_limit"
|
|
ErrTypeNetwork ErrorType = "network_error"
|
|
ErrTypeEmpty ErrorType = "empty_response"
|
|
ErrTypeUnknown ErrorType = "unknown"
|
|
)
|
|
|
|
// SourceError represents an error from a passive source
|
|
type SourceError struct {
|
|
Source string
|
|
Type ErrorType
|
|
Message string
|
|
StatusCode int
|
|
Duration time.Duration
|
|
Retryable bool
|
|
}
|
|
|
|
func (e *SourceError) Error() string {
|
|
if e.StatusCode > 0 {
|
|
return fmt.Sprintf("[%s] %s: %s (status: %d, took: %v)",
|
|
e.Type, e.Source, e.Message, e.StatusCode, e.Duration)
|
|
}
|
|
return fmt.Sprintf("[%s] %s: %s (took: %v)",
|
|
e.Type, e.Source, e.Message, e.Duration)
|
|
}
|
|
|
|
// NewTimeoutError creates a timeout error
|
|
func NewTimeoutError(source string, duration time.Duration) *SourceError {
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeTimeout,
|
|
Message: "request timed out",
|
|
Duration: duration,
|
|
Retryable: true,
|
|
}
|
|
}
|
|
|
|
// NewHTTPError creates an HTTP error
|
|
func NewHTTPError(source string, statusCode int, duration time.Duration) *SourceError {
|
|
retryable := statusCode >= 500 || statusCode == 429
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeHTTP,
|
|
Message: fmt.Sprintf("HTTP %d", statusCode),
|
|
StatusCode: statusCode,
|
|
Duration: duration,
|
|
Retryable: retryable,
|
|
}
|
|
}
|
|
|
|
// NewParseError creates a parse error
|
|
func NewParseError(source string, msg string, duration time.Duration) *SourceError {
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeParse,
|
|
Message: msg,
|
|
Duration: duration,
|
|
Retryable: false,
|
|
}
|
|
}
|
|
|
|
// NewRateLimitError creates a rate limit error
|
|
func NewRateLimitError(source string, duration time.Duration) *SourceError {
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeRateLimit,
|
|
Message: "rate limited",
|
|
Duration: duration,
|
|
Retryable: true,
|
|
}
|
|
}
|
|
|
|
// NewNetworkError creates a network error
|
|
func NewNetworkError(source string, msg string, duration time.Duration) *SourceError {
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeNetwork,
|
|
Message: msg,
|
|
Duration: duration,
|
|
Retryable: true,
|
|
}
|
|
}
|
|
|
|
// NewEmptyError creates an empty response error
|
|
func NewEmptyError(source string, duration time.Duration) *SourceError {
|
|
return &SourceError{
|
|
Source: source,
|
|
Type: ErrTypeEmpty,
|
|
Message: "empty response",
|
|
Duration: duration,
|
|
Retryable: false,
|
|
}
|
|
}
|
|
|
|
// SourceResult represents the result from a passive source
|
|
type SourceResult struct {
|
|
Source string
|
|
Subdomains []string
|
|
Error *SourceError
|
|
Duration time.Duration
|
|
Cached bool
|
|
}
|
|
|
|
// IsSuccess returns true if the result has no error
|
|
func (r *SourceResult) IsSuccess() bool {
|
|
return r.Error == nil
|
|
}
|
|
|
|
// Count returns the number of subdomains found
|
|
func (r *SourceResult) Count() int {
|
|
return len(r.Subdomains)
|
|
}
|
|
|
|
// SourceStats tracks statistics for all sources
|
|
type SourceStats struct {
|
|
TotalSources int
|
|
SuccessSources int
|
|
FailedSources int
|
|
TotalFound int
|
|
TotalDuration time.Duration
|
|
Errors []*SourceError
|
|
}
|
|
|
|
// AddResult adds a result to the stats
|
|
func (s *SourceStats) AddResult(result *SourceResult) {
|
|
s.TotalSources++
|
|
s.TotalDuration += result.Duration
|
|
|
|
if result.IsSuccess() {
|
|
s.SuccessSources++
|
|
s.TotalFound += result.Count()
|
|
} else {
|
|
s.FailedSources++
|
|
s.Errors = append(s.Errors, result.Error)
|
|
}
|
|
}
|
|
|
|
// SuccessRate returns the percentage of successful sources
|
|
func (s *SourceStats) SuccessRate() float64 {
|
|
if s.TotalSources == 0 {
|
|
return 0
|
|
}
|
|
return float64(s.SuccessSources) / float64(s.TotalSources) * 100
|
|
}
|
|
|
|
// Summary returns a human-readable summary
|
|
func (s *SourceStats) Summary() string {
|
|
return fmt.Sprintf("%d/%d sources succeeded (%.0f%%), found %d subdomains in %v",
|
|
s.SuccessSources, s.TotalSources, s.SuccessRate(),
|
|
s.TotalFound, s.TotalDuration.Round(time.Millisecond))
|
|
}
|
|
|
|
// ErrorsByType returns errors grouped by type
|
|
func (s *SourceStats) ErrorsByType() map[ErrorType][]*SourceError {
|
|
result := make(map[ErrorType][]*SourceError)
|
|
for _, err := range s.Errors {
|
|
result[err.Type] = append(result[err.Type], err)
|
|
}
|
|
return result
|
|
}
|