Security first validator
Skill smicolon/ai-kit/packs/django/skills/security-first-validator
Convention packs for any AI coding tool - agents, skills, commands, and rules for 15 tools including Claude Code, Cursor, Windsurf, and Copilot
npx -y skills add smicolon/ai-kit --skill security-first-validatorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
This skill should be used when the user asks to "create an API endpoint", "add a view", "write a viewset", "create a serializer", or when writing Django REST Framework code. Enforces security requirements (permissions, authentication, rate limiting).
SKILL.md
11.4 KB, as published. Nobody here has run it
Security-First Validator
Enforces security requirements for all Django REST Framework API endpoints.
Activation Triggers
This skill activates when:
- Creating API views or viewsets
- Creating serializers
- Mentioning "endpoint", "API", "view", "route"
- Writing DRF classes (APIView, ViewSet, Serializer)
- Creating URL patterns for APIs
- Discussing authentication or permissions
Security Requirements (MANDATORY)
Every API endpoint MUST have:
1. Permission Classes (REQUIRED)
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated] # ✅ REQUIRED
# ...
2. Serializer Validation (REQUIRED)
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'email', 'first_name']
# NO sensitive fields exposed ✅
def validate_email(self, value):
# Custom validation ✅
if not value.endswith('@company.com'):
raise serializers.ValidationError("Must use company email")
return value
3. No Raw SQL (REQUIRED)
# ❌ DANGEROUS
User.objects.raw(f"SELECT * FROM users WHERE id = {user_id}")
# ✅ SAFE
User.objects.filter(id=user_id)
4. Rate Limiting (REQUIRED for sensitive endpoints)
from rest_framework.throttling import UserRateThrottle
class LoginView(APIView):
throttle_classes = [UserRateThrottle] # ✅ REQUIRED for auth
# ...
Validation Process
Step 1: Detect New API Endpoint
When API code is being written:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Missing security! ❌
Step 2: Run Security Checklist
Verify:
- ✅ Permission classes defined
- ✅ Serializer has proper validation
- ✅ No raw SQL queries
- ✅ Rate limiting on sensitive endpoints
- ✅ No password/token fields in serializer
- ✅ Proper CORS configuration
Step 3: Auto-Add Security Requirements
Before (Insecure):
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
After (Secure):
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
class UserViewSet(viewsets.ModelViewSet):
"""User management endpoints."""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated] # ✅ Added
def get_queryset(self):
"""Filter queryset to exclude soft-deleted records."""
return super().get_queryset().filter(is_deleted=False)
Step 4: Validate Serializer Security
Check for exposed sensitive fields:
# ❌ DANGEROUS - Exposes password!
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__' # Never use __all__!
Auto-fix to:
# ✅ SAFE - Explicit field list
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'email', 'first_name', 'last_name']
read_only_fields = ['id', 'created_at', 'updated_at']
# Password excluded ✅
Step 5: Report Security Issues
Report to developer:
Security Issues Detected and Fixed
Fixed:
- ✅ Added
permission_classes = [IsAuthenticated]to UserViewSet- ✅ Changed serializer from
fields = '__all__'to explicit field list- ✅ Excluded sensitive fields (password, tokens)
- ✅ Added
read_only_fieldsfor audit fieldsWhy:
- Permission classes prevent unauthorized access
- Explicit fields prevent accidental data exposure
- Read-only fields prevent tampering with audit trail
Complete Secure Endpoint Example
import users.models as _users_models
import users.serializers as _users_serializers
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
class UserViewSet(viewsets.ModelViewSet):
"""
User management API.
Security:
- Requires authentication
- Users can only access their own data (see get_queryset)
- Rate limited for sensitive actions
- Input validation via serializer
"""
queryset = _users_models.User.objects.all()
serializer_class = _users_serializers.UserSerializer
permission_classes = [IsAuthenticated] # ✅ Authentication required
def get_queryset(self):
"""Users can only see active, non-deleted records."""
qs = super().get_queryset().filter(is_deleted=False)
# Users see only their own data unless admin
if not self.request.user.is_staff:
qs = qs.filter(id=self.request.user.id)
return qs
def perform_create(self, serializer):
"""Auto-set created_by on creation."""
serializer.save(created_by=self.request.user)
@action(
detail=False,
methods=['post'],
throttle_classes=[UserRateThrottle], # ✅ Rate limiting
permission_classes=[IsAuthenticated]
)
def change_password(self, request):
"""Change user password (rate limited)."""
serializer = _serializers.ChangePasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # ✅ Validation
user = request.user
user.set_password(serializer.validated_data['new_password'])
user.save()
return Response({'status': 'password changed'})
Security Checklist by Endpoint Type
Read-Only Endpoints (GET)
class PublicArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Public articles (read-only)."""
queryset = Article.objects.filter(is_published=True, is_deleted=False)
serializer_class = ArticleSerializer
permission_classes = [AllowAny] # ✅ Explicit - public endpoint
# Still apply rate limiting to prevent scraping
throttle_classes = [AnonRateThrottle]
Create Endpoints (POST)
class UserCreateView(generics.CreateAPIView):
"""User registration (public)."""
serializer_class = UserCreateSerializer
permission_classes = [AllowAny] # ✅ Public registration
throttle_classes = [AnonRateThrottle] # ✅ Prevent abuse
def perform_create(self, serializer):
"""
Create user and send verification email.
Security: Rate limited, email validation in serializer.
"""
user = serializer.save()
send_verification_email(user)
Update Endpoints (PUT/PATCH)
class UserUpdateView(generics.UpdateAPIView):
"""Update user profile."""
serializer_class = UserUpdateSerializer
permission_classes = [IsAuthenticated, IsOwner] # ✅ Auth + ownership
def get_object(self):
"""Users can only update their own profile."""
return self.request.user
Delete Endpoints (DELETE)
class UserDestroyView(generics.DestroyAPIView):
"""Soft delete user account."""
permission_classes = [IsAuthenticated, IsOwner] # ✅ Auth + ownership
def perform_destroy(self, instance):
"""Soft delete instead of hard delete."""
instance.is_deleted = True
instance.save(update_fields=['is_deleted', 'updated_at'])
Common Security Violations
Violation 1: No Permission Classes
# ❌ DANGEROUS - Anyone can access!
class AdminViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
Auto-fix:
# ✅ SECURE
class AdminViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
permission_classes = [IsAuthenticated, IsAdminUser] # Added!
Violation 2: Serializer Exposes Sensitive Data
# ❌ DANGEROUS - Exposes password!
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
I auto-fix:
# ✅ SECURE
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'email', 'first_name', 'last_name', 'created_at']
read_only_fields = ['id', 'created_at', 'updated_at']
# password, tokens excluded!
Violation 3: Raw SQL Injection Risk
# ❌ DANGEROUS - SQL injection!
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}"
return User.objects.raw(query)
I auto-fix:
# ✅ SECURE
def get_user(user_id):
return User.objects.filter(id=user_id).first()
Violation 4: No Rate Limiting on Auth
# ❌ DANGEROUS - Brute force attacks possible!
class LoginView(APIView):
def post(self, request):
# Login logic
pass
I auto-fix:
# ✅ SECURE
from rest_framework.throttling import AnonRateThrottle
class LoginView(APIView):
throttle_classes = [AnonRateThrottle] # Added!
def post(self, request):
# Login logic
pass
OWASP Top 10 Coverage
Automatically check for:
- Broken Access Control → Permission classes required
- Cryptographic Failures → No sensitive fields in serializers
- Injection → No raw SQL, use ORM
- Insecure Design → Enforce secure-by-default patterns
- Security Misconfiguration → CORS, HTTPS, rate limiting
- Vulnerable Components → (Check in CI/CD)
- Authentication Failures → Throttling on auth endpoints
- Data Integrity Failures → Serializer validation required
- Logging Failures → (Suggest logging for sensitive ops)
- SSRF → Validate URLs in serializers
Custom Permission Classes
Recognize and allow custom permissions:
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
"""User can only access their own objects."""
def has_object_permission(self, request, view, obj):
return obj.user == request.user
# Usage ✅ VALID
class ProfileViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, IsOwner]
Integration with Other Skills
Works together with:
- model-entity-validator: Ensures models don't expose sensitive fields
- test-coverage-advisor: Suggests security tests
- django-reviewer: Full security audit during review
Success Criteria
✅ ALL endpoints have permission classes ✅ ALL serializers use explicit field lists ✅ NO raw SQL queries ✅ Rate limiting on sensitive endpoints ✅ NO sensitive fields exposed ✅ Input validation on all write operations ✅ Developer understands OWASP risks
Behavior
Proactive enforcement:
- Check security without being asked
- Add permission classes automatically
- Fix serializer field exposure immediately
- Explain WHY each security measure is critical
- Reference OWASP categories
Never:
- Require explicit "check security" request
- Wait for pen test results
- Just warn without fixing
- Allow endpoints without security
Block completion if:
- No permission classes defined
- Serializer uses
fields = '__all__' - Raw SQL detected
- Sensitive endpoints lack rate limiting