/* ·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· : : : █▀ █ █▀▀ · Blazing-fast pentesting suite : : ▄█ █ █▀ · BSD 3-Clause License : : : : (c) 2022-2025 vmfunc (Celeste Hickenlooper), xyzeva, : : lunchcat alumni & contributors : : : ·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· */ package frameworks import ( "fmt" "io" "math" "net/http" "os" "strings" "sync" "time" "github.com/charmbracelet/log" "github.com/dropalldatabases/sif/internal/styles" "github.com/dropalldatabases/sif/pkg/logger" ) type FrameworkResult struct { Name string `json:"name"` Version string `json:"version"` Confidence float32 `json:"confidence"` VersionConfidence float32 `json:"version_confidence"` CVEs []string `json:"cves,omitempty"` Suggestions []string `json:"suggestions,omitempty"` RiskLevel string `json:"risk_level,omitempty"` } type FrameworkSignature struct { Pattern string Weight float32 HeaderOnly bool } var frameworkSignatures = map[string][]FrameworkSignature{ "Laravel": { {Pattern: `laravel_session`, Weight: 0.4, HeaderOnly: true}, {Pattern: `XSRF-TOKEN`, Weight: 0.3, HeaderOnly: true}, {Pattern: ` highestConfidence { highestConfidence = match.confidence bestMatch = match.framework } } if highestConfidence > 0.5 { // threshold for detection versionMatch := extractVersionOptimized(bodyStr, bestMatch) cves, suggestions := getVulnerabilities(bestMatch, versionMatch.Version) result := &FrameworkResult{ Name: bestMatch, Version: versionMatch.Version, Confidence: highestConfidence, VersionConfidence: versionMatch.Confidence, CVEs: cves, Suggestions: suggestions, RiskLevel: getRiskLevel(cves), } if logdir != "" { logEntry := fmt.Sprintf("Detected framework: %s (version: %s, confidence: %.2f, version_confidence: %.2f)\n", bestMatch, versionMatch.Version, highestConfidence, versionMatch.Confidence) if len(cves) > 0 { logEntry += fmt.Sprintf(" Risk Level: %s\n", result.RiskLevel) logEntry += fmt.Sprintf(" CVEs: %v\n", cves) logEntry += fmt.Sprintf(" Recommendations: %v\n", suggestions) } logger.Write(url, logdir, logEntry) } frameworklog.Infof("Detected %s framework (version: %s, confidence: %.2f)", styles.Highlight.Render(bestMatch), versionMatch.Version, highestConfidence) if versionMatch.Confidence > 0 { frameworklog.Debugf("Version detected from: %s (confidence: %.2f)", versionMatch.Source, versionMatch.Confidence) } if len(cves) > 0 { frameworklog.Warnf("Risk level: %s", styles.SeverityHigh.Render(result.RiskLevel)) for _, cve := range cves { frameworklog.Warnf("Found potential vulnerability: %s", styles.Highlight.Render(cve)) } for _, suggestion := range suggestions { frameworklog.Infof("Recommendation: %s", suggestion) } } return result, nil } frameworklog.Info("No framework detected with sufficient confidence") return nil, nil } func containsHeader(headers http.Header, signature string) bool { sigLower := strings.ToLower(signature) // check header names for name := range headers { if strings.Contains(strings.ToLower(name), sigLower) { return true } } // check header values for _, values := range headers { for _, value := range values { if strings.Contains(strings.ToLower(value), sigLower) { return true } } } return false } func detectVersion(body string, framework string) string { match := extractVersionOptimized(body, framework) return match.Version } func getVulnerabilities(framework, version string) ([]string, []string) { entries, exists := knownCVEs[framework] if !exists { return nil, nil } var cves []string var recommendations []string seenRecs := make(map[string]bool) for _, entry := range entries { for _, affectedVer := range entry.AffectedVersions { if version == affectedVer || strings.HasPrefix(version, affectedVer) { cves = append(cves, fmt.Sprintf("%s (%s)", entry.CVE, entry.Severity)) for _, rec := range entry.Recommendations { if !seenRecs[rec] { recommendations = append(recommendations, rec) seenRecs[rec] = true } } break } } } return cves, recommendations } // getRiskLevel determines overall risk based on detected CVEs func getRiskLevel(cves []string) string { if len(cves) == 0 { return "low" } for _, cve := range cves { if strings.Contains(cve, "critical") { return "critical" } } for _, cve := range cves { if strings.Contains(cve, "high") { return "high" } } return "medium" } // VersionMatch represents a version detection result with confidence type VersionMatch struct { Version string Confidence float32 Source string // where the version was found } func extractVersion(body string, framework string) string { match := extractVersionOptimized(body, framework) return match.Version }