/** * ValidationService - Centralized validation and sanitization for DollhouseMCP * * Eliminates duplicate validation code across element managers by providing * a unified security-first validation approach. * * Key Features: * - Security-first pattern: validate → sanitize → re-validate * - Wraps existing validators (UnicodeValidator, ContentValidator, sanitizeInput) * - Consistent validation algorithm across all element types * - Comprehensive error reporting and logging * - Full TypeScript type safety * * Security Pattern: * The service enforces the correct validation sequence to prevent security bypasses: * 1. VALIDATE input against pattern/rules (reject malicious input) * 2. SANITIZE if validation passes (clean up input) * 3. RE-VALIDATE sanitized output (ensure sanitization didn't break constraints) * * WRONG (common bug): * const sanitized = sanitizeInput(category); * metadata.category = sanitized.toLowerCase(); // No validation! * * CORRECT (what this service enforces): * if (!isValidCategory(category)) throw error; // Validate FIRST * const sanitized = sanitizeInput(category); // Then sanitize * if (!isValidCategory(sanitized)) throw error; // Re-validate * * @example * ```typescript * import { validationService } from './services/ValidationService'; * * // High-level coordinated validation * const result = validationService.validateAndSanitizeInput(userInput, { * maxLength: 200, * allowSpaces: true * }); * * if (!result.isValid) { * console.error(result.errors); * } else { * use(result.sanitizedValue); * } * * // Metadata field validation * const categoryResult = validationService.validateCategory('creative'); * const usernameResult = validationService.validateUsername('john-doe'); * const emailResult = validationService.validateEmail('user@example.com'); * ``` */ import type { ContentValidatorOptions, ContentValidationResult } from '../../security/contentValidator.js'; /** * Field types for validation with appropriate strictness levels */ export type ValidationFieldType = 'name' | 'description' | 'content' | 'filename'; /** * Options for input validation */ export interface InputValidationOptions { /** Maximum length of input (default: 1000) */ maxLength?: number; /** Allow spaces in input (default: false) */ allowSpaces?: boolean; /** Custom validation pattern (overrides default) */ customPattern?: RegExp; /** Skip Unicode normalization (default: false) */ skipUnicode?: boolean; /** Field type for appropriate validation strictness (default: validates based on allowSpaces) */ fieldType?: ValidationFieldType; } /** * Options for metadata field validation */ export interface FieldValidationOptions { /** Whether field is required (default: true) */ required?: boolean; /** Maximum field length (default: 500) */ maxLength?: number; /** Custom validation pattern */ pattern?: RegExp; } /** * Generic validation result */ export interface ValidationResult { /** Whether the input passed all validation checks */ isValid: boolean; /** Sanitized and validated value (only present if isValid is true) */ sanitizedValue?: string; /** List of validation errors (only present if isValid is false) */ errors?: string[]; /** List of validation warnings (non-fatal issues) */ warnings?: string[]; } /** * Field-specific validation result with additional metadata */ export interface FieldValidationResult extends ValidationResult { /** Original field name that was validated */ fieldName?: string; /** Whether the field was empty before validation */ wasEmpty?: boolean; } /** * Result from Unicode validation */ export interface UnicodeValidationResult { /** Whether the Unicode content is valid */ isValid: boolean; /** Normalized Unicode content */ normalizedContent: string; /** Detected Unicode issues */ detectedIssues?: string[]; /** Severity of detected issues */ severity?: 'low' | 'medium' | 'high' | 'critical'; } /** * Service for validating and sanitizing input across all element types * * This service provides centralized validation to eliminate code duplication * and enforce the security-first validation pattern across the codebase. */ export declare class ValidationService { /** * Validate and sanitize general input with sanitize-then-validate approach * * Validation process: * 1. Check for empty/null input * 2. SANITIZE input first (remove control chars, shell metacharacters, dangerous patterns) * 3. Normalize Unicode if enabled (convert confusables, remove zero-width) * 4. VALIDATE the cleaned result against field-appropriate pattern * 5. Log any security issues detected * * This approach allows legitimate Unicode content (like "Cafe Assistant") to pass * while still blocking security threats after they've been normalized/sanitized. * * @param input - Raw input string to validate * @param options - Validation options * @returns Validation result with sanitized value or errors * * @example * ```typescript * const result = service.validateAndSanitizeInput('user-input', { * maxLength: 100, * allowSpaces: true, * fieldType: 'name' * }); * * if (result.isValid) { * console.log('Clean input:', result.sanitizedValue); * } else { * console.error('Validation failed:', result.errors); * } * ``` */ validateAndSanitizeInput(input: string, options?: InputValidationOptions): ValidationResult; /** * Get the appropriate validation pattern for a field type * * @param options - Input validation options * @returns RegExp pattern appropriate for the field type */ private getPatternForField; /** * Validate a metadata field with sanitize-then-validate approach * * @param fieldName - Name of the field being validated * @param value - Field value to validate * @param options - Field validation options * @returns Field validation result * * @example * ```typescript * const result = service.validateMetadataField('category', 'creative', { * required: true, * maxLength: 50 * }); * ``` */ validateMetadataField(fieldName: string, value: unknown, options?: FieldValidationOptions): FieldValidationResult; /** * Validate a category field * * Enforces security-first pattern: * 1. Validate format (lowercase letters and hyphens only) * 2. Sanitize input * 3. Re-validate sanitized output * * @param category - Category string to validate * @returns Validation result with sanitized category * * @example * ```typescript * const result = service.validateCategory('creative'); * if (result.isValid) { * metadata.category = result.sanitizedValue; * } * ``` */ validateCategory(category: string): ValidationResult; /** * Validate a username field * * @param username - Username string to validate * @returns Validation result with sanitized username * * @example * ```typescript * const result = service.validateUsername('john-doe'); * ``` */ validateUsername(username: string): ValidationResult; /** * Validate an email address * * @param email - Email address to validate * @returns Validation result with sanitized email * * @example * ```typescript * const result = service.validateEmail('user@example.com'); * ``` */ validateEmail(email: string): ValidationResult; /** * Normalize Unicode content using UnicodeValidator * * Delegates to existing UnicodeValidator.normalize() to prevent * homograph attacks, direction override attacks, and other Unicode-based bypasses. * * @param text - Text to normalize * @returns Unicode validation result with normalized content * * @example * ```typescript * const result = service.normalizeUnicode('text with unicode'); * console.log(result.normalizedContent); * ``` */ normalizeUnicode(text: string): UnicodeValidationResult; /** * Validate content for security threats using ContentValidator * * Delegates to existing ContentValidator.validateAndSanitize() to detect * prompt injection, YAML bombs, command injection, and other content-based attacks. * * @param content - Content to validate * @param options - Content validation options * @returns Content validation result * * @example * ```typescript * const result = service.validateContent(personaContent, { * skipSizeCheck: false * }); * * if (!result.isValid) { * console.error('Security threat detected:', result.detectedPatterns); * } * ``` */ validateContent(content: string, options?: ContentValidatorOptions): ContentValidationResult; /** * Find characters in `input` that do not match `charTest`. * Returns deduplicated list of invalid characters. */ private findInvalidCharacters; /** * Format a list of characters for display in error messages. * Labels spaces and non-printable characters; caps output at 5 unique chars. */ private formatCharList; /** * Resolve which PATTERN_DESCRIPTIONS entry applies for InputValidationOptions. * Mirrors the logic of `getPatternForField`. */ private getPatternDescription; /** * Reverse-lookup from a RegExp instance to its PATTERN_DESCRIPTIONS entry. * Compares by source+flags. Returns undefined for unknown patterns. */ private getDescriptionForPattern; } /** * Singleton instance of ValidationService * * Use this instance throughout the application to ensure consistent * validation behavior across all element managers. * * @example * ```typescript * import { validationService } from './services/ValidationService'; * * // In any element manager: * const result = validationService.validateCategory(metadata.category); * if (!result.isValid) { * throw new Error(result.errors.join(', ')); * } * metadata.category = result.sanitizedValue; * ``` */ export declare const validationService: ValidationService; //# sourceMappingURL=ValidationService.d.ts.map