import { BaseAccessibilityTest, TestContext, TestResult } from '../base-test';

export class VulnerabilityTest extends BaseAccessibilityTest {
  name = 'Vulnerability Scan Test';
  description = 'Scans for common web vulnerabilities and security weaknesses';
  category = 'security';
  priority = 'critical';
  standards = ['OWASP Top 10', 'Security Best Practices', 'Vulnerability Assessment'];

  async run(context: TestContext): Promise<TestResult> {
    const { page, url } = context;
    
    try {
      await page.goto(url, { waitUntil: 'networkidle' });
      
      const issues: string[] = [];
      const warnings: string[] = [];
      const details: Record<string, any> = {};
      
      // Perform vulnerability analysis
      const vulnerabilityAnalysis = await this.performVulnerabilityScan(page, url);
      details.vulnerabilityAnalysis = vulnerabilityAnalysis;
      
      // Check for XSS vulnerabilities
      if (vulnerabilityAnalysis.xssVulnerabilities.length > 0) {
        issues.push(`XSS vulnerabilities detected: ${vulnerabilityAnalysis.xssVulnerabilities.length} potential issues`);
        details.xssVulnerabilities = vulnerabilityAnalysis.xssVulnerabilities;
      }
      
      // Check for injection vulnerabilities
      if (vulnerabilityAnalysis.injectionVulnerabilities.length > 0) {
        issues.push(`Injection vulnerabilities detected: ${vulnerabilityAnalysis.injectionVulnerabilities.length} potential issues`);
        details.injectionVulnerabilities = vulnerabilityAnalysis.injectionVulnerabilities;
      }
      
      // Check for information disclosure
      if (vulnerabilityAnalysis.infoDisclosure.length > 0) {
        issues.push(`Information disclosure detected: ${vulnerabilityAnalysis.infoDisclosure.length} potential issues`);
        details.infoDisclosure = vulnerabilityAnalysis.infoDisclosure;
      }
      
      // Check for sensitive data exposure
      if (vulnerabilityAnalysis.sensitiveDataExposure.length > 0) {
        issues.push(`Sensitive data exposure detected: ${vulnerabilityAnalysis.sensitiveDataExposure.length} potential issues`);
        details.sensitiveDataExposure = vulnerabilityAnalysis.sensitiveDataExposure;
      }
      
      // Check for broken authentication
      if (vulnerabilityAnalysis.brokenAuth.length > 0) {
        issues.push(`Broken authentication detected: ${vulnerabilityAnalysis.brokenAuth.length} potential issues`);
        details.brokenAuth = vulnerabilityAnalysis.brokenAuth;
      }
      
      // Check for security misconfigurations
      if (vulnerabilityAnalysis.securityMisconfig.length > 0) {
        warnings.push(`Security misconfigurations detected: ${vulnerabilityAnalysis.securityMisconfig.length} potential issues`);
        details.securityMisconfig = vulnerabilityAnalysis.securityMisconfig;
      }
      
      // Check for outdated components
      if (vulnerabilityAnalysis.outdatedComponents.length > 0) {
        warnings.push(`Outdated components detected: ${vulnerabilityAnalysis.outdatedComponents.length} potential issues`);
        details.outdatedComponents = vulnerabilityAnalysis.outdatedComponents;
      }
      
      // Check for insufficient logging
      if (vulnerabilityAnalysis.insufficientLogging.length > 0) {
        warnings.push(`Insufficient logging detected: ${vulnerabilityAnalysis.insufficientLogging.length} potential issues`);
        details.insufficientLogging = vulnerabilityAnalysis.insufficientLogging;
      }
      
      // Calculate vulnerability score
      const totalVulnerabilities = 
        vulnerabilityAnalysis.xssVulnerabilities.length +
        vulnerabilityAnalysis.injectionVulnerabilities.length +
        vulnerabilityAnalysis.infoDisclosure.length +
        vulnerabilityAnalysis.sensitiveDataExposure.length +
        vulnerabilityAnalysis.brokenAuth.length;
      
      const totalWarnings = 
        vulnerabilityAnalysis.securityMisconfig.length +
        vulnerabilityAnalysis.outdatedComponents.length +
        vulnerabilityAnalysis.insufficientLogging.length;
      
      const vulnerabilityScore = Math.max(0, 100 - (totalVulnerabilities * 10) - (totalWarnings * 5));
      
      details.vulnerabilityScore = vulnerabilityScore;
      details.totalVulnerabilities = totalVulnerabilities;
      details.totalWarnings = totalWarnings;
      
      return this.createResult(
        totalVulnerabilities === 0,
        totalVulnerabilities + totalWarnings,
        issues,
        warnings,
        details
      );
    } catch (error) {
      return this.createErrorResult(`Vulnerability Test failed: ${error}`);
    }
  }
  
  private async performVulnerabilityScan(page: any, url: string): Promise<any> {
    const analysis = {
      xssVulnerabilities: [] as string[],
      injectionVulnerabilities: [] as string[],
      infoDisclosure: [] as string[],
      sensitiveDataExposure: [] as string[],
      brokenAuth: [] as string[],
      securityMisconfig: [] as string[],
      outdatedComponents: [] as string[],
      insufficientLogging: [] as string[]
    };
    
    // Perform page analysis
    const pageAnalysis = await page.evaluate(() => {
      const result = {
        forms: [] as any[],
        inputs: [] as any[],
        scripts: [] as any[],
        comments: [] as string[],
        errorMessages: [] as string[],
        versionInfo: [] as string[],
        sensitiveData: [] as string[]
      };
      
      // Analyze forms for injection vulnerabilities
      const forms = document.querySelectorAll('form');
      forms.forEach((form, index) => {
        const formData = {
          action: form.getAttribute('action') || '',
          method: form.getAttribute('method') || 'GET',
          inputs: [] as any[]
        };
        
        const inputs = form.querySelectorAll('input, textarea, select');
        inputs.forEach(input => {
          const inputData = {
            type: input.getAttribute('type') || 'text',
            name: input.getAttribute('name') || '',
            id: input.getAttribute('id') || '',
            placeholder: input.getAttribute('placeholder') || '',
            value: input.getAttribute('value') || ''
          };
          formData.inputs.push(inputData);
        });
        
        result.forms.push(formData);
      });
      
      // Analyze all inputs
      const allInputs = document.querySelectorAll('input, textarea, select');
      allInputs.forEach(input => {
        const inputData = {
          type: input.getAttribute('type') || 'text',
          name: input.getAttribute('name') || '',
          id: input.getAttribute('id') || '',
          placeholder: input.getAttribute('placeholder') || '',
          value: input.getAttribute('value') || '',
          autocomplete: input.getAttribute('autocomplete') || ''
        };
        result.inputs.push(inputData);
      });
      
      // Analyze scripts for version information
      const scripts = document.querySelectorAll('script[src]');
      scripts.forEach(script => {
        const src = script.getAttribute('src') || '';
        if (src.includes('jquery') || src.includes('bootstrap') || src.includes('angular') || src.includes('react')) {
          result.scripts.push(src);
        }
      });
      
      // Extract comments that might contain sensitive information
      const walker = document.createTreeWalker(
        document,
        NodeFilter.SHOW_COMMENT,
        null
      );
      
      let comment;
      while (comment = walker.nextNode()) {
        const commentText = comment.textContent || '';
        if (commentText.includes('TODO') || commentText.includes('FIXME') || 
            commentText.includes('password') || commentText.includes('secret') ||
            commentText.includes('api') || commentText.includes('key')) {
          result.comments.push(commentText);
        }
      }
      
      // Look for error messages
      const errorElements = document.querySelectorAll('.error, .alert, .warning, [class*="error"], [class*="alert"]');
      errorElements.forEach(element => {
        const text = element.textContent || '';
        if (text.includes('error') || text.includes('exception') || text.includes('stack trace')) {
          result.errorMessages.push(text);
        }
      });
      
      // Look for version information in meta tags
      const metaTags = document.querySelectorAll('meta');
      metaTags.forEach(meta => {
        const name = meta.getAttribute('name') || '';
        const content = meta.getAttribute('content') || '';
        if (name.includes('version') || name.includes('generator') || content.includes('version')) {
          result.versionInfo.push(`${name}: ${content}`);
        }
      });
      
      // Look for sensitive data patterns
      const bodyText = document.body.textContent || '';
      const sensitivePatterns = [
        /password\s*[:=]\s*\w+/gi,
        /api[_-]?key\s*[:=]\s*\w+/gi,
        /secret\s*[:=]\s*\w+/gi,
        /token\s*[:=]\s*\w+/gi,
        /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, // Credit card pattern
        /\b\d{3}-\d{2}-\d{4}\b/g // SSN pattern
      ];
      
      sensitivePatterns.forEach(pattern => {
        const matches = bodyText.match(pattern);
        if (matches) {
          result.sensitiveData.push(...matches);
        }
      });
      
      return result;
    });
    
    // Analyze XSS vulnerabilities
    pageAnalysis.inputs.forEach((input: any) => {
      if (input.type === 'text' || input.type === 'textarea') {
        if (!input.name.includes('csrf') && !input.name.includes('token')) {
          analysis.xssVulnerabilities.push(`Potential XSS in input: ${input.name || input.id}`);
        }
      }
    });
    
    // Check for reflected XSS in URL parameters
    const urlParams = new URL(url).searchParams;
    urlParams.forEach((value, key) => {
      if (value.includes('<script>') || value.includes('javascript:') || value.includes('onerror=')) {
        analysis.xssVulnerabilities.push(`Potential reflected XSS in URL parameter: ${key}`);
      }
    });
    
    // Analyze injection vulnerabilities
    pageAnalysis.forms.forEach((form: any) => {
      if (form.method === 'GET') {
        analysis.injectionVulnerabilities.push(`Form uses GET method: ${form.action}`);
      }
      
      form.inputs.forEach((input: any) => {
        if (input.type === 'text' && (input.name.includes('sql') || input.name.includes('query'))) {
          analysis.injectionVulnerabilities.push(`Potential SQL injection in input: ${input.name}`);
        }
      });
    });
    
    // Check for information disclosure
    if (pageAnalysis.comments.length > 0) {
      analysis.infoDisclosure.push(`${pageAnalysis.comments.length} comments with potentially sensitive information`);
    }
    
    if (pageAnalysis.errorMessages.length > 0) {
      analysis.infoDisclosure.push(`${pageAnalysis.errorMessages.length} error messages that might reveal system information`);
    }
    
    if (pageAnalysis.versionInfo.length > 0) {
      analysis.infoDisclosure.push(`${pageAnalysis.versionInfo.length} version information exposed`);
    }
    
    // Check for sensitive data exposure
    if (pageAnalysis.sensitiveData.length > 0) {
      analysis.sensitiveDataExposure.push(`${pageAnalysis.sensitiveData.length} sensitive data patterns found`);
    }
    
    // Check for broken authentication
    pageAnalysis.inputs.forEach((input: any) => {
      if (input.type === 'password' && input.autocomplete === 'on') {
        analysis.brokenAuth.push(`Password field with autocomplete enabled: ${input.name}`);
      }
    });
    
    // Check for security misconfigurations
    if (pageAnalysis.scripts.length > 0) {
      analysis.securityMisconfig.push(`${pageAnalysis.scripts.length} external scripts loaded (potential security risk)`);
    }
    
    // Check for outdated components
    pageAnalysis.scripts.forEach((script: string) => {
      if (script.includes('jquery') && !script.includes('jquery-3.') && !script.includes('jquery-2.')) {
        analysis.outdatedComponents.push('Potentially outdated jQuery version');
      }
    });
    
    // Check for insufficient logging
    if (pageAnalysis.errorMessages.length === 0) {
      analysis.insufficientLogging.push('No error messages found (might indicate insufficient error logging)');
    }
    
    return analysis;
  }
} 