// Exfiltration Channel Detector - Detects data exfiltration methods
// Browser Techniques Line 223: Query-string steganography
// Small payloads encoded in URLs for GET/image beacons

import { BaseDetector } from '../base-detector.js';

export class ExfilChannelDetector extends BaseDetector {
  constructor(config = {}) {
    super({
      name: 'exfil-channel',
      category: 'data-theft',
      version: '1.0.0',
      description: 'Detects data exfiltration channels (query strings, beacons, etc.)',
      weight: 3,
      severity: 'high',
      ...config
    });
  }

  async detect(target, ruleRegistry) {
    const findings = [];

    const url = target.url;
    const method = target.method || 'GET';
    const queryParams = target.queryParams || this.extractQueryParams(url);
    const requestType = target.requestType;

    if (!url) {
      return this.createResult(false, [], 0);
    }

    const rules = ruleRegistry.getRules('data-theft', 'exfil-channel');

    if (!rules) {
      return this.createResult(false, [], 0);
    }

    // 1. Detect unusually long query strings
    const queryString = url.split('?')[1] || '';
    const queryLength = queryString.length;

    if (queryLength > (rules.longQueryThreshold || 1000)) {
      findings.push(this.createFinding({
        pattern: 'long-query-string',
        matched: `${queryLength} chars`,
        description: `Unusually long query string (${queryLength} chars) - potential data exfil`,
        severity: queryLength > 5000 ? 'high' : 'medium',
        weight: queryLength > 5000 ? 4 : 3,
        metadata: {
          queryLength,
          url: url.substring(0, 100) + '...',
          technique: 'query-string-exfiltration'
        }
      }));
    }

    // 2. Detect base64/hex encoded data in URLs
    for (const [key, value] of Object.entries(queryParams)) {
      const encodingDetection = this.detectEncoding(value, rules);

      if (encodingDetection.detected && value.length > 100) {
        findings.push(this.createFinding({
          pattern: 'encoded-data-in-url',
          matched: `${key}=${encodingDetection.type}`,
          description: `${encodingDetection.type} encoded data in URL parameter (potential exfil)`,
          severity: 'high',
          weight: 4,
          metadata: {
            paramName: key,
            encodingType: encodingDetection.type,
            dataLength: value.length,
            technique: 'encoded-exfiltration'
          }
        }));
      }
    }

    // 3. Detect image beacon exfiltration
    if (requestType === 'image' && queryLength > (rules.imageBeaconThreshold || 200)) {
      findings.push(this.createFinding({
        pattern: 'image-beacon-exfil',
        matched: 'Image beacon with data',
        description: 'Image beacon request with suspicious query string data',
        severity: 'medium',
        weight: 3,
        metadata: {
          queryLength,
          technique: 'image-beacon-exfiltration'
        }
      }));
    }

    // 4. Detect suspicious fetch/XHR destinations
    if (
      (requestType === 'fetch' || requestType === 'xhr') &&
      target.destination
    ) {
      const isSuspicious = this.isSuspiciousDestination(
        target.destination,
        window.location.hostname,
        rules
      );

      if (isSuspicious) {
        findings.push(this.createFinding({
          pattern: 'suspicious-exfil-destination',
          matched: target.destination,
          description: 'Data sent to suspicious external domain',
          severity: 'high',
          weight: 4,
          metadata: {
            destination: target.destination,
            technique: 'cross-origin-exfiltration'
          }
        }));
      }
    }

    // 5. Detect WebSocket data channels
    if (requestType === 'websocket' && target.hasDataChannel) {
      findings.push(this.createFinding({
        pattern: 'websocket-data-channel',
        matched: target.destination,
        description: 'WebSocket connection with data channel (potential exfil)',
        severity: 'medium',
        weight: 2,
        metadata: {
          destination: target.destination,
          technique: 'websocket-exfiltration'
        }
      }));
    }

    // 6. Detect DNS-over-HTTPS covert channels
    if (target.isDohRequest && queryLength > 500) {
      findings.push(this.createFinding({
        pattern: 'doh-covert-channel',
        matched: 'DNS-over-HTTPS with large query',
        description: 'DNS-over-HTTPS request with unusually large query (covert channel)',
        severity: 'high',
        weight: 4,
        metadata: {
          queryLength,
          technique: 'dns-tunneling'
        }
      }));
    }

    // 7. Detect data in POST body to external domains
    if (method === 'POST' && target.bodySize && target.bodySize > 10000) {
      const isExternal = target.destination &&
        !target.destination.includes(window.location.hostname);

      if (isExternal) {
        findings.push(this.createFinding({
          pattern: 'large-post-external',
          matched: `${target.bodySize} bytes to ${target.destination}`,
          description: 'Large POST request to external domain (potential exfil)',
          severity: 'high',
          weight: 4,
          metadata: {
            destination: target.destination,
            bodySize: target.bodySize,
            technique: 'post-exfiltration'
          }
        }));
      }
    }

    // 8. Detect URL parameter with sensitive patterns
    for (const [key, value] of Object.entries(queryParams)) {
      if (this.containsSensitivePattern(value, rules)) {
        findings.push(this.createFinding({
          pattern: 'sensitive-data-in-url',
          matched: `${key} contains sensitive pattern`,
          description: 'Sensitive data detected in URL parameter',
          severity: 'critical',
          weight: 5,
          metadata: {
            paramName: key,
            technique: 'credential-exfiltration'
          }
        }));
      }
    }

    // 9. Detect steganography in URLs (repetitive patterns)
    if (this.detectSteganography(queryString, rules)) {
      findings.push(this.createFinding({
        pattern: 'url-steganography',
        matched: 'Steganographic pattern detected',
        description: 'Query string contains potential steganographic encoding',
        severity: 'medium',
        weight: 2,
        metadata: {
          technique: 'steganographic-exfiltration'
        }
      }));
    }

    const matched = findings.length > 0;
    const riskScore = findings.reduce((sum, f) => sum + (f.weight || 1), 0);

    return this.createResult(matched, findings, riskScore);
  }

  detectEncoding(value, rules) {
    // Detect base64
    const base64Pattern = /^[A-Za-z0-9+/]+=*$/;
    if (base64Pattern.test(value) && value.length > 20) {
      return { detected: true, type: 'base64' };
    }

    // Detect hex
    const hexPattern = /^[0-9a-fA-F]+$/;
    if (hexPattern.test(value) && value.length > 20) {
      return { detected: true, type: 'hex' };
    }

    // Detect URL encoding with high percentage
    const urlEncodedCount = (value.match(/%[0-9a-fA-F]{2}/g) || []).length;
    if (urlEncodedCount > value.length * 0.3) {
      return { detected: true, type: 'url-encoded' };
    }

    return { detected: false };
  }

  isSuspiciousDestination(destination, currentHost, rules) {
    // Check against known tracking/exfil domains
    const suspiciousTLDs = rules.suspiciousTLDs || [
      '.tk',
      '.ml',
      '.ga',
      '.cf',
      '.gq',
      '.pw',
      '.cc'
    ];

    for (const tld of suspiciousTLDs) {
      if (destination.endsWith(tld)) {
        return true;
      }
    }

    // Check if destination is on blocklist
    if (rules.blockedDomains && rules.blockedDomains.includes(destination)) {
      return true;
    }

    return false;
  }

  containsSensitivePattern(value, rules) {
    const sensitivePatterns = rules.sensitivePatterns || [
      /password/i,
      /passwd/i,
      /pwd/i,
      /token/i,
      /api[_-]?key/i,
      /secret/i,
      /bearer/i,
      /authorization/i,
      /session/i,
      /cookie/i
    ];

    return sensitivePatterns.some(pattern => pattern.test(value));
  }

  detectSteganography(queryString, rules) {
    // Simple steganography detection: look for repetitive character patterns
    if (queryString.length < 100) return false;

    const charFrequency = {};
    for (const char of queryString) {
      charFrequency[char] = (charFrequency[char] || 0) + 1;
    }

    // If any character appears > 30% of the time, might be steganography
    const maxFreq = Math.max(...Object.values(charFrequency));
    return maxFreq > queryString.length * 0.3;
  }
}
