Overview
The isXSSSafe() function checks if a string contains potential Cross-Site Scripting (XSS) attack vectors. It detects common XSS patterns including script tags, event handlers, dangerous protocols, and other malicious HTML elements.
Common Use Cases
- Validating user-generated content
- Sanitizing form inputs before display
- Checking comment and message content
- Validating rich text editor output
- API input validation
- Content moderation systems
Important Security NoteThis validator detects common XSS patterns but should NOT be your only defense against XSS attacks. Always use proper output encoding, Content Security Policy (CSP), and trusted sanitization libraries like DOMPurify for production applications.
Function Signature
isXSSSafe(input, options)
Parameters
The string to check for XSS vulnerabilities.
Configuration options for XSS validation.Whether to return detailed information about detected XSS patterns instead of a boolean.
Return Value
When details is false: Returns true if safe, false if XSS detected.When details is true: Returns an object with the following properties:Whether the input is safe from detected XSS patterns.
Array of detected XSS pattern descriptions, or null if safe.
The input string that was validated.
Examples
Basic XSS Detection
Safe Content
Dangerous Content
Detailed Results
import { isXSSSafe } from 'validauth';
// Safe text content
isXSSSafe('Hello, this is a normal comment!');
// Returns: true
isXSSSafe('Check out my website: https://example.com');
// Returns: true
isXSSSafe('User input with <b>bold</b> text');
// Returns: true (basic HTML tags are allowed)
import { isXSSSafe } from 'validauth';
// Script tag injection
isXSSSafe('<script>alert("XSS")</script>');
// Returns: false
// Event handler injection
isXSSSafe('<img src=x onerror=alert(1)>');
// Returns: false
// JavaScript protocol
isXSSSafe('<a href="javascript:alert(1)">Click</a>');
// Returns: false
// Iframe injection
isXSSSafe('<iframe src="evil.com"></iframe>');
// Returns: false
import { isXSSSafe } from 'validauth';
const result = isXSSSafe(
'<script>alert("xss")</script><img onerror=alert(1)>',
{ details: true }
);
console.log(result);
/* Returns:
{
safe: false,
errors: [
'Potential XSS detected: <script[^>]*>[\\s\\S]*?</script>',
'Potential XSS detected: onerror\\s*='
],
input: '<script>alert("xss")</script><img onerror=alert(1)>'
}
*/
Content Validation Middleware
import { isXSSSafe } from 'validauth';
function validateUserContent(req, res, next) {
const fieldsToCheck = ['comment', 'message', 'bio', 'title'];
const errors = [];
for (const field of fieldsToCheck) {
if (req.body[field]) {
const result = isXSSSafe(req.body[field], { details: true });
if (!result.safe) {
errors.push({
field: field,
message: 'Content contains potentially dangerous code',
patterns: result.errors
});
}
}
}
if (errors.length > 0) {
return res.status(400).json({
error: 'Invalid content detected',
details: errors
});
}
next();
}
// Usage with Express
// app.post('/api/comment', validateUserContent, createComment);
import { isXSSSafe } from 'validauth';
class CommentValidator {
constructor() {
this.maxLength = 1000;
}
validateComment(comment) {
const errors = [];
// Check length
if (!comment || comment.trim().length === 0) {
errors.push('Comment cannot be empty');
}
if (comment.length > this.maxLength) {
errors.push(`Comment too long (max ${this.maxLength} characters)`);
}
// Check for XSS
const xssResult = isXSSSafe(comment, { details: true });
if (!xssResult.safe) {
errors.push('Comment contains potentially dangerous content');
// Log the specific patterns detected for security monitoring
console.warn('XSS attempt detected:', {
patterns: xssResult.errors,
comment: comment.substring(0, 100) // Log only first 100 chars
});
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : null,
xssDetected: !xssResult.safe
};
}
sanitizeAndValidate(comment) {
const validation = this.validateComment(comment);
if (!validation.valid) {
return {
success: false,
errors: validation.errors
};
}
// Even if XSS check passes, use proper encoding when displaying
return {
success: true,
sanitizedComment: this.htmlEncode(comment)
};
}
// Basic HTML encoding (use a proper library in production)
htmlEncode(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
}
// Usage
const validator = new CommentValidator();
const userComment = 'This is a great article!';
const result = validator.sanitizeAndValidate(userComment);
if (result.success) {
// Save to database
console.log('Safe comment:', result.sanitizedComment);
} else {
console.error('Invalid comment:', result.errors);
}
Rich Text Editor Validation
import { isXSSSafe } from 'validauth';
class RichTextValidator {
constructor() {
// Allowed HTML tags for rich text
this.allowedTags = [
'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3',
'ul', 'ol', 'li', 'a', 'blockquote'
];
}
validateRichText(html) {
// First, check for obvious XSS attacks
const xssCheck = isXSSSafe(html, { details: true });
if (!xssCheck.safe) {
return {
valid: false,
message: 'Content contains dangerous scripts or code',
xssPatterns: xssCheck.errors
};
}
// Additional validation for rich text
const validation = {
valid: true,
warnings: []
};
// Check for data URIs (can be used for XSS)
if (/data:text\/html/gi.test(html)) {
validation.valid = false;
validation.warnings.push('Data URIs are not allowed');
}
// Check for embedded objects
if (/<object/gi.test(html) || /<embed/gi.test(html)) {
validation.valid = false;
validation.warnings.push('Embedded objects are not allowed');
}
// Warn about external links
const externalLinks = html.match(/href=["']https?:\/\/[^"']+["']/gi);
if (externalLinks && externalLinks.length > 0) {
validation.warnings.push(
`Contains ${externalLinks.length} external link(s)`
);
}
return validation;
}
// Validate and sanitize (use DOMPurify in production)
processRichText(html) {
const validation = this.validateRichText(html);
if (!validation.valid) {
return {
success: false,
message: 'Content failed security validation',
errors: validation.warnings
};
}
// In production, use DOMPurify or similar:
// const clean = DOMPurify.sanitize(html, {
// ALLOWED_TAGS: this.allowedTags
// });
return {
success: true,
html: html, // In production, return sanitized HTML
warnings: validation.warnings
};
}
}
// Usage
const richTextValidator = new RichTextValidator();
const editorContent = '<p>Hello <strong>world</strong>!</p>';
const result = richTextValidator.processRichText(editorContent);
if (result.success) {
console.log('Safe content:', result.html);
if (result.warnings.length > 0) {
console.warn('Warnings:', result.warnings);
}
} else {
console.error('Validation failed:', result.errors);
}
Detected XSS Patterns
The validator detects the following common XSS attack patterns:
Detected Patterns
<script> tags and their contents
javascript: protocol in URLs
vbscript: protocol in URLs
data:text/html protocol
- Event handlers:
onload=, onerror=, onclick=, onmouseover=, onmouseout=
<iframe> tags
<object> tags
<embed> tags
<form> tags
<input> tags
<meta> tags
<link> tags
<style> tags and their contents
Complete Security Example
import { isXSSSafe } from 'validauth';
class SecurityValidator {
constructor() {
this.suspiciousPatterns = [
/eval\s*\(/gi,
/expression\s*\(/gi,
/vbscript:/gi,
/javascript:/gi
];
}
// Multi-layer validation
validateUserInput(input, fieldName) {
const results = {
safe: true,
checks: []
};
// Layer 1: Basic XSS check
const xssCheck = isXSSSafe(input, { details: true });
results.checks.push({
name: 'XSS Detection',
passed: xssCheck.safe,
issues: xssCheck.errors
});
if (!xssCheck.safe) {
results.safe = false;
}
// Layer 2: Additional suspicious pattern check
const suspiciousFound = this.suspiciousPatterns.some(
pattern => pattern.test(input)
);
results.checks.push({
name: 'Suspicious Patterns',
passed: !suspiciousFound,
issues: suspiciousFound ? ['Suspicious code patterns detected'] : null
});
if (suspiciousFound) {
results.safe = false;
}
// Layer 3: SQL injection patterns (basic check)
const sqlPatterns = [
/('|(\-\-)|(;)|(\|\|)|(\*))/,
/(union|select|insert|update|delete|drop|create|alter)/gi
];
const sqlFound = sqlPatterns.some(pattern => pattern.test(input));
results.checks.push({
name: 'SQL Injection',
passed: !sqlFound,
issues: sqlFound ? ['Potential SQL injection detected'] : null
});
if (sqlFound) {
results.safe = false;
}
// Log security events
if (!results.safe) {
this.logSecurityEvent({
field: fieldName,
input: input.substring(0, 100),
checks: results.checks.filter(c => !c.passed),
timestamp: new Date().toISOString()
});
}
return results;
}
logSecurityEvent(event) {
// In production, send to security monitoring system
console.warn('SECURITY EVENT:', event);
}
// Get user-friendly error message
getSecurityMessage(results) {
if (results.safe) {
return 'Content is safe';
}
const failedChecks = results.checks
.filter(check => !check.passed)
.map(check => check.name);
return `Security validation failed: ${failedChecks.join(', ')}`;
}
}
// Usage
const validator = new SecurityValidator();
const userInput = '<script>alert("xss")</script>';
const results = validator.validateUserInput(userInput, 'comment');
if (results.safe) {
console.log('Input is safe');
} else {
console.error(validator.getSecurityMessage(results));
console.log('Failed checks:', results.checks.filter(c => !c.passed));
}
Best Practices
Critical Security Practices
- Never rely solely on client-side validation - Always validate on the server
- Use established sanitization libraries - DOMPurify, sanitize-html, etc.
- Implement Content Security Policy (CSP) - Add CSP headers to your application
- Encode output - Always encode user content when displaying it
- Use parameterized queries - Prevent SQL injection attacks
- Implement rate limiting - Prevent automated attacks
- Log security events - Monitor and alert on suspicious activity
Defense in Depth StrategyCombine multiple security layers:
- Input validation (this validator)
- Output encoding (HTML/JS/URL encoding)
- Content Security Policy headers
- Secure cookies (HttpOnly, Secure, SameSite)
- Regular security audits
- Keep dependencies updated
Output Encoding Example
import { isXSSSafe } from 'validauth';
class SecureContentRenderer {
// Validate input
validateContent(content) {
return isXSSSafe(content, { details: true });
}
// Encode for HTML context
encodeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// Encode for JavaScript context
encodeJS(str) {
return str
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r');
}
// Safe rendering
renderContent(content, context = 'html') {
const validation = this.validateContent(content);
if (!validation.safe) {
throw new Error('Content failed XSS validation');
}
switch (context) {
case 'html':
return this.encodeHTML(content);
case 'js':
return this.encodeJS(content);
default:
return this.encodeHTML(content);
}
}
}
Content Security Policy
// Express middleware for CSP headers
function setSecurityHeaders(req, res, next) {
// Content Security Policy
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"font-src 'self'; " +
"connect-src 'self'; " +
"frame-ancestors 'none';"
);
// Other security headers
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
next();
}
// app.use(setSecurityHeaders);
Limitations
Important LimitationsThis validator has important limitations:
- Pattern-based detection can be bypassed with obfuscation
- Does not validate against all possible XSS vectors
- Cannot detect context-specific XSS attacks
- Should be used as one layer in a defense-in-depth strategy
- Not a replacement for proper output encoding and CSP
Testing
import { isXSSSafe } from 'validauth';
// Test cases for XSS validation
const testCases = [
{ input: 'Normal text', expected: true },
{ input: '<script>alert(1)</script>', expected: false },
{ input: '<img src=x onerror=alert(1)>', expected: false },
{ input: 'javascript:alert(1)', expected: false },
{ input: '<iframe src="evil.com"></iframe>', expected: false },
{ input: '<p>Safe HTML</p>', expected: true }
];
testCases.forEach((test, index) => {
const result = isXSSSafe(test.input);
const passed = result === test.expected;
console.log(
`Test ${index + 1}: ${passed ? 'PASS' : 'FAIL'}`,
`"${test.input.substring(0, 30)}..."`
);
});