Skip to content
Security Scanning logo

Security Scanning

> **Skill Purpose:** API security assessment, vulnerability scanning, and security validation procedures

Coverage-Creatives/zeus0installs0stars

SKILL.md

Full skill instructions

Security Scanning

Skill Purpose: API security assessment, vulnerability scanning, and security validation procedures


Core Skill Pattern

Objective: Establish comprehensive security scanning strategy with automated vulnerability detection, security testing, and compliance validation.

Universal Pattern:

  1. Define security scanning strategy and scope
  2. Create automated vulnerability scanning procedures
  3. Establish security testing patterns
  4. Set up compliance validation and reporting
  5. Create security remediation and tracking procedures

Key Decisions (Project-Specific):

  • Security scanning tools and frameworks
  • Vulnerability severity thresholds and policies
  • Security testing depth and coverage
  • Compliance requirements and standards
  • Remediation timelines and procedures

Project-Specific Implementation Notes

Customize per project:

  • Security tools based on tech stack and compliance needs
  • Scanning frequency based on deployment cadence
  • Vulnerability thresholds based on risk tolerance
  • Testing depth based on data sensitivity
  • Remediation approach based on team capabilities

Example Implementation (API Security Pattern)

Note: This is an example pattern using automated security scanning with Snyk, npm audit, and custom security tests. Adapt security tools and patterns based on your specific project requirements and compliance standards.

Prerequisites (Example)

  • API endpoints identified and documented
  • Security requirements and compliance standards defined
  • Security scanning tools selected and configured
  • Vulnerability management process established

Example: API Security Implementation

Framework-Specific Example: This demonstrates API security patterns with Next.js and automated scanning. Adapt for your security framework and API requirements.

Security Configuration

// lib/​security-config.ts
export interface SecurityConfig {
  rateLimiting: {
    windowMs: number;
    maxRequests: number;
  };
  authentication: {
    jwtSecret: string;
    tokenExpiry: string;
  };
  validation: {
    maxPayloadSize: number;
    allowedOrigins: string[];
  };
  scanning: {
    enabled: boolean;
    severityThreshold: 'low' | 'medium' | 'high' | 'critical';
    scanInterval: number; // hours
  };
}

export const securityConfig: SecurityConfig = {
  rateLimiting: {
    windowMs: 15 * 60 * 1000, // 15 minutes
    maxRequests: 100,
  },
  authentication: {
    jwtSecret: process.env.JWT_SECRET || 'fallback-secret',
    tokenExpiry: '24h',
  },
  validation: {
    maxPayloadSize: 10 * 1024 * 1024, // 10MB
    allowedOrigins: [
      'http://localhost:3000',
      'https://yourdomain.com',
    ],
  },
  scanning: {
    enabled: process.env.NODE_ENV === 'production',
    severityThreshold: 'medium',
    scanInterval: 24,
  },
};

Security Middleware

// middleware/​security.ts
import { NextResponse } from 'next/​server';
import type { NextRequest } from 'next/​server';
import { securityConfig } from '../​lib/​security-config';

export function securityMiddleware(request: NextRequest) {
  const response = NextResponse.next();
  
  // Security headers
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-XSS-Protection', '1; mode=block');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
  
  // CORS validation
  const origin = request.headers.get('origin');
  if (origin && !securityConfig.validation.allowedOrigins.includes(origin)) {
    return NextResponse.json(
      { error: 'Origin not allowed' },
      { status: 403 }
    );
  }
  
  // Payload size validation
  const contentLength = request.headers.get('content-length');
  if (contentLength && parseInt(contentLength) > securityConfig.validation.maxPayloadSize) {
    return NextResponse.json(
      { error: 'Payload too large' },
      { status: 413 }
    );
  }
  
  return response;
}

export const config = {
  matcher: ['/​api/:path*'],
};

Input Validation Security

// lib/​security-validation.ts
import { z } from 'zod';

export class SecurityValidator {
  static sanitizeInput(input: string): string {
    return input
      .trim()
      .replace(/[<>]/​g, '') // Remove potential HTML tags
      .replace(/​javascript:/​gi, '') // Remove javascript: protocol
      .replace(/​on\w+=/​gi, ''); // Remove event handlers
  }
  
  static validateEmail(email: string): boolean {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email) && email.length <= 254;
  }
  
  static validatePassword(password: string): {
    isValid: boolean;
    errors: string[];
  } {
    const errors: string[] = [];
    
    if (password.length < 8) {
      errors.push('Password must be at least 8 characters long');
    }
    
    if (!/[A-Z]/​.test(password)) {
      errors.push('Password must contain at least one uppercase letter');
    }
    
    if (!/[a-z]/​.test(password)) {
      errors.push('Password must contain at least one lowercase letter');
    }
    
    if (!/\d/​.test(password)) {
      errors.push('Password must contain at least one number');
    }
    
    if (!/[!@#$%^&*(),.?":{}|<>]/​.test(password)) {
      errors.push('Password must contain at least one special character');
    }
    
    return {
      isValid: errors.length === 0,
      errors,
    };
  }
  
  static createSecureUserSchema() {
    return z.object({
      email: z.string()
        .email('Invalid email format')
        .max(254, 'Email too long')
        .transform(this.sanitizeInput),
      name: z.string()
        .min(1, 'Name is required')
        .max(100, 'Name too long')
        .transform(this.sanitizeInput),
      password: z.string()
        .min(8, 'Password too short')
        .max(128, 'Password too long')
        .refine((password) => this.validatePassword(password).isValid, {
          message: 'Password does not meet security requirements',
        }),
    });
  }
}

API Security Tests

// __tests__/​security/​api-security.test.ts
import { createMocks } from 'node-mocks-http';
import { POST } from '@/​app/​api/​users/​route';

describe('API Security Tests', () => {
  describe('Input Validation', () => {
    it('should reject XSS attempts in user input', async () => {
      const maliciousPayload = {
        email: '[email protected]',
        name: '<script>alert("xss")</​script>',
      };

      const { req } = createMocks({
        method: 'POST',
        body: maliciousPayload,
      });

      const response = await POST(req);
      
      // Should either sanitize or reject the input
      expect([200, 400]).toContain(response.status);
      
      if (response.status === 200) {
        const data = await response.json();
        expect(data.data.name).not.toContain('<script>');
      }
    });

    it('should reject SQL injection attempts', async () => {
      const sqlInjectionPayload = {
        email: "'; DROP TABLE users; --",
        name: 'Test User',
      };

      const { req } = createMocks({
        method: 'POST',
        body: sqlInjectionPayload,
      });

      const response = await POST(req);
      expect([400, 422]).toContain(response.status);
    });

    it('should validate email format strictly', async () => {
      const invalidEmails = [
        'plainaddress',
        '@missingdomain.com',
        '[email protected]',
        'missing@domain',
        'spaces @domain.com',
        '[email protected]',
      ];

      for (const email of invalidEmails) {
        const { req } = createMocks({
          method: 'POST',
          body: {
            email,
            name: 'Test User',
          },
        });

        const response = await POST(req);
        expect(response.status).toBe(400);
      }
    });
  });

  describe('Authentication Security', () => {
    it('should reject requests without proper authentication', async () => {
      const { req } = createMocks({
        method: 'GET',
        headers: {
          // Missing Authorization header
        },
      });

      // Test protected endpoint
      const response = await GET(req);
      expect(response.status).toBe(401);
    });

    it('should reject invalid JWT tokens', async () => {
      const { req } = createMocks({
        method: 'GET',
        headers: {
          Authorization: 'Bearer invalid.token.here',
        },
      });

      const response = await GET(req);
      expect(response.status).toBe(401);
    });
  });

  describe('Rate Limiting', () => {
    it('should implement rate limiting on sensitive endpoints', async () => {
      const requests = Array.from({ length: 10 }, () =>
        createMocks({
          method: 'POST',
          body: {
            email: '[email protected]',
            password: 'password123',
          },
        })
      );

      const responses = await Promise.all(
        requests.map(({ req }) => POST(req))
      );

      // Some requests should be rate limited
      const rateLimitedResponses = responses.filter(
        response => response.status === 429
      );

      expect(rateLimitedResponses.length).toBeGreaterThan(0);
    });
  });
});

Automated Security Scanning

// scripts/​security-scan.ts
import { execSync } from 'child_process';
import { writeFileSync, readFileSync } from 'fs';
import { join } from 'path';

interface VulnerabilityReport {
  timestamp: string;
  npmAudit: NpmAuditResult;
  snykScan: SnykResult;
  customTests: SecurityTestResult[];
}

interface NpmAuditResult {
  vulnerabilities: number;
  high: number;
  moderate: number;
  low: number;
  info: number;
}

interface SnykResult {
  vulnerabilities: number;
  high: number;
  medium: number;
  low: number;
}

interface SecurityTestResult {
  testName: string;
  passed: boolean;
  issues: string[];
}

export class SecurityScanner {
  static async runSecurityScan(): Promise<VulnerabilityReport> {
    const report: VulnerabilityReport = {
      timestamp: new Date().toISOString(),
      npmAudit: await this.runNpmAudit(),
      snykScan: await this.runSnykScan(),
      customTests: await this.runCustomSecurityTests(),
    };

    this.generateReport(report);
    this.checkThresholds(report);
    
    return report;
  }

  private static async runNpmAudit(): Promise<NpmAuditResult> {
    try {
      const auditResult = execSync('npm audit --json', { encoding: 'utf8' });
      const audit = JSON.parse(auditResult);
      
      return {
        vulnerabilities: audit.metadata?.vulnerabilities?.total || 0,
        high: audit.metadata?.vulnerabilities?.high || 0,
        moderate: audit.metadata?.vulnerabilities?.moderate || 0,
        low: audit.metadata?.vulnerabilities?.low || 0,
        info: audit.metadata?.vulnerabilities?.info || 0,
      };
    } catch (error) {
      console.error('npm audit failed:', error);
      return {
        vulnerabilities: 0,
        high: 0,
        moderate: 0,
        low: 0,
        info: 0,
      };
    }
  }

  private static async runSnykScan(): Promise<SnykResult> {
    try {
      const snykResult = execSync('snyk test --json', { encoding: 'utf8' });
      const snyk = JSON.parse(snykResult);
      
      return {
        vulnerabilities: snyk.length || 0,
        high: snyk.filter((v: any) => v.severity === 'high').length,
        medium: snyk.filter((v: any) => v.severity === 'medium').length,
        low: snyk.filter((v: any) => v.severity === 'low').length,
      };
    } catch (error) {
      console.error('Snyk scan failed:', error);
      return {
        vulnerabilities: 0,
        high: 0,
        medium: 0,
        low: 0,
      };
    }
  }

  private static async runCustomSecurityTests(): Promise<SecurityTestResult[]> {
    // Run custom security tests
    const tests: SecurityTestResult[] = [
      await this.testAuthenticationSecurity(),
      await this.testInputValidation(),
      await this.testRateLimiting(),
      await this.testHttpsEnforcement(),
    ];

    return tests;
  }

  private static async testAuthenticationSecurity(): Promise<SecurityTestResult> {
    // Test authentication mechanisms
    const issues: string[] = [];
    
    // Check for weak JWT secrets
    if (process.env.JWT_SECRET === 'fallback-secret') {
      issues.push('Using fallback JWT secret in production');
    }
    
    // Check for missing authentication on sensitive endpoints
    // This would involve making actual API calls
    
    return {
      testName: 'Authentication Security',
      passed: issues.length === 0,
      issues,
    };
  }

  private static async testInputValidation(): Promise<SecurityTestResult> {
    // Test input validation
    const issues: string[] = [];
    
    // Test for XSS vulnerabilities
    // Test for SQL injection
    // Test for CSRF protection
    
    return {
      testName: 'Input Validation',
      passed: issues.length === 0,
      issues,
    };
  }

  private static async testRateLimiting(): Promise<SecurityTestResult> {
    // Test rate limiting implementation
    const issues: string[] = [];
    
    // Make multiple rapid requests to test rate limiting
    // Check if rate limiting is properly implemented
    
    return {
      testName: 'Rate Limiting',
      passed: issues.length === 0,
      issues,
    };
  }

  private static async testHttpsEnforcement(): Promise<SecurityTestResult> {
    // Test HTTPS enforcement
    const issues: string[] = [];
    
    if (process.env.NODE_ENV === 'production' && !process.env.FORCE_HTTPS) {
      issues.push('HTTPS not explicitly enforced in production');
    }
    
    return {
      testName: 'HTTPS Enforcement',
      passed: issues.length === 0,
      issues,
    };
  }

  private static generateReport(report: VulnerabilityReport): void {
    const reportPath = join(process.cwd(), 'security-report.json');
    writeFileSync(reportPath, JSON.stringify(report, null, 2));
    console.log(`Security report generated: ${reportPath}`);
  }

  private static checkThresholds(report: VulnerabilityReport): void {
    const config = securityConfig.scanning;
    
    if (report.npmAudit.high > 0 || report.snykScan.high > 0) {
      console.error('🚨 HIGH severity vulnerabilities detected!');
      process.exit(1);
    }
    
    if (config.severityThreshold === 'medium' && 
        (report.npmAudit.moderate > 0 || report.snykScan.medium > 0)) {
      console.error('⚠️ MEDIUM severity vulnerabilities detected!');
      process.exit(1);
    }
    
    const failedTests = report.customTests.filter(test => !test.passed);
    if (failedTests.length > 0) {
      console.error('❌ Security tests failed:');
      failedTests.forEach(test => {
        console.error(`  - ${test.testName}: ${test.issues.join(', ')}`);
      });
      process.exit(1);
    }
    
    console.log('✅ Security scan passed!');
  }
}

// Run security scan if called directly
if (require.main === module) {
  SecurityScanner.runSecurityScan()
    .then(() => process.exit(0))
    .catch((error) => {
      console.error('Security scan failed:', error);
      process.exit(1);
    });
}

Security Monitoring

// lib/​security-monitoring.ts
export class SecurityMonitor {
  static logSecurityEvent(
    event: string,
    severity: 'low' | 'medium' | 'high' | 'critical',
    details: Record<string, any>
  ) {
    const securityEvent = {
      timestamp: new Date().toISOString(),
      event,
      severity,
      details,
      ip: details.ip || 'unknown',
      userAgent: details.userAgent || 'unknown',
    };
    
    // Log to security monitoring system
    console.warn('SECURITY EVENT:', JSON.stringify(securityEvent, null, 2));
    
    // Send to external security service
    if (severity === 'high' || severity === 'critical') {
      this.sendAlert(securityEvent);
    }
  }
  
  private static sendAlert(event: any) {
    // Send to security team, Slack, or monitoring service
    // Implementation depends on your alerting infrastructure
  }
  
  static detectSuspiciousActivity(request: NextRequest): boolean {
    const ip = request.ip || 'unknown';
    const userAgent = request.headers.get('user-agent') || 'unknown';
    
    // Detect common attack patterns
    const suspiciousPatterns = [
      /\bunion\s+select\b/​i, // SQL injection
      /<script\b[^<]*(?:(?!<\/​script>)<[^<]*)*<\/​script>/​gi, // XSS
      /\.\.\//, // Path traversal
      /​javascript:/​i, // JavaScript protocol
    ];
    
    const url = request.url;
    const body = request.body;
    
    for (const pattern of suspiciousPatterns) {
      if (pattern.test(url) || (body && pattern.test(JSON.stringify(body)))) {
        this.logSecurityEvent('Suspicious pattern detected', 'high', {
          pattern: pattern.source,
          url,
          ip,
          userAgent,
        });
        return true;
      }
    }
    
    return false;
  }
}

Best Practices

  1. Defense in depth - Implement multiple layers of security controls
  2. Principle of least privilege - Grant minimum necessary permissions
  3. Regular scanning - Automate vulnerability detection and monitoring
  4. Input validation - Validate and sanitize all user inputs
  5. Security headers - Implement comprehensive HTTP security headers

Stop Conditions

STOP and escalate if:

  • Security requirements unclear or incomplete
  • Vulnerability thresholds not defined
  • Security testing coverage inadequate
  • Remediation procedures not established

Skill Version: 1.0.0