Security Guide

Best practices for secure implementation

Introduction

Important: While ts-ascii-engine has built-in security measures, proper integration requires following security best practices outlined in this guide.

ts-ascii-engine processes user input and generates HTML output, which requires careful handling to maintain security in your applications. This guide covers the security measures implemented in the library and best practices for secure integration.

Built-in Security Protections

Charset Sanitization

Custom charset strings are automatically sanitized to remove dangerous characters:

  • Characters removed: <, >, ', ", &, `
  • Prevents XSS via charset injection
  • Automatic, no user action required
// Dangerous characters automatically removed
const generator = new AsciiGenerator({
  charset: '<script>alert("XSS")</script>'
});
// Resulting charset: 'scriptalert("XSS")/script'

Dimension Limits

Maximum limits enforced to prevent memory exhaustion (DoS attacks):

  • Max width/height: 10,000 characters
  • Max total characters: 25,000,000 (5000×5000)
  • Throws error if exceeded
// Throws error - exceeds maximum
const generator = new AsciiGenerator({
  width: 1000000
});
// Error: Width exceeds maximum allowed (10000)

HTML Escaping

All character output is properly HTML-escaped:

  • Escapes: &, <, >, ", ', `
  • Prevents XSS in HTML output
  • Automatic for all conversions

Text Input Validation

Text-to-ASCII conversion has strict limits:

  • Max text length: 10,000 characters
  • Max font size: 1,000 pixels
  • Font parameters validated against whitelist

CORS Error Handling

Clear error messages for cross-origin image issues:

  • Detects CORS security errors
  • Provides actionable error messages
  • Guides users to proper CORS configuration

Array Bounds Checking

All pixel data access is bounds-checked:

  • Prevents out-of-bounds array access
  • Handles edge cases gracefully
  • No buffer overflows possible

Security Best Practices

1. Sanitize Custom Charsets

Never pass user-controlled data directly as custom charsets:

Unsafe

const userCharset = prompt("Enter charset:");
const generator = new AsciiGenerator({
  charset: userCharset
});

Safe

function isValidCharset(charset) {
  return /^[\w\s\.,-_]+$/.test(charset)
    && charset.length <= 20;
}

const userCharset = prompt("Enter charset:");
if (isValidCharset(userCharset)) {
  const generator = new AsciiGenerator({
    charset: userCharset
  });
}

2. Limit Input Dimensions

Set application-specific limits:

// Recommended approach
function createGenerator(userWidth) {
  const MAX_WIDTH = 300; // Your application's limit
  const safeWidth = Math.min(userWidth, MAX_WIDTH);

  return new AsciiGenerator({ width: safeWidth });
}
Use Case Recommended Width Rationale
Mobile thumbnails 40-60 Fast processing, low memory
Desktop displays 80-120 Good balance of quality and performance
High-quality images 150-200 Maximum detail for static images
Real-time video 60-80 Maintains 30+ FPS

3. Use Content Security Policy (CSP)

Implement CSP headers when displaying HTML output:

<!-- Recommended CSP headers -->
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; style-src 'unsafe-inline'">
Note: 'unsafe-inline' is needed for inline styles if using colored mode. For stricter CSP, use plain text output instead.
// Strictest security - use text output
const result = generator.convertImage(img);
document.getElementById('output').textContent = result.text;

4. Handle CORS Properly

Always set crossOrigin for external images:

// Correct approach
async function loadExternalImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous'; // Important!
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = url;
  });
}

// Usage
try {
  const img = await loadExternalImage('https://example.com/image.jpg');
  const result = generator.convertImage(img);
} catch (error) {
  console.error('Failed to load image:', error);
}

5. Validate Text Inputs

// Safe text conversion
function convertUserText(text) {
  const MAX_LENGTH = 100;
  const safeText = text.substring(0, MAX_LENGTH);

  return generator.convertText(safeText, {
    fontSize: 48,
    fontWeight: 'bold'
  });
}

6. Whitelist Font Families

// Safe font selection
const ALLOWED_FONTS = [
  'Arial',
  'Helvetica',
  'Times New Roman',
  'Courier New'
];

function getSafeFont(userFont) {
  return ALLOWED_FONTS.includes(userFont) ? userFont : 'Arial';
}

const result = generator.convertText('Hello', {
  font: getSafeFont(userInput)
});

7. Implement Rate Limiting

// Debounce user input
import { debounce } from 'lodash';

const debouncedConvert = debounce((img) => {
  const result = generator.convertImage(img);
  updateDisplay(result.html);
}, 300);

inputElement.addEventListener('change', (e) => {
  debouncedConvert(e.target.files[0]);
});

Complete Secure Implementation

Example: Secure ASCII Converter Class

import { AsciiGenerator, CharsetPreset } from 'ts-ascii-engine';

class SecureAsciiConverter {
  private generator: AsciiGenerator;
  private readonly MAX_WIDTH = 200;
  private readonly MAX_TEXT_LENGTH = 500;
  private readonly ALLOWED_FONTS = ['Arial', 'Courier New', 'Georgia'];

  constructor() {
    // Use preset, not user input
    this.generator = new AsciiGenerator({
      charset: CharsetPreset.STANDARD,
      width: this.MAX_WIDTH,
      colored: true
    });
  }

  async convertImageSafely(imageUrl: string): Promise<string> {
    // Validate URL
    if (!this.isValidImageUrl(imageUrl)) {
      throw new Error('Invalid image URL');
    }

    // Load with CORS
    const img = await this.loadImageWithCORS(imageUrl);

    // Convert
    const result = this.generator.convertImage(img);

    return result.html;
  }

  convertTextSafely(text: string, font: string = 'Arial'): string {
    // Sanitize inputs
    const safeText = text.substring(0, this.MAX_TEXT_LENGTH);
    const safeFont = this.ALLOWED_FONTS.includes(font) ? font : 'Arial';

    // Convert
    const result = this.generator.convertText(safeText, {
      font: safeFont,
      fontSize: 48,
      fontWeight: 'bold'
    });

    return result.html;
  }

  private async loadImageWithCORS(url: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = url;
    });
  }

  private isValidImageUrl(url: string): boolean {
    try {
      const parsed = new URL(url);
      return ['http:', 'https:'].includes(parsed.protocol);
    } catch {
      return false;
    }
  }
}

// Usage
const converter = new SecureAsciiConverter();

try {
  const html = await converter.convertImageSafely(userProvidedUrl);
  document.getElementById('output').innerHTML = html;
} catch (error) {
  console.error('Conversion failed:', error);
}

Security Checklist

Before Deploying to Production

Threat Model

Protected Against

  • XSS via charset injection
  • DoS via memory exhaustion
  • HTML injection
  • Out-of-bounds array access
  • CSS injection

Cannot Protect Against

  • Server-side attacks (client-side library)
  • Network attacks during image loading
  • Social engineering
  • Application-level logic flaws

Vulnerability Reporting

Responsible Disclosure: If you discover a security vulnerability, please report it responsibly.

How to Report

  1. Do not open a public issue
  2. Email details to: [your-security-email@example.com]
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if any)
  4. We will respond within 48 hours

Security Updates

Stay Updated: Security fixes are released as patch versions. Always use the latest version.
# Check for updates
npm outdated ts-ascii-engine

# Update to latest version
npm update ts-ascii-engine

Consider using automated dependency management tools:

Additional Resources