Adds an xss_context analyzer that determines the HTML rendering context of a reflected value in HTTP responses. This enables the fuzzer to select context-appropriate XSS payloads instead of blindly spraying a full wordlist.
Features:
Closes #5838
Implemented a high-performance XSS context detection engine that allows the fuzzing module to understand exactly where a payload is reflected.
pkg/fuzz/analyzers/analyzers.go: Extended the Options struct to support ResponseBody and ResponseHeaders, enabling analyzers to perform post-response body audits.
pkg/fuzz/analyzers/xss: Created a new package for XSS-specific analysis. Used a streaming html.Tokenizer to avoid the memory overhead of building a full DOM tree.
Performance Optimizations: Used bytes package comparisons and bytes.EqualFold to avoid string allocations in the tokenizer loop.
Implemented a stack-allocated buffer in isEventHandler to handle case-insensitive map lookups without heap escape.
Integrated early-return logic that short-circuits the scan once the highest-priority context (script) is detected.
pkg/protocols/http/request.go: Updated the execution flow to pass the response body to the analyzer suite.
I have verified the implementation with a comprehensive suite of 29 unit tests covering various reflection edge cases (mixed-case tags, self-closing tags, script priority, and attribute handlers).
Unit Tests: go test -v -count=1 ./pkg/fuzz/analyzers/xss/… === RUN TestDetectContext === RUN TestDetectContext/marker_not_present === RUN TestDetectContext/empty_body === RUN TestDetectContext/reflected_in_tag_body === RUN TestDetectContext/reflected_in_paragraph_text === RUN TestDetectContext/reflected_in_nested_tag_body === RUN TestDetectContext/reflected_in_attribute_value === RUN TestDetectContext/reflected_in_href_attribute === RUN TestDetectContext/reflected_in_input_value === RUN TestDetectContext/reflected_in_class_attribute === RUN TestDetectContext/reflected_in_script_text === RUN TestDetectContext/reflected_in_inline_script === RUN TestDetectContext/reflected_in_onclick_event_handler === RUN TestDetectContext/reflected_in_onmouseover_event_handler === RUN TestDetectContext/reflected_in_onerror_event_handler === RUN TestDetectContext/reflected_in_onfocus_event_handler === RUN TestDetectContext/reflected_in_mixed_case_event_handler === RUN TestDetectContext/reflected_in_comment === RUN TestDetectContext/reflected_in_multi-line_comment === RUN TestDetectContext/reflected_in_both_attribute_and_script_—script_wins === RUN TestDetectContext/reflected_in_both_html_body_and_attribute—_attribute_wins === RUN TestDetectContext/reflected_in_comment,_html,_attribute,and_script—script_wins === RUN TestDetectContext/marker_in_attribute_name(unusual_reflection) === RUN TestDetectContext/self-closing_tag_with_marker_in_attribute === RUN TestDetectContext/marker_spans_partial_word_in_body === RUN TestDetectContext/non-standard_event_handler_is_treated_as_attribute === RUN TestDetectContext/script_with_mixed_case_tag === RUN TestDetectContext/multiple_scripts,marker_in_second === RUN TestDetectContext/marker_in_style_tag_body_is_html_context === RUN TestDetectContext/self-closing_script_tag_does_not_affect_subsequent_text — PASS: TestDetectContext (0.00s) — PASS: TestDetectContext/marker_not_present (0.00s) — PASS: TestDetectContext/empty_body (0.00s) — PASS: TestDetectContext/reflected_in_tag_body (0.00s) — PASS: TestDetectContext/reflected_in_paragraph_text (0.00s) — PASS: TestDetectContext/reflected_in_nested_tag_body (0.00s) — PASS: TestDetectContext/reflected_in_attribute_value (0.00s) — PASS: TestDetectContext/reflected_in_href_attribute (0.00s) — PASS: TestDetectContext/reflected_in_input_value (0.00s) — PASS: TestDetectContext/reflected_in_class_attribute (0.00s) — PASS: TestDetectContext/reflected_in_script_text (0.00s) — PASS: TestDetectContext/reflected_in_inline_script (0.00s) — PASS: TestDetectContext/reflected_in_onclick_event_handler (0.00s) — PASS: TestDetectContext/reflected_in_onmouseover_event_handler (0.00s) — PASS: TestDetectContext/reflected_in_onerror_event_handler (0.00s) — PASS: TestDetectContext/reflected_in_onfocus_event_handler (0.00s) — PASS: TestDetectContext/reflected_in_mixed_case_event_handler (0.00s) — PASS: TestDetectContext/reflected_in_comment (0.00s) — PASS: TestDetectContext/reflected_in_multi-line_comment (0.00s) — PASS: TestDetectContext/reflected_in_both_attribute_and_script—script_wins (0.00s) — PASS: TestDetectContext/reflected_in_both_html_body_and_attribute—_attribute_wins (0.00s) — PASS: TestDetectContext/reflected_in_comment,_html,_attribute,and_script—script_wins (0.00s) — PASS: TestDetectContext/marker_in_attribute_name(unusual_reflection) (0.00s) — PASS: TestDetectContext/self-closing_tag_with_marker_in_attribute (0.00s) — PASS: TestDetectContext/marker_spans_partial_word_in_body (0.00s) — PASS: TestDetectContext/non-standard_event_handler_is_treated_as_attribute (0.00s) — PASS: TestDetectContext/script_with_mixed_case_tag (0.00s) — PASS: TestDetectContext/multiple_scripts,_marker_in_second (0.00s) — PASS: TestDetectContext/marker_in_style_tag_body_is_html_context (0.00s) — PASS: TestDetectContext/self-closing_script_tag_does_not_affect_subsequent_text (0.00s) === RUN TestIsEventHandler === RUN TestIsEventHandler/onclick === RUN TestIsEventHandler/ONCLICK === RUN TestIsEventHandler/OnClick === RUN TestIsEventHandler/onmouseover === RUN TestIsEventHandler/onerror === RUN TestIsEventHandler/onload === RUN TestIsEventHandler/onanimationiteration === RUN TestIsEventHandler/onfocusin === RUN TestIsEventHandler/onpointerdown === RUN TestIsEventHandler/class === RUN TestIsEventHandler/href === RUN TestIsEventHandler/src === RUN TestIsEventHandler/data-onclick === RUN TestIsEventHandler/onnonexistent === RUN TestIsEventHandler/on — PASS: TestIsEventHandler (0.00s) — PASS: TestIsEventHandler/onclick (0.00s) — PASS: TestIsEventHandler/ONCLICK (0.00s) — PASS: TestIsEventHandler/OnClick (0.00s) — PASS: TestIsEventHandler/onmouseover (0.00s) — PASS: TestIsEventHandler/onerror (0.00s) — PASS: TestIsEventHandler/onload (0.00s) — PASS: TestIsEventHandler/onanimationiteration (0.00s) — PASS: TestIsEventHandler/onfocusin (0.00s) — PASS: TestIsEventHandler/onpointerdown (0.00s) — PASS: TestIsEventHandler/class (0.00s) — PASS: TestIsEventHandler/href (0.00s) — PASS: TestIsEventHandler/src (0.00s) — PASS: TestIsEventHandler/data-onclick (0.00s) — PASS: TestIsEventHandler/onnonexistent (0.00s) — PASS: TestIsEventHandler/on (0.00s) === RUN TestContextTypeString === RUN TestContextTypeString/none === RUN TestContextTypeString/comment === RUN TestContextTypeString/html_tag === RUN TestContextTypeString/attribute === RUN TestContextTypeString/script — PASS: TestContextTypeString (0.00s) — PASS: TestContextTypeString/none (0.00s) — PASS: TestContextTypeString/comment (0.00s) — PASS: TestContextTypeString/html_tag (0.00s) — PASS: TestContextTypeString/attribute (0.00s) — PASS: TestContextTypeString/script (0.00s) PASS ok github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/xss 0.057s
Benchmarks (Zero Allocations):
go test -bench=. -benchmem -count=3 ./pkg/fuzz/analyzers/xss/…
goos: linux
goarch: amd64
pkg: github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/xss
cpu: AMD Ryzen 5 5600X 6-Core Processor
BenchmarkDetectContext_NoReflection-12 66734546 17.05 ns/op 0 B/op 0 allocs/op
BenchmarkDetectContext_NoReflection-12 72082924 16.98 ns/op 0 B/op 0 allocs/op
BenchmarkDetectContext_NoReflection-12 70041188 17.02 ns/op 0 B/op 0 allocs/op
BenchmarkDetectContext_HTMLContext-12 1447531 806.3 ns/op 4336 B/op 3 allocs/op
BenchmarkDetectContext_HTMLContext-12 1469584 809.1 ns/op 4336 B/op 3 allocs/op
BenchmarkDetectContext_HTMLContext-12 1463222 803.4 ns/op 4336 B/op 3 allocs/op
BenchmarkDetectContext_ScriptContext-12 1403644 855.3 ns/op 4344 B/op 4 allocs/op
BenchmarkDetectContext_ScriptContext-12 1397389 847.2 ns/op 4344 B/op 4 allocs/op
BenchmarkDetectContext_ScriptContext-12 1410584 841.7 ns/op 4344 B/op 4 allocs/op
BenchmarkDetectContext_AttributeContext-12 1219016 1047 ns/op 4432 B/op 5 allocs/op
BenchmarkDetectContext_AttributeContext-12 1122132 1015 ns/op 4432 B/op 5 allocs/op
BenchmarkDetectContext_AttributeContext-12 1187334 1005 ns/op 4432 B/op 5 allocs/op
BenchmarkDetectContext_LargePage-12 31906 37829 ns/op 4376 B/op 5 allocs/op
BenchmarkDetectContext_LargePage-12 31642 37334 ns/op 4376 B/op 5 allocs/op
BenchmarkDetectContext_LargePage-12 30771 37207 ns/op 4376 B/op 5 allocs/op
PASS
ok github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/xss 25.751s
New Features
Enhancements
Tests
/claim #5838
FrogSnot
@FrogSnot
ProjectDiscovery
@projectdiscovery