security-testing-verification
SKILL.md
Security Testing & Verification
Built-In Security Tests
This project includes automated tests and verification scripts for all security features.
Testing Rate Limiting
Automated Test Script
# Run the provided test script
node scripts/test-rate-limit.js
What it tests:
- Makes 10 consecutive requests to rate-limited endpoint
- Verifies first 5 succeed (HTTP 200)
- Verifies requests 6-10 are blocked (HTTP 429)
- Tests rate limit reset after 60 seconds
Expected output:
Testing Rate Limiting (5 requests/minute per IP)
Request 1: ✓ 200 - Success
Request 2: ✓ 200 - Success
Request 3: ✓ 200 - Success
Request 4: ✓ 200 - Success
Request 5: ✓ 200 - Success
Request 6: ✗ 429 - Too many requests
Request 7: ✗ 429 - Too many requests
Request 8: ✗ 429 - Too many requests
Request 9: ✗ 429 - Too many requests
Request 10: ✗ 429 - Too many requests
✓ Rate limiting is working correctly!
Manual Testing
# Test rate limiting manually
for i in {1..10}; do
echo "Request $i:"
curl -s -o /dev/null -w "%{http_code}\n" \
http://localhost:3000/api/test-rate-limit
sleep 0.1
done
# Expected:
# Requests 1-5: 200
# Requests 6-10: 429
Test Reset After Window
# Make 5 requests
for i in {1..5}; do
curl http://localhost:3000/api/test-rate-limit
done
# Wait 61 seconds (rate limit window = 60 seconds)
sleep 61
# Try again - should succeed
curl http://localhost:3000/api/test-rate-limit
# Expected: 200 OK (limit reset)
Testing CSRF Protection
Test 1: Request Without Token (Should Fail)
curl -X POST http://localhost:3000/api/example-protected \
-H "Content-Type: application/json" \
-d '{"title": "test"}'
# Expected: 403 Forbidden
# {
# "error": "CSRF token missing"
# }
Test 2: Request With Valid Token (Should Succeed)
# Step 1: Get CSRF token
TOKEN=$(curl -s http://localhost:3000/api/csrf \
-c cookies.txt | jq -r '.csrfToken')
# Step 2: Use token in request
curl -X POST http://localhost:3000/api/example-protected \
-b cookies.txt \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: $TOKEN" \
-d '{"title": "test"}'
# Expected: 200 OK
Test 3: Token Reuse (Should Fail)
# Get token
TOKEN=$(curl -s http://localhost:3000/api/csrf \
-c cookies.txt | jq -r '.csrfToken')
# Use once (succeeds)
curl -X POST http://localhost:3000/api/example-protected \
-b cookies.txt \
-H "X-CSRF-Token: $TOKEN" \
-d '{"title": "test"}'
# Try to reuse same token (should fail)
curl -X POST http://localhost:3000/api/example-protected \
-b cookies.txt \
-H "X-CSRF-Token: $TOKEN" \
-d '{"title": "test2"}'
# Expected: 403 Forbidden - Token already used
Test 4: Invalid Token (Should Fail)
curl -X POST http://localhost:3000/api/example-protected \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: fake-token-12345" \
-d '{"title": "test"}'
# Expected: 403 Forbidden
# {
# "error": "CSRF token invalid"
# }
Testing Input Validation
Test XSS Sanitization
# Test script tags removal
curl -X POST http://localhost:3000/api/example-protected \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <get-token-first>" \
-d '{"title": "<script>alert(1)</script>"}'
# Expected: 200 OK
# Title sanitized to: "alert(1)"
# < and > removed
Test Length Validation
# Test too-long input
curl -X POST http://localhost:3000/api/example-protected \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <token>" \
-d "{\"title\": \"$(printf 'A%.0s' {1..200})\"}"
# Expected: 400 Bad Request
# {
# "error": "Validation failed",
# "details": {
# "title": "String must contain at most 100 character(s)"
# }
# }
Test Email Validation
curl -X POST http://localhost:3000/api/contact \
-H "Content-Type: application/json" \
-d '{
"name": "Test User",
"email": "not-an-email",
"subject": "Test",
"message": "Test message"
}'
# Expected: 400 Bad Request
# {
# "error": "Validation failed",
# "details": {
# "email": "Invalid email"
# }
# }
Test Required Fields
curl -X POST http://localhost:3000/api/contact \
-H "Content-Type: application/json" \
-d '{
"name": "Test User"
}'
# Expected: 400 Bad Request with missing field errors
Testing Security Headers
Test All Headers
curl -I http://localhost:3000
# Expected headers:
# Content-Security-Policy: default-src 'self'; ...
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# (HSTS only in production)
Test CSP
# Check CSP includes required domains
curl -I http://localhost:3000 | grep "Content-Security-Policy"
# Should include:
# - script-src with Clerk domain
# - connect-src with Convex domain
# - frame-src with Stripe domain
Test HSTS (Production Only)
# In production environment
curl -I https://yourapp.com | grep "Strict-Transport-Security"
# Should return:
# Strict-Transport-Security: max-age=31536000; includeSubDomains
Test Protected Route Headers
curl -I http://localhost:3000/dashboard
# Should include:
# X-Robots-Tag: noindex, nofollow
Testing Authentication
Test Unauthenticated Access
# Try to access protected API without auth
curl http://localhost:3000/api/protected-endpoint
# Expected: 401 Unauthorized
# {
# "error": "Unauthorized",
# "message": "Authentication required"
# }
Test Authenticated Access
# With valid Clerk session cookie
curl http://localhost:3000/api/protected-endpoint \
-H "Cookie: __session=<clerk-session-token>"
# Expected: 200 OK (with authorized response)
Test Authorization (Resource Ownership)
# Try to access another user's resource
curl http://localhost:3000/api/posts/user-abc-post-123 \
-H "Cookie: __session=<different-user-token>"
# Expected: 403 Forbidden
# {
# "error": "Forbidden",
# "message": "You do not have access to this resource"
# }
Test Subscription Gating
# Try premium feature with free account
curl http://localhost:3000/api/premium/generate \
-H "Cookie: __session=<free-user-token>"
# Expected: 403 Forbidden
# {
# "error": "Forbidden",
# "message": "Premium subscription required"
# }
Testing Error Handling
Test Production Error Messages
# Set NODE_ENV=production temporarily
export NODE_ENV=production
# Trigger error in API
curl http://localhost:3000/api/error-test
# Expected: Generic message (no stack trace)
# {
# "error": "Internal server error",
# "message": "An unexpected error occurred"
# }
Test Development Error Messages
# In development (NODE_ENV=development)
curl http://localhost:3000/api/error-test
# Expected: Detailed error with stack trace
# {
# "error": "Internal server error",
# "message": "Specific error message",
# "stack": "Error: ...\n at ...",
# "context": "error-test"
# }
Testing Dependency Security
Run npm Audit
# Check for vulnerabilities
npm audit
# Expected: 0 vulnerabilities
# found 0 vulnerabilities
Run Production Audit
# Only check production dependencies
npm audit --production
# Expected: 0 vulnerabilities
Check Outdated Packages
npm outdated
# Expected: All packages up-to-date
# (or list of safe minor/patch updates available)
Run Security Check Script
bash scripts/security-check.sh
# Expected:
# - 0 vulnerabilities
# - Minimal outdated packages
# - Fix commands if needed
Online Security Testing Tools
Security Headers Scanner
Tool: https://securityheaders.com/
How to use:
- Deploy your app
- Enter URL in Security Headers scanner
- Check for A+ rating
What it checks:
- Content-Security-Policy
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security
- Referrer-Policy
- Permissions-Policy
Mozilla Observatory
Tool: https://observatory.mozilla.org/
How to use:
- Enter your deployed URL
- Run scan
- Check score (aim for A+)
What it checks:
- Security headers
- Cookie security
- HTTPS configuration
- Subresource integrity
- Content Security Policy
SSL Labs
Tool: https://www.ssllabs.com/ssltest/
How to use:
- Enter your domain
- Wait for scan (takes ~2 minutes)
- Check for A+ rating
What it checks:
- SSL/TLS configuration
- Certificate validity
- Protocol support
- Cipher suite strength
- HSTS configuration
Pre-Deployment Security Checklist
Run through this checklist before every production deployment:
Environment & Configuration
- All environment variables set in production
-
CSRF_SECRETgenerated and configured (32+ bytes) -
SESSION_SECRETgenerated and configured (32+ bytes) - Clerk production keys configured
- Stripe live mode keys configured (if using payments)
-
.env.localNOT committed to git
Dependencies
- Run
npm audit --production- 0 vulnerabilities - Run
npm outdated- Check for critical updates -
package-lock.jsoncommitted - Next.js on latest stable version
Security Features
- CSRF protection tested (see tests above)
- Rate limiting tested (see tests above)
- Input validation tested (see tests above)
- Security headers verified (securityheaders.com)
- HSTS enabled in production
- Error messages are generic in production
Authentication & Authorization
- Protected routes require authentication
- Resource ownership checked before access
- Subscription status verified for premium features
- Webhook signatures verified (Clerk, Stripe)
- Session expiration handled gracefully
API Security
- All POST/PUT/DELETE routes have CSRF protection
- All public endpoints have rate limiting
- All user input validated with Zod
- All errors handled with error handler utilities
- No sensitive data in logs
- No hardcoded secrets in code
Payment Security (if applicable)
- Using Clerk Billing + Stripe (not handling cards directly)
- Webhooks verified (Svix signatures)
- Subscription status checked on server
- Test mode disabled in production
Testing
- Run rate limit test:
node scripts/test-rate-limit.js - Test CSRF protection manually
- Test input validation with malicious input
- Check security headers:
curl -I https://yourapp.com - Test authentication flows
- Test error handling in production mode
Monitoring
- Error logging configured (Vercel logs)
- Failed auth attempts tracked (Clerk dashboard)
- GitHub Dependabot alerts enabled
- Security headers monitored (automated checks)
Automated Testing Script
security-test.sh
Create a comprehensive test script:
#!/bin/bash
echo "================================="
echo "Security Testing Suite"
echo "================================="
echo ""
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Test counter
PASSED=0
FAILED=0
# Function to run test
run_test() {
local test_name=$1
local command=$2
local expected=$3
echo -n "Testing $test_name... "
result=$(eval $command 2>&1)
if echo "$result" | grep -q "$expected"; then
echo -e "${GREEN}✓ PASS${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAIL${NC}"
echo " Expected: $expected"
echo " Got: $result"
((FAILED++))
fi
}
echo "=== Dependency Security ==="
run_test "npm audit" "npm audit --production" "found 0 vulnerabilities"
echo ""
echo "=== Rate Limiting ==="
echo "Running rate limit test script..."
node scripts/test-rate-limit.js
echo ""
echo "=== Security Headers ==="
run_test "X-Frame-Options" "curl -I http://localhost:3000" "X-Frame-Options: DENY"
run_test "X-Content-Type-Options" "curl -I http://localhost:3000" "X-Content-Type-Options: nosniff"
run_test "Content-Security-Policy" "curl -I http://localhost:3000" "Content-Security-Policy"
echo ""
echo "================================="
echo "Tests Passed: $PASSED"
echo "Tests Failed: $FAILED"
echo "================================="
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}All tests passed!${NC}"
exit 0
else
echo -e "${RED}Some tests failed!${NC}"
exit 1
fi
Run it:
bash scripts/security-test.sh
Continuous Security Testing
Add to CI/CD Pipeline
# .github/workflows/security.yml
name: Security Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run npm audit
run: npm audit --production
- name: Check for outdated packages
run: npm outdated || true
- name: Build application
run: npm run build
- name: Start server (background)
run: npm run dev &
env:
NODE_ENV: test
- name: Wait for server
run: npx wait-on http://localhost:3000
- name: Run security tests
run: bash scripts/security-test.sh
- name: Stop server
run: pkill -f "npm run dev"
Manual Penetration Testing
Test XSS in All Input Fields
-
Try these payloads in every input:
<script>alert('XSS')</script> <img src=x onerror=alert('XSS')> <svg onload=alert('XSS')> javascript:alert('XSS') "><script>alert('XSS')</script> -
Verify all are sanitized
Test SQL Injection
-
Try these in search/query fields:
' OR '1'='1 '; DROP TABLE users; -- ' UNION SELECT * FROM users -- -
Verify input validation blocks or sanitizes
Test CSRF
-
Create malicious HTML file:
<form action="http://localhost:3000/api/delete-account" method="POST"> <input type="hidden" name="confirm" value="yes" /> </form> <script>document.forms[0].submit();</script> -
Open while logged in
-
Verify request blocked (403 Forbidden)
Test Authorization
- Create resource as User A
- Try to access/modify as User B
- Verify 403 Forbidden
What To Monitor Post-Deployment
Daily
- Error rates (Vercel dashboard)
- Failed authentication attempts (Clerk dashboard)
- Rate limit violations (check logs for 429 responses)
Weekly
- Run
npm audit --production - Check GitHub Dependabot alerts
- Review error logs for patterns
Monthly
- Full security audit
- Update dependencies
- Re-run security testing suite
- Check security headers (securityheaders.com)
References
- OWASP Testing Guide: https://owasp.org/www-project-web-security-testing-guide/
- Security Headers Scanner: https://securityheaders.com/
- Mozilla Observatory: https://observatory.mozilla.org/
- SSL Labs: https://www.ssllabs.com/ssltest/
- npm Audit: https://docs.npmjs.com/cli/v8/commands/npm-audit
Next Steps
- For fixing issues: Use appropriate security skill (csrf-protection, rate-limiting, etc.)
- For deployment: Complete pre-deployment checklist above
- For monitoring: Set up automated security scans in CI/CD
- For ongoing maintenance: Run monthly security audit
Weekly Installs
17
Repository
harperaa/secure…e-skillsGitHub Stars
5
First Seen
Feb 1, 2026
Security Audits
Installed on
claude-code17
opencode15
github-copilot15
cursor15
gemini-cli14
amp14