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:

  • Streaming HTML tokenizer with zero-allocation hot path
  • Priority-based classification (script > attribute > html > comment)
  • Event handler detection with case-insensitive matching
  • Marker persistence across transformation and analysis phases

Closes #5838

Proposed changes

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.

Proof

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_scriptscript_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_scriptscript_wins (0.00s) — PASS: TestDetectContext/reflected_in_both_html_body_and_attribute—_attribute_wins (0.00s) — PASS: TestDetectContext/reflected_in_comment,_html,_attribute,and_scriptscript_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

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • Added XSS context detection during HTTP fuzzing, identifying reflected payloads and their specific locations (HTML text, script, attributes, comments) and reporting context-specific details.
    • Added playground endpoints and new fuzz test cases covering body, attribute, script, comment, event, and encoded scenarios.
  • Enhancements

    • Analyzer auto-registered for HTTP fuzzing and now examines response body and headers for improved accuracy.
    • Stability and concurrency improvements for randomization used in fuzzing.
  • Tests

    • Extensive unit tests and benchmarks for context detection and verification.

/claim #5838

Claim

Total prize pool $200
Total paid $0
Status Pending
Submitted February 09, 2026
Last updated February 09, 2026

Contributors

FR

FrogSnot

@FrogSnot

100%

Sponsors

PR

ProjectDiscovery

@projectdiscovery

$200