From 78a385d4f42ee2da6d637edb28d960ef391a0a0b Mon Sep 17 00:00:00 2001 From: Celeste Hickenlooper Date: Fri, 2 Jan 2026 19:04:37 -0800 Subject: [PATCH] fix: improve version detection and add documentation - fix version detection to validate reasonable version numbers (major < 100) - remove overly permissive patterns that caused false positives - add comprehensive framework contribution documentation to CONTRIBUTING.md - document signature patterns, version detection, and CVE data format - add configuration documentation for flags and env vars - outline future enhancements for community contributions --- CONTRIBUTING.md | 92 +++++++++++++++++++++++++++++++++++ pkg/scan/frameworks/detect.go | 38 +++++++++++---- 2 files changed, 121 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da72596..0053311 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,6 +53,98 @@ When making a pull request, please adhere to the following conventions: If you have any questions, feel free to ask around on the IRC channel. +## Contributing Framework Detection Patterns + +The framework detection module (`pkg/scan/frameworks/detect.go`) identifies web frameworks by analyzing HTTP headers and response bodies. To add support for a new framework: + +### Adding a New Framework Signature + +1. Add your framework to the `frameworkSignatures` map: + +```go +"MyFramework": { + {Pattern: `unique-identifier`, Weight: 0.5}, + {Pattern: `header-signature`, Weight: 0.4, HeaderOnly: true}, + {Pattern: `body-signature`, Weight: 0.3}, +}, +``` + +**Pattern Guidelines:** +- `Weight`: How much this signature contributes to detection (0.0-1.0) +- `HeaderOnly`: Set to `true` for HTTP header patterns +- Use unique identifiers that won't false-positive on other frameworks +- Include multiple patterns for higher confidence + +### Adding Version Detection + +Add version patterns to `extractVersionWithConfidence()`: + +```go +"MyFramework": { + {`MyFramework[/\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)`, 0.9, "explicit version"}, + {`"myframework":\s*"[~^]?(\d+\.\d+(?:\.\d+)?)"`, 0.85, "package.json"}, +}, +``` + +### Adding CVE Data + +Add known vulnerabilities to the `knownCVEs` map: + +```go +"MyFramework": { + { + CVE: "CVE-YYYY-XXXXX", + AffectedVersions: []string{"1.0.0", "1.0.1", "1.1.0"}, + FixedVersion: "1.2.0", + Severity: "high", // critical, high, medium, low + Description: "Brief description of the vulnerability", + Recommendations: []string{"Update to 1.2.0 or later"}, + }, +}, +``` + +### Testing Your Changes + +Always add tests for new frameworks in `detect_test.go`: + +```go +func TestDetectFramework_MyFramework(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`unique-identifier`)) + })) + defer server.Close() + + result, err := DetectFramework(server.URL, 5*time.Second, "") + // assertions... +} +``` + +### Future Enhancements (Help Wanted) + +- **Custom Signature Support**: Allow users to define signatures via config file +- **CVE API Integration**: Real-time CVE data from NVD or other sources +- **Automated Signature Updates**: Fetch new signatures from a central repository +- **Framework Fingerprint Database**: Community-maintained signature database + +## Configuration + +### Framework Detection Flags + +| Flag | Description | +|------|-------------| +| `-framework` | Enable framework detection | +| `-timeout` | HTTP request timeout (affects all modules) | +| `-threads` | Number of concurrent workers | +| `-log` | Directory to save scan results | +| `-debug` | Enable debug logging for verbose output | + +### Environment Variables + +| Variable | Description | +|----------|-------------| +| `SHODAN_API_KEY` | API key for Shodan host intelligence | + ## Packaging We'd love it if you helped us bring sif to your distribution. diff --git a/pkg/scan/frameworks/detect.go b/pkg/scan/frameworks/detect.go index ee7cc29..ff83239 100644 --- a/pkg/scan/frameworks/detect.go +++ b/pkg/scan/frameworks/detect.go @@ -618,6 +618,24 @@ type VersionMatch struct { Source string // where the version was found } +// isValidVersion checks if a version string looks reasonable +func isValidVersion(version string) bool { + if version == "" || version == "unknown" { + return false + } + // parse major version and check if reasonable (< 100) + parts := strings.Split(version, ".") + if len(parts) < 2 { + return false + } + var major int + _, err := fmt.Sscanf(parts[0], "%d", &major) + if err != nil || major >= 100 || major < 0 { + return false + } + return true +} + func extractVersion(body string, framework string) string { match := extractVersionWithConfidence(body, framework) return match.Version @@ -665,9 +683,8 @@ func extractVersionWithConfidence(body string, framework string) VersionMatch { {`Werkzeug[/\s]+(\d+\.\d+(?:\.\d+)?)`, 0.7, "werkzeug version"}, }, "Next.js": { - {`Next\.js[/\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)`, 0.9, "explicit version"}, - {`"next":\s*"[~^]?(\d+\.\d+(?:\.\d+)?)"`, 0.85, "package.json"}, - {`_next/static/.*?(\d+\.\d+(?:\.\d+)?)`, 0.6, "static path"}, + {`Next\.js[/\s]+[Vv]?(\d{1,2}\.\d+(?:\.\d+)?)`, 0.9, "explicit version"}, + {`"next":\s*"[~^]?(\d{1,2}\.\d+(?:\.\d+)?)"`, 0.85, "package.json"}, }, "Nuxt.js": { {`Nuxt[/\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)`, 0.9, "explicit version"}, @@ -696,9 +713,8 @@ func extractVersionWithConfidence(body string, framework string) VersionMatch { {`"@sveltejs/kit":\s*"[~^]?(\d+\.\d+(?:\.\d+)?)"`, 0.85, "package.json"}, }, "WordPress": { - {`WordPress[/\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)`, 0.9, "explicit version"}, - {`wp-includes/version\.php.*?(\d+\.\d+(?:\.\d+)?)`, 0.85, "version.php"}, {` 1 && p.confidence > bestMatch.Confidence { - bestMatch = VersionMatch{ - Version: matches[1], - Confidence: p.confidence, - Source: p.source, + candidate := matches[1] + // validate version looks reasonable + if isValidVersion(candidate) { + bestMatch = VersionMatch{ + Version: candidate, + Confidence: p.confidence, + Source: p.source, + } } } }