xss-prevention
OWASP A07 - Cross-Site Scripting (XSS) Prevention. Use this skill when rendering user input in HTML, handling DOM manipulation, or building frontend components. Activate when: XSS, cross-site scripting, user input display, innerHTML, dangerouslySetInnerHTML, template injection, script injection, sanitize HTML, escape output.
What this skill does
# XSS Prevention (OWASP A07)
**Prevent Cross-Site Scripting attacks by properly encoding output and sanitizing user input.**
## When to Use
- Displaying user-generated content
- Building dynamic HTML
- Implementing rich text editors
- Rendering markdown or HTML
- Working with URL parameters in pages
- Building search results pages
## XSS Types
| Type | Vector | Example |
|------|--------|---------|
| Reflected | URL parameters | `?search=<script>alert(1)</script>` |
| Stored | Database content | Comment with malicious script |
| DOM-based | Client-side JS | `document.write(location.hash)` |
## Vulnerable Patterns
### Server-Side
```javascript
// VULNERABLE - Direct interpolation
app.get('/search', (req, res) => {
res.send(`<h1>Results for: ${req.query.q}</h1>`);
});
// VULNERABLE - Template without escaping
res.render('profile', { bio: user.bio }); // If template doesn't auto-escape
```
### Client-Side
```javascript
// VULNERABLE - innerHTML with user data
element.innerHTML = userInput;
document.getElementById('output').innerHTML = data;
// VULNERABLE - document.write
document.write(location.search);
// VULNERABLE - eval with user data
eval(userCode);
// VULNERABLE - jQuery html()
$('#output').html(userData);
// VULNERABLE - React dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html: userContent}} />
```
## Secure Implementation
### 1. Output Encoding
```javascript
// HTML entity encoding
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
return text.replace(/[&<>"'/]/g, char => map[char]);
}
// Usage
app.get('/search', (req, res) => {
const safeQuery = escapeHtml(req.query.q);
res.send(`<h1>Results for: ${safeQuery}</h1>`);
});
```
### 2. Context-Aware Encoding
```javascript
// Different contexts need different encoding
const encoders = {
// HTML body context
html: (str) => str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>'),
// HTML attribute context
attr: (str) => str
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, '''),
// JavaScript string context
js: (str) => str
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n'),
// URL parameter context
url: (str) => encodeURIComponent(str),
// CSS context
css: (str) => str.replace(/[^a-zA-Z0-9]/g, char =>
'\\' + char.charCodeAt(0).toString(16) + ' '
)
};
// Usage based on context
`<div>${encoders.html(userInput)}</div>`
`<input value="${encoders.attr(userInput)}">`
`<script>var x = '${encoders.js(userInput)}';</script>`
`<a href="/search?q=${encoders.url(userInput)}">`
```
### 3. Content Security Policy (CSP)
```javascript
// Express with helmet
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"], // No 'unsafe-inline'!
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"]
}
}));
// With nonce for inline scripts (when needed)
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
next();
});
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`]
}
}));
// In template
`<script nonce="${nonce}">...</script>`
```
### 4. HTML Sanitization
```javascript
// Using DOMPurify (recommended)
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
function sanitizeHtml(dirty) {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'target'],
ALLOW_DATA_ATTR: false
});
}
// For rich text editors
function sanitizeRichText(html) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: [
'h1', 'h2', 'h3', 'p', 'br', 'ul', 'ol', 'li',
'b', 'i', 'u', 'strong', 'em', 'a', 'img',
'blockquote', 'code', 'pre'
],
ALLOWED_ATTR: ['href', 'src', 'alt', 'class'],
FORBID_TAGS: ['script', 'style', 'iframe', 'form', 'input'],
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']
});
}
```
### 5. React Safe Patterns
```jsx
// SAFE - React auto-escapes by default
function UserProfile({ user }) {
return <div>{user.name}</div>; // Automatically escaped
}
// SAFE - Use textContent for DOM
useEffect(() => {
document.getElementById('output').textContent = userInput;
}, [userInput]);
// When you MUST render HTML, sanitize first
import DOMPurify from 'dompurify';
function RichContent({ html }) {
const sanitized = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{__html: sanitized}} />;
}
// SAFE - URL validation
function SafeLink({ url, children }) {
const isValidUrl = url.startsWith('https://') || url.startsWith('/');
if (!isValidUrl) {
return <span>{children}</span>;
}
return <a href={url}>{children}</a>;
}
```
### 6. Vue.js Safe Patterns
```vue
<!-- SAFE - Vue auto-escapes -->
<template>
<div>{{ userInput }}</div>
</template>
<!-- DANGEROUS - v-html -->
<template>
<div v-html="userContent"></div> <!-- XSS risk! -->
</template>
<!-- SAFE - Sanitize before v-html -->
<script>
import DOMPurify from 'dompurify';
export default {
computed: {
safeContent() {
return DOMPurify.sanitize(this.userContent);
}
}
}
</script>
<template>
<div v-html="safeContent"></div>
</template>
```
### 7. URL Validation
```javascript
// Prevent javascript: URLs
function isSafeUrl(url) {
try {
const parsed = new URL(url, window.location.origin);
return ['http:', 'https:', 'mailto:'].includes(parsed.protocol);
} catch {
return false;
}
}
// Usage
function SafeAnchor({ href, children }) {
if (!isSafeUrl(href)) {
return <span>{children}</span>;
}
return <a href={href} rel="noopener noreferrer">{children}</a>;
}
```
## Framework-Specific Guidance
### Express/Node.js
- Use template engines with auto-escaping (EJS, Pug, Handlebars)
- Set `{escape: true}` in template options
- Use helmet for CSP headers
### Django
- Templates auto-escape by default
- Use `|safe` filter only with sanitized content
- Enable CSP middleware
### Rails
- ERB auto-escapes with `<%= %>`
- Use `raw()` or `html_safe` only with sanitized content
- Use `sanitize()` helper for user HTML
### Laravel
- Blade `{{ }}` auto-escapes
- Use `{!! !!}` only with sanitized content
- Use `e()` helper for manual escaping
## Code Review Checklist
- [ ] All user input encoded before output
- [ ] Context-appropriate encoding used
- [ ] No innerHTML with unsanitized data
- [ ] No dangerouslySetInnerHTML without sanitization
- [ ] No eval() or new Function() with user data
- [ ] CSP headers configured
- [ ] URL schemes validated (no javascript:)
- [ ] Rich text sanitized with DOMPurify or similar
- [ ] Template engine auto-escaping enabled
- [ ] No user data in script blocks
## Testing for XSS
```html
<!-- Test payloads -->
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<body onload=alert('XSS')>
"><script>alert('XSS')</script>
'-alert('XSS')-'
javascript:alert('XSS')
data:text/html,<script>alert('XSS')</script>
```
```bash
# Automated scanning
# Use Burp Suite, OWASP ZAP, or XSStrike
python xsstrike.py -u "http://target.com/search?q=test"
```
## Best Practices
1. **Encode Output**: Always encode data based on context
2. **Use CSP**: Defense in depth against XSS
3. **Sanitize HTML**: Use DOMPurify for user HTML
4. **Validate URLs**: Block javascript: and data: URLs
5. **HttpOnly Cookies**: Prevent cookie theft
6Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.