import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { logger } from '../logger.js';
import { Email } from '../mail/core/classes.email.js';
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from './classes.securitylogger.js';
import { RustSecurityBridge } from './classes.rustsecuritybridge.js';
import { LRUCache } from 'lru-cache';
/**
 * Threat categories
 */
export var ThreatCategory;
(function (ThreatCategory) {
    ThreatCategory["SPAM"] = "spam";
    ThreatCategory["PHISHING"] = "phishing";
    ThreatCategory["MALWARE"] = "malware";
    ThreatCategory["EXECUTABLE"] = "executable";
    ThreatCategory["SUSPICIOUS_LINK"] = "suspicious_link";
    ThreatCategory["MALICIOUS_MACRO"] = "malicious_macro";
    ThreatCategory["XSS"] = "xss";
    ThreatCategory["SENSITIVE_DATA"] = "sensitive_data";
    ThreatCategory["BLACKLISTED_CONTENT"] = "blacklisted_content";
    ThreatCategory["CUSTOM_RULE"] = "custom_rule";
})(ThreatCategory || (ThreatCategory = {}));
/**
 * Content Scanner for detecting malicious email content
 */
export class ContentScanner {
    static instance;
    scanCache;
    options;
    /**
     * Default options for the content scanner
     */
    static DEFAULT_OPTIONS = {
        maxCacheSize: 10000,
        cacheTTL: 24 * 60 * 60 * 1000, // 24 hours
        scanSubject: true,
        scanBody: true,
        scanAttachments: true,
        maxAttachmentSizeToScan: 10 * 1024 * 1024, // 10MB
        scanAttachmentNames: true,
        blockExecutables: true,
        blockMacros: true,
        customRules: [],
        minThreatScore: 30, // Minimum score to consider content as a threat
        highThreatScore: 70 // Score above which content is considered high threat
    };
    /**
     * Constructor for the ContentScanner
     * @param options Configuration options
     */
    constructor(options = {}) {
        // Merge with default options
        this.options = {
            ...ContentScanner.DEFAULT_OPTIONS,
            ...options
        };
        // Initialize cache
        this.scanCache = new LRUCache({
            max: this.options.maxCacheSize,
            ttl: this.options.cacheTTL,
        });
        logger.log('info', 'ContentScanner initialized');
    }
    /**
     * Get the singleton instance of the scanner
     * @param options Configuration options
     * @returns Singleton scanner instance
     */
    static getInstance(options = {}) {
        if (!ContentScanner.instance) {
            ContentScanner.instance = new ContentScanner(options);
        }
        return ContentScanner.instance;
    }
    /**
     * Scan an email for malicious content.
     * Delegates text/subject/html/filename pattern scanning to Rust.
     * Binary attachment scanning (PE headers, VBA macros) stays in TS.
     * @param email The email to scan
     * @returns Scan result
     */
    async scanEmail(email) {
        try {
            // Generate a cache key from the email
            const cacheKey = this.generateCacheKey(email);
            // Check cache first
            const cachedResult = this.scanCache.get(cacheKey);
            if (cachedResult) {
                logger.log('info', `Using cached scan result for email ${email.getMessageId()}`);
                return cachedResult;
            }
            // Delegate text/subject/html/filename scanning to Rust
            const bridge = RustSecurityBridge.getInstance();
            const rustResult = await bridge.scanContent({
                subject: this.options.scanSubject ? email.subject : undefined,
                textBody: this.options.scanBody ? email.text : undefined,
                htmlBody: this.options.scanBody ? email.html : undefined,
                attachmentNames: this.options.scanAttachmentNames
                    ? email.attachments?.map(a => a.filename) ?? []
                    : [],
            });
            const result = {
                isClean: true,
                threatScore: rustResult.threatScore,
                threatType: rustResult.threatType ?? undefined,
                threatDetails: rustResult.threatDetails ?? undefined,
                scannedElements: rustResult.scannedElements,
                timestamp: Date.now(),
            };
            // Attachment binary scanning stays in TS (PE headers, macro detection)
            if (this.options.scanAttachments && email.attachments?.length > 0) {
                for (const attachment of email.attachments) {
                    this.scanAttachmentBinary(attachment, result);
                }
            }
            // Apply custom rules (TS-only, runtime-configured)
            this.applyCustomRules(email, result);
            // Determine if the email is clean based on threat score
            result.isClean = result.threatScore < this.options.minThreatScore;
            // Save to cache
            this.scanCache.set(cacheKey, result);
            // Log high threat findings
            if (result.threatScore >= this.options.highThreatScore) {
                this.logHighThreatFound(email, result);
            }
            else if (!result.isClean) {
                this.logThreatFound(email, result);
            }
            return result;
        }
        catch (error) {
            logger.log('error', `Error scanning email: ${error.message}`, {
                messageId: email.getMessageId(),
                error: error.stack
            });
            // Return a safe default with error indication
            return {
                isClean: true,
                threatScore: 0,
                scannedElements: ['error'],
                timestamp: Date.now(),
                threatType: 'scan_error',
                threatDetails: `Scan error: ${error.message}`
            };
        }
    }
    /**
     * Generate a cache key from an email
     * @param email The email to generate a key for
     * @returns Cache key
     */
    generateCacheKey(email) {
        // Use message ID if available
        if (email.getMessageId()) {
            return `email:${email.getMessageId()}`;
        }
        // Fallback to a hash of key content
        const contentToHash = [
            email.from,
            email.subject || '',
            email.text?.substring(0, 1000) || '',
            email.html?.substring(0, 1000) || '',
            email.attachments?.length || 0
        ].join(':');
        return `email:${plugins.crypto.createHash('sha256').update(contentToHash).digest('hex')}`;
    }
    /**
     * Scan attachment binary content for PE headers and VBA macros.
     * This stays in TS because it accesses raw Buffer data (too large for IPC).
     * @param attachment The attachment to scan
     * @param result The scan result to update
     */
    scanAttachmentBinary(attachment, result) {
        if (!attachment.content) {
            return;
        }
        // Skip large attachments
        if (attachment.content.length > this.options.maxAttachmentSizeToScan) {
            return;
        }
        const filename = attachment.filename.toLowerCase();
        // Check for PE headers (Windows executables disguised with non-.exe extensions)
        if (attachment.content.length > 64 &&
            attachment.content[0] === 0x4D &&
            attachment.content[1] === 0x5A) { // 'MZ' header
            result.threatScore += 80;
            result.threatType = ThreatCategory.EXECUTABLE;
            result.threatDetails = `Attachment contains executable code: ${filename}`;
            return;
        }
        // Check for VBA macro indicators in Office documents
        if (this.options.blockMacros && this.likelyContainsMacros(attachment)) {
            result.threatScore += 60;
            result.threatType = ThreatCategory.MALICIOUS_MACRO;
            result.threatDetails = `Attachment appears to contain macros: ${filename}`;
        }
    }
    /**
     * Apply custom rules (runtime-configured patterns) to the email.
     * These stay in TS because they are configured at runtime.
     * @param email The email to check
     * @param result The scan result to update
     */
    applyCustomRules(email, result) {
        if (!this.options.customRules.length) {
            return;
        }
        const textsToCheck = [];
        if (email.subject)
            textsToCheck.push(email.subject);
        if (email.text)
            textsToCheck.push(email.text);
        if (email.html)
            textsToCheck.push(email.html);
        for (const rule of this.options.customRules) {
            const pattern = rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern, 'i');
            for (const text of textsToCheck) {
                if (pattern.test(text)) {
                    result.threatScore += rule.score;
                    result.threatType = rule.type;
                    result.threatDetails = rule.description;
                    return;
                }
            }
        }
    }
    /**
     * Extract text from a binary buffer for scanning
     * @param buffer Binary content
     * @returns Extracted text (may be partial)
     */
    extractTextFromBuffer(buffer) {
        try {
            // Limit the amount we convert to avoid memory issues
            const sampleSize = Math.min(buffer.length, 100 * 1024); // 100KB max sample
            const sample = buffer.slice(0, sampleSize);
            // Try to convert to string, filtering out non-printable chars
            return sample.toString('utf8')
                .replace(/[\x00-\x09\x0B-\x1F\x7F-\x9F]/g, '') // Remove control chars
                .replace(/\uFFFD/g, ''); // Remove replacement char
        }
        catch (error) {
            logger.log('warn', `Error extracting text from buffer: ${error.message}`);
            return '';
        }
    }
    /**
     * Check if an Office document likely contains macros
     * @param attachment The attachment to check
     * @returns Whether the file likely contains macros
     */
    likelyContainsMacros(attachment) {
        const content = this.extractTextFromBuffer(attachment.content);
        const macroIndicators = [
            /vbaProject\.bin/i,
            /Microsoft VBA/i,
            /\bVBA\b/,
            /Auto_Open/i,
            /AutoExec/i,
            /DocumentOpen/i,
            /AutoOpen/i,
            /\bExecute\(/i,
            /\bShell\(/i,
            /\bCreateObject\(/i
        ];
        for (const indicator of macroIndicators) {
            if (indicator.test(content)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Log a high threat finding to the security logger
     * @param email The email containing the threat
     * @param result The scan result
     */
    logHighThreatFound(email, result) {
        SecurityLogger.getInstance().logEvent({
            level: SecurityLogLevel.ERROR,
            type: SecurityEventType.MALWARE,
            message: `High threat content detected in email from ${email.from} to ${email.to.join(', ')}`,
            details: {
                messageId: email.getMessageId(),
                threatType: result.threatType,
                threatDetails: result.threatDetails,
                threatScore: result.threatScore,
                scannedElements: result.scannedElements,
                subject: email.subject
            },
            success: false,
            domain: email.getFromDomain()
        });
    }
    /**
     * Log a threat finding to the security logger
     * @param email The email containing the threat
     * @param result The scan result
     */
    logThreatFound(email, result) {
        SecurityLogger.getInstance().logEvent({
            level: SecurityLogLevel.WARN,
            type: SecurityEventType.SPAM,
            message: `Suspicious content detected in email from ${email.from} to ${email.to.join(', ')}`,
            details: {
                messageId: email.getMessageId(),
                threatType: result.threatType,
                threatDetails: result.threatDetails,
                threatScore: result.threatScore,
                scannedElements: result.scannedElements,
                subject: email.subject
            },
            success: false,
            domain: email.getFromDomain()
        });
    }
    /**
     * Get threat level description based on score
     * @param score Threat score
     * @returns Threat level description
     */
    static getThreatLevel(score) {
        if (score < 20) {
            return 'none';
        }
        else if (score < 40) {
            return 'low';
        }
        else if (score < 70) {
            return 'medium';
        }
        else {
            return 'high';
        }
    }
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 11508 characters]