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. This closes #5838.

How it works

  1. Canary injection: The analyzer replaces [XSS_CANARY] in payloads with a canary string containing HTML/JS-significant probe characters: <>'" and backtick
  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 12 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 parsing
    • context_test.go — 23 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)

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 23 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_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)
PASS
ok github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/xss 0.087s

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
    • Context-aware XSS detector that finds reflections and assesses exploitability across HTML body, attributes, script contexts, comments, styles, and URL attributes.
  • Chores
    • Analyzers now receive serialized HTTP response body and headers during scans to improve detection accuracy.
  • Tests
    • Added comprehensive unit tests covering context classification, canary handling, and detection/formatting logic.

/claim #5838

Claim

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

Contributors

NE

Nenad Ilic

@nenadilic84

100%

Sponsors

PR

ProjectDiscovery

@projectdiscovery

$200