Skip to main content

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

input
string
required
The string to check for XSS vulnerabilities.
options
object
Configuration options for XSS validation.
options.details
boolean
default:"false"
Whether to return detailed information about detected XSS patterns instead of a boolean.

Return Value

result
boolean | object
When details is false: Returns true if safe, false if XSS detected.When details is true: Returns an object with the following properties:
safe
boolean
Whether the input is safe from detected XSS patterns.
errors
string[] | null
Array of detected XSS pattern descriptions, or null if safe.
input
string
The input string that was validated.

Examples

Basic XSS Detection

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)

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);

Comment System Example

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, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#x27;')
      .replace(/\//g, '&#x2F;');
  }
}

// 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
  1. Never rely solely on client-side validation - Always validate on the server
  2. Use established sanitization libraries - DOMPurify, sanitize-html, etc.
  3. Implement Content Security Policy (CSP) - Add CSP headers to your application
  4. Encode output - Always encode user content when displaying it
  5. Use parameterized queries - Prevent SQL injection attacks
  6. Implement rate limiting - Prevent automated attacks
  7. 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)}..."`
  );
});

Build docs developers (and LLMs) love