lumaspeech-backend / utils /input_validation.py
yasmine hemmati
Revert "Auto-deploy from GitHub 2026-02-11"
24ea8aa
Raw
History Blame Contribute Delete
2.68 kB
"""
Input validation utilities for audio uploads and text sanitization.
"""
import re
import logging
from functools import wraps
from flask import request, jsonify
logger = logging.getLogger(__name__)
# Patterns commonly used in prompt injection attempts
_INJECTION_PATTERNS = [
re.compile(r'ignore\s+(all\s+)?previous\s+instructions', re.IGNORECASE),
re.compile(r'ignore\s+(all\s+)?above\s+instructions', re.IGNORECASE),
re.compile(r'^system\s*:', re.IGNORECASE | re.MULTILINE),
re.compile(r'you\s+are\s+now\s+(?:a|an|the)\s+', re.IGNORECASE),
re.compile(r'pretend\s+(?:you\s+are|to\s+be)', re.IGNORECASE),
re.compile(r'act\s+as\s+(?:a|an|if)', re.IGNORECASE),
re.compile(r'new\s+instructions?\s*:', re.IGNORECASE),
re.compile(r'disregard\s+(?:all\s+)?(?:previous|prior|above)', re.IGNORECASE),
]
def validate_audio_upload(field_name='audio'):
"""Decorator to validate audio file uploads.
Checks that a file is present, not empty, and within size limits.
Returns 400 JSON on validation failure.
"""
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if request.method == 'OPTIONS':
return f(*args, **kwargs)
if field_name not in request.files:
return jsonify({'success': False, 'error': f'No {field_name} file provided'}), 400
audio_file = request.files[field_name]
if not audio_file.filename:
return jsonify({'success': False, 'error': 'Empty filename'}), 400
# Check file size (read content length from stream)
audio_file.seek(0, 2) # seek to end
file_size = audio_file.tell()
audio_file.seek(0) # reset to beginning
if file_size < 100:
return jsonify({'success': False, 'error': 'Audio file too small (possibly empty recording)'}), 400
if file_size > 30 * 1024 * 1024: # 30MB
return jsonify({'success': False, 'error': 'Audio file too large. Maximum size is 30MB.'}), 400
return f(*args, **kwargs)
return wrapper
return decorator
def sanitize_text_for_llm(text, max_length=5000):
"""Sanitize user-provided text before passing to LLM.
Truncates to max_length and strips common prompt injection patterns.
"""
if not text:
return ''
# Truncate
text = text[:max_length]
# Strip injection patterns
for pattern in _INJECTION_PATTERNS:
text = pattern.sub('', text)
# Remove excessive whitespace that could result from stripping
text = re.sub(r'\n{3,}', '\n\n', text)
text = text.strip()
return text