Loading...
Loading...
Test security features and verify implementation before deployment. Use this skill when you need to test CSRF protection, rate limiting, input validation, verify security headers, run security audits, or check the pre-deployment security checklist. Triggers include "test security", "security testing", "verify security", "security checklist", "pre-deployment", "test CSRF", "test rate limit", "security verification".
npx skill4agent add harperaa/secure-claude-skills security-testing-verification# Run the provided test script
node scripts/test-rate-limit.jsTesting 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!# 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# 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)curl -X POST http://localhost:3000/api/example-protected \
-H "Content-Type: application/json" \
-d '{"title": "test"}'
# Expected: 403 Forbidden
# {
# "error": "CSRF token missing"
# }# 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# 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 usedcurl -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"
# }# 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 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)"
# }
# }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"
# }
# }curl -X POST http://localhost:3000/api/contact \
-H "Content-Type: application/json" \
-d '{
"name": "Test User"
}'
# Expected: 400 Bad Request with missing field errorscurl -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)# 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# In production environment
curl -I https://yourapp.com | grep "Strict-Transport-Security"
# Should return:
# Strict-Transport-Security: max-age=31536000; includeSubDomainscurl -I http://localhost:3000/dashboard
# Should include:
# X-Robots-Tag: noindex, nofollow# Try to access protected API without auth
curl http://localhost:3000/api/protected-endpoint
# Expected: 401 Unauthorized
# {
# "error": "Unauthorized",
# "message": "Authentication required"
# }# With valid Clerk session cookie
curl http://localhost:3000/api/protected-endpoint \
-H "Cookie: __session=<clerk-session-token>"
# Expected: 200 OK (with authorized response)# 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"
# }# 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"
# }# 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"
# }# 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"
# }# Check for vulnerabilities
npm audit
# Expected: 0 vulnerabilities
# found 0 vulnerabilities# Only check production dependencies
npm audit --production
# Expected: 0 vulnerabilitiesnpm outdated
# Expected: All packages up-to-date
# (or list of safe minor/patch updates available)bash scripts/security-check.sh
# Expected:
# - 0 vulnerabilities
# - Minimal outdated packages
# - Fix commands if neededCSRF_SECRETSESSION_SECRET.env.localnpm audit --productionnpm outdatedpackage-lock.jsonnode scripts/test-rate-limit.jscurl -I https://yourapp.com#!/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
fibash scripts/security-test.sh# .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"<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
javascript:alert('XSS')
"><script>alert('XSS')</script>' OR '1'='1
'; DROP TABLE users; --
' UNION SELECT * FROM users --<form action="http://localhost:3000/api/delete-account" method="POST">
<input type="hidden" name="confirm" value="yes" />
</form>
<script>document.forms[0].submit();</script>npm audit --production