security-audit

SKILL.md

Audit security configurations for your webapp and backend services.

When to Use

  • Before production deployments
  • After adding new integrations
  • Reviewing authentication/authorization
  • Checking for common vulnerabilities

Project Context

Before starting, identify the security-relevant files in the current project:

# Locate security-relevant files
grep -rn "Content-Security-Policy" --include="*.html" --include="*.tsx" --include="*.jsx"
grep -rn "corsHeaders\|Access-Control" --include="*.ts" --include="*.js"
find . -name "*.sql" -path "*/migrations/*" | head -20
grep -rn "sanitize\|DOMPurify\|xss" --include="*.ts" --include="*.js" --include="*.jsx" --include="*.tsx"
grep -rn "AuthContext\|AuthProvider\|useAuth\|getSession" --include="*.ts" --include="*.js" --include="*.jsx" --include="*.tsx"

Security Checklist

1. Content Security Policy (CSP)

Look for: CSP meta tags in HTML or CSP headers in server/deployment config.

Reference policy:

<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: blob:;
  font-src 'self';
  connect-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
">

Audit Points:

  • default-src 'self' - Restrictive default
  • No unsafe-inline for scripts (if possible)
  • All external domains explicitly listed
  • object-src 'none' - Blocks plugins
  • frame-src limited to trusted sources

When Adding New Services:

Stripe → add https://js.stripe.com to script-src
Analytics → add domain to connect-src
Fonts → add to font-src
Embeds → add to frame-src

2. Security Headers

Look for: Headers in deployment config (e.g., vercel.json, netlify.toml, nginx config, middleware).

Required headers:

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-XSS-Protection", "value": "1; mode=block" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" },
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000; includeSubDomains"
        }
      ]
    }
  ]
}

Audit Points:

  • HSTS enabled with long max-age
  • X-Frame-Options DENY or SAMEORIGIN
  • X-Content-Type-Options nosniff
  • Referrer-Policy configured
  • Permissions-Policy restricts unused APIs

3. Row Level Security (RLS)

Applies to: Projects using Supabase or similar row-level security.

Required Policies Per Table:

-- Enable RLS
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;

-- SELECT: Users see own data
CREATE POLICY "Users can view own data"
ON table_name FOR SELECT
USING (auth.uid() = owner_id);

-- INSERT: Users create own data
CREATE POLICY "Users can insert own data"
ON table_name FOR INSERT
WITH CHECK (auth.uid() = owner_id);

-- UPDATE: Users modify own data
CREATE POLICY "Users can update own data"
ON table_name FOR UPDATE
USING (auth.uid() = owner_id);

-- DELETE: Users delete own data
CREATE POLICY "Users can delete own data"
ON table_name FOR DELETE
USING (auth.uid() = owner_id);

Audit Points:

  • RLS enabled on ALL user data tables
  • No tables with public access unintentionally
  • Policies use auth.uid() correctly
  • Service role used only in backend / Edge Functions
  • No policies with USING (true) on sensitive tables

4. Input Sanitization

Look for: Sanitization utilities in the project.

Reference implementation:

import DOMPurify from 'dompurify';

export function sanitizeHtml(dirty) {
  return DOMPurify.sanitize(dirty, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
    ALLOWED_ATTR: [],
  });
}

export function sanitizeText(input) {
  if (typeof input !== 'string') return '';
  return input
    .replace(/[<>]/g, '') // Remove angle brackets
    .trim()
    .slice(0, 10000); // Length limit
}

Audit Points:

  • All user input sanitized before display
  • DOMPurify configured restrictively
  • No dangerouslySetInnerHTML without sanitization
  • Length limits on all text inputs
  • SQL injection prevented (parameterized queries or ORM)

5. Authentication

Audit Points:

  • Session refresh working correctly
  • Logout clears all tokens
  • Protected routes check auth state
  • No tokens stored in localStorage (use cookies)
  • Password reset flow secure
// Secure session handling pattern
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
  // Redirect to login
}

// Secure logout
await supabase.auth.signOut();

6. API Keys & Secrets

Environment Variables Audit:

# PUBLIC (OK to expose in frontend — framework-prefixed)
VITE_SUPABASE_URL=https://xxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJ...       # Limited permissions via RLS
NEXT_PUBLIC_SENTRY_DSN=https://...

# SECRET (Never expose in frontend)
SUPABASE_SERVICE_ROLE_KEY=eyJ...     # Full database access
STRIPE_SECRET_KEY=sk_...
DATABASE_URL=postgres://...

Audit Points:

  • No secrets in frontend code
  • .env files in .gitignore
  • Secrets only in backend / Edge Functions
  • Framework prefix (VITE_, NEXT_PUBLIC_) only on public variables
  • No hardcoded API keys in source

7. Dependency Vulnerabilities

Commands:

# Check for vulnerabilities
npm audit --production

# Auto-fix where possible
npm audit fix --production

# Check for outdated packages
npm outdated

Audit Points:

  • No critical vulnerabilities
  • High severity issues addressed
  • Dependencies up to date
  • Lock file committed (package-lock.json)

8. CORS Configuration

Reference:

const corsHeaders = {
  "Access-Control-Allow-Origin": "https://yourdomain.com", // Not "*" in production
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
  "Access-Control-Allow-Methods": "POST, OPTIONS",
};

Audit Points:

  • No wildcard * origin in production
  • Specific domains whitelisted
  • Credentials mode configured correctly
  • Preflight requests handled

9. Webhook Security

Signature Verification:

// Always verify webhook signatures
const signature = req.headers.get("stripe-signature");
const event = stripe.webhooks.constructEvent(body, signature, webhookSecret);

Idempotency:

// Prevent duplicate processing
const { data: existing } = await db
  .from("webhook_events")
  .select("id")
  .eq("event_id", event.id)
  .single();

if (existing) {
  return new Response("Already processed", { status: 200 });
}

Audit Points:

  • All webhooks verify signatures
  • Idempotency keys stored
  • Timeout handling for slow processing
  • Error responses don't leak info

10. Rate Limiting

Audit Points:

  • Auth endpoints rate limited
  • API endpoints rate limited
  • Brute force protection on login
  • Rate limits configured on hosting/database platform

Security Report Template

# Security Audit Report
Date: YYYY-MM-DD
Auditor: [Name]

## Summary
- Critical Issues: X
- High Issues: X
- Medium Issues: X
- Low Issues: X

## Critical Findings
1. [Issue description]
   - Location: [file:line]
   - Risk: [description]
   - Remediation: [steps]

## Recommendations
1. [Recommendation]

## Checklist Results
- [x] CSP configured
- [x] Security headers set
- [ ] RLS audit pending
- [x] Input sanitization in place
...

Automated Checks

Add to package.json:

{
  "scripts": {
    "security:check": "npm audit --production --audit-level=high && npm outdated",
    "precommit:security": "npm audit --production --audit-level=critical"
  }
}

Common Vulnerabilities to Check

  1. XSS: User input displayed without sanitization
  2. CSRF: Forms without anti-CSRF tokens
  3. SQL Injection: Raw queries without parameterization
  4. Auth Bypass: Missing route protection
  5. Data Exposure: RLS not enabled or misconfigured
  6. Secret Leakage: API keys in frontend code
  7. Dependency Vulns: Outdated packages
Weekly Installs
2
GitHub Stars
4
First Seen
11 days ago
Installed on
amp2
cline2
opencode2
cursor2
kimi-cli2
kiro-cli2