/*
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
:                                                                               :
:   █▀ █ █▀▀   ·   Blazing-fast pentesting suite                                :
:   ▄█ █ █▀    ·   BSD 3-Clause License                                         :
:                                                                               :
:   (c) 2022-2026 vmfunc, xyzeva,                                               :
:                 lunchcat alumni & contributors                                :
:                                                                               :
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
*/

package detectors

import (
	"net/http"
	"testing"

	fw "github.com/vmfunc/sif/internal/scan/frameworks"
)

// TestWAFDetectors_Positive fires each WAF detector on a marker shaped like the
// real header, Set-Cookie, or block-page body that WAF emits.
func TestWAFDetectors_Positive(t *testing.T) {
	tests := []struct {
		name     string
		detector fw.Detector
		body     string
		headers  http.Header
	}{
		{"Cloudflare mitigated header", &cloudflareWAFDetector{}, "", http.Header{"Cf-Mitigated": {"challenge"}}},
		{"Cloudflare challenge page", &cloudflareWAFDetector{}, `<title>Attention Required! | Cloudflare</title><script>window.__cf_chl_opt={};</script>`, http.Header{}},
		{"Sucuri id header", &sucuriDetector{}, "", http.Header{"X-Sucuri-Id": {"17.5"}}},
		{"Sucuri block page", &sucuriDetector{}, `<title>Sucuri WebSite Firewall - Access Denied</title>`, http.Header{"X-Sucuri-Cache": {"MISS"}}},
		{"Incapsula iinfo header", &incapsulaDetector{}, "", http.Header{"X-Iinfo": {"9-12345-0 0NNN RT(1) q(0) r(0)"}}},
		{"Incapsula cookies", &incapsulaDetector{}, "", http.Header{"Set-Cookie": {"visid_incap_123=abc; path=/", "incap_ses_456=def; path=/"}}},
		{"Incapsula incident page", &incapsulaDetector{}, `Request unsuccessful. Incapsula incident ID: 123-456`, http.Header{}},
		{"F5 BIG-IP ASM block", &bigipASMDetector{}, `<html><body>The requested URL was rejected. Please consult with your administrator.<br>Your support ID is: 987654321</body></html>`, http.Header{}},
		{"ModSecurity block page", &modSecurityDetector{}, `<p>This error was generated by Mod_Security.</p>`, http.Header{}},
		{"ModSecurity server token", &modSecurityDetector{}, `This error was generated by Mod_Security.`, http.Header{"Server": {"Apache Mod_Security"}}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			conf, _ := tt.detector.Detect(tt.body, tt.headers)
			if conf <= 0.5 {
				t.Errorf("%s: confidence = %.3f, want > 0.5", tt.name, conf)
			}
		})
	}
}

// TestWAFDetectors_Negative keeps the detectors quiet on prose that names the
// vendor and on unrelated proxy headers, so a mention or a coincidental header
// cannot trip a WAF finding.
func TestWAFDetectors_Negative(t *testing.T) {
	tests := []struct {
		name     string
		detector fw.Detector
		body     string
		headers  http.Header
	}{
		{"Cloudflare plain proxy", &cloudflareWAFDetector{}, "", http.Header{"Cf-Ray": {"8a1-EWR"}}},
		{"Cloudflare prose", &cloudflareWAFDetector{}, `<p>We compared Cloudflare and Fastly for WAF coverage.</p>`, http.Header{}},
		{"Sucuri prose", &sucuriDetector{}, `<p>Sucuri is a popular website firewall.</p>`, http.Header{}},
		{"Incapsula prose", &incapsulaDetector{}, `<p>Imperva Incapsula competes with Cloudflare.</p>`, http.Header{}},
		{"F5 unrelated body", &bigipASMDetector{}, `<p>Our support team can help with your F5 BIG-IP setup.</p>`, http.Header{}},
		{"ModSecurity prose", &modSecurityDetector{}, `<p>We enabled ModSecurity rules on the origin.</p>`, http.Header{}},
		{"plain nginx", &sucuriDetector{}, "", http.Header{"Server": {"nginx/1.25.3"}}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			conf, _ := tt.detector.Detect(tt.body, tt.headers)
			if conf > 0.5 {
				t.Errorf("%s: confidence = %.3f, want <= 0.5", tt.name, conf)
			}
		})
	}
}
