quantum-ai/agent_base/views.py
Claude c61263e7b7 🔒 Enhance marketplace security with comprehensive protections
- Add rate limiting to marketplace views (60/min) and API (30/min)
- Implement input validation for category and search parameters
- Add server-side search with sanitization and length validation
- Secure API data exposure with pagination and essential fields only
- Add comprehensive security logging for monitoring suspicious activity
- Update marketplace template with secure form-based search
- Preserve search queries in category filter navigation
- Add security-specific loggers for better monitoring

Security improvements:
• Rate limiting prevents abuse and DoS attacks
• Input validation prevents injection attacks
• Server-side search replaces vulnerable client-side filtering
• API pagination limits response size and data exposure
• Enhanced logging enables security monitoring
• Template updates maintain UX while improving security

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-26 08:22:59 +05:30

160 lines
6.5 KiB
Python

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.http import JsonResponse
from django.db.models import Q
from django_ratelimit.decorators import ratelimit
from django_ratelimit import UNSAFE
from .models import BaseAgent
import logging
logger = logging.getLogger('agent_base.security')
@ratelimit(key='ip', rate='60/m', method='GET', block=False)
def marketplace_view(request):
"""Professional marketplace view with agent system - Rate limited to 60 requests per minute per IP"""
# Check if rate limited
if getattr(request, 'limited', False):
logger.warning(f"Marketplace rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
messages.error(request, 'Too many requests. Please wait a moment before refreshing.')
# Still show marketplace but with warning
# Get all agents for marketplace with optimized query
agents_queryset = BaseAgent.objects.filter(is_active=True).select_related().order_by('category', 'name')
# Server-side search with validation
search_query = request.GET.get('search', '').strip()
if search_query:
# Validate search query (max length and safe characters)
if len(search_query) > 100:
logger.warning(f"Search query too long: {len(search_query)} characters")
messages.error(request, 'Search query too long. Please keep it under 100 characters.')
search_query = search_query[:100]
# Remove potential SQL injection patterns and sanitize
import re
search_query = re.sub(r'[^\w\s\-\.]', '', search_query)
if search_query:
agents_queryset = agents_queryset.filter(
Q(name__icontains=search_query) |
Q(description__icontains=search_query)
)
logger.info(f"Marketplace search performed: '{search_query}'")
# Filter by category if specified with validation
category = request.GET.get('category')
if category:
# Validate category against allowed choices
valid_categories = [choice[0] for choice in BaseAgent.CATEGORIES]
if category in valid_categories:
agents_queryset = agents_queryset.filter(category=category)
logger.info(f"Marketplace filtered by valid category: {category}")
else:
logger.warning(f"Invalid category parameter attempted: {category}")
category = None # Reset to show all agents
# Get agents and categories in single query
agents = list(agents_queryset)
categories = BaseAgent.objects.filter(is_active=True).values_list('category', 'category').distinct()
context = {
'user_balance': request.user.wallet_balance if request.user.is_authenticated else 0,
'agents': agents,
'categories': categories,
'selected_category': category,
'search_query': search_query if 'search_query' in locals() else '',
}
return render(request, 'agent_base/marketplace.html', context)
@ratelimit(key='ip', rate='30/m', method='GET', block=False)
def agents_api_view(request):
"""API endpoint for agents list - Rate limited to 30 requests per minute per IP"""
# Check if rate limited
if getattr(request, 'limited', False):
logger.warning(f"Agents API rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
return JsonResponse({
'error': 'Rate limit exceeded. Please try again later.',
'agents': [],
'total_count': 0,
}, status=429)
agents = BaseAgent.objects.filter(is_active=True)
# Server-side search with validation for API
search_query = request.GET.get('search', '').strip()
if search_query:
# Validate search query (max length and safe characters)
if len(search_query) > 100:
logger.warning(f"API search query too long: {len(search_query)} characters")
return JsonResponse({
'error': 'Search query too long. Maximum 100 characters allowed.',
'agents': [],
'total_count': 0,
}, status=400)
# Remove potential SQL injection patterns and sanitize
import re
search_query = re.sub(r'[^\w\s\-\.]', '', search_query)
if search_query:
agents = agents.filter(
Q(name__icontains=search_query) |
Q(description__icontains=search_query)
)
logger.info(f"API search performed: '{search_query}'")
# Filter by category if specified with validation
category = request.GET.get('category')
if category:
# Validate category against allowed choices
valid_categories = [choice[0] for choice in BaseAgent.CATEGORIES]
if category in valid_categories:
agents = agents.filter(category=category)
logger.info(f"API filtered by valid category: {category}")
else:
logger.warning(f"Invalid category parameter in API: {category}")
return JsonResponse({
'error': 'Invalid category parameter',
'valid_categories': valid_categories,
'agents': [],
'total_count': 0,
}, status=400)
# Add pagination for security (limit large responses) with validation
try:
page_size = min(int(request.GET.get('limit', 50)), 100) # Max 100 agents per request
offset = max(int(request.GET.get('offset', 0)), 0)
except (ValueError, TypeError):
logger.warning(f"Invalid pagination parameters in API request")
return JsonResponse({
'error': 'Invalid pagination parameters. Limit and offset must be integers.',
'agents': [],
'total_count': 0,
}, status=400)
agents_page = agents[offset:offset + page_size]
# Only return essential data (minimize information disclosure)
agents_data = []
for agent in agents_page:
agents_data.append({
'name': agent.name,
'slug': agent.slug,
'description': agent.description[:200], # Limit description length
'category': agent.category,
'price': float(agent.price),
'icon': agent.icon,
'rating': float(agent.rating),
})
return JsonResponse({
'agents': agents_data,
'total_count': agents.count(),
'returned_count': len(agents_data),
'offset': offset,
'limit': page_size,
})