cross-site-scripting
Skill2 starsChanged 4 months agoMITView on GitHub
SAST detection methodology for cross-site scripting (CWE-79), including reflected, stored, and DOM-based XSS, mutation XSS (mXSS), framework sink gadgets (dangerouslySetInnerHTML, v-html, bypassSecurityTrust), template auto-escape bypasses, and HTML/attribute/JS/URL/CSS context confusion. Use when reviewing code that emits user data into HTML, the DOM, or a template. Writes confirmed findings to findings/13-cross-site-scripting.md.
In this repository49 other files it gives its agents.
- authentication-flawsSkill
- broken-access-control-idorSkill
- business-logic-flawsSkill
- cicd-and-pipeline-injectionSkill
- code-injectionSkill
- command-injectionSkill
- cors-misconfigurationSkill
- crlf-and-header-injectionSkill
- csrfSkill
- dependency-and-supply-chainSkill
- dom-clobbering-and-postmessageSkill
- graphql-injection-and-abuseSkill
- hardcoded-secretsSkill
- http-request-smugglingSkill
- iac-misconfigurationSkill
- information-disclosureSkill
- insecure-deserializationSkill
- insecure-randomnessSkill
- jwt-vulnerabilitiesSkill
- ldap-injectionSkill
- llm-and-ai-application-securitySkill
- mass-assignmentSkill
- memory-safetySkill
- mobile-android-securitySkill
- mobile-ios-securitySkill
- nosql-injectionSkill
- oauth-oidc-saml-flawsSkill
- open-redirectSkill
- path-traversal-and-file-inclusionSkill
- prototype-pollutionSkill
- race-conditions-and-toctouSkill
- redosSkill
- sast-final-reportSkill
- sast-finding-validationSkill
- sast-methodologySkill
- security-logging-and-monitoring-failuresSkill
- security-misconfiguration-and-headersSkill
- server-side-template-injectionSkill
- session-managementSkill
- smart-contract-securitySkill
- sql-injectionSkill
- ssrfSkill
- unrestricted-file-uploadSkill
- unsafe-parsers-and-yamlSkill
- weak-cryptographySkill
- web-cache-poisoning-and-deceptionSkill
- websocket-securitySkill
- xpath-and-xml-injectionSkill
- xxeSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 41
All HTTP inputs (query/body/path/header/cookie), plus DOM-local sources for DOM XSS: location.href/search/hash/pathname, document.URL, …
- Distinctive: line 150
Stop at a literal/constant.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Cross-Site Scripting (XSS) — SAST Methodology
Class: Cross-Site Scripting · CWE-79 · OWASP: A03 Injection
Findings file: findings/13-cross-site-scripting.md
1. Overview
XSS occurs when attacker-controlled data reaches an HTML/DOM/script rendering
sink without encoding appropriate to the output context, letting the attacker
inject markup or script that executes in a victim's browser session. The core
test: does a source reach a sink that interprets its value as markup/code, and is
the encoding on that path (a) present and (b) correct for that specific context?
Encoding that is safe in HTML text is unsafe in an attribute, a URL, a <script>
block, or a style value — context is everything.
2. Where it lives
- Server-side templates that interpolate values (
render,f-stringsinto HTML,String.format, PHP echo, JSP<%= %>, ERB<%= %>). - Client-side DOM manipulation in JS/TS (
innerHTML,document.write, jQuery). - SPA framework escape hatches (
dangerouslySetInnerHTML,v-html,[innerHTML],bypassSecurityTrustHtml). - API responses reflected into the DOM by a front-end; JSON injected into inline
<script>bootstrap blocks (window.__STATE__ = {{ data }}). - Error pages, search results, chat/comment rendering, markdown-to-HTML,
WYSIWYG/rich-text fields, filenames,
Referer/User-Agentechoed into pages.
3. Sources (tainted input)
All HTTP inputs (query/body/path/header/cookie), plus DOM-local sources for
DOM XSS: location.href/search/hash/pathname, document.URL, document.referrer,
document.cookie, window.name, postMessage data, localStorage/
sessionStorage. Plus stored/second-order: values persisted (DB, cache, file)
then rendered later (comments, profile fields, admin dashboards viewing user data).
Trace stored values back to their write source.
4. Sinks (dangerous operations)
// DOM XSS — dangerous JS sinks
el.innerHTML = location.hash.slice(1); // markup parsed
el.outerHTML = userInput;
el.insertAdjacentHTML('beforeend', userInput);
document.write(userInput); // and document.writeln
el.setAttribute('href', 'javascript:' + x); // URL/JS context
new Function(userInput); eval(userInput); // script context via XSS gadget
$('#x').html(userInput); $('#x').append(userInput); // jQuery parses HTML
$(userInput); // jQuery( '<img onerror=..>' )// React — dangerous
<div dangerouslySetInnerHTML={{ __html: userInput }} />
<a href={userInput}> // javascript: URL not blocked by React<!-- Vue — dangerous -->
<div v-html="userInput"></div>// Angular — dangerous (defeats built-in sanitizer)
this.sanitizer.bypassSecurityTrustHtml(userInput);
this.sanitizer.bypassSecurityTrustResourceUrl(userInput);
element.innerHTML = userInput; // outside Angular binding# Jinja2 / Django — dangerous (auto-escape disabled/bypassed)
Markup(user_input) # marks safe, no escaping
"{{ user_input | safe }}" # Jinja |safe
mark_safe(user_input) # Django
render_template_string(user_input) # SSTI-adjacent, also XSS// PHP — dangerous
echo $_GET['q']; // no htmlspecialchars
print "<div>{$_POST['bio']}</div>";{{! Handlebars — dangerous: triple-stash disables escaping }}
{{{ userInput }}}{# Twig — dangerous #}
{{ userInput | raw }}5. Sanitizers / safe patterns
Safe:
- Context-correct output encoding at the sink: HTML-entity encode for HTML text
(
htmlspecialchars,escape, template auto-escape ON), attribute encoding for attribute context, JS-string encoding +JSON.stringifyfor script context, URL encoding for URL context, and URL-scheme allowlisting (https:/http:/ relative) forhref/src. - Assigning via safe DOM APIs:
textContent,innerText,setAttributefor non-URL attributes,el.classList, framework text interpolation ({{ }}with escaping, React{value}children,[textContent]). - HTML sanitizer libraries for rich text with a strict allowlist: DOMPurify
(
DOMPurify.sanitize), OWASP Java HTML Sanitizer, Bleach (with tight config), Rubysanitize.
Fails / not a real sanitizer:
- Right encoder, wrong context — HTML-encoding a value placed inside a
<script>block, ajavascript:URL, an unquoted attribute, or a CSSstyle. - Blocklist/regex stripping of
<script>,onerror, etc. — bypassed by case, nesting (<scr<script>ipt>), event handlers, SVG/MathML,data:/javascript:URLs, HTML entities. - Encode-then-decode — value encoded server-side but a later
decodeURIComponent/unescape/.innerHTMLre-parses it (mXSS territory). - DOMPurify misconfig —
ALLOWED_ATTRincludinghref/on*,ADD_TAGSre-enabling dangerous elements,RETURN_DOM_FRAGMENTre-inserted viainnerHTMLafter mutation (mXSS), or sanitizing then modifying the string. URL/scheme check that missesjavascript:variants (java\tscript:,JaVaScRiPt:,data:text/html, leading whitespace/control chars).- Angular
bypassSecurityTrust*— by name it disables the sanitizer; never a fix. - Escaping only one of several interpolation points on the same sink.
6. Detection methodology
- Find DOM/HTML sinks:
rg -n 'innerHTML|outerHTML|insertAdjacentHTML|document\.write(ln)?|\.html\(|\.append\(|\.prepend\(|\.before\(|\.after\(' rg -n 'dangerouslySetInnerHTML|v-html|\[innerHTML\]|bypassSecurityTrust' rg -n 'eval\(|new Function\(|setTimeout\(\s*[\'"]|setInterval\(\s*[\'"]' - Find template escape bypasses:
rg -n '\|\s*safe|\|\s*raw|mark_safe|Markup\(|\{\{\{|render_template_string|autoescape\s*(off|false)' rg -n 'html\.escape|htmlspecialchars|escapeHtml' -l # inventory who *does* encode - Find DOM sources feeding sinks:
rg -n 'location\.(hash|search|href|pathname)|document\.(URL|referrer|cookie)|window\.name|localStorage|\.data\b.*addEventListener\([\'"]message' - Determine the output context of each sink hit: HTML text, attribute
(quoted/unquoted), URL attribute (
href/src/action/formaction), inline JS/<script>, event handler, or CSS. The context dictates which encoder is correct. - Trace the operand to a source (direct, DOM-local, or stored/second-order). Stop at a literal/constant.
- Check for a real, context-correct sanitizer on the path (see §5). Verify it isn't bypassable and matches the context.
- Confirm reachability & persistence: reflected (needs a crafted request), stored (persists, hits other users/admins), or DOM (no server round-trip).
7. Modern & niche variants
- Reflected vs stored vs DOM: DOM XSS never touches the server response body — a server-side grep will miss it; you must trace client JS source→sink. Stored is highest-impact (fires for every viewer, including admins) and often second-order.
- Mutation XSS (mXSS): markup that is inert as a string but becomes active
after the browser's HTML parser re-serializes and re-parses it — e.g. inside
<template>,<noscript>,<svg>/<math>foreign-content, or namespace confusion. Bypasses naive sanitizers that inspect the pre-mutation string. Sanitize-then-reinsert-as-HTML and innerHTML round-trips are the classic trigger. - Sink-specific JS gadgets:
innerHTML/outerHTML/insertAdjacentHTML/document.writeparse markup; jQuery.html()/.append()/.after()and even$(selector)parse HTML if the string looks like a tag. ReactdangerouslySetInnerHTML, Vuev-html, Angular[innerHTML]all bypass the framework's default escaping. - Framework sink gadgets: Angular
bypassSecurityTrust{Html,Url,ResourceUrl, Script,Style}and template-injection into Angular expressions; Reacthref/srcacceptingjavascript:; Vue render functions withdomProps.innerHTML. - Template auto-escape bypass: Jinja
|safe/Markup, Twig|raw, Handlebars{{{ }}}, Djangomark_safe/|safe, ERB<%== %>(Railsraw/html_safe), Gotemplate.HTML/template.JS/template.URLtype casts, EJS<%- %>. - Context confusion: the same value is safe in one context and lethal in
another. HTML-text-encoded data in an attribute (breaks out via
"or space), in ajavascript:URL, in an inline<script>/JSON island (breaks out via</script>or), or in CSS (expression(),url(javascript:)). href="javascript:"/ URL-scheme sinks: any sink settinghref,src,action,formaction,xlink:href,data,posterwhere the scheme isn't allowlisted;javascript:,data:text/html,vbscript:.- SVG/MathML vectors:
<svg><script>,<svg onload=…>,<math>withxlink:href,<foreignObject>— foreign-content parsing rules enable mXSS and sanitizer bypass.
8. Common false positives
- Value emitted only via
textContent/innerText/escaped interpolation. - Framework text binding (
{value},{{ }}with auto-escape ON) — safe unless adangerously*/v-html/|safeescape hatch is used. - Static/constant strings or hard-coded HTML not reachable from a source.
- Value passed through a correctly-configured DOMPurify/sanitizer for that context.
hrefbound to a value that is scheme-allowlisted before assignment.innerHTMLset from a trusted, developer-authored constant template.
9. Severity & exploitability
Base Medium for reflected/DOM XSS; High for stored XSS in an authenticated
context (fires for every viewer, can hit admins → session/account takeover).
Raise toward Critical when it yields admin session theft, CSRF-token
exfiltration enabling full account takeover, or executes in a privileged
origin/extension context. Lower if the sink is behind strict CSP that blocks the
payload class, or requires improbable victim interaction. See
references/severity-model.md.
10. Remediation
Encode output for the exact context at the sink (HTML, attribute, JS, URL, CSS) —
prefer framework auto-escaping and never disable it. Use safe DOM APIs
(textContent) instead of innerHTML. For rich text, run a well-configured
allowlist sanitizer (DOMPurify) and re-sanitize after any DOM mutation. Allowlist
URL schemes for href/src. Deploy a strict Content-Security-Policy
(nonce/hash-based, no unsafe-inline) as defense-in-depth. Never use
bypassSecurityTrust*, |safe, raw, {{{ }}}, or mark_safe on tainted data.
11. Output
Append each confirmed finding to findings/13-cross-site-scripting.md using
references/finding-template.md. Set Class: Cross-Site Scripting, CWE: CWE-79,
and in Chain potential note primitives such as script execution in victim
origin (→ session/CSRF-token theft, credential capture, keylogging), DOM sink
gadget (consumes prototype-pollution or DOM-clobbering primitives), or stored
payload delivery (→ privileged-user compromise). XSS frequently consumes an
open-redirect or CORS misconfig and provides the execution primitive that
turns other flaws into account takeover.
Primitives (controlled): provides JS_EXEC; consumes HTML_OUTPUT,GADGET
References
- CWE-79; OWASP A03:2021 Injection; OWASP XSS Prevention & DOM XSS Prevention Cheat Sheets; DOMPurify documentation; Google mXSS research.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

