security-first-validator
Scannednpx machina-cli add skill smicolon/ai-kit/security-first-validator --openclawSecurity-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
Source
git clone https://github.com/smicolon/ai-kit/blob/main/packs/django/skills/security-first-validator/SKILL.mdView on GitHub Overview
Security-First Validator enforces Django REST Framework security for new API endpoints. It ensures mandatory protections like authentication, permissions, serializer validation, and rate limiting, and can auto-apply secure defaults when you create views, serializers, or routes.
How This Skill Works
When you write DRF code, the skill runs a validation process: detect new API endpoints, run a security checklist, and auto-add security requirements. It enforces permission_classes (e.g., IsAuthenticated), promotes explicit, safe serializer fields with validation, discourages raw SQL, and applies rate limiting on sensitive endpoints by adding throttle classes where appropriate.
When to Use It
- Creating an API endpoint or viewset in DRF
- Designing a serializer for DRF to expose data safely
- Defining URL patterns or API routes for DRF endpoints
- Writing DRF classes (APIView, ViewSet, Serializer)
- Discussing authentication, permissions, or rate limiting in DRF
Quick Start
- Step 1: Detect a new API endpoint in your DRF code (APIView/ViewSet) or when creating a Serializer.
- Step 2: Run the security checklist: verify permission_classes, ensure serializer validation, remove raw SQL, and add rate limiting if endpoint is sensitive.
- Step 3: Auto-add security requirements and review changes (e.g., IsAuthenticated, explicit serializer fields, and throttle classes).
Best Practices
- Always define permission_classes (e.g., IsAuthenticated) on every endpoint
- Use explicit, safe serializer fields and validate critical data (e.g., emails)
- Avoid raw SQL; prefer ORM queries and parameterized calls
- Apply rate limiting on sensitive endpoints (e.g., login or token issuance)
- Avoid exposing sensitive fields; use read_only_fields and explicit field lists
Example Use Cases
- Example 1: Securing a UserViewSet with authentication and filtered queryset from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = [IsAuthenticated] def get_queryset(self): return super().get_queryset().filter(is_deleted=False)
- Example 2: Serializer with explicit fields and safe validation from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'email', 'first_name', 'last_name'] read_only_fields = ['id', 'created_at', 'updated_at'] def validate_email(self, value): if not value.endswith('@company.com'): raise serializers.ValidationError("Must use company email") return value
- Example 3: Validation to prevent sensitive data exposure class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'email', 'first_name', 'last_name'] # Avoid fields like password or tokens
- Example 4: Apply rate limiting on a sensitive endpoint from rest_framework.throttling import UserRateThrottle class LoginView(APIView): throttle_classes = [UserRateThrottle] # ...
- Example 5: Replacing raw SQL with safe ORM usage # ❌ DANGEROUS User.objects.raw(f"SELECT * FROM users WHERE id = {user_id}") # ✅ SAFE User.objects.filter(id=user_id)