Files
Barış Keserandgoogle-labs-jules[bot] d3e25d6450 🛡️ Sentinel: [HIGH] Fix XSS vulnerability in reviews (#277)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-03-11 05:06:51 +00:00

64 lines
2.4 KiB
Python

import bleach
from rest_framework import serializers, viewsets, permissions, status
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from django.db import IntegrityError
from ..models import Review, Anime
class ReviewCreateThrottle(UserRateThrottle):
scope = 'review'
def allow_request(self, request, view):
if request.method != 'POST':
return True
return super().allow_request(request, view)
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit or delete it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the review.
return obj.user == request.user
class ReviewSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = Review
fields = ['id', 'user', 'anime', 'rating', 'text', 'created_at']
read_only_fields = ['user', 'created_at']
def validate_text(self, value):
# Sanitize HTML input using bleach
return bleach.clean(value, tags=[], strip=True)
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class ReviewViewSet(viewsets.ModelViewSet):
# Optimization: Use select_related('user') to avoid N+1 queries when serializing the user field
queryset = Review.objects.select_related('user').all().order_by('-created_at')
serializer_class = ReviewSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
throttle_classes = [ReviewCreateThrottle]
def create(self, request, *args, **kwargs):
try:
return super().create(request, *args, **kwargs)
except IntegrityError:
return Response(
{"detail": "You have already reviewed this anime."},
status=status.HTTP_400_BAD_REQUEST
)
def perform_create(self, serializer):
user = self.request.user
serializer.save(user=user)
# Note: Critic badge check is handled automatically via post_save signal in users.signals