mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 11:52:57 +00:00
CRITICAL FIXES: - 🔴 Remove hardcoded admin passwords (security vulnerability) - 🔴 Fix SSRF vulnerability in webhook URL validation - 🔴 Add atomic wallet transactions (race condition fix) - 🔴 Configure production security headers and CSP - 🔴 Fix Railway deployment issues (logging import, start command) SECURITY ENHANCEMENTS: - 🛡️ Comprehensive input validation and XSS prevention - 🛡️ Rate limiting on all API endpoints (10-60 req/min) - 🛡️ Advanced security monitoring middleware - 🛡️ Suspicious activity detection and logging - 🛡️ Enhanced HTTPS, HSTS, and cookie security PERFORMANCE OPTIMIZATIONS: - ⚡ Database query optimization (select_related, indexes) - ⚡ Enhanced Redis caching with proper invalidation - ⚡ Optimized wallet statistics with database aggregation - ⚡ Improved session configuration INFRASTRUCTURE: - 📦 New dependencies: bleach, django-ratelimit - 📊 Enhanced logging with security.log rotation - 🗃️ Database indexes for performance - 🔧 Railway-safe deployment configuration All changes tested and deployment-ready with rollback safety. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
136 lines
6.1 KiB
Python
136 lines
6.1 KiB
Python
"""
|
|
Utility functions for the agents app.
|
|
Contains webhook validation, message formatting, and other helper functions.
|
|
"""
|
|
|
|
import ipaddress
|
|
from urllib.parse import urlparse
|
|
from django.conf import settings
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def validate_webhook_url(url):
|
|
"""
|
|
Validate webhook URL to prevent SSRF attacks.
|
|
Implements strict security controls with special handling for development.
|
|
"""
|
|
try:
|
|
parsed = urlparse(url)
|
|
|
|
# Only allow HTTP/HTTPS protocols
|
|
if parsed.scheme not in ['http', 'https']:
|
|
raise ValueError("Only HTTP/HTTPS URLs are allowed")
|
|
|
|
# Get hostname
|
|
hostname = parsed.hostname
|
|
if not hostname:
|
|
raise ValueError("Invalid hostname in URL")
|
|
|
|
# Production security: Only HTTPS allowed
|
|
if not settings.DEBUG and parsed.scheme != 'https':
|
|
raise ValueError("Only HTTPS URLs allowed in production")
|
|
|
|
# Block dangerous localhost access in production
|
|
if not settings.DEBUG:
|
|
# Block ALL localhost/internal access in production
|
|
localhost_patterns = [
|
|
'localhost', '127.0.0.1', '0.0.0.0', '::1',
|
|
'local', 'internal', 'private'
|
|
]
|
|
if any(pattern in hostname.lower() for pattern in localhost_patterns):
|
|
raise ValueError("Localhost/internal addresses not allowed in production")
|
|
|
|
# Development mode: Allow specific localhost ports for N8N
|
|
if settings.DEBUG and hostname in ['localhost', '127.0.0.1']:
|
|
allowed_dev_ports = [5678, 8000, 8080, 3000] # Common development ports
|
|
if parsed.port in allowed_dev_ports:
|
|
logger.info(f"Development mode: Allowing localhost URL {url}")
|
|
return True
|
|
|
|
# Check if hostname is an IP address
|
|
try:
|
|
ip = ipaddress.ip_address(hostname)
|
|
|
|
# Block all private/internal IPs in production
|
|
if not settings.DEBUG:
|
|
if (ip.is_private or ip.is_loopback or ip.is_reserved or
|
|
ip.is_link_local or ip.is_multicast or ip.is_unspecified):
|
|
raise ValueError("Internal/private IP addresses not allowed in production")
|
|
|
|
# In development, only allow specific ranges
|
|
elif settings.DEBUG:
|
|
if ip.is_loopback:
|
|
# Allow loopback only for specific ports
|
|
allowed_dev_ports = [5678, 8000, 8080, 3000]
|
|
if parsed.port not in allowed_dev_ports:
|
|
raise ValueError(f"Loopback IP only allowed on ports {allowed_dev_ports}")
|
|
elif (ip.is_private or ip.is_reserved or ip.is_link_local or
|
|
ip.is_multicast or ip.is_unspecified):
|
|
raise ValueError("Internal/private IP addresses not allowed")
|
|
|
|
except ValueError as e:
|
|
if "does not appear to be an IPv4 or IPv6 address" not in str(e):
|
|
raise # Re-raise if it's not just a "not an IP" error
|
|
# If it's not an IP, it's a domain name - continue validation
|
|
|
|
# Additional domain validation for production
|
|
if not settings.DEBUG:
|
|
# Block suspicious domain patterns
|
|
suspicious_patterns = [
|
|
'.local', '.internal', '.private', '.corp', '.lan',
|
|
'metadata', 'instance-data', 'user-data'
|
|
]
|
|
if any(pattern in hostname.lower() for pattern in suspicious_patterns):
|
|
raise ValueError(f"Suspicious domain pattern detected: {hostname}")
|
|
|
|
# Log successful validation
|
|
logger.info(f"Webhook URL validated successfully: {url}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Webhook URL validation failed for {url}: {str(e)}")
|
|
raise ValueError(f"Invalid webhook URL: {str(e)}")
|
|
|
|
|
|
def format_agent_message(agent_slug, input_data):
|
|
"""Format input data into a message for N8N webhook based on agent type"""
|
|
if agent_slug == 'social-ads-generator':
|
|
description = input_data.get('description', '')
|
|
platform = input_data.get('social_platform', '')
|
|
emoji = input_data.get('include_emoji', 'yes')
|
|
language = input_data.get('language', 'English')
|
|
|
|
return f"Execute Social Media Ad Creator with the following parameters:. Describe what you'd like to generate: {description}. Include Emoji: {emoji.title()}. For Social Media Platform: {platform.title()}. Language: {language}."
|
|
|
|
elif agent_slug == 'job-posting-generator':
|
|
job_title = input_data.get('job_title', '')
|
|
company_name = input_data.get('company_name', '')
|
|
description = input_data.get('job_description', '')
|
|
seniority = input_data.get('seniority_level', '')
|
|
contract = input_data.get('contract_type', '')
|
|
location = input_data.get('location', '')
|
|
language = input_data.get('language', 'English')
|
|
|
|
return f"Create a professional job posting for: {job_title} at {company_name}. Description: {description}. Seniority: {seniority}. Contract: {contract}. Location: {location}. Language: {language}. Make it comprehensive and attractive to candidates."
|
|
|
|
# Default formatting for other agents
|
|
params = [f"{key}: {value}" for key, value in input_data.items() if value]
|
|
return f"Execute {agent_slug.replace('-', ' ').title()} with parameters: {'. '.join(params)}."
|
|
|
|
|
|
class AgentCompat:
|
|
"""
|
|
Compatibility class to convert file-based agent data to object format
|
|
for templates and views that expect object attributes.
|
|
"""
|
|
def __init__(self, data):
|
|
self.slug = data['slug']
|
|
self.name = data['name']
|
|
self.price = float(data['price'])
|
|
self.webhook_url = data['webhook_url']
|
|
self.access_url_name = data.get('access_url_name', '')
|
|
self.display_url_name = data.get('display_url_name', '')
|
|
self.id = data['slug'] # Use slug as ID for file-based agents
|
|
self.message_limit = data.get('message_limit', 50) |