Files
god-eye/internal/sources/errors.go
Vyntral 14c26dc726 feat: Add Multi-Agent AI Orchestration with 8 specialized agents
- 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
2025-11-21 15:23:11 +01:00

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
}