// Session Hijacking Detector - Detects session token theft attempts
// Browser Techniques Line 209-220: Token theft from storage, cookies, AiTM
// Steal cookies/tokens and replay to impersonate sessions

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

export class SessionHijackingDetector extends BaseDetector {
  constructor(config = {}) {
    super({
      name: 'session-hijacking',
      category: 'data-theft',
      version: '1.0.0',
      description: 'Detects session hijacking and token theft attempts',
      weight: 4,
      severity: 'critical',
      ...config
    });
  }

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

    // This detector requires storage/cookie monitoring data
    const storageAccess = target.storageAccess || [];
    const cookieAccess = target.cookieAccess || [];
    const cookies = target.cookies || [];
    const exfilAttempts = target.exfilAttempts || [];

    if (storageAccess.length === 0 && cookieAccess.length === 0 && cookies.length === 0) {
      return this.createResult(false, [], 0);
    }

    const rules = ruleRegistry.getRules('data-theft', 'session-hijacking');

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

    // 1. Detect suspicious localStorage/sessionStorage access
    for (const access of storageAccess) {
      if (this.isSessionTokenAccess(access, rules)) {
        const isCrossOrigin = this.isCrossOriginScript(access.scriptOrigin, target.currentOrigin);

        if (isCrossOrigin) {
          findings.push(this.createFinding({
            pattern: 'cross-origin-storage-access',
            matched: access.key,
            description: 'Cross-origin script accessing session storage',
            severity: 'critical',
            weight: 5,
            metadata: {
              key: access.key,
              scriptOrigin: access.scriptOrigin,
              operation: access.operation,
              technique: 'session-hijacking-storage'
            }
          }));
        }
      }
    }

    // 2. Detect cookie theft attempts
    for (const access of cookieAccess) {
      if (access.operation === 'read' && this.isSessionCookie(access.cookieName, rules)) {
        const isSuspicious = this.isSuspiciousCookieAccess(access, rules);

        if (isSuspicious) {
          findings.push(this.createFinding({
            pattern: 'suspicious-cookie-access',
            matched: access.cookieName,
            description: 'Suspicious access to session cookies',
            severity: 'critical',
            weight: 5,
            metadata: {
              cookieName: access.cookieName,
              scriptSrc: access.scriptSrc,
              technique: 'session-hijacking-cookie'
            }
          }));
        }
      }
    }

    // 3. Detect session tokens being sent to external domains
    for (const exfil of exfilAttempts) {
      if (this.containsSessionToken(exfil.data, rules)) {
        findings.push(this.createFinding({
          pattern: 'session-token-exfiltration',
          matched: exfil.destination,
          description: 'Session token being exfiltrated to external domain',
          severity: 'critical',
          weight: 5,
          metadata: {
            destination: exfil.destination,
            method: exfil.method,
            technique: 'session-hijacking-exfil'
          }
        }));
      }
    }

    // 4. Detect non-HttpOnly session cookies (vulnerability)
    for (const cookie of cookies) {
      if (this.isSessionCookie(cookie.name, rules) && !cookie.httpOnly) {
        findings.push(this.createFinding({
          pattern: 'non-httponly-session-cookie',
          matched: cookie.name,
          description: 'Session cookie without HttpOnly flag (vulnerable to theft)',
          severity: 'high',
          weight: 4,
          metadata: {
            cookieName: cookie.name,
            technique: 'session-hijacking-vulnerable-cookie'
          }
        }));
      }
    }

    // 5. Detect cookies with overly broad domain scope
    for (const cookie of cookies) {
      if (this.isSessionCookie(cookie.name, rules) && this.hasBroadDomain(cookie.domain)) {
        findings.push(this.createFinding({
          pattern: 'broad-domain-session-cookie',
          matched: `${cookie.name} on ${cookie.domain}`,
          description: 'Session cookie with overly broad domain scope',
          severity: 'medium',
          weight: 3,
          metadata: {
            cookieName: cookie.name,
            domain: cookie.domain,
            technique: 'session-hijacking-broad-domain'
          }
        }));
      }
    }

    // 6. Detect insecure session cookies (not Secure flag on HTTPS)
    if (target.isHttps) {
      for (const cookie of cookies) {
        if (this.isSessionCookie(cookie.name, rules) && !cookie.secure) {
          findings.push(this.createFinding({
            pattern: 'insecure-session-cookie',
            matched: cookie.name,
            description: 'Session cookie without Secure flag on HTTPS site',
            severity: 'high',
            weight: 4,
            metadata: {
              cookieName: cookie.name,
              technique: 'session-hijacking-insecure-cookie'
            }
          }));
        }
      }
    }

    // 7. Detect suspicious postMessage with session data
    if (target.postMessageWithSessionData) {
      findings.push(this.createFinding({
        pattern: 'postmessage-session-leak',
        matched: 'postMessage with session data',
        description: 'Session data sent via postMessage (potential leak)',
        severity: 'high',
        weight: 4,
        metadata: {
          technique: 'session-hijacking-postmessage'
        }
      }));
    }

    // 8. Detect rapid cookie modifications
    if (target.rapidCookieChanges && target.rapidCookieChanges > (rules.rapidCookieChangeThreshold || 5)) {
      findings.push(this.createFinding({
        pattern: 'rapid-cookie-manipulation',
        matched: `${target.rapidCookieChanges} rapid cookie changes`,
        description: 'Rapid cookie modifications detected (session fixation attempt)',
        severity: 'high',
        weight: 4,
        metadata: {
          changeCount: target.rapidCookieChanges,
          technique: 'session-hijacking-fixation'
        }
      }));
    }

    // 9. Detect beacon requests with authentication headers
    if (target.beaconRequests) {
      for (const beacon of target.beaconRequests) {
        if (beacon.hasAuthHeader || beacon.hasCookies) {
          findings.push(this.createFinding({
            pattern: 'beacon-with-auth',
            matched: beacon.url,
            description: 'Beacon request includes authentication credentials',
            severity: 'high',
            weight: 3,
            metadata: {
              beaconUrl: beacon.url,
              technique: 'session-hijacking-beacon'
            }
          }));
        }
      }
    }

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

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

  isSessionTokenAccess(access, rules) {
    const sessionKeyPatterns = rules.sessionKeyPatterns || [
      'token', 'session', 'auth', 'jwt', 'access_token',
      'refresh_token', 'id_token', 'bearer', 'csrf', 'xsrf'
    ];

    const keyLower = (access.key || '').toLowerCase();
    return sessionKeyPatterns.some(pattern => keyLower.includes(pattern));
  }

  isSessionCookie(cookieName, rules) {
    const sessionCookiePatterns = rules.sessionCookiePatterns || [
      'session', 'sess', 'token', 'auth', 'jwt',
      'jsessionid', 'phpsessid', 'asp.net_sessionid',
      'connect.sid', '__session', '_session_id'
    ];

    const cookieNameLower = (cookieName || '').toLowerCase();
    return sessionCookiePatterns.some(pattern => cookieNameLower.includes(pattern));
  }

  isSuspiciousCookieAccess(access, rules) {
    // Check if script is from different origin
    if (access.scriptSrc) {
      try {
        const scriptUrl = new URL(access.scriptSrc);
        // Third-party scripts accessing cookies is suspicious
        return true;
      } catch {
        // Inline scripts accessing cookies can be suspicious
        return true;
      }
    }

    return false;
  }

  containsSessionToken(data, rules) {
    if (!data) return false;

    const tokenPatterns = rules.tokenPatterns || [
      /[a-zA-Z0-9_-]{20,}/,  // Generic long token
      /eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/,  // JWT format
      /sess?i?o?n?[_-]?[a-z]*[=:][a-zA-Z0-9]+/i  // Session= pattern
    ];

    return tokenPatterns.some(pattern => pattern.test(data));
  }

  isCrossOriginScript(scriptOrigin, currentOrigin) {
    if (!scriptOrigin || !currentOrigin) return false;

    try {
      return scriptOrigin !== currentOrigin;
    } catch {
      return false;
    }
  }

  hasBroadDomain(domain) {
    if (!domain) return false;

    // Check if domain is too broad (e.g., .com, .co.uk)
    const parts = domain.split('.');
    return parts.length <= 2;  // e.g., .example.com is fine, .com is too broad
  }
}
