MCPInstall

input-sanitization

Эксперт по санитизации ввода. Используй для XSS prevention, encoding, validation и security headers.

Installs
69
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/dengineproblem/agents-monorepo --skill input-sanitization
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads input-sanitization automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Input Sanitization Expert

Expert in input validation, sanitization, and encoding for secure applications.

Core Principles

Validation vs Sanitization vs Encoding

  • Validation: Reject invalid input entirely (preferred)
  • Sanitization: Clean/modify input to make it safe
  • Encoding: Transform input for safe use in specific contexts
  • Apply in order: Validate first, sanitize if needed, encode for output context

Defense in Depth

  • Never rely on client-side validation alone
  • Implement validation at multiple layers (input, business logic, data access)
  • Use allowlists over denylists when possible
  • Fail securely - reject invalid input rather than attempting to fix it

Input Validation Strategies

Strict Validation Patterns (Python)

import re
from typing import Optional

class InputValidator:
    """Strict input validation using allowlist patterns."""

    PATTERNS = {
        'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
        'username': r'^[a-zA-Z0-9_]{3,20}$',
        'phone': r'^\+?1?[0-9]{10,14}$',
        'alphanumeric': r'^[a-zA-Z0-9]+$',
        'safe_filename': r'^[a-zA-Z0-9._-]+$',
        'uuid': r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$',
        'slug': r'^[a-z0-9]+(?:-[a-z0-9]+)*$'
    }

    @staticmethod
    def validate_input(value: str, pattern_type: str, max_length: int = 255) -> Optional[str]:
        """Validate input against allowlist pattern."""
        if not value or len(value) > max_length:
            return None

        pattern = InputValidator.PATTERNS.get(pattern_type)
        if pattern and re.match(pattern, value):
            return value.strip()
        return None

    @staticmethod
    def validate_integer(value: str, min_val: int = None, max_val: int = None) -> Optional[int]:
        """Validate and convert string to integer with bounds checking."""
        try:
            num = int(value)
            if min_val is not None and num < min_val:
                return None
            if max_val is not None and num > max_val:
                return None
            return num
        except (ValueError, TypeError):
            return None

    @staticmethod
    def validate_enum(value: str, allowed_values: set) -> Optional[str]:
        """Validate value against allowed set."""
        if value in allowed_values:
            return value
        return None

JavaScript/TypeScript Validation

class InputValidator {
  private static readonly PATTERNS: Record<string, RegExp> = {
    email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
    username: /^[a-zA-Z0-9_]{3,20}$/,
    phone: /^\+?1?[0-9]{10,14}$/,
    alphanumeric: /^[a-zA-Z0-9]+$/,
    uuid: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
  };

  static validate(value: string, type: string, maxLength = 255): string | null {
    if (!value || value.length > maxLength) {
      return null;
    }

    const pattern = this.PATTERNS[type];
    if (pattern && pattern.test(value)) {
      return value.trim();
    }
    return null;
  }

  static validateInteger(value: string, min?: number, max?: number): number | null {
    const num = parseInt(value, 10);
    if (isNaN(num)) return null;
    if (min !== undefined && num < min) return null;
    if (max !== undefined && num > max) return null;
    return num;
  }

  static sanitizeHtml(input: string): string {
    const map: Record<string, string> = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#x27;',
      '/': '&#x2F;'
    };
    return input.replace(/[&<>"'/]/g, char => map[char]);
  }
}

Context-Specific Encoding

HTML Output Encoding

import html
from markupsafe import escape

def safe_html_output(user_input: str) -> str:
    """Encode for HTML context."""
    return html.escape(user_input, quote=True)

def safe_html_attribute(user_input: str) -> str:
    """Encode for HTML attribute context - more restrictive."""
    encoded = html.escape(user_input, quote=True)
    # Additional encoding for attribute-specific risks
    encoded = encoded.replace("'", "&#x27;").replace("`", "&#x60;")
    return encoded

def safe_html_url(user_input: str) -> str:
    """Encode URL for use in href/src attributes."""
    from urllib.parse import quote
    # Only allow safe URL schemes
    if not user_input.lower().startswith(('http://', 'https://', '/')):
        return '#'
    return quote(user_input, safe=':/?&=#')

JavaScript Context Encoding

class JSEncoder {
    static encodeForJS(input) {
        if (typeof input !== 'string') {
            input = String(input);
        }

        return input
            .replace(/\\/g, '\\\\')
            .replace(/'/g, "\\'")
            .replace(/"/g, '\\"')
            .replace(/\n/g, '\\n')
            .replace(/\r/g, '\\r')
            .replace(/\t/g, '\\t')
            .replace(/</g, '\\u003c')
            .replace(/>/g, '\\u003e');
    }

    static safeJSONStringify(obj) {
        return JSON.stringify(obj)
            .replace(/</g, '\\u003c')
            .replace(/>/g, '\\u003e')
            .replace(/&/g, '\\u0026');
    }

    static encodeForHTMLAttribute(input) {
        return input
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#x27;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    }
}

SQL Context - Parameterized Queries

import sqlite3
from typing import List, Any, Optional

class SafeDatabaseAccess:
    """Always use parameterized queries - never string concatenation."""

    def __init__(self, db_path: str):
        self.db_path = db_path

    def safe_query(self, query: str, params: tuple = ()) -> List[Any]:
        """Execute query with parameters - prevents SQL injection."""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            # Parameters are automatically escaped
            cursor.execute(query, params)
            return cursor.fetchall()

    def get_user_by_email(self, email: str) -> Optional[dict]:
        """Example: safe parameterized query."""
        # Validate email first
        if not InputValidator.validate_input(email, 'email'):
            return None

        # Use ? placeholder - NEVER f-string or .format()
        query = "SELECT id, username, email FROM users WHERE email = ?"
        results = self.safe_query(query, (email,))

        if results:
            return dict(zip(['id', 'username', 'email'], results[0]))
        return None

    # WRONG - SQL Injection vulnerable:
    # query = f"SELECT * FROM users WHERE email = '{email}'"
    # query = "SELECT * FROM users WHERE email = '%s'" % email

File Upload Sanitization

import os
import hashlib
import magic
from pathlib import Path
from typing import Optional

class FileUploadSanitizer:
    """Secure file upload handling."""

    ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.pdf', '.txt', '.docx'}
    ALLOWED_MIME_TYPES = {
        'image/jpeg', 'image/png', 'image/gif',
        'application/pdf', 'text/plain',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    }
    MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB

    @staticmethod
    def sanitize_filename(filename: str) -> str:
        """Generate safe filename."""
        # Remove path components (directory traversal prevention)
        filename = os.path.basename(filename)

        # Remove dangerous characters - allowlist approach
        safe_chars = "-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        filename = ''.join(c for c in filename if c in safe_chars)

        # Limit length
        if len(filename) > 100:
            name, ext = os.path.splitext(filename)
            filename = name[:95] + ext

        return filename or "unnamed_file"

    @classmethod
    def validate_upload(cls, file_data: bytes, filename: str, content_type: str) -> dict:
        """Comprehensive file upload validation."""
        result = {'valid': False, 'errors': [], 'safe_filename': None}

        # Check file size
        if len(file_data) > cls.MAX_FILE_SIZE:
            result['errors'].append(f"File too large: {len(file_data)} bytes (max: {cls.MAX_FILE_SIZE})")

        # Check extension
        ext = Path(filename).suffix.lower()
        if ext not in cls.ALLOWED_EXTENSIONS:
            result['errors'].append(f"Extension not allowed: {ext}")

        # Check declared MIME type
        if content_type not in cls.ALLOWED_MIME_TYPES:
            result['errors'].append(f"MIME type not allowed: {content_type}")

        # Verify actual content type matches (magic bytes)
        try:
            detected_mime = magic.from_buffer(file_data, mime=True)
            if detected_mime != content_type:
                result['errors'].append(f"MIME type mismatch: declared={content_type}, detected={detected_mime}")
        except Exception:
            result['errors'].append("Could not verify file content type")

        result['valid'] = len(result['errors']) == 0
        result['safe_filename'] = cls.sanitize_filename(filename)

        return result

    @staticmethod
    def generate_safe_path(base_dir: str, filename: str) -> str:
        """Generate unique, safe file path."""
        safe_name = FileUploadSanitizer.sanitize_filename(filename)
        # Add hash to prevent overwriting
        hash_prefix = hashlib.md5(os.urandom(16)).hexdigest()[:8]
        name, ext = os.path.splitext(safe_name)
        return os.path.join(base_dir, f"{name}_{hash_prefix}{ext}")

URL and Path Sanitization

from urllib.parse import urlparse, quote
import os.path

class URLSanitizer:
    @staticmethod
    def validate_redirect_url(url: str, allowed_hosts: set) -> Optional[str]:
        """Validate redirect URLs to prevent open redirects."""
        try:
            parsed = urlparse(url)

            # Only allow specific schemes
            if parsed.scheme not in ('http', 'https', ''):
                return None

            # For relative URLs
            if not parsed.netloc:
                # Prevent protocol-relative URLs (//evil.com)
                if url.startswith('//'):
                    return None
                return url

            # Check against allowlist of hosts
            if parsed.netloc not in allowed_hosts:
                return None

            return url
        except Exception:
            return None

    @staticmethod
    def sanitize_path_parameter(path: str, base_dir: str) -> Optional[str]:
        """Prevent directory traversal attacks."""
        # Normalize the path
        normalized = os.path.normpath(path)

        # Check for directory traversal attempts
        if '..' in normalized or normalized.startswith('/') or normalized.startswith('\\'):
            return None

        # Ensure path stays within base directory
        full_path = os.path.abspath(os.path.join(base_dir, normalized))
        base_abs = os.path.abspath(base_dir)

        if not full_path.startswith(base_abs + os.sep):
            return None

        return normalized

Content Security Policy Headers

from flask import Flask, Response

def apply_security_headers(response: Response) -> Response:
    """Apply comprehensive security headers."""

    response.headers.update({
        # Content Security Policy - prevent XSS
        'Content-Security-Policy': '; '.join([
            "default-src 'self'",
            "script-src 'self' https://trusted-cdn.com",
            "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
            "img-src 'self' data: https:",
            "font-src 'self' https://fonts.gstatic.com",
            "connect-src 'self' https://api.example.com",
            "frame-ancestors 'none'",
            "base-uri 'self'",
            "form-action 'self'"
        ]),

        # Prevent MIME type sniffing
        'X-Content-Type-Options': 'nosniff',

        # Clickjacking protection
        'X-Frame-Options': 'DENY',

        # XSS filter (legacy browsers)
        'X-XSS-Protection': '1; mode=block',

        # Force HTTPS
        'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',

        # Referrer policy
        'Referrer-Policy': 'strict-origin-when-cross-origin',

        # Permissions policy
        'Permissions-Policy': 'geolocation=(), microphone=(), camera=()'
    })

    return response


# Express.js equivalent
'''
const helmet = require('helmet');

app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "https://trusted-cdn.com"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
            frameAncestors: ["'none'"]
        }
    },
    hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
    }
}));
'''

XSS Prevention Checklist

Output Encoding:
  - HTML body: Use HTML entity encoding
  - HTML attributes: Use attribute encoding, always quote values
  - JavaScript: Use JavaScript encoding, prefer JSON.stringify
  - CSS: Use CSS encoding
  - URLs: Use URL encoding

Input Validation:
  - Validate type, length, format, range
  - Use allowlists over denylists
  - Reject invalid input, don't sanitize

Security Headers:
  - Implement Content Security Policy
  - Set X-Content-Type-Options: nosniff
  - Set X-Frame-Options: DENY
  - Enable HSTS

Framework Protections:
  - Use auto-escaping template engines
  - Enable CSRF protection
  - Use HttpOnly and Secure cookie flags
  - Implement SameSite cookie attribute

Лучшие практики

  1. Validate at server — никогда не доверяйте client-side валидации
  2. Allowlist approach — определяйте допустимое, не запрещённое
  3. Context-aware encoding — разные контексты требуют разного encoding
  4. Parameterized queries — никогда не конкатенируйте SQL
  5. Defense in depth — валидация на каждом уровне
  6. Fail securely — отклоняйте invalid input, не пытайтесь исправить

FAQ

What does the input-sanitization skill do?

Эксперт по санитизации ввода. Используй для XSS prevention, encoding, validation и security headers.

How do I install input-sanitization?

Run npx skills add https://github.com/dengineproblem/agents-monorepo --skill input-sanitization in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/input-sanitization/ (personal) or .claude/skills/input-sanitization/ (project).

Which AI agents can use input-sanitization?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is input-sanitization safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains input-sanitization?

It is published by dengineproblem in dengineproblem/agents-monorepo. It has 69 recorded installs.

More from dengineproblem/agents-monorepo

View all →

social-media-marketing

dengineproblem/agents-monorepo

Эксперт по SMM. Используй для стратегий в Instagram, Facebook, TikTok, LinkedIn, контент-планов и community management.

Marketing & SEO1.2K installs

frontend-design

dengineproblem/agents-monorepo

Open the skill to read its SKILL.md instructions.

Frontend & Design288 installs

video-marketing

dengineproblem/agents-monorepo

Open the skill to read its SKILL.md instructions.

Marketing & SEO259 installs

k6-load-test

dengineproblem/agents-monorepo

Эксперт k6 нагрузочного тестирования. Используй для performance testing, load scenarios и stress tests.

Testing & QA181 installs

b2b-saas-marketing

dengineproblem/agents-monorepo

Эксперт по B2B SaaS маркетингу. Используй для стратегий генерации спроса, growth-маркетинга, PLG, расчёта CAC/LTV, оптимизации воронки и маркетинговых операций.

Marketing & SEO141 installs

seo-expert

dengineproblem/agents-monorepo

Open the skill to read its SKILL.md instructions.

Marketing & SEO129 installs

Popular security skills

View category →

azure-compliance

microsoft/azure-skills

Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.

OfficialSecurity567K installs

minimal-run-and-audit

lllllllama/rigorpilot-skills

Rigor Run skill for README-first deep learning repo reproduction. Use when the task is specifically to capture or normalize evidence from the selected smoke test or documented inference or evaluation command and write standardized reprooutputs/ files, including patch notes when repository files changed. Do not use for training execution, initial repo intake, generic environment setup, paper lookup, target selection, hidden scientific-meaning changes, or end-to-end orchestration by itself.

Security477.4K installs

seo-audit

coreyhaines31/marketingskills

When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," "indexing issues," "stuck on page 2," "striking distance keywords," "title tag rewrite," "low CTR," "local SEO," "Google Business Profile," or "map pack." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema. For AI search optimization, see ai-seo.

Security238.7K installs

firebase-security-rules-auditor

firebase/agent-skills

Audits Firebase (Firestore, Cloud Storage) security rules for vulnerabilities, privilege escalation, role bypasses, create vs update inconsistencies, resource exhaustion, type safety, size limits, and hasOnly ownership checks. Use when auditing/reviewing rules, running red-team rule assessments, or scoring against auditor checklists. Don't use for Firebase CLI (login, deploy), Auth, Crashlytics, Remote Config, or database queries.

OfficialSecurity139.1K installs

audit

pbakaus/impeccable

Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.

Security81.9K installs

competitors

coreyhaines31/marketingskills

When the user wants to create competitor comparison or alternative pages for SEO and buyer-facing use. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'competitor teardown,' 'audit our competitor pages,' 'are our comparison pages out of date,' or 'competitive asset audit.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For auditing existing claims (not researching competitors from scratch, which is competitor-profiling; not technical SEO on these pages, which is seo-audit), use the asset audit here. For internal battle cards and sales-specific competitor docs, see sales-enablement.

Security79.3K installs