Security Audit Tools
Quick Security Audit Script
Run this from your project root to check for potential security issues:
bash
#!/bin/bash
# security-audit.sh - Quick security audit for API routes
echo "🔍 Security Audit Report"
echo "========================"
echo -e "\n1. Routes without authentication middleware:"
echo " (These should use withAuth, withEmailAccount, or have custom auth)"
grep -r "export const \(GET\|POST\|PUT\|DELETE\)" apps/web/app/api/ | \
grep -v "withEmailAccount\|withAuth\|withError" | \
cut -d: -f1 | sort | uniq
echo -e "\n2. Direct Prisma queries without user scoping:"
echo " (Check these manually for emailAccountId/userId filtering)"
grep -r "prisma\.\w*\.find" apps/web/app/api/ | \
grep -v "emailAccountId\|userId" | \
head -10
echo -e "\n3. Routes using withError (verify they have custom auth):"
grep -r "withError" apps/web/app/api/ | \
grep "export const" | \
cut -d: -f1 | sort | uniq
echo -e "\n4. Potential parameter injection points:"
grep -r "params\." apps/web/app/api/ | \
grep -v "await params" | \
head -5
echo -e "\n5. 🚨 CRITICAL: Potential unprotected cron endpoints:"
echo " (Check manually - these use withError but not hasCronSecret)"
echo " (Some may be API endpoints with custom auth like validateApiKey)"
grep -r "withError.*async.*request" apps/web/app/api/ | \
grep -v "hasCronSecret\|hasPostCronSecret\|webhook\|linking\|validateApiKey" | \
cut -d: -f1 | sort | uniq
echo -e "\n6. Cron endpoints (verify they use proper secret validation):"
grep -r "hasCronSecret\|hasPostCronSecret" apps/web/app/api/ | \
cut -d: -f1 | sort | uniq
echo -e "\n7. 🚨 QStash endpoints (verify they use verifySignatureAppRouter):"
echo " (These endpoints are called from QStash and should verify signatures)"
grep -r "publishToQstash\|publishToQstashQueue" apps/web/ | \
grep -E "(url.*api/|body.*api/)" | \
grep -o "/api/[^\"]*" | \
sort | uniq | \
while read endpoint; do
if ! grep -r "verifySignatureAppRouter" "apps/web/app$endpoint" > /dev/null 2>&1; then
echo " ❌ $endpoint - Missing verifySignatureAppRouter"
else
echo " ✅ $endpoint - Uses verifySignatureAppRouter"
fi
done
echo -e "\n✅ Audit complete! Review flagged items manually."
Manual Security Checklist
For each API route, verify:
Authentication
- • Uses
withAuthorwithEmailAccountmiddleware - • Or uses
withErrorwith custom authentication logic - • No public access to sensitive data
Authorization
- • All database queries include user/account filtering
- • Uses
emailAccountIdoruserIdin WHERE clauses - • Related resources use proper relationship filtering
Input Validation
- • Parameters are validated before use
- • Request bodies use Zod schemas
- • No direct parameter usage in queries
Output Security
- • Only returns necessary fields (use
select) - • No sensitive data in error messages
- • Consistent error response format
Security Testing Commands
bash
# Find routes without proper middleware
grep -r "export const" apps/web/app/api/ | grep -v "withEmailAccount\|withAuth\|withError"
# Find potential IDOR vulnerabilities
grep -r "findUnique.*where.*id" apps/web/app/api/ | grep -v "emailAccountId\|userId"
# Find unvalidated parameter usage
grep -r "params\." apps/web/app/api/ | grep -v "await.*params"
# Find potential information disclosure in errors
grep -r "throw.*Error" apps/web/app/api/ | grep -v "SafeError"
# 🚨 CRITICAL: Find unprotected cron endpoints
grep -r "withError.*async.*request" apps/web/app/api/ | grep -v "hasCronSecret\|hasPostCronSecret\|webhook\|linking"
# Find cron endpoints (verify they have proper authentication)
grep -r "hasCronSecret\|hasPostCronSecret" apps/web/app/api/
# 🚨 CRITICAL: Find QStash endpoints without signature verification
echo "QStash endpoints that should use verifySignatureAppRouter:"
grep -r "publishToQstash\|publishToQstashQueue" apps/web/ | \
grep -E "(url.*api/|body.*api/)" | \
grep -o "/api/[^\"]*" | \
sort | uniq | \
while read endpoint; do
if ! grep -r "verifySignatureAppRouter" "apps/web/app$endpoint" > /dev/null 2>&1; then
echo "❌ $endpoint - Missing verifySignatureAppRouter"
else
echo "✅ $endpoint - Uses verifySignatureAppRouter"
fi
done
# Check for weak cron secrets (should not exist)
grep -r "secret.*=.*[\"'].*[\"']" apps/web/app/api/ | grep -v "CRON_SECRET"
Common Issues to Look For
1. Missing User Scoping
typescript
// ❌ BAD: Missing user scoping
const schedule = await prisma.schedule.findUnique({
where: { id: scheduleId }
});
// ✅ GOOD: Properly scoped
const schedule = await prisma.schedule.findUnique({
where: { id: scheduleId, emailAccountId }
});
2. Information Disclosure
typescript
// ❌ BAD: Reveals internal details
if (!rule) {
throw new Error(`Rule ${ruleId} not found for user ${userId}`);
}
// ✅ GOOD: Generic error
if (!rule) {
throw new SafeError("Rule not found");
}
3. Unvalidated Parameters
typescript
// ❌ BAD: Direct parameter usage
const { id } = await params;
const rule = await prisma.rule.findUnique({ where: { id } });
// ✅ GOOD: Validated parameters
const { id } = await params;
if (!id || typeof id !== 'string') {
return NextResponse.json({ error: "Invalid ID" }, { status: 400 });
}
4. QStash Endpoint Security
typescript
// ❌ BAD: QStash endpoint without signature verification
export const POST = withError(async (request: NextRequest) => {
const json = await request.json();
// No signature verification - vulnerable to spoofing
});
// ✅ GOOD: QStash endpoint with signature verification
export const POST = withError(
verifySignatureAppRouter(async (request: NextRequest) => {
const json = await request.json();
// Signature verified - secure from spoofing
}),
);
QStash endpoints that MUST use verifySignatureAppRouter:
- •
/api/ai/digest- Called from digest queue - •
/api/resend/digest- Called from digest email queue - •
/api/clean/gmail- Called from cleanup queue - •
/api/user/categorize/senders/batch- Called from categorization queue
Security Review Process
Before Code Review
- •Run the security audit script
- •Check all flagged routes manually
- •Verify new routes follow security patterns
During Code Review
- •Check middleware usage on new routes
- •Verify database queries include user scoping
- •Look for potential IDOR vulnerabilities
- •Check error handling for information disclosure
- •🚨 CRITICAL: Verify QStash endpoints use
verifySignatureAppRouter- •Any endpoint called via
publishToQstashorpublishToQstashQueue - •Must wrap the handler with
verifySignatureAppRouter - •Prevents request spoofing and ensures authenticity
- •Any endpoint called via
Regular Security Audits
- •Run audit script weekly
- •Review any new withError usage
- •Check for new parameter handling patterns
- •Monitor for security-related dependencies
- •🚨 CRITICAL: Audit QStash endpoint security
- •Verify all QStash-called endpoints use
verifySignatureAppRouter - •Check for new endpoints added to QStash queues
- •Ensure signature verification is properly implemented
- •Verify all QStash-called endpoints use
Integration with CI/CD
Add this to your GitHub Actions or CI pipeline:
yaml
# .github/workflows/security-audit.yml
name: Security Audit
on: [push, pull_request]
jobs:
security-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Security Audit
run: |
# Check for routes without middleware
UNPROTECTED=$(grep -r "export const \(GET\|POST\|PUT\|DELETE\)" apps/web/app/api/ | grep -v "withEmailAccount\|withAuth\|withError" | wc -l)
if [ $UNPROTECTED -gt 0 ]; then
echo "❌ Found $UNPROTECTED potentially unprotected routes"
grep -r "export const \(GET\|POST\|PUT\|DELETE\)" apps/web/app/api/ | grep -v "withEmailAccount\|withAuth\|withError"
exit 1
else
echo "✅ All routes use proper middleware"
fi
Remember: This audit script helps identify potential issues, but manual review is still essential for security.