Skip to content

Final xss fix#7024

Open
Eliene-byte wants to merge 9 commits intoprojectdiscovery:devfrom
Eliene-byte:final-xss-fix
Open

Final xss fix#7024
Eliene-byte wants to merge 9 commits intoprojectdiscovery:devfrom
Eliene-byte:final-xss-fix

Conversation

@Eliene-byte
Copy link

@Eliene-byte Eliene-byte commented Feb 24, 2026

The XSS Context Analyzer identifies the exact HTML environment where a fuzzed payload is reflected. This allows the fuzzer to adapt its strategy based on whether the injection lands inside an attribute, a text node, or an unknown area.

How it works:

Memory Safety: The analyzer implements protection using io.LimitReader, capping the response body processing at 4MB to prevent Out-Of-Memory (OOM) errors on large targets.

State-Aware Tokenization: Utilizing the golang.org/x/net/html tokenizer, the analyzer tracks nesting levels with a tagDepth counter. This ensures reflections are only classified as "text" if they reside within a valid HTML element.

Context Mapping:

Attribute Context (attr:name:tag): Detected when the canary is found within an attribute value (e.g., returns attr:value:input).

Text Context (text:content): Detected when the canary is reflected as a text node inside an element (e.g.,

pd_xss
).

Fallback Context (reflected:unknown): Triggered if the canary is present but not mapped to a specific HTML structure.

Dynamic Matching: It uses a shared xssCanaryMarker and checks the FuzzGenerated.Value to ensure the detection matches the actual string injected during the fuzzing process.

Usage Tutorial
To utilize this analyzer, reference it in the analyzers section of your Nuclei fuzzer template.

  1. Example Nuclei Template:

YAML
id: xss-context-fuzzing

info:
name: XSS Context-Aware Fuzzing
author: Eliene-byte
severity: info

http:

  • method: GET
    path:

    • "{{BaseURL}}?q={{reflection}}"

    fuzzing:

    • part: query
      type: replace
      mode: multiple
      keys:
      • q
        fuzz:
      • "{{reflection}}"

    analyzers:

    • xss-context

    matchers:

    • type: word
      part: xss-context
      words:

      • "attr:value:input"
      1. Running the Scan:

Bash
nuclei -t xss-context-fuzzing.yaml -u http://example.com/search
3. Interpreting Results:

The analyzer provides structured output strings that help guide payload selection:

If output is attr:value:input: The payload is inside an attribute. Recommendation: Use quotes (") to break the context.

If output is text:pd_xss: The payload is in plain text. Recommendation: Use HTML tags like or <script>.

If output is reflected:unknown: The reflection is in an unmapped area (e.g., a comment or script block).

@auto-assign auto-assign bot requested a review from Mzack9999 February 24, 2026 21:33
@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Feb 24, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Refactored XSS context analyzer tests to use httptest.Server for proper HTTP transaction testing
  • Added comprehensive test coverage for text context, attribute context, and unknown context detection paths
  • Improved nil pointer safety with guard clause in Analyze() method

Comment @neo help for available commands. · Open in Neo

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an exported XSSContextAnalyzer that appends a canary ("pd_xss"), issues the rebuilt HTTP request, reads up to 4MB of the response, tokenizes the HTML, and detects canary reflections in attribute values or text nodes; registers the analyzer and adds unit tests covering several cases.

Changes

Cohort / File(s) Summary
XSS Context Analyzer
pkg/fuzz/analyzers/xss_analyzer.go
Adds XSSContextAnalyzer with Name(), ApplyInitialTransformation() (appends "pd_xss"), and Analyze(options *Options) (bool,string,error) which validates options, performs the HTTP request using options.HttpClient, reads response body (4MB cap), tokenizes HTML, tracks tag depth, and returns detections: attr:<attr-key>:<tag>, text:<text>, or reflected:unknown. Registers via init().
Analyzer Tests
pkg/fuzz/analyzers/xss_analyzer_test.go
New tests TestXSSContextAnalyzer_Analyze with subtests: text-context-detection, no-canary-detection, and nil-options-guard. Each spins up an HTTP test server, uses setupTestOptions to build Options and a fuzz.GeneratedRequest (canary "pd_xss"), invokes Analyze, and asserts expected booleans/contexts. Includes setupTestOptions helper.

Sequence Diagram

sequenceDiagram
    participant Test
    participant Analyzer as XSSContextAnalyzer
    participant HttpClient
    participant Server
    participant Tokenizer as HTMLTokenizer

    Test->>Analyzer: Analyze(options)
    activate Analyzer

    Analyzer->>Analyzer: ApplyInitialTransformation(data -> data + "pd_xss")
    Analyzer->>HttpClient: Do(rebuilt request)
    HttpClient->>Server: HTTP request
    Server-->>HttpClient: HTTP response
    HttpClient-->>Analyzer: response

    Analyzer->>Analyzer: Read body (limit 4MB)
    Analyzer->>Tokenizer: Tokenize HTML
    Tokenizer-->>Analyzer: token stream

    loop scan tokens
        Analyzer->>Analyzer: check attributes / text for canary
    end

    alt attribute match
        Analyzer-->>Test: (true, "attr:<attr-key>:<tag>", nil)
    else text match
        Analyzer-->>Test: (true, "text:<text>", nil)
    else no match
        Analyzer-->>Test: (true, "reflected:unknown", nil)
    end

    deactivate Analyzer
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I stitched a tiny canary onto a seed,
Sent it hopping through HTML groves at speed,
In attributes and text I peered around,
Where echoes lived my little echo found,
I nibbled a carrot and watched the log feed.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Final xss fix' is vague and generic. While it references XSS, it uses non-descriptive language ('final', 'fix') that doesn't convey the specific implementation details (introducing XSSContextAnalyzer with HTML reflection analysis, attribute/text token detection, and test coverage). Use a more specific title like 'Implement XSSContextAnalyzer with attribute and text token detection' to clearly describe the primary change and what was added.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
pkg/fuzz/analyzers/xss_analyzer_test.go (2)

67-69: MockComponent.Rebuild() uses raw string concatenation without URL-encoding the value.

If a future test uses a payload with &, =, #, or spaces, the reconstructed URL will be malformed and the test will break in a confusing way. Use url.Values for correctness.

Proposed fix
+import "net/url"
+
 func (m *MockComponent) Rebuild() (*retryablehttp.Request, error) {
-	return retryablehttp.NewRequest("GET", fmt.Sprintf("%s?%s=%s", m.URL, m.Key, m.CurrentValue), nil)
+	params := url.Values{}
+	params.Set(m.Key, m.CurrentValue)
+	return retryablehttp.NewRequest("GET", m.URL+"?"+params.Encode(), nil)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 67 - 69,
MockComponent.Rebuild builds the request URL by raw string concatenation which
fails for values containing &, =, #, or spaces; update the Rebuild method to
construct the query using url.Values (or url.QueryEscape) and append the encoded
query to m.URL so that m.Key and m.CurrentValue are properly URL-encoded before
calling retryablehttp.NewRequest; ensure you reference the existing symbols
(MockComponent.Rebuild, m.URL, m.Key, m.CurrentValue) and return the same
retryablehttp.NewRequest(...) result after encoding.

15-47: Test coverage is limited to a single happy-path scenario.

The test only covers text-node reflection. To gain confidence, consider adding at least:

  1. Attribute reflection — server echoes payload inside a tag attribute.
  2. Negative case — server returns a page that does not reflect the input; assert matched == false.
  3. Error propagation — e.g., unreachable server, verify error is returned.

These are important because the analyzer has multiple code paths (attribute check, text check, error handling, no-match).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 15 - 47, Add two new
subtests inside TestXSSContextAnalyzer_Analyze to cover attribute reflection,
negative/no-match, and error propagation: (1) "reflection-in-attribute" — spin
up an httptest.Server whose handler echoes the query value into an HTML
attribute (e.g., <div data-val="%s">), build Options with fuzz.GeneratedRequest
and MockComponent like the existing case, call XSSContextAnalyzer.Analyze and
assert matched==true, message mentions "attribute" (or attribute-related text),
and verify opts.FuzzGenerated.Component.(*MockComponent).CurrentValue restored
to original; (2) "no-reflection" — server returns static HTML that does not
include the query value, call Analyze and assert matched==false (and no error),
plus component restored; (3) "error-propagation" — use an unreachable URL or
create and Close() an httptest.Server before sending the request, call Analyze
and assert an error is returned; reuse the same
Options/fuzz.GeneratedRequest/MockComponent setup and check CurrentValue
restoration in each case to ensure cleanup logic in XSSContextAnalyzer.Analyze
works.
pkg/fuzz/analyzers/xss_analyzer.go (2)

20-77: The "context-aware" XSS analysis only covers attribute values and text nodes — important HTML contexts are missing.

For a "context-aware" XSS analyzer, reflections inside <script> blocks, HTML comments (CommentToken), and <style> blocks are critical XSS injection vectors that are not detected here. The current implementation would silently miss reflections in those contexts, potentially producing false negatives for real-world XSS.

This doesn't need to block the initial merge, but should be tracked — at minimum, html.CommentToken is easy to add now.

Minimal addition for comment token handling
 		case html.TextToken:
 			if strings.Contains(token.Data, payload) {
 				return true, "reflection in html text node", nil
 			}
+		case html.CommentToken:
+			if strings.Contains(token.Data, payload) {
+				return true, "reflection in html comment", nil
+			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 20 - 77, The Analyze method
of XSSContextAnalyzer only checks html.StartTagToken/html.SelfClosingTagToken
and html.TextToken; add handling for html.CommentToken and contexts for
script/style blocks: inspect html.CommentToken for payload reflections and treat
html.StartTagToken with token.Data == "script" or "style" so you parse
subsequent html.TextToken (or html.RawTextToken if applicable) within those tags
for payload occurrences; update the tokenizer switch in Analyze (referencing
tokenizer, tokenType, token, payload) to return the same true/description
pattern when reflections are found in comments, <script> content, or <style>
content.

29-31: Silently discarding the restore error could leave the component in a mutated state.

Per the Component contract, the value must be restored after mutation. If the deferred SetValue fails, downstream code will operate on stale/mutated state. Consider at least logging the error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 29 - 31, The deferred
restore currently discards any error from gr.Component.SetValue(gr.Key,
gr.Value) which can leave the component mutated; update the deferred func to
capture the returned error and at minimum log it (instead of assigning to `_`).
Specifically, change the defer block around gr.Component.SetValue to something
like capturing err := gr.Component.SetValue(...) and if err != nil call the
package or component logger (or log.Printf) with context including gr.Key and
gr.Value so failures to restore are visible; keep the same deferred semantics
and use the same symbols gr.Component.SetValue, gr.Key and gr.Value to locate
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Line 27: The test discards the error returned by retryablehttp.NewRequest and
may panic later; update the call that creates req (retryablehttp.NewRequest) to
capture the error and assert it with require.NoError(t, err) (or the existing
test assertion helper) immediately after creation so failures produce clear
diagnostics rather than a nil-pointer panic.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 38-42: The call rebuilt.WithContext(rebuilt.Context()) is a no-op;
change the request to use a real upstream context (or drop WithContext
entirely). Replace the current call to
options.HttpClient.Do(rebuilt.WithContext(rebuilt.Context())) by either
propagating the caller/options context (e.g., rebuilt =
rebuilt.WithContext(options.Context or options.Ctx) and then
options.HttpClient.Do(rebuilt)) or simply call options.HttpClient.Do(rebuilt) if
no propagation is needed; update the code paths around options.HttpClient.Do and
rebuilt to use the chosen context propagation approach.
- Around line 45-48: The code currently calls io.ReadAll(resp.Body) (producing
bodyBytes) which can OOM on large HTTP responses; replace that call with reading
from io.LimitReader(resp.Body, maxBodySize+1) using a defined constant (e.g.,
maxBodySize) so the read is capped, then detect if the result length >
maxBodySize to treat as "too large" and return an error or truncated result.
Update the logic around bodyBytes and err to use the LimitReader, close
resp.Body as before, and ensure any downstream code that expects the full body
handles the truncated-case consistently.
- Around line 16-18: The ApplyInitialTransformation function currently appends
the hardcoded canary "pd_xss", causing false positives; modify
XSSContextAnalyzer.ApplyInitialTransformation to call the shared
randStringBytesMask() (from analyzers.go) to generate a per-request randomized
canary, append that generated string to data, and return it, and ensure the test
harness captures and exposes the generated canary value so reflection checks
verify against the dynamic payload.

---

Nitpick comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 67-69: MockComponent.Rebuild builds the request URL by raw string
concatenation which fails for values containing &, =, #, or spaces; update the
Rebuild method to construct the query using url.Values (or url.QueryEscape) and
append the encoded query to m.URL so that m.Key and m.CurrentValue are properly
URL-encoded before calling retryablehttp.NewRequest; ensure you reference the
existing symbols (MockComponent.Rebuild, m.URL, m.Key, m.CurrentValue) and
return the same retryablehttp.NewRequest(...) result after encoding.
- Around line 15-47: Add two new subtests inside TestXSSContextAnalyzer_Analyze
to cover attribute reflection, negative/no-match, and error propagation: (1)
"reflection-in-attribute" — spin up an httptest.Server whose handler echoes the
query value into an HTML attribute (e.g., <div data-val="%s">), build Options
with fuzz.GeneratedRequest and MockComponent like the existing case, call
XSSContextAnalyzer.Analyze and assert matched==true, message mentions
"attribute" (or attribute-related text), and verify
opts.FuzzGenerated.Component.(*MockComponent).CurrentValue restored to original;
(2) "no-reflection" — server returns static HTML that does not include the query
value, call Analyze and assert matched==false (and no error), plus component
restored; (3) "error-propagation" — use an unreachable URL or create and Close()
an httptest.Server before sending the request, call Analyze and assert an error
is returned; reuse the same Options/fuzz.GeneratedRequest/MockComponent setup
and check CurrentValue restoration in each case to ensure cleanup logic in
XSSContextAnalyzer.Analyze works.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 20-77: The Analyze method of XSSContextAnalyzer only checks
html.StartTagToken/html.SelfClosingTagToken and html.TextToken; add handling for
html.CommentToken and contexts for script/style blocks: inspect
html.CommentToken for payload reflections and treat html.StartTagToken with
token.Data == "script" or "style" so you parse subsequent html.TextToken (or
html.RawTextToken if applicable) within those tags for payload occurrences;
update the tokenizer switch in Analyze (referencing tokenizer, tokenType, token,
payload) to return the same true/description pattern when reflections are found
in comments, <script> content, or <style> content.
- Around line 29-31: The deferred restore currently discards any error from
gr.Component.SetValue(gr.Key, gr.Value) which can leave the component mutated;
update the deferred func to capture the returned error and at minimum log it
(instead of assigning to `_`). Specifically, change the defer block around
gr.Component.SetValue to something like capturing err :=
gr.Component.SetValue(...) and if err != nil call the package or component
logger (or log.Printf) with context including gr.Key and gr.Value so failures to
restore are visible; keep the same deferred semantics and use the same symbols
gr.Component.SetValue, gr.Key and gr.Value to locate the change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d771daa and dbc98f5.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

16-45: Add coverage for comment reflections.

The analyzer now detects comment reflections; a small subtest will prevent regressions.

✅ Suggested test
 	t.Run("reflection-in-attribute", func(t *testing.T) {
 		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			fmt.Fprintf(w, "<input value='%s'>", r.URL.Query().Get("q"))
 		}))
 		defer server.Close()
 		runTest(t, analyzer, server.URL, "attribute")
 	})
 
+	t.Run("reflection-in-comment", func(t *testing.T) {
+		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			fmt.Fprintf(w, "<!--%s-->", r.URL.Query().Get("q"))
+		}))
+		defer server.Close()
+		runTest(t, analyzer, server.URL, "comment")
+	})
+
 	t.Run("no-reflection", func(t *testing.T) {
 		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			fmt.Fprint(w, "<html>safe content</html>")
 		}))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 16 - 45, Add a new
subtest to TestXSSContextAnalyzer_Analyze that verifies comment reflections are
detected: create an httptest server handler that writes the query param inside
an HTML comment (e.g., "<!--%s-->"), defer Close, and call runTest(t, analyzer,
server.URL, "comment") like the existing text/attribute cases; alternatively,
mimic the "no-reflection" flow by using setupOptions and invoking
analyzer.Analyze(opts) and asserting matched == true and err == nil to ensure
XSSContextAnalyzer detects comment context reflections.
pkg/fuzz/analyzers/xss_analyzer.go (1)

49-55: Avoid silent truncation of large bodies.

The current cap prevents OOM, but it silently truncates responses, which can hide reflections. Consider detecting overflow (e.g., read maxBodySize+1) and return a clear error (or at least log) when exceeded.

🔧 Suggested adjustment
-	const maxBodySize = 4 * 1024 * 1024
-	bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))
+	const maxBodySize = 4 * 1024 * 1024
+	bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize+1))
 	if err != nil {
 		return false, "", err
 	}
+	if len(bodyBytes) > maxBodySize {
+		return false, "", fmt.Errorf("response body exceeds %d bytes", maxBodySize)
+	}
 	body := string(bodyBytes)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 49 - 55, The current read
using maxBodySize with io.LimitReader silently truncates large responses and may
hide reflections; change the read call to use maxBodySize+1 (use
io.LimitReader(resp.Body, maxBodySize+1) and read into bodyBytes), then if
len(bodyBytes) > maxBodySize return a clear error (or log a specific truncation
error) instead of proceeding; update the code around maxBodySize,
io.LimitReader, bodyBytes and body (in the XSS analysis function in
xss_analyzer.go) so callers receive an explicit error when the response exceeds
the cap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 16-45: Add a new subtest to TestXSSContextAnalyzer_Analyze that
verifies comment reflections are detected: create an httptest server handler
that writes the query param inside an HTML comment (e.g., "<!--%s-->"), defer
Close, and call runTest(t, analyzer, server.URL, "comment") like the existing
text/attribute cases; alternatively, mimic the "no-reflection" flow by using
setupOptions and invoking analyzer.Analyze(opts) and asserting matched == true
and err == nil to ensure XSSContextAnalyzer detects comment context reflections.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 49-55: The current read using maxBodySize with io.LimitReader
silently truncates large responses and may hide reflections; change the read
call to use maxBodySize+1 (use io.LimitReader(resp.Body, maxBodySize+1) and read
into bodyBytes), then if len(bodyBytes) > maxBodySize return a clear error (or
log a specific truncation error) instead of proceeding; update the code around
maxBodySize, io.LimitReader, bodyBytes and body (in the XSS analysis function in
xss_analyzer.go) so callers receive an explicit error when the response exceeds
the cap.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbc98f5 and 1947b63.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
pkg/fuzz/analyzers/xss_analyzer.go (2)

64-64: Avoid the []bytestringio.Reader double conversion.

strings.NewReader(string(bodyBytes)) allocates a new string copy unnecessarily. bytes.NewReader accepts the slice directly.

♻️ Proposed fix
-	return a.analyzeContent(strings.NewReader(string(bodyBytes)), payload)
+	return a.analyzeContent(bytes.NewReader(bodyBytes), payload)

And add "bytes" to the import block (remove "strings" if it is no longer used elsewhere in this file).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` at line 64, Replace the unnecessary
[]byte→string conversion when calling analyzeContent by passing a bytes.Reader
directly: change the call in the function that currently does return
a.analyzeContent(strings.NewReader(string(bodyBytes)), payload) to use
bytes.NewReader(bodyBytes) instead; also update the import block to add "bytes"
and remove "strings" if it is no longer used elsewhere in this file so imports
remain correct.

40-41: Replace stdlib log.Printf with the project's gologger.

The project uses github.com/projectdiscovery/gologger throughout. Using stdlib log here is inconsistent; gologger.Print() is the appropriate call for stderr-bound error output.

♻️ Proposed fix
-import (
-	"fmt"
-	"io"
-	"log"
-	"strings"
-	...
-)
+import (
+	"fmt"
+	"io"
+	"strings"
+	...
+	"github.com/projectdiscovery/gologger"
+)
-			log.Printf("[%s] critical: failed to restore component state: %v", XSSAnalyzerName, err)
+			gologger.Print().Msgf("[%s] critical: failed to restore component state: %v", XSSAnalyzerName, err)

Based on learnings: "In projectdiscovery/gologger, the Print() function writes to stderr and Silent() writes to stdout … ensure code uses Print() for error/output going to stderr."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 40 - 41, Replace the stdlib
call log.Printf(...) with the project's gologger by removing the "log" import
and using github.com/projectdiscovery/gologger.Print to emit the same formatted
error message (keep the existing formatting and XSSAnalyzerName/err values);
update imports to add gologger and remove the stdlib log import so the
gologger.Print call is used where the current log.Printf("[%s] critical: failed
to restore component state: %v", XSSAnalyzerName, err) appears.
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

53-60: Fragile server handler — safe case identified by name string comparison.

The condition tt.name != "No Reflection (Safe)" couples server behavior to the test name, making it easy for a future case to silently misbehave. Since the safe-case responseBody contains no %s verbs, fmt.Fprintf is already a no-op for it. Simplify to always use fmt.Fprintf:

♻️ Proposed fix
-			if tt.expectedMatch && tt.name != "No Reflection (Safe)" {
-				fmt.Fprintf(w, tt.responseBody, val)
-			} else {
-				fmt.Fprint(w, tt.responseBody)
-			}
+			fmt.Fprintf(w, tt.responseBody, val)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 53 - 60, The test
server handler is fragile because it switches behavior based on tt.name; instead
always use fmt.Fprintf(w, tt.responseBody, val) in the httptest server handler
so the safe case (whose tt.responseBody contains no %s) remains safe and you
avoid coupling logic to tt.name — update the anonymous handler used to create
server := httptest.NewServer(http.HandlerFunc(...)) to call fmt.Fprintf
unconditionally and remove the tt.name check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 38-43: The "HTML Comment Reflection" unit test is broken because
the server uses fmt.Fprintf(w, "", val) so the payload is never reflected;
update the test case in xss_analyzer_test.go for the subtest named "HTML Comment
Reflection" to use an HTML comment format string (e.g. "<!--%s-->") as the
responseBody so the payload is actually embedded and the analyzer can detect the
reflection (leave expectedMatch and expectedMsg as-is).
- Line 78: Rename the helper function setupSeniorOptions to setupServerOptions
and update any call sites (e.g., the test that currently calls
setupSeniorOptions) to use the new name; ensure the function signature (*Options
parameters) and all references in pkg/fuzz/analyzers/xss_analyzer_test.go match
the renamed identifier so tests compile and run.
- Around line 99-105: MockComponent.Rebuild is discarding errors from url.Parse
and retryablehttp.NewRequest causing nil requests and opaque panics; update
Rebuild to capture and return both errors instead of using "_"—validate the
parsed URL (from url.Parse) and return an error if parsing fails, then call
retryablehttp.NewRequest and return any error it returns, so Rebuild returns
(*retryablehttp.Request, error) with meaningful errors propagated to callers.

---

Nitpick comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 53-60: The test server handler is fragile because it switches
behavior based on tt.name; instead always use fmt.Fprintf(w, tt.responseBody,
val) in the httptest server handler so the safe case (whose tt.responseBody
contains no %s) remains safe and you avoid coupling logic to tt.name — update
the anonymous handler used to create server :=
httptest.NewServer(http.HandlerFunc(...)) to call fmt.Fprintf unconditionally
and remove the tt.name check.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Line 64: Replace the unnecessary []byte→string conversion when calling
analyzeContent by passing a bytes.Reader directly: change the call in the
function that currently does return
a.analyzeContent(strings.NewReader(string(bodyBytes)), payload) to use
bytes.NewReader(bodyBytes) instead; also update the import block to add "bytes"
and remove "strings" if it is no longer used elsewhere in this file so imports
remain correct.
- Around line 40-41: Replace the stdlib call log.Printf(...) with the project's
gologger by removing the "log" import and using
github.com/projectdiscovery/gologger.Print to emit the same formatted error
message (keep the existing formatting and XSSAnalyzerName/err values); update
imports to add gologger and remove the stdlib log import so the gologger.Print
call is used where the current log.Printf("[%s] critical: failed to restore
component state: %v", XSSAnalyzerName, err) appears.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1947b63 and b4c02dd.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Comment on lines 38 to 43
{
name: "HTML Comment Reflection",
responseBody: "",
expectedMatch: true,
expectedMsg: "comment",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

"HTML Comment Reflection" test case is broken — payload is never reflected.

responseBody is "". The server handler calls fmt.Fprintf(w, "", val): an empty format string ignores val, so the response body is empty. The analyzer returns matched=false but the test asserts expectedMatch=true, so this subtest will fail.

Provide the HTML comment template as the format string so the payload is actually embedded:

🐛 Proposed fix
 		{
 			name:          "HTML Comment Reflection",
-			responseBody:  "",
+			responseBody:  "<!-- %s -->",
 			expectedMatch: true,
 			expectedMsg:   "comment",
 		},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
name: "HTML Comment Reflection",
responseBody: "",
expectedMatch: true,
expectedMsg: "comment",
},
{
name: "HTML Comment Reflection",
responseBody: "<!-- %s -->",
expectedMatch: true,
expectedMsg: "comment",
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 38 - 43, The "HTML
Comment Reflection" unit test is broken because the server uses fmt.Fprintf(w,
"", val) so the payload is never reflected; update the test case in
xss_analyzer_test.go for the subtest named "HTML Comment Reflection" to use an
HTML comment format string (e.g. "<!--%s-->") as the responseBody so the payload
is actually embedded and the analyzer can detect the reflection (leave
expectedMatch and expectedMsg as-is).

Comment on lines 99 to 105
func (m *MockComponent) Rebuild() (*retryablehttp.Request, error) {
u, _ := url.Parse(m.URL)
q := u.Query()
q.Set(m.Key, m.CurrentValue)
u.RawQuery = q.Encode()
return retryablehttp.NewRequest("GET", u.String(), nil)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Discarded errors in MockComponent.Rebuild can cause opaque panics.

Both url.Parse and retryablehttp.NewRequest errors are silently ignored with _. If either fails (e.g., malformed URL due to a future test change), the returned nil request will cause an unclear panic downstream.

♻️ Proposed fix
 func (m *MockComponent) Rebuild() (*retryablehttp.Request, error) {
-	u, _ := url.Parse(m.URL)
+	u, err := url.Parse(m.URL)
+	if err != nil {
+		return nil, fmt.Errorf("MockComponent: invalid URL %q: %w", m.URL, err)
+	}
 	q := u.Query()
 	q.Set(m.Key, m.CurrentValue)
 	u.RawQuery = q.Encode()
-	return retryablehttp.NewRequest("GET", u.String(), nil)
+	req, err := retryablehttp.NewRequest("GET", u.String(), nil)
+	if err != nil {
+		return nil, fmt.Errorf("MockComponent: failed to build request: %w", err)
+	}
+	return req, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 99 - 105,
MockComponent.Rebuild is discarding errors from url.Parse and
retryablehttp.NewRequest causing nil requests and opaque panics; update Rebuild
to capture and return both errors instead of using "_"—validate the parsed URL
(from url.Parse) and return an error if parsing fails, then call
retryablehttp.NewRequest and return any error it returns, so Rebuild returns
(*retryablehttp.Request, error) with meaningful errors propagated to callers.

@Eliene-byte
Copy link
Author

@dogancanbakir I have Create one New update Final check please

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/projectdiscovery/nuclei/issues/comments/3954862313","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds XSSContextAnalyzer that appends the canary \"pd_xss\" to inputs, issues an HTTP request via provided Options.HttpClient, tokenizes the HTML response, and detects reflections of the canary in attributes, text nodes, or a generic fallback. Includes unit tests covering text, attribute, and unknown-context cases.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**XSS Context Analyzer** <br> `pkg/fuzz/analyzers/xss_analyzer.go`|New `XSSContextAnalyzer` type registered as `\"xss-context\"`. Implements `Name()`, `ApplyInitialTransformation()` (appends literal `\"pd_xss\"`), and `Analyze()` which sends the rebuilt request via `Options.HttpClient`, reads the response body, tokenizes HTML, and reports reflections found in attributes (`attr:<attrKey>:<tag>`), text (`text:<textContent>`), or returns `reflected:unknown` as a fallback. Propagates HTTP/body errors.|\n|**Tests** <br> `pkg/fuzz/analyzers/xss_analyzer_test.go`|Adds `TestXSSContextAnalyzer_Analyze` with three subtests: `text-context-detection`, `attribute-context-detection`, and `unknown-context-fallback`. Uses a local HTTP test server and `setupTestOptions(targetURL string) *Options` helper to build `Options` with a retryable HTTP client and request. Asserts detection messages for each scenario.|\n\n## Sequence Diagram\n\n```mermaid\nsequenceDiagram\n    participant Tester\n    participant Analyzer as XSSContextAnalyzer\n    participant Options\n    participant HttpClient\n    participant Server\n    participant Tokenizer as HTMLTokenizer\n\n    Tester->>Analyzer: Analyze(options)\n    activate Analyzer\n\n    Analyzer->>Options: use provided request (rebuilt with canary \"pd_xss\")\n    Analyzer->>HttpClient: Do(request)\n    HttpClient->>Server: HTTP request\n    Server-->>HttpClient: HTTP response\n    HttpClient-->>Analyzer: response\n\n    Analyzer->>Analyzer: Read response body\n    Analyzer->>Tokenizer: Tokenize HTML stream\n    Tokenizer-->>Analyzer: tokens (StartTag, Text, EndTag, ...)\n    loop scan tokens\n      Analyzer->>Analyzer: check attributes / text for \"pd_xss\"\n    end\n\n    alt found in attribute\n      Analyzer-->>Tester: (true, \"attr:<key>:<tag>\", nil)\n    else found in text\n      Analyzer-->>Tester: (true, \"text:<content>\", nil)\n    else fallback\n      Analyzer-->>Tester: (true, \"reflected:unknown\", nil)\n    end\n    deactivate Analyzer\n```\n\n## Estimated code review effort\n\n🎯 3 (Moderate) | ⏱️ ~25 minutes\n\n## Poem\n\n> 🐰 I tucked a tiny \"pd_xss\" in a string,  \n> Sent it hopping out to hear the web sing,  \n> In text or attr it winked and replied,  \n> I munched the tokens and then I pried,  \n> Tidied the burrow and dashed on my spring.\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n### ❌ Failed checks (1 warning, 1 inconclusive)\n\n|     Check name     | Status         | Explanation                                                                                                                                                | Resolution                                                                                                                                                                                    |\n| :----------------: | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.                                                                       | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                            |\n|     Title check    | ❓ Inconclusive | The title 'Final xss fix' is vague and generic, using non-descriptive phrasing that doesn't clearly convey what was actually implemented in the changeset. | Use a more specific title that describes the actual change, such as 'Add XSSContextAnalyzer for HTML reflection detection' or 'Implement XSS context analyzer with text/attribute detection'. |\n\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                 |\n| :---------------: | :------- | :---------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings (stacked PR)\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> 📝 Generate docstrings (commit on current branch)\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=projectdiscovery/nuclei&utm_content=7024)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}}}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
pkg/fuzz/analyzers/xss_analyzer.go (2)

16-18: ⚠️ Potential issue | 🟠 Major

Use a per-request canary and source it from fuzzed value, not a hardcoded literal.

Hardcoding "pd_xss" makes matching brittle and can produce false positives. Generate a unique canary in transformation and in Analyze read the effective value from options.FuzzGenerated.Value.

Suggested fix
 func (a *XSSContextAnalyzer) ApplyInitialTransformation(data string, params map[string]interface{}) string {
-	return data + "pd_xss"
+	return data + randStringBytesMask(8)
 }
@@
-	canary := "pd_xss"
+	canary := options.FuzzGenerated.Value
+	if canary == "" {
+		return false, "", nil
+	}

Based on learnings: "extract the canary from FuzzGenerated.Value (the final reflected string) ... ensures compatibility across all fuzzing modes, including KV-mode."

Also applies to: 33-33

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 16 - 18, The
XSSContextAnalyzer currently hardcodes the canary string in
ApplyInitialTransformation ("pd_xss"), which is brittle; update
ApplyInitialTransformation (on XSSContextAnalyzer) to generate or embed a
per-request canary derived from the fuzz input instead of a literal, and change
Analyze to read the effective canary from options.FuzzGenerated.Value (the final
reflected string) for matching; locate ApplyInitialTransformation and Analyze in
XSSContextAnalyzer, create/use a generated canary value passed through the fuzz
options (options.FuzzGenerated.Value) and use that dynamic value for both
transformation and verification so KV- and other fuzzing modes work correctly.

31-31: ⚠️ Potential issue | 🟠 Major

Avoid unbounded body reads and handle read errors.

Line 31 ignores io.ReadAll errors and reads without a cap, which can hurt analyzer stability on large responses.

Suggested fix
 import (
+	"fmt"
 	"io"
 	"strings"
@@
-	bodyBytes, _ := io.ReadAll(resp.Body)
+	const maxBodySize = 4 * 1024 * 1024 // 4MB
+	bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize+1))
+	if err != nil {
+		return false, "", err
+	}
+	if len(bodyBytes) > maxBodySize {
+		return false, "", fmt.Errorf("response body exceeds %d bytes", maxBodySize)
+	}
#!/bin/bash
# Verify unbounded/unchecked response body read in current file
rg -nP 'io\.ReadAll\(|LimitReader|bodyBytes,\s*_' pkg/fuzz/analyzers/xss_analyzer.go -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` at line 31, Replace the unchecked
unbounded read of resp.Body (the line using io.ReadAll and variable bodyBytes)
with a capped read and proper error handling: wrap resp.Body with io.LimitReader
using a defined maxResponseBodyBytes constant (e.g., maxResponseBodyBytes) and
call io.ReadAll on that reader, then check and handle the returned error instead
of discarding it; ensure the function that uses bodyBytes (the xss analyzer code
that reads resp.Body) also defers/closes resp.Body appropriately and handles the
case where the read was truncated due to the limit.
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

61-64: ⚠️ Potential issue | 🟡 Minor

Don’t discard request-construction errors in test setup.

Line 63 ignores the retryablehttp.NewRequest error, which can lead to nil-request panics and opaque failures.

Suggested fix
-func setupTestOptions(targetURL string) *Options {
+func setupTestOptions(t *testing.T, targetURL string) *Options {
+	t.Helper()
 	client := retryablehttp.NewClient(retryablehttp.DefaultOptionsSingle)
-	req, _ := retryablehttp.NewRequest("GET", targetURL, nil)
+	req, err := retryablehttp.NewRequest("GET", targetURL, nil)
+	require.NoError(t, err)
 	return &Options{
-		opts := setupTestOptions(server.URL)
+		opts := setupTestOptions(t, server.URL)
@@
-		opts := setupTestOptions(server.URL)
+		opts := setupTestOptions(t, server.URL)
@@
-		opts := setupTestOptions(server.URL)
+		opts := setupTestOptions(t, server.URL)
#!/bin/bash
# Verify discarded request-construction errors in test helper
rg -nP 'retryablehttp\.NewRequest\(.+\)\s*$|,\s*_\s*:=' pkg/fuzz/analyzers/xss_analyzer_test.go -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 61 - 64, The test
helper setupTestOptions currently ignores the error returned by
retryablehttp.NewRequest, which can produce nil-request panics; update
setupTestOptions to capture (req, err := retryablehttp.NewRequest(...)) and
handle err instead of discarding it—either return nil and an error from
setupTestOptions or panic/log.Fatalf with a clear message; ensure the req
variable is only used when err == nil and mention the symbols involved:
setupTestOptions, Options, retryablehttp.NewRequest, and req.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Line 40: The inTag boolean in xss_analyzer.go loses nesting context — replace
it with an integer depth counter (e.g., tagDepth) and update logic in the
functions that parse tags (the code around the current inTag usage and the block
referenced at lines 52-63) so you increment tagDepth when encountering a start
tag (including when encountering '<' followed by a name), decrement tagDepth on
end tags ('</...>'), handle self-closing tags by not incrementing or by
immediately decrementing, and change all checks from "inTag" to "tagDepth > 0"
so nested elements are tracked correctly and text reflections are classified
accurately.
- Around line 21-25: The code checks options and options.FuzzGenerated.Request
but calls options.HttpClient.Do(...) without guarding options.HttpClient; add a
nil check for options.HttpClient before invoking Do (e.g., if options.HttpClient
== nil { return false, "", nil } or return a suitable error) so the call to
options.HttpClient.Do(request) cannot nil-deref; update the logic around the
resp, err := options.HttpClient.Do(options.FuzzGenerated.Request) site to
perform this check and handle the nil-client case consistently with the existing
return values.

---

Duplicate comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 61-64: The test helper setupTestOptions currently ignores the
error returned by retryablehttp.NewRequest, which can produce nil-request
panics; update setupTestOptions to capture (req, err :=
retryablehttp.NewRequest(...)) and handle err instead of discarding it—either
return nil and an error from setupTestOptions or panic/log.Fatalf with a clear
message; ensure the req variable is only used when err == nil and mention the
symbols involved: setupTestOptions, Options, retryablehttp.NewRequest, and req.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 16-18: The XSSContextAnalyzer currently hardcodes the canary
string in ApplyInitialTransformation ("pd_xss"), which is brittle; update
ApplyInitialTransformation (on XSSContextAnalyzer) to generate or embed a
per-request canary derived from the fuzz input instead of a literal, and change
Analyze to read the effective canary from options.FuzzGenerated.Value (the final
reflected string) for matching; locate ApplyInitialTransformation and Analyze in
XSSContextAnalyzer, create/use a generated canary value passed through the fuzz
options (options.FuzzGenerated.Value) and use that dynamic value for both
transformation and verification so KV- and other fuzzing modes work correctly.
- Line 31: Replace the unchecked unbounded read of resp.Body (the line using
io.ReadAll and variable bodyBytes) with a capped read and proper error handling:
wrap resp.Body with io.LimitReader using a defined maxResponseBodyBytes constant
(e.g., maxResponseBodyBytes) and call io.ReadAll on that reader, then check and
handle the returned error instead of discarding it; ensure the function that
uses bodyBytes (the xss analyzer code that reads resp.Body) also defers/closes
resp.Body appropriately and handles the case where the read was truncated due to
the limit.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4c02dd and e2a944c.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
pkg/fuzz/analyzers/xss_analyzer.go (2)

32-34: ⚠️ Potential issue | 🟠 Major

Unbounded io.ReadAll with discarded error, and canary hard-coded instead of extracted from FuzzGenerated.Value.

Two issues here:

  1. io.ReadAll has no size cap and its error is silently discarded. In a fuzzing context the target may return arbitrarily large bodies — this can OOM the process. Use io.LimitReader and handle the error.

  2. Canary should be derived from FuzzGenerated.Value, not re-declared as a literal. This ensures compatibility across all fuzzing modes (including KV-mode) and keeps the canary source consistent with the transformation step.

Proposed fix
-	bodyBytes, _ := io.ReadAll(resp.Body)
+	const maxBodySize = 4 * 1024 * 1024 // 4 MB
+	bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))
+	if err != nil {
+		return false, "", err
+	}
 	body := string(bodyBytes)
-	canary := "pd_xss"
+	canary := options.FuzzGenerated.Value // extract canary from the fuzzed value

Based on learnings: "In Go code related to fuzz analyzers (e.g., XSS analyzer in pkg/fuzz/analyzers), extract the canary from FuzzGenerated.Value (the final reflected string) rather than from GeneratedRequest.OriginalPayload."

(unbounded ReadAll was flagged previously)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 32 - 34, Replace the
unbounded, ignored-error read and the hard-coded canary: use io.LimitReader on
resp.Body (e.g., wrap resp.Body with io.LimitReader using a safe max size) and
call io.ReadAll (or io.ReadFull) capturing and handling the error instead of
discarding it, then convert to string into the existing body variable; and
derive the canary from the fuzz result (use FuzzGenerated.Value / the final
reflected string) rather than re-declaring canary = "pd_xss" so the analyzer
uses the actual generated payload (not GeneratedRequest.OriginalPayload or a
literal). Ensure any resources (resp.Body) are closed as currently done and
return/log errors as appropriate.

16-18: Still using hardcoded "pd_xss" canary — high false-positive risk.

This was flagged in a prior review round and marked as addressed, but the code still appends a static literal. A six-character constant can naturally appear in responses. Use randStringBytesMask() (already available in analyzers.go) to generate a unique canary per transformation, and store it so Analyze can retrieve it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 16 - 18, The
ApplyInitialTransformation currently appends a hardcoded "pd_xss" canary; change
it to generate a unique canary with randStringBytesMask() (from analyzers.go),
append that canary to the returned string, and store it on the
XSSContextAnalyzer instance (e.g., add a field like lastCanary or canary) so the
Analyze method can retrieve and match it; update Analyze to read that stored
canary when checking for the marker instead of expecting the static literal.
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

61-70: ⚠️ Potential issue | 🟠 Major

Discarded error from retryablehttp.NewRequest and missing FuzzGenerated.Value.

Two issues in this helper:

  1. The error from retryablehttp.NewRequest on Line 63 is silently discarded — if it fails, req is nil and the test panics with an opaque message. Use require.NoError (you'd need to accept *testing.T).

  2. FuzzGenerated.Value is never set. Once the analyzer is corrected to extract the canary from Value (per the learning about canary extraction), all three subtests will fail because the canary will be an empty string.

Proposed fix
-func setupTestOptions(targetURL string) *Options {
+func setupTestOptions(t *testing.T, targetURL string) *Options {
+	t.Helper()
 	client := retryablehttp.NewClient(retryablehttp.DefaultOptionsSingle)
-	req, _ := retryablehttp.NewRequest("GET", targetURL, nil)
+	req, err := retryablehttp.NewRequest("GET", targetURL, nil)
+	require.NoError(t, err)
 	return &Options{
 		HttpClient: client,
 		FuzzGenerated: fuzz.GeneratedRequest{
 			Request: req,
+			Value:   "pd_xss", // canary the analyzer should look for
 		},
 	}
 }

Update call sites accordingly:

-		opts := setupTestOptions(server.URL)
+		opts := setupTestOptions(t, server.URL)

(discarded error was flagged previously)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 61 - 70, In
setupTestOptions, stop discarding the error from retryablehttp.NewRequest by
changing the signature to accept *testing.T and using require.NoError(t, err)
after creating req (so a failed request fails the test clearly); also populate
the returned Options.FuzzGenerated.Value with the expected canary string (e.g.,
the same payload used in FuzzGenerated.Request) so the analyzer can extract the
canary; update all call sites of setupTestOptions to pass the *testing.T
argument.
🧹 Nitpick comments (1)
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

14-58: Tests only cover happy paths — consider adding negative/edge-case subtests.

All three subtests expect found == true. Consider adding at least:

  • A subtest where the response does not contain the canary → verifies found == false.
  • A subtest with nil options → verifies the nil guard returns (false, "", nil) without panic.

This would exercise the early-return paths in Analyze and improve confidence in the guard clauses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 14 - 58, Add two
negative subtests inside TestXSSContextAnalyzer_Analyze to cover early-return
paths: (1) "no-canary-detection" should start an httptest server that responds
without the canary value, call setupTestOptions(server.URL) and assert
analyzer.Analyze(opts) returns found == false (and context == ""), and (2)
"nil-options-guard" should call analyzer.Analyze(nil) and assert it returns
(false, "", nil) to ensure the nil guard in XSSContextAnalyzer.Analyze is
exercised; use the existing analyzer variable and
require.NoError/require.False/require.Equal assertions similar to the other
subtests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 61-70: In setupTestOptions, stop discarding the error from
retryablehttp.NewRequest by changing the signature to accept *testing.T and
using require.NoError(t, err) after creating req (so a failed request fails the
test clearly); also populate the returned Options.FuzzGenerated.Value with the
expected canary string (e.g., the same payload used in FuzzGenerated.Request) so
the analyzer can extract the canary; update all call sites of setupTestOptions
to pass the *testing.T argument.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 32-34: Replace the unbounded, ignored-error read and the
hard-coded canary: use io.LimitReader on resp.Body (e.g., wrap resp.Body with
io.LimitReader using a safe max size) and call io.ReadAll (or io.ReadFull)
capturing and handling the error instead of discarding it, then convert to
string into the existing body variable; and derive the canary from the fuzz
result (use FuzzGenerated.Value / the final reflected string) rather than
re-declaring canary = "pd_xss" so the analyzer uses the actual generated payload
(not GeneratedRequest.OriginalPayload or a literal). Ensure any resources
(resp.Body) are closed as currently done and return/log errors as appropriate.
- Around line 16-18: The ApplyInitialTransformation currently appends a
hardcoded "pd_xss" canary; change it to generate a unique canary with
randStringBytesMask() (from analyzers.go), append that canary to the returned
string, and store it on the XSSContextAnalyzer instance (e.g., add a field like
lastCanary or canary) so the Analyze method can retrieve and match it; update
Analyze to read that stored canary when checking for the marker instead of
expecting the static literal.

---

Nitpick comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 14-58: Add two negative subtests inside
TestXSSContextAnalyzer_Analyze to cover early-return paths: (1)
"no-canary-detection" should start an httptest server that responds without the
canary value, call setupTestOptions(server.URL) and assert
analyzer.Analyze(opts) returns found == false (and context == ""), and (2)
"nil-options-guard" should call analyzer.Analyze(nil) and assert it returns
(false, "", nil) to ensure the nil guard in XSSContextAnalyzer.Analyze is
exercised; use the existing analyzer variable and
require.NoError/require.False/require.Equal assertions similar to the other
subtests.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4c02dd and c4f7909.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
pkg/fuzz/analyzers/xss_analyzer.go (1)

16-18: Remove tool-specific review history from code comments.

The note referencing reviewer feedback is non-functional and will age poorly in production code comments.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 16 - 18, Remove the
non-functional reviewer note from the comment above ApplyInitialTransformation;
leave a concise description of purpose only (e.g., "ApplyInitialTransformation
appends a marker to track reflection.") and do not include any tool- or
reviewer-specific history or names such as "CodeRabbit" or references to past
suggestions; ensure remaining comment may still reference project pattern like
FuzzGenerated.Value in Analyze if needed for context.
pkg/fuzz/analyzers/xss_analyzer_test.go (1)

14-47: Add subtests for attribute-context and unknown-fallback branches.

Current tests validate text detection and no-reflection/nil guards, but they don’t cover the attr:* path or the "reflected:unknown" fallback returned when reflection exists outside recognized contexts.

✅ Suggested additions
 func TestXSSContextAnalyzer_Analyze(t *testing.T) {
 	analyzer := &XSSContextAnalyzer{}
@@
 	t.Run("nil-options-guard", func(t *testing.T) {
 		found, _, err := analyzer.Analyze(nil)
 		require.NoError(t, err)
 		require.False(t, found)
 	})
+
+	t.Run("attribute-context-detection", func(t *testing.T) {
+		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			fmt.Fprint(w, `<html><body><img src="pd_xss" /></body></html>`)
+		}))
+		defer server.Close()
+
+		opts := setupTestOptions(t, server.URL)
+		found, context, err := analyzer.Analyze(opts)
+		require.NoError(t, err)
+		require.True(t, found)
+		require.Equal(t, "attr:src:img", context)
+	})
+
+	t.Run("unknown-context-fallback", func(t *testing.T) {
+		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			// Reflection outside any HTML tag text node
+			fmt.Fprint(w, "pd_xss")
+		}))
+		defer server.Close()
+
+		opts := setupTestOptions(t, server.URL)
+		found, context, err := analyzer.Analyze(opts)
+		require.NoError(t, err)
+		require.True(t, found)
+		require.Equal(t, "reflected:unknown", context)
+	})
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer_test.go` around lines 14 - 47, Add two
subtests to TestXSSContextAnalyzer_Analyze that exercise the attribute-context
and unknown-fallback branches of XSSContextAnalyzer.Analyze: 1)
"attr-context-detection" — spin up an httptest server that returns HTML with the
canary reflected inside an attribute (e.g., <div title="pd_xss">), call
setupTestOptions(server.URL) and analyzer.Analyze(opts), assert found==true and
context contains the attribute-style token (e.g., "attr:title:pd_xss" or the
analyzer's attr:<name>:<canary> format). 2) "unknown-fallback-detection" —
return HTML where the canary is reflected but not in a recognized
text/attr/event context (e.g., inside a comment or script fragment the analyzer
treats as unknown), call Analyze and assert found==true and context equals or
contains "reflected:unknown". Use the existing analyzer variable and
setupTestOptions helper; mirror the style of the existing subtests for server
lifecycle and require assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 16-21: ApplyInitialTransformation appends a fixed marker
("pd_xss") but the context detection later compares against the entire
options.FuzzGenerated.Value; update the lookup in the XSSContextAnalyzer
analysis path (the code that reads options.FuzzGenerated.Value) to match the
appended marker instead of exact equality—either extract the marker into a
shared constant used by ApplyInitialTransformation and the analyzer, or detect
the marker with strings.HasSuffix/strings.Contains on
options.FuzzGenerated.Value and fall back to previous logic; reference
ApplyInitialTransformation, XSSContextAnalyzer, and options.FuzzGenerated.Value
when making the change.
- Around line 1-92: Remove the trailing space in the package comment near the
ApplyInitialTransformation block (the comment above ApplyInitialTransformation
in XSSContextAnalyzer) and add a final newline at EOF so the file ends with a
newline; run gofmt (or manually remove the extra whitespace after "follow" in
that comment and ensure the file terminates with a newline) to satisfy
formatting for functions/types XSSContextAnalyzer and ApplyInitialTransformation
and the init registration.

---

Nitpick comments:
In `@pkg/fuzz/analyzers/xss_analyzer_test.go`:
- Around line 14-47: Add two subtests to TestXSSContextAnalyzer_Analyze that
exercise the attribute-context and unknown-fallback branches of
XSSContextAnalyzer.Analyze: 1) "attr-context-detection" — spin up an httptest
server that returns HTML with the canary reflected inside an attribute (e.g.,
<div title="pd_xss">), call setupTestOptions(server.URL) and
analyzer.Analyze(opts), assert found==true and context contains the
attribute-style token (e.g., "attr:title:pd_xss" or the analyzer's
attr:<name>:<canary> format). 2) "unknown-fallback-detection" — return HTML
where the canary is reflected but not in a recognized text/attr/event context
(e.g., inside a comment or script fragment the analyzer treats as unknown), call
Analyze and assert found==true and context equals or contains
"reflected:unknown". Use the existing analyzer variable and setupTestOptions
helper; mirror the style of the existing subtests for server lifecycle and
require assertions.

In `@pkg/fuzz/analyzers/xss_analyzer.go`:
- Around line 16-18: Remove the non-functional reviewer note from the comment
above ApplyInitialTransformation; leave a concise description of purpose only
(e.g., "ApplyInitialTransformation appends a marker to track reflection.") and
do not include any tool- or reviewer-specific history or names such as
"CodeRabbit" or references to past suggestions; ensure remaining comment may
still reference project pattern like FuzzGenerated.Value in Analyze if needed
for context.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4f7909 and 644112c.

📒 Files selected for processing (2)
  • pkg/fuzz/analyzers/xss_analyzer.go
  • pkg/fuzz/analyzers/xss_analyzer_test.go

Comment on lines 1 to 92
package analyzers

import (
"io"
"strings"

"golang.org/x/net/html"
)

type XSSContextAnalyzer struct{}

func (a *XSSContextAnalyzer) Name() string {
return "xss-context"
}

// ApplyInitialTransformation appends a marker to track reflection.
// Note: CodeRabbit suggested unique canaries, but for now we follow
// the existing project pattern using FuzzGenerated.Value in Analyze.
func (a *XSSContextAnalyzer) ApplyInitialTransformation(data string, params map[string]interface{}) string {
return data + "pd_xss"
}

func (a *XSSContextAnalyzer) Analyze(options *Options) (bool, string, error) {
if options == nil || options.HttpClient == nil || options.FuzzGenerated.Request == nil {
return false, "", nil
}

resp, err := options.HttpClient.Do(options.FuzzGenerated.Request)
if err != nil {
return false, "", err
}
defer resp.Body.Close()

// Limit response reading to 4MB to prevent Out Of Memory (OOM) issues
const maxBodySize = 4 * 1024 * 1024
bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))
if err != nil {
return false, "", err
}

body := string(bodyBytes)
// Use the actual fuzzed value as the canary instead of a hardcoded string
canary := options.FuzzGenerated.Value
if canary == "" {
canary = "pd_xss" // Fallback if Value is not set
}

if !strings.Contains(body, canary) {
return false, "", nil
}

tokenizer := html.NewTokenizer(strings.NewReader(body))
tagDepth := 0

for {
tokenType := tokenizer.Next()
if tokenType == html.ErrorToken {
break
}

token := tokenizer.Token()
switch tokenType {
case html.StartTagToken:
tagDepth++
for _, attr := range token.Attr {
if strings.Contains(attr.Val, canary) {
return true, "attr:" + attr.Key + ":" + token.Data, nil
}
}
case html.SelfClosingTagToken:
for _, attr := range token.Attr {
if strings.Contains(attr.Val, canary) {
return true, "attr:" + attr.Key + ":" + token.Data, nil
}
}
case html.EndTagToken:
if tagDepth > 0 {
tagDepth--
}
case html.TextToken:
if tagDepth > 0 && strings.Contains(token.Data, canary) {
return true, "text:" + token.Data, nil
}
}
}

return true, "reflected:unknown", nil
}

func init() {
RegisterAnalyzer("xss-context", &XSSContextAnalyzer{})
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1/2] gofmt check (should print nothing if properly formatted)"
gofmt -l pkg/fuzz/analyzers/xss_analyzer.go 2>&1 || true

echo ""
echo "[2/2] go vet check"
go vet ./pkg/fuzz/analyzers/... 2>&1 || true

Repository: projectdiscovery/nuclei

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1/1] gofmt check on specific file"
if gofmt -l pkg/fuzz/analyzers/xss_analyzer.go 2>&1 | grep -q .; then
  echo "File needs formatting"
  gofmt -d pkg/fuzz/analyzers/xss_analyzer.go
else
  echo "File is properly formatted"
fi

Repository: projectdiscovery/nuclei

Length of output: 851


Fix Go formatting violations in this file.

The code does not meet gofmt standards. Two issues found:

  1. Line 17: trailing whitespace after "follow" in the comment
  2. Line 92: missing newline at end of file

Run gofmt -w pkg/fuzz/analyzers/xss_analyzer.go to fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/analyzers/xss_analyzer.go` around lines 1 - 92, Remove the trailing
space in the package comment near the ApplyInitialTransformation block (the
comment above ApplyInitialTransformation in XSSContextAnalyzer) and add a final
newline at EOF so the file ends with a newline; run gofmt (or manually remove
the extra whitespace after "follow" in that comment and ensure the file
terminates with a newline) to satisfy formatting for functions/types
XSSContextAnalyzer and ApplyInitialTransformation and the init registration.

@Eliene-byte
Copy link
Author

@Mzack9999 waiting your response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants