import DOMPurify from "dompurify";
import { SecurityError } from "./errors";
/**
 * 敏感数据标识（扩展列表）
 */ export const SENSITIVE_FIELDS = [
    'password',
    'pwd',
    'passwd',
    'token',
    'secret',
    'key',
    'apiKey',
    'apikey',
    'api_key',
    'accessToken',
    'access_token',
    'refreshToken',
    'refresh_token',
    'authorization',
    'auth',
    'cookie',
    'session',
    'sessionId',
    'session_id',
    'creditCard',
    'credit_card',
    'cardNumber',
    'card_number',
    'cvv',
    'cvc',
    'ssn',
    'socialSecurityNumber',
    'social_security_number',
    'phone',
    'phoneNumber',
    'phone_number',
    'mobile',
    'email',
    'emailAddress',
    'email_address',
    'privateKey',
    'private_key',
    'secretKey',
    'secret_key',
    'apiSecret',
    'api_secret'
];
/**
 * 敏感信息检测模式（正则表达式）
 */ const SENSITIVE_PATTERNS = [
    /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/,
    /\b\d{3}-\d{2}-\d{4}\b/,
    /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
    /\b\d{10,}\b/
];
/**
 * 安全工具类
 */ export class SecurityUtils {
    /**
   * 清理 HTML，防止 XSS 攻击
   */ static sanitizeHTML(html, config) {
        if (typeof window === 'undefined') {
            // Node.js 环境，返回原始字符串（需要服务端处理）
            return html;
        }
        try {
            if (config?.allowHTML) {
                // 默认禁止所有标签，除非明确允许
                const allowedTags = config.allowedTags && config.allowedTags.length > 0 ? config.allowedTags : [];
                const allowedAttributes = config.allowedAttributes && config.allowedAttributes.length > 0 ? config.allowedAttributes : [];
                return DOMPurify.sanitize(html, {
                    ALLOWED_TAGS: allowedTags,
                    ALLOWED_ATTR: allowedAttributes,
                    FORBID_TAGS: [
                        'script',
                        'iframe',
                        'object',
                        'embed',
                        'form',
                        'link',
                        'meta',
                        'style'
                    ],
                    FORBID_ATTR: [
                        'onerror',
                        'onload',
                        'onclick',
                        'onmouseover',
                        'onfocus',
                        'onblur',
                        'onchange'
                    ],
                    // 添加更多安全配置
                    KEEP_CONTENT: false,
                    RETURN_DOM: false,
                    RETURN_DOM_FRAGMENT: false,
                    RETURN_TRUSTED_TYPE: false
                });
            }
            // 默认模式：完全清理，不允许任何 HTML
            return DOMPurify.sanitize(html, {
                ALLOWED_TAGS: [],
                KEEP_CONTENT: false
            });
        } catch (error) {
            // 错误处理：不泄露敏感信息，记录错误但不抛出详细错误
            const errorMessage = error instanceof Error ? error.message : 'Unknown error';
            // 只记录错误类型，不记录具体内容
            console.error('HTML 清理失败:', errorMessage.substring(0, 50));
            // 返回安全的空字符串或清理后的文本
            return this.sanitizeText(html);
        }
    }
    /**
   * 清理文本内容（移除所有 HTML 标签）
   */ static sanitizeText(text) {
        if (typeof window === 'undefined') {
            return text.replace(/<[^>]*>/g, '');
        }
        try {
            return DOMPurify.sanitize(text, {
                ALLOWED_TAGS: []
            });
        } catch (error) {
            throw new SecurityError('文本清理失败', error instanceof Error ? error : undefined);
        }
    }
    /**
   * 检查字符串是否包含潜在的危险内容
   */ static containsDangerousContent(text) {
        const dangerousPatterns = [
            /<script[\s\S]*?>[\s\S]*?<\/script>/gi,
            /javascript:/gi,
            /on\w+\s*=/gi,
            /<iframe[\s\S]*?>/gi,
            /<object[\s\S]*?>/gi,
            /<embed[\s\S]*?>/gi,
            /<link[\s\S]*?>/gi,
            /<meta[\s\S]*?>/gi,
            /data:text\/html/gi,
            /vbscript:/gi,
            /expression\s*\(/gi,
            /@import/gi,
            /<style[\s\S]*?>[\s\S]*?<\/style>/gi
        ];
        return dangerousPatterns.some((pattern)=>pattern.test(text));
    }
    /**
   * 验证 URL 是否安全
   */ static isSafeUrl(url) {
        try {
            const urlObj = new URL(url);
            // 检查协议
            if (![
                'http:',
                'https:'
            ].includes(urlObj.protocol)) {
                return false;
            }
            // 检查是否包含危险内容
            return !this.containsDangerousContent(url);
        } catch  {
            return false;
        }
    }
    /**
   * 检查字段名是否为敏感字段
   */ static isSensitiveField(fieldName) {
        const lowerFieldName = fieldName.toLowerCase();
        return SENSITIVE_FIELDS.some((field)=>lowerFieldName.includes(field.toLowerCase()));
    }
    /**
   * 检查值是否包含敏感信息（基于模式匹配）
   */ static containsSensitiveData(value) {
        return SENSITIVE_PATTERNS.some((pattern)=>pattern.test(value));
    }
    /**
   * 生成 CSP（Content Security Policy）头
   */ static generateCSP(config) {
        const directives = [];
        const allowUnsafeInline = config?.allowUnsafeInline ?? false;
        const allowUnsafeEval = config?.allowUnsafeEval ?? false;
        if (config?.defaultSrc) {
            directives.push(`default-src ${config.defaultSrc.join(' ')}`);
        } else {
            directives.push("default-src 'self'");
        }
        if (config?.scriptSrc) {
            directives.push(`script-src ${config.scriptSrc.join(' ')}`);
        } else {
            // 默认不允许 unsafe-inline 和 unsafe-eval，提高安全性
            const scriptSrc = [
                "'self'"
            ];
            if (allowUnsafeInline) {
                scriptSrc.push("'unsafe-inline'");
            }
            if (allowUnsafeEval) {
                scriptSrc.push("'unsafe-eval'");
            }
            directives.push(`script-src ${scriptSrc.join(' ')}`);
        }
        if (config?.styleSrc) {
            directives.push(`style-src ${config.styleSrc.join(' ')}`);
        } else {
            // 对于样式，可以使用 nonce 或 hash 替代 unsafe-inline
            const styleSrc = [
                "'self'"
            ];
            if (allowUnsafeInline) {
                styleSrc.push("'unsafe-inline'");
            }
            directives.push(`style-src ${styleSrc.join(' ')}`);
        }
        if (config?.imgSrc) {
            directives.push(`img-src ${config.imgSrc.join(' ')}`);
        } else {
            directives.push("img-src 'self' data: https:");
        }
        if (config?.connectSrc) {
            directives.push(`connect-src ${config.connectSrc.join(' ')}`);
        } else {
            directives.push("connect-src 'self'");
        }
        if (config?.fontSrc) {
            directives.push(`font-src ${config.fontSrc.join(' ')}`);
        } else {
            directives.push("font-src 'self' data:");
        }
        if (config?.frameSrc) {
            directives.push(`frame-src ${config.frameSrc.join(' ')}`);
        } else {
            directives.push("frame-src 'none'");
        }
        return directives.join('; ');
    }
    /**
   * 转义 HTML 特殊字符
   */ static escapeHTML(text) {
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, (char)=>map[char]);
    }
    /**
   * 验证输入是否安全
   */ static validateInput(input) {
        if (typeof input !== 'string') {
            return {
                safe: true
            };
        }
        if (this.containsDangerousContent(input)) {
            return {
                safe: false,
                sanitized: this.sanitizeText(input),
                reason: '包含潜在的危险内容'
            };
        }
        // 检查是否包含敏感数据模式
        if (this.containsSensitiveData(input)) {
            return {
                safe: false,
                sanitized: this.sanitizeText(input),
                reason: '可能包含敏感信息'
            };
        }
        return {
            safe: true,
            sanitized: input
        };
    }
    /**
   * 深度脱敏处理（递归处理嵌套对象）
   */ static deepSanitize(data, maxDepth = 10, currentDepth = 0) {
        if (currentDepth >= maxDepth) {
            return '***'; // 防止无限递归
        }
        if (data === null || data === undefined) {
            return data;
        }
        if (typeof data === 'string') {
            if (this.containsDangerousContent(data) || this.containsSensitiveData(data)) {
                return this.sanitizeText(data);
            }
            return data;
        }
        if (typeof data === 'number' || typeof data === 'boolean') {
            return data;
        }
        if (Array.isArray(data)) {
            return data.map((item)=>this.deepSanitize(item, maxDepth, currentDepth + 1));
        }
        if (typeof data === 'object') {
            const sanitized = {};
            for (const [key, value] of Object.entries(data)){
                if (this.isSensitiveField(key)) {
                    sanitized[key] = '***';
                } else {
                    sanitized[key] = this.deepSanitize(value, maxDepth, currentDepth + 1);
                }
            }
            return sanitized;
        }
        return data;
    }
}

//# sourceMappingURL=security.js.map