Skip to content

security-audit

Access: /template security-audit or /t security-audit

Performs a comprehensive security audit of your MakerKit application, identifying vulnerabilities and recommending fixes.

What It Does

The security-audit command helps you:

  • Scan for common security vulnerabilities
  • Check authentication and authorization
  • Review database security and RLS policies
  • Audit API endpoints and webhooks
  • Check for exposed secrets and credentials
  • Review security headers and CSP
  • Generate security report with fixes

Usage

bash
/template security-audit "Description"
# or
/t security-audit "Description"

When prompted, specify:

  • Audit depth (basic, comprehensive, pentesting)
  • Areas to focus on
  • Compliance requirements
  • Report format preference

Prerequisites

  • A MakerKit application to audit
  • Understanding of security requirements
  • Commercial MakerKit license from MakerKit

What Gets Checked

1. Authentication Security

typescript
// Checks performed:
const authenticationChecks = [
  {
    name: 'Password Policy',
    check: async () => {
      // Check password requirements
      const policy = await getPasswordPolicy();
      const issues = [];
      
      if (policy.minLength < 8) {
        issues.push('Password minimum length should be at least 8 characters');
      }
      if (!policy.requireUppercase) {
        issues.push('Should require uppercase letters');
      }
      if (!policy.requireNumbers) {
        issues.push('Should require numbers');
      }
      if (!policy.requireSpecialChars) {
        issues.push('Should require special characters');
      }
      
      return { passed: issues.length === 0, issues };
    },
  },
  
  {
    name: 'Session Management',
    check: async () => {
      const issues = [];
      
      // Check session timeout
      if (!process.env.SESSION_TIMEOUT || parseInt(process.env.SESSION_TIMEOUT) > 86400) {
        issues.push('Session timeout should be configured and less than 24 hours');
      }
      
      // Check secure cookies
      const cookieConfig = await getCookieConfiguration();
      if (!cookieConfig.secure && process.env.NODE_ENV === 'production') {
        issues.push('Cookies must use secure flag in production');
      }
      if (!cookieConfig.httpOnly) {
        issues.push('Auth cookies should use httpOnly flag');
      }
      if (!cookieConfig.sameSite) {
        issues.push('Cookies should use sameSite attribute');
      }
      
      return { passed: issues.length === 0, issues };
    },
  },
  
  {
    name: 'Multi-Factor Authentication',
    check: async () => {
      const mfaEnabled = await isMFAAvailable();
      const mfaEnforced = await isMFAEnforced();
      
      const issues = [];
      if (!mfaEnabled) {
        issues.push('MFA should be available for all users');
      }
      if (!mfaEnforced) {
        issues.push('Consider enforcing MFA for admin accounts');
      }
      
      return { passed: issues.length === 0, issues };
    },
  },
];

2. Authorization & Access Control

typescript
// RLS Policy Audit
async function auditRLSPolicies() {
  const tables = await getPublicTables();
  const issues = [];
  
  for (const table of tables) {
    // Check if RLS is enabled
    const rlsEnabled = await isRLSEnabled(table);
    if (!rlsEnabled) {
      issues.push({
        severity: 'critical',
        table,
        issue: 'Row Level Security is not enabled',
        fix: `ALTER TABLE public.${table} ENABLE ROW LEVEL SECURITY;`,
      });
    }
    
    // Check for policies
    const policies = await getTablePolicies(table);
    if (policies.length === 0 && rlsEnabled) {
      issues.push({
        severity: 'critical',
        table,
        issue: 'RLS enabled but no policies defined',
        fix: 'Add appropriate policies for all operations',
      });
    }
    
    // Check for overly permissive policies
    for (const policy of policies) {
      if (policy.definition.includes('true')) {
        issues.push({
          severity: 'high',
          table,
          policy: policy.name,
          issue: 'Policy may be overly permissive',
          fix: 'Review policy to ensure proper access control',
        });
      }
    }
  }
  
  return issues;
}

// API Endpoint Security
async function auditAPIEndpoints() {
  const endpoints = await scanAPIRoutes();
  const issues = [];
  
  for (const endpoint of endpoints) {
    // Check authentication
    if (!endpoint.requiresAuth && endpoint.modifiesData) {
      issues.push({
        severity: 'high',
        endpoint: endpoint.path,
        issue: 'Data-modifying endpoint lacks authentication',
      });
    }
    
    // Check rate limiting
    if (!endpoint.hasRateLimit) {
      issues.push({
        severity: 'medium',
        endpoint: endpoint.path,
        issue: 'No rate limiting configured',
      });
    }
    
    // Check input validation
    if (!endpoint.hasValidation) {
      issues.push({
        severity: 'high',
        endpoint: endpoint.path,
        issue: 'Missing input validation',
      });
    }
  }
  
  return issues;
}

3. Data Security

typescript
// Database Security Checks
const databaseSecurityChecks = [
  {
    name: 'Encryption at Rest',
    check: async () => {
      const encrypted = await isDatabaseEncrypted();
      return {
        passed: encrypted,
        issue: 'Database should be encrypted at rest',
        fix: 'Enable encryption in Supabase dashboard',
      };
    },
  },
  
  {
    name: 'Connection Security',
    check: async () => {
      const issues = [];
      
      // Check SSL enforcement
      if (!process.env.DATABASE_URL?.includes('sslmode=require')) {
        issues.push('Database connections should require SSL');
      }
      
      // Check for exposed credentials
      if (process.env.DATABASE_URL?.includes('@db.')) {
        issues.push('Using direct database URL instead of Supabase client');
      }
      
      return { passed: issues.length === 0, issues };
    },
  },
  
  {
    name: 'Sensitive Data Handling',
    check: async () => {
      const issues = [];
      
      // Check for PII in logs
      const logsContainPII = await scanLogsForPII();
      if (logsContainPII) {
        issues.push('Logs may contain personally identifiable information');
      }
      
      // Check for unencrypted sensitive fields
      const unencryptedFields = await findUnencryptedSensitiveData();
      if (unencryptedFields.length > 0) {
        issues.push(`Sensitive fields not encrypted: ${unencryptedFields.join(', ')}`);
      }
      
      return { passed: issues.length === 0, issues };
    },
  },
];

4. Dependency Vulnerabilities

typescript
// NPM Audit
async function auditDependencies() {
  const { stdout } = await execAsync('npm audit --json');
  const audit = JSON.parse(stdout);
  
  const vulnerabilities = {
    critical: [],
    high: [],
    moderate: [],
    low: [],
  };
  
  for (const [, advisory] of Object.entries(audit.advisories || {})) {
    vulnerabilities[advisory.severity].push({
      module: advisory.module_name,
      title: advisory.title,
      severity: advisory.severity,
      vulnerable_versions: advisory.vulnerable_versions,
      recommendation: advisory.recommendation,
      fix: advisory.patched_versions,
    });
  }
  
  return vulnerabilities;
}

// Outdated Dependencies
async function checkOutdatedDependencies() {
  const { stdout } = await execAsync('npm outdated --json');
  const outdated = JSON.parse(stdout);
  
  const criticalPackages = [
    'next',
    '@supabase/supabase-js',
    'stripe',
    '@node-saml/node-saml',
  ];
  
  const issues = [];
  for (const [pkg, info] of Object.entries(outdated)) {
    if (criticalPackages.includes(pkg)) {
      const current = info.current;
      const latest = info.latest;
      
      if (semver.major(latest) > semver.major(current)) {
        issues.push({
          package: pkg,
          current,
          latest,
          severity: 'high',
          message: 'Major version behind, may have security fixes',
        });
      }
    }
  }
  
  return issues;
}

5. Secret Management

typescript
// Secret Scanning
async function scanForSecrets() {
  const secretPatterns = [
    {
      name: 'AWS Access Key',
      pattern: /AKIA[0-9A-Z]{16}/g,
    },
    {
      name: 'Private Key',
      pattern: /-----BEGIN (RSA |EC )?PRIVATE KEY-----/g,
    },
    {
      name: 'API Key',
      pattern: /api[_-]?key[_-]?[:=]\s*['"]?[a-zA-Z0-9]{32,}['"]?/gi,
    },
    {
      name: 'JWT Secret',
      pattern: /jwt[_-]?secret[_-]?[:=]\s*['"]?[a-zA-Z0-9]{32,}['"]?/gi,
    },
  ];
  
  const issues = [];
  const files = await glob('**/*.{js,ts,jsx,tsx,json,env}', {
    ignore: ['node_modules/**', '.next/**', 'dist/**'],
  });
  
  for (const file of files) {
    const content = await fs.readFile(file, 'utf-8');
    
    for (const { name, pattern } of secretPatterns) {
      const matches = content.match(pattern);
      if (matches) {
        issues.push({
          file,
          type: name,
          severity: 'critical',
          message: `Potential ${name} found in source code`,
        });
      }
    }
  }
  
  return issues;
}

// Environment Variable Audit
async function auditEnvironmentVariables() {
  const issues = [];
  
  // Check for secrets in repository
  if (await fileExists('.env')) {
    issues.push({
      severity: 'critical',
      message: '.env file found in repository',
      fix: 'Add .env to .gitignore and remove from repository',
    });
  }
  
  // Check for missing required variables
  const requiredVars = [
    'NEXT_PUBLIC_SUPABASE_URL',
    'SUPABASE_SERVICE_ROLE_KEY',
    'STRIPE_SECRET_KEY',
    'STRIPE_WEBHOOK_SECRET',
  ];
  
  for (const varName of requiredVars) {
    if (!process.env[varName]) {
      issues.push({
        severity: 'high',
        message: `Missing required environment variable: ${varName}`,
      });
    }
  }
  
  return issues;
}

6. Security Headers

typescript
// Header Security Audit
async function auditSecurityHeaders() {
  const response = await fetch(process.env.NEXT_PUBLIC_APP_URL!);
  const headers = response.headers;
  
  const requiredHeaders = {
    'strict-transport-security': {
      expected: /max-age=31536000/,
      severity: 'high',
      message: 'HSTS header missing or misconfigured',
    },
    'x-frame-options': {
      expected: /DENY|SAMEORIGIN/,
      severity: 'medium',
      message: 'X-Frame-Options header missing',
    },
    'x-content-type-options': {
      expected: /nosniff/,
      severity: 'medium',
      message: 'X-Content-Type-Options header missing',
    },
    'content-security-policy': {
      expected: /default-src/,
      severity: 'high',
      message: 'Content Security Policy not configured',
    },
    'permissions-policy': {
      expected: /.+/,
      severity: 'low',
      message: 'Permissions Policy not configured',
    },
  };
  
  const issues = [];
  
  for (const [header, config] of Object.entries(requiredHeaders)) {
    const value = headers.get(header);
    if (!value || !config.expected.test(value)) {
      issues.push({
        header,
        current: value || 'not set',
        severity: config.severity,
        message: config.message,
      });
    }
  }
  
  return issues;
}

7. OWASP Top 10 Checks

typescript
// SQL Injection Prevention
async function checkSQLInjection() {
  const issues = [];
  const files = await glob('**/*.{ts,tsx,js,jsx}');
  
  for (const file of files) {
    const content = await fs.readFile(file, 'utf-8');
    
    // Check for raw SQL queries
    if (content.includes('.sql`') || content.includes('raw(')) {
      const lines = content.split('\n');
      lines.forEach((line, index) => {
        if (line.includes('${') && (line.includes('.sql`') || line.includes('raw('))) {
          issues.push({
            file,
            line: index + 1,
            severity: 'critical',
            issue: 'Potential SQL injection via string interpolation',
            code: line.trim(),
          });
        }
      });
    }
  }
  
  return issues;
}

// XSS Prevention
async function checkXSSVulnerabilities() {
  const issues = [];
  const files = await glob('**/*.{tsx,jsx}');
  
  for (const file of files) {
    const content = await fs.readFile(file, 'utf-8');
    
    // Check for dangerouslySetInnerHTML
    if (content.includes('dangerouslySetInnerHTML')) {
      issues.push({
        file,
        severity: 'high',
        issue: 'Use of dangerouslySetInnerHTML detected',
        recommendation: 'Ensure content is properly sanitized',
      });
    }
    
    // Check for user input in URLs
    const urlPattern = /href=\{[^}]*user[^}]*\}/g;
    if (urlPattern.test(content)) {
      issues.push({
        file,
        severity: 'medium',
        issue: 'User input in URL detected',
        recommendation: 'Validate and sanitize URLs',
      });
    }
  }
  
  return issues;
}

8. Security Report Generation

typescript
// Generate comprehensive report
export async function generateSecurityReport() {
  console.log('🔒 Starting Security Audit...\n');
  
  const report = {
    timestamp: new Date().toISOString(),
    summary: {
      critical: 0,
      high: 0,
      medium: 0,
      low: 0,
    },
    findings: {},
  };
  
  // Run all checks
  const checks = [
    { name: 'Authentication', fn: auditAuthentication },
    { name: 'Authorization', fn: auditAuthorization },
    { name: 'Database Security', fn: auditDatabase },
    { name: 'Dependencies', fn: auditDependencies },
    { name: 'Secrets', fn: scanForSecrets },
    { name: 'Headers', fn: auditSecurityHeaders },
    { name: 'OWASP', fn: auditOWASP },
  ];
  
  for (const check of checks) {
    console.log(`Checking ${check.name}...`);
    const results = await check.fn();
    report.findings[check.name] = results;
    
    // Update summary
    for (const issue of results) {
      report.summary[issue.severity]++;
    }
  }
  
  // Generate HTML report
  const html = generateHTMLReport(report);
  await fs.writeFile('security-report.html', html);
  
  // Generate JSON report
  await fs.writeFile('security-report.json', JSON.stringify(report, null, 2));
  
  console.log('\n📊 Security Audit Complete!');
  console.log(`Critical: ${report.summary.critical}`);
  console.log(`High: ${report.summary.high}`);
  console.log(`Medium: ${report.summary.medium}`);
  console.log(`Low: ${report.summary.low}`);
  console.log('\n📄 Reports generated: security-report.html, security-report.json');
  
  return report;
}

Automated Fixes

The command can automatically fix certain issues:

typescript
// Auto-fix security issues
export async function autoFixSecurityIssues() {
  console.log('🔧 Applying automatic security fixes...\n');
  
  // Fix missing security headers
  await updateMiddleware();
  
  // Update vulnerable dependencies
  await execAsync('npm audit fix');
  
  // Add missing RLS policies
  await applyDefaultRLSPolicies();
  
  // Update environment example
  await updateEnvExample();
  
  console.log('✅ Automatic fixes applied');
}

License Requirement

Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.

Built with ❤️ for the AI Coding community, by Praney Behl