
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- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads input-sanitization automatically when relevant.
- 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> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
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("'", "'").replace("`", "`")
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, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
}
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
Лучшие практики
- Validate at server — никогда не доверяйте client-side валидации
- Allowlist approach — определяйте допустимое, не запрещённое
- Context-aware encoding — разные контексты требуют разного encoding
- Parameterized queries — никогда не конкатенируйте SQL
- Defense in depth — валидация на каждом уровне
- 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
social-media-marketing
dengineproblem/agents-monorepo
Эксперт по SMM. Используй для стратегий в Instagram, Facebook, TikTok, LinkedIn, контент-планов и community management.
frontend-design
dengineproblem/agents-monorepo
Open the skill to read its SKILL.md instructions.
video-marketing
dengineproblem/agents-monorepo
Open the skill to read its SKILL.md instructions.
k6-load-test
dengineproblem/agents-monorepo
Эксперт k6 нагрузочного тестирования. Используй для performance testing, load scenarios и stress tests.
b2b-saas-marketing
dengineproblem/agents-monorepo
Эксперт по B2B SaaS маркетингу. Используй для стратегий генерации спроса, growth-маркетинга, PLG, расчёта CAC/LTV, оптимизации воронки и маркетинговых операций.
seo-expert
dengineproblem/agents-monorepo
Open the skill to read its SKILL.md instructions.
Popular security skills
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.
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.
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.
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.
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.
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.