
Xss Prevention
- 428 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
xss-prevention is an agent skill that audits and fixes cross-site scripting risks through input sanitization, output encoding, Content Security Policy headers, and secure templating for developers shipping user-generated
About
xss-prevention is a useful-ai-prompts security skill for web applications displaying user-generated content, rich text editors, comment systems, search results, and dynamic HTML templates. It provides Node.js quick-start patterns using DOMPurify and he HTML entity encoding, with four reference guides covering Node.js, Python, React XSS prevention, and Content Security Policy configuration. Best practices emphasize encoding output by default, sanitizing rich HTML, validating URLs, using HTTPOnly cookies, and avoiding innerHTML and eval. Developers reach for xss-prevention before launch when auditing templates, comment forms, or search pages for reflected, stored, or DOM-based XSS vectors across JavaScript, Python, and React stacks.
- Output encoding rules
- Content Security Policy templates
- HTML sanitization patterns
- DOM XSS mitigation
- User-generated content hardening
Xss Prevention by the numbers
- 428 all-time installs (skills.sh)
- Ranked #533 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill xss-preventionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 428 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you prevent XSS in user-generated web content?
Audit and fix cross-site scripting risks in web apps, templates, and user-generated content surfaces before public launch.
Who is it for?
Web developers shipping comment systems, rich text editors, search pages, or dynamic templates that render untrusted user input.
Skip if: Server-side authorization bugs, SQL injection, or CSRF-only reviews unrelated to HTML and JavaScript injection in the browser.
When should I use this skill?
The application displays user-generated content, uses rich text editors, renders dynamic HTML, or needs a pre-launch XSS security audit.
What you get
Sanitized rendering code, CSP header configuration, encoded output helpers, and a checklist of fixed XSS vectors across affected templates.
- sanitized rendering helpers
- CSP header config
- XSS audit fixes
By the numbers
- Includes 4 reference guides: Node.js, Python, React XSS prevention, and CSP
- Quick-start demonstrates DOMPurify sanitizeHTML and he encodeHTML helpers
Files
XSS Prevention
Table of Contents
Overview
Implement comprehensive Cross-Site Scripting (XSS) prevention using input sanitization, output encoding, CSP headers, and secure coding practices.
When to Use
- User-generated content display
- Rich text editors
- Comment systems
- Search functionality
- Dynamic HTML generation
- Template rendering
Quick Start
Minimal working example:
// xss-prevention.js
const createDOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");
const he = require("he");
const window = new JSDOM("").window;
const DOMPurify = createDOMPurify(window);
class XSSPrevention {
/**
* HTML Entity Encoding - Safest for text content
*/
static encodeHTML(str) {
return he.encode(str, {
useNamedReferences: true,
encodeEverything: false,
});
}
/**
* Sanitize HTML - For rich content
*/
static sanitizeHTML(dirty) {
const config = {
ALLOWED_TAGS: [
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Node.js XSS Prevention | Node.js XSS Prevention |
| Python XSS Prevention | Python XSS Prevention |
| React XSS Prevention | React XSS Prevention |
| Content Security Policy | Content Security Policy |
Best Practices
✅ DO
- Encode output by default
- Use templating engines
- Implement CSP headers
- Sanitize rich content
- Validate URLs
- Use HTTPOnly cookies
- Regular security testing
- Use secure frameworks
❌ DON'T
- Trust user input
- Use innerHTML directly
- Skip output encoding
- Allow inline scripts
- Use eval()
- Mix contexts (HTML/JS)
Content Security Policy
Content Security Policy
// csp-config.js
const helmet = require("helmet");
function setupCSP(app) {
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
// Only allow scripts from trusted sources
scriptSrc: [
"'self'",
"'nonce-RANDOM_NONCE'", // Use dynamic nonces
"https://cdn.example.com",
],
// Styles
styleSrc: [
"'self'",
"'nonce-RANDOM_NONCE'",
"https://fonts.googleapis.com",
],
// No inline styles/scripts
objectSrc: ["'none'"],
baseUri: ["'self'"],
// Report violations
reportUri: ["/api/csp-violations"],
},
}),
);
// CSP violation reporter
app.post("/api/csp-violations", (req, res) => {
console.error("CSP Violation:", req.body);
res.status(204).end();
});
}
// Generate nonce for inline scripts
function generateNonce() {
return require("crypto").randomBytes(16).toString("base64");
}
// Express middleware to add nonce
app.use((req, res, next) => {
res.locals.nonce = generateNonce();
next();
});
// In templates: <script nonce="<%= nonce %>">Node.js XSS Prevention
Node.js XSS Prevention
// xss-prevention.js
const createDOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");
const he = require("he");
const window = new JSDOM("").window;
const DOMPurify = createDOMPurify(window);
class XSSPrevention {
/**
* HTML Entity Encoding - Safest for text content
*/
static encodeHTML(str) {
return he.encode(str, {
useNamedReferences: true,
encodeEverything: false,
});
}
/**
* Sanitize HTML - For rich content
*/
static sanitizeHTML(dirty) {
const config = {
ALLOWED_TAGS: [
"p",
"br",
"strong",
"em",
"u",
"h1",
"h2",
"h3",
"ul",
"ol",
"li",
"a",
"img",
"blockquote",
"code",
],
ALLOWED_ATTR: ["href", "src", "alt", "title", "class"],
ALLOWED_URI_REGEXP: /^(?:https?|mailto):/i,
KEEP_CONTENT: true,
RETURN_DOM: false,
RETURN_DOM_FRAGMENT: false,
};
return DOMPurify.sanitize(dirty, config);
}
/**
* Strict sanitization - For untrusted HTML
*/
static sanitizeStrict(dirty) {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ["b", "i", "em", "strong"],
ALLOWED_ATTR: [],
KEEP_CONTENT: true,
});
}
/**
* JavaScript context encoding
*/
static encodeForJS(str) {
return str.replace(/[<>"'&]/g, (char) => {
const escape = {
"<": "\\x3C",
">": "\\x3E",
'"': "\\x22",
"'": "\\x27",
"&": "\\x26",
};
return escape[char];
});
}
/**
* URL parameter encoding
*/
static encodeURL(str) {
return encodeURIComponent(str);
}
/**
* Attribute context encoding
*/
static encodeAttribute(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/\//g, "/");
}
/**
* Validate and sanitize URLs
*/
static sanitizeURL(url) {
try {
const parsed = new URL(url);
// Only allow safe protocols
if (!["http:", "https:", "mailto:"].includes(parsed.protocol)) {
return "";
}
return parsed.href;
} catch {
return "";
}
}
/**
* Strip all HTML tags
*/
static stripHTML(str) {
return str.replace(/<[^>]*>/g, "");
}
/**
* React-style JSX escaping
*/
static escapeForReact(str) {
return {
__html: DOMPurify.sanitize(str),
};
}
}
// Express middleware
function xssProtection(req, res, next) {
// Sanitize request body
if (req.body) {
req.body = sanitizeObject(req.body);
}
// Sanitize query parameters
if (req.query) {
req.query = sanitizeObject(req.query);
}
next();
}
function sanitizeObject(obj) {
const sanitized = {};
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "string") {
sanitized[key] = XSSPrevention.stripHTML(value);
} else if (typeof value === "object" && value !== null) {
sanitized[key] = sanitizeObject(value);
} else {
sanitized[key] = value;
}
}
return sanitized;
}
// Express example
const express = require("express");
const app = express();
app.use(express.json());
app.use(xssProtection);
app.post("/api/comments", (req, res) => {
const { comment } = req.body;
// Additional sanitization for rich content
const safeComment = XSSPrevention.sanitizeHTML(comment);
// Store in database
// db.comments.insert({ content: safeComment });
res.json({ comment: safeComment });
});
module.exports = XSSPrevention;Python XSS Prevention
Python XSS Prevention
# xss_prevention.py
import html
import bleach
from urllib.parse import urlparse, quote
import re
class XSSPrevention:
# Allowed HTML tags for rich content
ALLOWED_TAGS = [
'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3',
'ul', 'ol', 'li', 'a', 'blockquote', 'code'
]
ALLOWED_ATTRIBUTES = {
'a': ['href', 'title'],
'img': ['src', 'alt']
}
@staticmethod
def encode_html(text: str) -> str:
"""HTML entity encoding - safest for text content"""
return html.escape(text, quote=True)
@staticmethod
def sanitize_html(dirty_html: str) -> str:
"""Sanitize HTML - for rich content"""
return bleach.clean(
dirty_html,
tags=XSSPrevention.ALLOWED_TAGS,
attributes=XSSPrevention.ALLOWED_ATTRIBUTES,
strip=True
)
@staticmethod
def sanitize_strict(dirty_html: str) -> str:
"""Strict sanitization - strip all HTML"""
return bleach.clean(
dirty_html,
tags=[],
attributes={},
strip=True
)
@staticmethod
def strip_html(text: str) -> str:
"""Remove all HTML tags"""
return re.sub(r'<[^>]*>', '', text)
@staticmethod
def sanitize_url(url: str) -> str:
"""Validate and sanitize URLs"""
try:
parsed = urlparse(url)
# Only allow safe protocols
if parsed.scheme not in ['http', 'https', 'mailto']:
return ''
return url
except:
return ''
@staticmethod
def encode_for_javascript(text: str) -> str:
"""Encode for JavaScript context"""
escape_map = {
'<': '\\x3C',
'>': '\\x3E',
'"': '\\x22',
"'": '\\x27',
'&': '\\x26',
'/': '\\x2F'
}
return ''.join(escape_map.get(char, char) for char in text)
@staticmethod
def encode_url_param(text: str) -> str:
"""Encode for URL parameters"""
return quote(text, safe='')
# Flask integration
from flask import Flask, request, jsonify
from functools import wraps
app = Flask(__name__)
def sanitize_input(f):
"""Decorator to sanitize all request inputs"""
@wraps(f)
def decorated_function(*args, **kwargs):
if request.is_json:
data = request.get_json()
request._cached_json = sanitize_dict(data)
return f(*args, **kwargs)
return decorated_function
def sanitize_dict(data: dict) -> dict:
"""Recursively sanitize dictionary values"""
sanitized = {}
for key, value in data.items():
if isinstance(value, str):
sanitized[key] = XSSPrevention.strip_html(value)
elif isinstance(value, dict):
sanitized[key] = sanitize_dict(value)
elif isinstance(value, list):
sanitized[key] = [
sanitize_dict(item) if isinstance(item, dict)
else XSSPrevention.strip_html(item) if isinstance(item, str)
else item
for item in value
]
else:
sanitized[key] = value
return sanitized
@app.route('/api/comments', methods=['POST'])
@sanitize_input
def create_comment():
data = request.get_json()
comment = data.get('comment', '')
# Additional rich content sanitization
safe_comment = XSSPrevention.sanitize_html(comment)
return jsonify({'comment': safe_comment})
# Django template filter
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(name='sanitize_html')
def sanitize_html_filter(value):
"""Django template filter for HTML sanitization"""
sanitized = XSSPrevention.sanitize_html(value)
return mark_safe(sanitized)
# Usage in templates:
# {{ user_content|sanitize_html }}React XSS Prevention
React XSS Prevention
// XSSSafeComponent.jsx
import React from "react";
import DOMPurify from "dompurify";
// Safe text rendering (React automatically escapes)
function SafeText({ text }) {
return <div>{text}</div>;
}
// Sanitized HTML rendering
function SafeHTML({ html }) {
const sanitized = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ["p", "br", "strong", "em", "u", "a"],
ALLOWED_ATTR: ["href"],
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}
// Safe URL attribute
function SafeLink({ href, children }) {
const safeHref = sanitizeURL(href);
return (
<a href={safeHref} rel="noopener noreferrer" target="_blank">
{children}
</a>
);
}
function sanitizeURL(url) {
try {
const parsed = new URL(url);
if (!["http:", "https:"].includes(parsed.protocol)) {
return "";
}
return parsed.href;
} catch {
return "";
}
}
// Input sanitization hook
function useSanitizedInput(initialValue = "") {
const [value, setValue] = React.useState(initialValue);
const handleChange = (e) => {
const sanitized = DOMPurify.sanitize(e.target.value, {
ALLOWED_TAGS: [],
KEEP_CONTENT: true,
});
setValue(sanitized);
};
return [value, handleChange];
}
// Usage
function CommentForm() {
const [comment, handleCommentChange] = useSanitizedInput();
const handleSubmit = async (e) => {
e.preventDefault();
await fetch("/api/comments", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ comment }),
});
};
return (
<form onSubmit={handleSubmit}>
<textarea
value={comment}
onChange={handleCommentChange}
placeholder="Enter comment"
/>
<button type="submit">Submit</button>
</form>
);
}
export { SafeText, SafeHTML, SafeLink, useSanitizedInput };// Component: [Name]
// TODO: Customize for your framework (React, Vue, Svelte, etc.)
import React from 'react';
interface Props {
// TODO: Define props
}
export function ComponentName({ }: Props) {
// TODO: Add state and effects
return (
<div>
{/* TODO: Add component markup */}
</div>
);
}
Related skills
How it compares
Use xss-prevention for browser-side injection hardening rather than general OWASP checklists that span unrelated vulnerability classes.
FAQ
What XSS techniques does xss-prevention cover?
xss-prevention implements HTML entity encoding for text, DOMPurify sanitization for rich HTML, URL validation, Content Security Policy headers, HTTPOnly cookies, and framework-specific guidance in Node.js, Python, and React reference guides.
When should xss-prevention run in development?
xss-prevention applies when web apps display user-generated content, rich text editors, comment systems, search functionality, or dynamically generated HTML templates—typically during pre-launch security review of rendering surfaces.
What should xss-prevention avoid in code?
xss-prevention warns against trusting user input, using innerHTML directly, skipping output encoding, allowing inline scripts, calling eval, and mixing HTML and JavaScript contexts without proper encoding or sanitization boundaries.