Proposed changes

Implements a new xss_context analyzer for the fuzzing engine that classifies reflected XSS injection points by their HTML context and determines exploitability. Closes #5838.

How it works

  1. Canary injection: Replaces [XSS_CANARY] in payloads with a canary string containing HTML/JS-significant probe characters: `<>’”``
  2. Reflection detection: Finds all occurrences of the canary in the HTTP response body (case-insensitive)
  3. Context classification: Classifies each reflection into one of 11 context types:
    • html-body — between HTML tags (<div>VALUE</div>)
    • html-attr-double-quoted — inside "..." attribute
    • html-attr-single-quoted — inside '...' attribute
    • html-attr-unquoted — unquoted attribute value
    • script-block — inside <script> outside strings
    • script-string-double — inside JS "..." string
    • script-string-single — inside JS '...' string
    • script-template — inside JS template literal
    • html-comment — inside <!-- ... -->
    • style-block — inside <style> tag
    • url-attribute — in href/src/action attributes
  4. Exploitability check: Verifies whether context-specific breakout characters survive encoding (e.g., <> for html-body, " for double-quoted attributes)

Architecture

  • New package pkg/fuzz/analyzers/xss/ with three files:
    • analyzer.go — implements analyzers.Analyzer interface, registered as xss_context
    • context.go — HTML context classification engine using heuristic quote-tracking
    • context_test.go — 25 comprehensive tests
  • Modified pkg/fuzz/analyzers/analyzers.go — added ResponseBody and ResponseHeaders to Options
  • Modified pkg/protocols/http/request.go — passes response body/headers to analyzer
  • Modified pkg/protocols/http/http.go — blank import for auto-registration

Design decisions

  • Uses heuristic quote-tracking instead of Go’s html.Tokenizer for context classification because the canary itself contains HTML-breaking characters (<>'\") that would cause a standard tokenizer to misparse the document
  • Follows the same registration pattern as the existing time_delay analyzer
  • Reuses the response body already available in the request execution path (no extra HTTP requests needed when ResponseBody is provided via Options)
  • Falls back to issuing its own request when ResponseBody is not available
  • Applies io.LimitReader (10 MB cap) to prevent memory exhaustion from large response bodies
  • Restores component state after mutation via defer

Example template usage

http:
- method: GET
path:
- "{{BaseURL}}/search?q=[XSS_CANARY]"
fuzzing:
- type: query
part: value
mode: single
fuzz:
- "[XSS_CANARY]"
analyzer:
name: xss_context

Proof

All 25 tests pass with go vet clean:

$ go vet ./pkg/fuzz/analyzers/xss/...
$ go test ./pkg/fuzz/analyzers/xss/... -v -count=1
=== RUN TestClassifyReflections_HTMLBody
--- PASS: TestClassifyReflections_HTMLBody (0.00s)
=== RUN TestClassifyReflections_AttrDoubleQuoted
--- PASS: TestClassifyReflections_AttrDoubleQuoted (0.00s)
=== RUN TestClassifyReflections_AttrSingleQuoted
--- PASS: TestClassifyReflections_AttrSingleQuoted (0.00s)
=== RUN TestClassifyReflections_AttrUnquoted
--- PASS: TestClassifyReflections_AttrUnquoted (0.00s)
=== RUN TestClassifyReflections_ScriptBlock
--- PASS: TestClassifyReflections_ScriptBlock (0.00s)
=== RUN TestClassifyReflections_ScriptStringDouble
--- PASS: TestClassifyReflections_ScriptStringDouble (0.00s)
=== RUN TestClassifyReflections_ScriptStringSingle
--- PASS: TestClassifyReflections_ScriptStringSingle (0.00s)
=== RUN TestClassifyReflections_ScriptTemplateLiteral
--- PASS: TestClassifyReflections_ScriptTemplateLiteral (0.00s)
=== RUN TestClassifyReflections_HTMLComment
--- PASS: TestClassifyReflections_HTMLComment (0.00s)
=== RUN TestClassifyReflections_StyleBlock
--- PASS: TestClassifyReflections_StyleBlock (0.00s)
=== RUN TestClassifyReflections_URLAttribute
--- PASS: TestClassifyReflections_URLAttribute (0.00s)
=== RUN TestClassifyReflections_MultipleReflections
--- PASS: TestClassifyReflections_MultipleReflections (0.00s)
=== RUN TestClassifyReflections_NoReflection
--- PASS: TestClassifyReflections_NoReflection (0.00s)
=== RUN TestClassifyReflections_EmptyInputs
--- PASS: TestClassifyReflections_EmptyInputs (0.00s)
=== RUN TestClassifyReflections_CaseInsensitive
--- PASS: TestClassifyReflections_CaseInsensitive (0.00s)
=== RUN TestIsExploitable_HTMLBody
--- PASS: TestIsExploitable_HTMLBody (0.00s)
=== RUN TestIsExploitable_HTMLBodyEncoded
--- PASS: TestIsExploitable_HTMLBodyEncoded (0.00s)
=== RUN TestIsExploitable_AttrDoubleQuoted
--- PASS: TestIsExploitable_AttrDoubleQuoted (0.00s)
=== RUN TestIsExploitable_ScriptBlock
--- PASS: TestIsExploitable_ScriptBlock (0.00s)
=== RUN TestBuildCanary
--- PASS: TestBuildCanary (0.00s)
=== RUN TestBuildCanary_CustomPrefix
--- PASS: TestBuildCanary_CustomPrefix (0.00s)
=== RUN TestContextString
--- PASS: TestContextString (0.00s)
=== RUN TestClassifyJSContext
--- PASS: TestClassifyJSContext (0.00s)
=== RUN TestFormatFindings
--- PASS: TestFormatFindings (0.00s)
=== RUN TestFormatFindings_Empty
--- PASS: TestFormatFindings_Empty (0.00s)
PASS
ok github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/xss 0.990s

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)

/claim #5838

Summary by CodeRabbit

  • New Features

    • Added an XSS Context Analyzer that detects payload reflections, classifies HTML/JS contexts (body, attributes, script, style, URL, etc.), and reports exploitability with breakout hints.
    • Analyzer now receives full response content (body, headers, status code) via new response fields to enable deeper analysis.
  • Tests

    • Comprehensive unit tests covering context classification, exploitability detection, canary handling, and report formatting.
  • Chores

    • Integrated the new analyzer into the HTTP pipeline.

Claim

Total prize pool $200
Total paid $0
Status Pending
Submitted March 15, 2026
Last updated March 15, 2026

Contributors

PA

pannous

@pannous

100%

Sponsors

PR

ProjectDiscovery

@projectdiscovery

$200