"use strict";
/**
 * Layer 1: High-Entropy String Detection
 * Uses Shannon entropy to detect potential secrets that don't match known patterns
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateEntropy = calculateEntropy;
exports.detectHighEntropyStrings = detectHighEntropyStrings;
const context_helpers_1 = require("../utils/context-helpers");
// Shannon entropy calculation
function calculateEntropy(str) {
    if (str.length === 0)
        return 0;
    const freq = {};
    for (const char of str) {
        freq[char] = (freq[char] || 0) + 1;
    }
    let entropy = 0;
    const len = str.length;
    for (const char in freq) {
        const p = freq[char] / len;
        entropy -= p * Math.log2(p);
    }
    return entropy;
}
// Extract string literals from code
function extractStringLiterals(content) {
    const strings = [];
    const lines = content.split('\n');
    // Patterns for string literals using unrolled loop pattern to prevent catastrophic backtracking
    // Pattern explanation: "start [non-special-chars]* (escape-sequence [non-special-chars]*)* end"
    // This avoids nested quantifiers that cause exponential backtracking
    const patterns = [
        /"[^"\\]{20,}(?:\\.[^"\\]*)*"/g, // Double-quoted strings 20+ chars (unrolled loop)
        /'[^'\\]{20,}(?:\\.[^'\\]*)*'/g, // Single-quoted strings 20+ chars (unrolled loop)
        /`[^`\\]{20,}(?:\\.[^`\\]*)*`/g, // Template literals 20+ chars (unrolled loop)
    ];
    lines.forEach((line, index) => {
        for (const pattern of patterns) {
            let match;
            const regex = new RegExp(pattern.source, pattern.flags);
            while ((match = regex.exec(line)) !== null) {
                // Remove quotes and get the actual string value
                const value = match[0].slice(1, -1);
                strings.push({
                    value,
                    line: index + 1,
                    lineContent: line.trim(),
                });
            }
        }
    });
    return strings;
}
// Check if string looks like a known safe pattern (URLs, paths, etc.)
function isSafePattern(str) {
    const safePatterns = [
        /^https?:\/\//i, // URLs
        /^\/[a-z0-9_/-]+$/i, // File paths
        /^\d{4}-\d{2}-\d{2}/, // Dates
        /^[a-f0-9]{32}$/i, // MD5 hashes (often used as IDs)
        /^[a-f0-9]{40}$/i, // SHA1 hashes
        /^[a-f0-9]{64}$/i, // SHA256 hashes
        /^data:[a-z]+\/[a-z]+;base64,/i, // Data URLs
        /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i, // Emails
        /^\s*$/, // Whitespace only
        /^[a-z\s]+$/i, // Only letters and spaces (likely text)
        /^\/?[\(\)\[\]\{\}\|\?\*\+\.\^\$\\:!_a-z0-9/-]+$/i, // Regex patterns (route matchers, etc.)
    ];
    return safePatterns.some(pattern => pattern.test(str));
}
// Check if string is a PEM header/footer (not an actual secret)
function isPEMHeader(str) {
    const pemPatterns = [
        /^-{3,}BEGIN\s+(PRIVATE|PUBLIC|RSA|DSA|EC|ENCRYPTED|CERTIFICATE)/i,
        /^-{3,}END\s+(PRIVATE|PUBLIC|RSA|DSA|EC|ENCRYPTED|CERTIFICATE)/i,
        /-----BEGIN\s+\w+\s+KEY-----/i,
        /-----END\s+\w+\s+KEY-----/i,
    ];
    return pemPatterns.some(p => p.test(str));
}
// Check if string looks like encrypted/encoded content (not the key itself)
function isEncryptedContent(str, lineContent) {
    // Patterns for encrypted content blocks (not the key)
    const encryptedPatterns = [
        /encrypted_content/i,
        /ciphertext/i,
        /encrypted_data/i,
        /encrypted_value/i,
        // Base64 encoded binary data (very long, uniform character set)
        /^[A-Za-z0-9+/]{100,}={0,2}$/, // Long base64 strings are often encrypted payloads
    ];
    // Check line context for encrypted content indicators
    const contextIndicators = [
        /["']encrypted_content["']\s*:/i,
        /["']ciphertext["']\s*:/i,
        /gAAAA/, // Fernet encryption prefix
    ];
    return (encryptedPatterns.some(p => p.test(str)) ||
        contextIndicators.some(p => p.test(lineContent)));
}
// Check if string looks like a JWT segment (base64url encoded, starts with eyJ)
function isJWTSegment(str) {
    // JWT segments typically start with 'eyJ' (base64 for '{"')
    // Full JWT format: header.payload.signature (all base64url)
    if (str.startsWith('eyJ') && /^[A-Za-z0-9_-]+$/.test(str)) {
        return true;
    }
    // Check for full JWT pattern (3 dot-separated base64url segments)
    if (/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(str)) {
        return true;
    }
    return false;
}
// Check if string looks like a regex/route matcher pattern
function isRegexPattern(str) {
    // Common regex metacharacters and patterns
    const regexIndicators = ['(?', '(?!', '(?:', '(?=', '\\.', '\\.', '.*', '.+', '[^', '|', '$)', '^', '$'];
    const indicatorCount = regexIndicators.filter(ind => str.includes(ind)).length;
    // If it has multiple regex indicators, it's likely a regex pattern
    return indicatorCount >= 2;
}
// Check if string is a template literal with code expressions
function isTemplateWithCode(str, lineContent) {
    // Check if the line contains template literal syntax with expressions
    if (!lineContent.includes('`') && !lineContent.includes('${')) {
        return false;
    }
    // Common code patterns inside template literals that create high entropy
    const codePatterns = [
        /\$\{[^}]*\.(toString|padStart|padEnd|toFixed|toLocaleString)\s*\(/i, // Method calls
        /\$\{[^}]*\?\.[^}]*\}/, // Optional chaining
        /\$\{[^}]*\s*\?\s*[^:]+\s*:\s*[^}]+\}/, // Ternary operators
        /var\s*\(\s*\$\{/, // CSS var() with template
        /\$\{[^}]*\.find\s*\(/i, // Array methods
        /\$\{[^}]*\.map\s*\(/i,
        /\$\{[^}]*\.filter\s*\(/i,
        /\$\{new\s+Date\(\)/i, // Date formatting
    ];
    return codePatterns.some(pattern => pattern.test(lineContent));
}
// Check if string is human-readable text/markdown content
function isHumanReadableContent(str) {
    // Skip short strings
    if (str.length < 30)
        return false;
    // Check for markdown indicators
    const markdownIndicators = ['## ', '# ', '**', '- [ ]', '- ', '\n\n', '\\n'];
    const hasMarkdown = markdownIndicators.some(ind => str.includes(ind));
    // Check word-like pattern ratio (spaces between word-like tokens)
    const words = str.split(/\s+/).filter(w => w.length > 0);
    const wordLikeTokens = words.filter(w => /^[a-zA-Z][a-zA-Z0-9'-]*[:.!?,]?$/.test(w));
    // If more than 50% of tokens look like words, it's probably text
    const wordRatio = words.length > 0 ? wordLikeTokens.length / words.length : 0;
    return hasMarkdown || wordRatio > 0.5;
}
// Check if string looks like a UI/display string (model names, descriptions, etc.)
function isUIString(str, lineContent) {
    // Common UI string patterns
    const uiPatterns = [
        /['"`].*Claude.*['"`]/i,
        /['"`].*GPT.*['"`]/i,
        /['"`].*Sonnet.*['"`]/i,
        /['"`].*for\s+(chat|embeddings|completion).*['"`]/i,
        /['"`]Uses\s+/i,
        /['"`]Note:\s*/i,
        /placeholder['"`:]/i,
        /description['"`:]/i,
        /label['"`:]/i,
        /title['"`:]/i,
        /message['"`:]/i,
        /tooltip['"`:]/i,
    ];
    return uiPatterns.some(pattern => pattern.test(lineContent));
}
// Check if string is in a React/JSX UI context (component props, JSX text)
function isJSXUIContext(lineContent) {
    // JSX patterns that indicate UI context
    const jsxUIPatterns = [
        // Component props (common UI props)
        /\b(placeholder|title|label|message|description|tooltip|alt|aria-label|name|id|className|testId|data-testid)\s*=\s*['"`]/i,
        // JSX text children (text between tags)
        />\s*['"`][^<]*['"`]\s*</,
        // Common UI component names
        /<(Button|Text|Label|Title|Heading|Paragraph|Span|Input|Tooltip|Badge|Alert|Toast)/i,
        // Return statement with JSX template literal
        /return\s+`[^`]*\$\{/,
        // Template literals used for display
        /['"`]Synced\s+/i,
        /['"`]\d+\s*(h|hr|hour|m|min|minute|s|sec|second)s?\s+ago['"`]/i,
        // Display formatting patterns
        /\.toLocaleString\s*\(|\.toFixed\s*\(|\.padStart\s*\(/,
    ];
    return jsxUIPatterns.some(pattern => pattern.test(lineContent));
}
// Check if string is natural language (high ratio of common English words)
function isNaturalLanguage(str) {
    // Skip short strings
    if (str.length < 25)
        return false;
    // Common English words that appear in natural language
    const commonWords = new Set([
        'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
        'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
        'should', 'may', 'might', 'must', 'shall', 'can', 'need', 'to', 'of',
        'in', 'for', 'on', 'with', 'at', 'by', 'from', 'up', 'about', 'into',
        'through', 'during', 'before', 'after', 'above', 'below', 'between',
        'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when',
        'where', 'why', 'how', 'all', 'each', 'few', 'more', 'most', 'other',
        'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than',
        'too', 'very', 'just', 'also', 'now', 'and', 'but', 'or', 'if', 'as',
        'your', 'you', 'this', 'that', 'it', 'they', 'we', 'he', 'she', 'my',
        'their', 'our', 'his', 'her', 'its', 'ago', 'synced', 'updated', 'created',
    ]);
    // Split into words and count common ones
    const words = str.toLowerCase().split(/\s+/).filter(w => w.length > 1);
    if (words.length < 3)
        return false;
    const commonWordCount = words.filter(w => commonWords.has(w.replace(/[^a-z]/g, ''))).length;
    const commonWordRatio = commonWordCount / words.length;
    // If more than 30% of words are common English words, it's likely natural language
    return commonWordRatio > 0.3;
}
// Check if string looks like CSS/Tailwind classes
function isCSSClasses(str) {
    // Tailwind/CSS class patterns
    const cssIndicators = [
        'flex', 'grid', 'block', 'inline', 'hidden',
        'items-', 'justify-', 'gap-', 'space-',
        'text-', 'font-', 'bg-', 'border-', 'rounded',
        'px-', 'py-', 'pt-', 'pb-', 'pl-', 'pr-', 'p-',
        'mx-', 'my-', 'mt-', 'mb-', 'ml-', 'mr-', 'm-',
        'w-', 'h-', 'min-', 'max-',
        'hover:', 'focus:', 'active:', 'disabled:',
        'sm:', 'md:', 'lg:', 'xl:', '2xl:',
        'dark:', 'light:',
        'transition', 'duration-', 'ease-',
        'absolute', 'relative', 'fixed', 'sticky',
        'top-', 'bottom-', 'left-', 'right-',
        'z-', 'overflow-', 'opacity-',
        'ring-', 'shadow-', 'outline-',
    ];
    // Count how many CSS-like tokens are in the string
    const tokens = str.toLowerCase().split(/\s+/);
    const cssTokenCount = tokens.filter(token => cssIndicators.some(indicator => token.includes(indicator))).length;
    // If more than 30% of tokens look like CSS classes, it's probably CSS
    return cssTokenCount > 0 && (cssTokenCount / tokens.length) > 0.3;
}
// Check if string looks like CSS-in-JS (styled-components, emotion, etc.)
function isCSSInJS(lineContent) {
    const cssInJSPatterns = [
        /styled\./, // styled.div, styled.button
        /styled\(/, // styled(Component)
        /css`/, // css`` template literal
        /keyframes`/, // keyframes`` template literal
        /@emotion/, // @emotion imports
        /createGlobalStyle/, // styled-components global
        /\$\{\s*props\s*=>/, // ${props => ...} in styled
        /\$\{\s*\(\s*\{/, // ${({ theme }) => ...}
    ];
    return cssInJSPatterns.some(p => p.test(lineContent));
}
// Check if file is documentation/README
function isDocumentationFile(filePath) {
    const docPatterns = [
        /README/i,
        /CHANGELOG/i,
        /CONTRIBUTING/i,
        /LICENSE/i,
        /CODE_OF_CONDUCT/i,
        /SECURITY/i,
        /AUTHORS/i,
        /HISTORY/i,
        /\.md$/i,
        /\.mdx$/i,
        /\.rst$/i, // reStructuredText
        /\.adoc$/i, // AsciiDoc
        /\.txt$/i, // Plain text docs
        /\/docs\//i,
        /\/documentation\//i,
        /\/wiki\//i,
        /\/guides?\//i,
        /\/tutorials?\//i,
        /\/examples?\//i, // Example directories often have sample configs
    ];
    return docPatterns.some(p => p.test(filePath));
}
// Check if string is a console.log/debug statement content
function isDebugLogContent(lineContent) {
    const debugPatterns = [
        /console\.(log|debug|info|warn|error)\s*\(/i,
        /logger\.(log|debug|info|warn|error)\s*\(/i,
        /\[.*Debug.*\]/i,
        /\[.*Log.*\]/i,
    ];
    return debugPatterns.some(pattern => pattern.test(lineContent));
}
// Check if string looks like a CLI command or usage snippet (not a secret)
function isCommandLineSnippet(value, lineContent) {
    // Common places where commands appear (help text, docs, quick fixes)
    const commandContextPatterns = [
        /\b(quickFix|command|example|usage|cli|help|hint)\b/i,
        /\b(run|exec|execute|install)\b\s*:/i,
    ];
    // Shell/env assignment patterns
    const envAssignmentPatterns = [
        /^\s*(export\s+)?[A-Z_][A-Z0-9_]*=/, // export VAR=... or VAR=...
        /\bNODE_OPTIONS=/i,
    ];
    // Command-like patterns (flags + known commands)
    const commandPatterns = [
        /^\s*\$\s+/, // shell prompt
        /\s--[a-z0-9][a-z0-9-]*/i, // CLI flags
        /\b(npm|pnpm|yarn|npx|node|bun|deno|git|curl|wget|oculum|python|pip|brew)\b/i,
    ];
    const hasCommandContext = commandContextPatterns.some(p => p.test(lineContent));
    const looksLikeEnvAssignment = envAssignmentPatterns.some(p => p.test(value));
    const looksLikeCommand = commandPatterns.some(p => p.test(value));
    // Commands are usually multi-token and contain spaces
    const hasMultipleTokens = value.trim().split(/\s+/).length >= 2;
    return (hasCommandContext || looksLikeEnvAssignment || looksLikeCommand) && hasMultipleTokens;
}
/**
 * Check if string is a SQL query (not a secret)
 * SQL queries have high entropy due to mixed case keywords and table names
 */
function isSQLQuery(value, lineContent) {
    const sqlPatterns = [
        /\bSELECT\s+/i,
        /\bINSERT\s+INTO\b/i,
        /\bUPDATE\s+.*\bSET\b/i,
        /\bDELETE\s+FROM\b/i,
        /\bCREATE\s+(TABLE|INDEX|DATABASE)\b/i,
        /\bALTER\s+TABLE\b/i,
        /\bDROP\s+(TABLE|INDEX|DATABASE)\b/i,
        /\bJOIN\s+.*\bON\b/i,
        /\bWHERE\s+/i,
        /\bGROUP\s+BY\b/i,
        /\bORDER\s+BY\b/i,
        /\bHAVING\s+/i,
        /\bUNION\s+/i,
    ];
    // Tagged template literal SQL context
    const sqlContextPatterns = [
        /\bsql`/i, // Drizzle, Kysely, etc.
        /\bprisma\.\$queryRaw/i,
        /\.query\s*\(/i,
        /\.execute\s*\(/i,
        /\.raw\s*\(/i,
    ];
    return (sqlPatterns.some(p => p.test(value)) ||
        sqlContextPatterns.some(p => p.test(lineContent)));
}
/**
 * Check if string is an i18n/translation string (not a secret)
 * Internationalization strings can have high entropy
 */
function isI18nString(value, lineContent) {
    const i18nPatterns = [
        /defaultMessage\s*:/i,
        /\bt\s*\(/i, // t('key')
        /\bi18n\./i,
        /\buseTranslation/i,
        /\bformatMessage\s*\(/i,
        /\bintl\./i,
        /\bmsg`/i, // Lingui msg tagged template
        /\btrans\s*\(/i,
        /\b_\s*\(/i, // Common i18n alias
        /description\s*:/i, // Often i18n context
        /\bLocale/i,
    ];
    // Check if file is in i18n/locales directory
    const isI18nFile = /\/(i18n|locales?|translations?|messages?)\//i.test(value);
    return i18nPatterns.some(p => p.test(lineContent)) || isI18nFile;
}
/**
 * Check if string is a CSS transform or animation value (not a secret)
 */
function isCSSTransformOrAnimation(value) {
    const transformPatterns = [
        /\btranslate(?:3d|X|Y|Z)?\s*\(/i,
        /\brotate(?:3d|X|Y|Z)?\s*\(/i,
        /\bscale(?:3d|X|Y|Z)?\s*\(/i,
        /\bskew(?:X|Y)?\s*\(/i,
        /\bmatrix(?:3d)?\s*\(/i,
        /\bperspective\s*\(/i,
        /\bcubic-bezier\s*\(/i,
        /\bsteps\s*\(/i,
        /\b(ease|linear|ease-in|ease-out|ease-in-out)\b/i,
        /\btransform:\s*/i,
        /\banimation:\s*/i,
        /\btransition:\s*/i,
        /\b@keyframes\b/i,
    ];
    return transformPatterns.some(p => p.test(value));
}
/**
 * Check if string is a URL template/path pattern (not a secret)
 * API routes and URL patterns can have high entropy
 */
function isURLTemplate(value) {
    // URL path with template variables
    const urlTemplatePatterns = [
        /^\/[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*(?:\/\$\{[^}]+\}|\/:[\w]+)+/, // /api/users/${id} or /api/users/:id
        /^https?:\/\/[^/]+\/.*\$\{/, // Full URL with template
        /\/\[[\w]+\]\//, // Next.js dynamic routes: /[slug]/
        /\/\[\.\.\.[^\]]+\]/, // Next.js catch-all: /[...slug]
        /\/:[\w]+\//, // Express-style params: /:id/
        /\{[\w]+\}/, // OpenAPI-style params: {userId}
    ];
    return urlTemplatePatterns.some(p => p.test(value));
}
/**
 * Check if string is a blockchain address (not a secret)
 * Public blockchain addresses have high entropy but are meant to be public
 */
function isBlockchainAddress(value) {
    const blockchainPatterns = [
        // Ethereum addresses (0x followed by 40 hex chars)
        /^0x[a-fA-F0-9]{40}$/,
        // Bitcoin addresses (various formats)
        /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/, // Legacy P2PKH
        /^3[a-km-zA-HJ-NP-Z1-9]{25,34}$/, // P2SH
        /^bc1[a-z0-9]{39,59}$/, // Bech32
        // Solana addresses (base58, 32-44 chars)
        /^[1-9A-HJ-NP-Za-km-z]{32,44}$/,
    ];
    return blockchainPatterns.some(p => p.test(value));
}
/**
 * Check if string is shader/WebGL code (not a secret)
 * Shader code has high entropy due to mathematical notation
 */
function isShaderCode(value, lineContent) {
    const shaderPatterns = [
        // GLSL keywords and functions
        /\bgl_\w+\b/, // gl_Position, gl_FragColor, etc.
        /\bvec[234]\s*\(/, // vec2(), vec3(), vec4()
        /\bmat[234]\s*\(/, // mat2(), mat3(), mat4()
        /\buniform\s+\w+/, // uniform declarations
        /\bvarying\s+\w+/, // varying declarations
        /\battribute\s+\w+/, // attribute declarations
        /\bprecision\s+(highp|mediump|lowp)/,
        /\bfloat\s+\w+\s*=/, // float variable declarations
        /\bsampler2D\b/,
        /\btexture2D\s*\(/,
        /\bnormalize\s*\(/,
        /\bdot\s*\(/,
        /\bcross\s*\(/,
        /\bmix\s*\(/,
        /\bclamp\s*\(/,
        /\bstep\s*\(/,
        /\bsmoothstep\s*\(/,
        // WebGL context indicators
        /\bWebGLRenderingContext\b/,
        /\.createShader\s*\(/,
        /\.shaderSource\s*\(/,
        /\.compileShader\s*\(/,
    ];
    return shaderPatterns.some(p => p.test(value) || p.test(lineContent));
}
// Check if string is inline style (JSX or HTML)
function isInlineStyle(lineContent) {
    // JSX inline styles
    const jsxStylePatterns = [
        /style\s*=\s*\{\{/, // style={{...}}
        /style\s*=\s*\{[^}]*:/, // style={{ color: ... }}
        /className\s*=\s*["`'][^"`']*gradient/i, // gradient classes
        /className\s*=\s*["`'][^"`']*bg-/i, // bg- classes
    ];
    // HTML inline styles
    const htmlStylePatterns = [
        /style\s*=\s*["'][^"']*:/, // style="color: ..."
        /<style[^>]*>/i, // <style> tags
        /background:\s*linear-gradient/i, // CSS gradients
        /background:\s*radial-gradient/i, // Radial gradients
    ];
    return [...jsxStylePatterns, ...htmlStylePatterns].some(p => p.test(lineContent));
}
// Check if string contains CSS tokens (colors, units, functions)
function hasCSSTokens(str) {
    const cssTokens = [
        // CSS units
        /\d+px\b/, /\d+%\b/, /\d+em\b/, /\d+rem\b/, /\d+deg\b/, /\d+vh\b/, /\d+vw\b/,
        // Hex colors (standalone or in context)
        /#[0-9a-f]{3,8}\b/i,
        // CSS color functions
        /rgb\s*\(/, /rgba\s*\(/, /hsl\s*\(/, /hsla\s*\(/,
        /oklab\s*\(/, /oklch\s*\(/, /lab\s*\(/, /lch\s*\(/, // Modern color functions
        // CSS gradients (all types)
        /linear-gradient/, /radial-gradient/, /conic-gradient/,
        /repeating-linear-gradient/, /repeating-radial-gradient/,
        // Gradient direction keywords (Tailwind-style)
        /\bfrom-/, /\bto-/, /\bvia-/,
        // CSS custom properties
        /var\s*\(--/,
        // Common CSS properties
        /\bopacity\s*:\s*[\d.]+/,
        /\btransform\s*:/,
        /\btransition\s*:/,
        /\banimation\s*:/,
        // Box shadow patterns
        /\bshadow-/, /box-shadow/,
        /\d+px\s+\d+px\s+\d+px/, // Shadow offset pattern
        // Color stops in gradients
        /\b\d+%\s*(,|$)/, // Percentage color stops
    ];
    // Single strong indicators (only need 1 match)
    const strongIndicators = [
        /^#[0-9a-f]{6}$/i, // Standalone 6-digit hex color
        /^#[0-9a-f]{8}$/i, // Standalone 8-digit hex color with alpha
        /linear-gradient\s*\(/, // Gradient function
        /radial-gradient\s*\(/,
        /conic-gradient\s*\(/,
        /rgba?\s*\(\s*\d/, // rgb/rgba with numbers
        /hsla?\s*\(\s*\d/, // hsl/hsla with numbers
    ];
    // If any strong indicator matches, it's definitely CSS
    if (strongIndicators.some(pattern => pattern.test(str))) {
        return true;
    }
    // Must match at least 2 CSS indicators to be confident it's CSS
    const tokenCount = cssTokens.filter(pattern => pattern.test(str)).length;
    return tokenCount >= 2;
}
// Check if value/line contains environment variable placeholders (shell scripts, test files)
function isEnvVarPlaceholder(lineContent, value) {
    // Shell script patterns
    const shellEnvPatterns = [
        /\$[A-Z_][A-Z0-9_]*/, // $VAR_NAME
        /\$\{[A-Z_][A-Z0-9_]*\}/, // ${VAR_NAME}
        /\bexport\s+[A-Z_][A-Z0-9_]*=["']?\$/, // export VAR=$OTHER
        /:\s*\$\{[A-Z_][A-Z0-9_]*:-/, // ${VAR:-default}
    ];
    // Test file env var patterns (common placeholder names)
    const testEnvPatterns = [
        /FREE_KEY|PRO_KEY|ULTRA_KEY|TEST_KEY/i,
        /BASE_URL|API_URL|ENDPOINT_URL/i,
        /YOUR_[A-Z_]*KEY|REPLACE_[A-Z_]*KEY/i,
        /\$\{?\w+\}?_KEY|\$\{?\w+\}?_TOKEN/i, // $SOME_KEY, ${SOME_TOKEN}
    ];
    return (shellEnvPatterns.some(p => p.test(lineContent)) ||
        testEnvPatterns.some(p => p.test(value)) ||
        testEnvPatterns.some(p => p.test(lineContent)));
}
/**
 * Check if string is a CSS calc pattern or similar dynamic expression
 * These can have high entropy but are not secrets
 */
function isCSSCalcOrExpression(value, lineContent) {
    const calcPatterns = [
        // CSS calc with template literal: `${(100 / steps.length) * order}%`
        /\$\{[^}]*\/[^}]*\}.*%/,
        // CSS calc function
        /calc\s*\(/i,
        // Window env injection: `window.__ENV__ = ${JSON.stringify(env)}`
        /window\.__[A-Z_]+__\s*=/,
        // Boolean expressions with process.env
        /&&.*process\.env\.|process\.env\.\w+\s*&&/,
        // Path construction patterns: `/path/${var}/endpoint`
        /\/[^/]+\/\$\{[^}]+\}\/[^/]+/,
        // Array/object destructuring patterns
        /\[\s*\d+\s*\]\s*=/,
        // Numeric calculations in template literals
        /\$\{\s*\d+\s*[\/*+-]\s*\d+/,
        // JSON.stringify in template
        /JSON\.stringify\s*\(/,
        // Object spread/spread operator patterns
        /\.\.\.\w+/,
    ];
    return calcPatterns.some(p => p.test(value) || p.test(lineContent));
}
/**
 * Check if file is minified JavaScript
 * Minified files have high entropy strings that are NOT secrets
 */
function isMinifiedFile(filePath) {
    const minifiedPatterns = [
        /\.min\.js$/i,
        /\.min\.mjs$/i,
        /\.min\.cjs$/i,
        /\.bundle\.js$/i,
        /-min\.js$/i,
        /\.packed\.js$/i,
        /\.compressed\.js$/i,
        /\/dist\/.*\.js$/i, // dist/ typically contains bundled/minified output
        /\/build\/.*\.js$/i, // build/ output directories
        /\/vendor\//i, // vendor directories
        /\/node_modules\//i, // node_modules should never be scanned anyway
    ];
    return minifiedPatterns.some(p => p.test(filePath));
}
function detectHighEntropyStrings(content, filePath, options) {
    const vulnerabilities = [];
    // Skip minified files - they have high entropy but no actual secrets
    if (isMinifiedFile(filePath)) {
        return vulnerabilities;
    }
    // Skip scanner/fixture files to avoid self-detection
    if ((0, context_helpers_1.isScannerOrFixtureFile)(filePath)) {
        return vulnerabilities;
    }
    // Skip fixture files (__fixtures__, .fixture., mock-data, etc.)
    if ((0, context_helpers_1.isFixtureFile)(filePath)) {
        return vulnerabilities;
    }
    // Skip example files
    if ((0, context_helpers_1.isExampleFile)(filePath)) {
        return vulnerabilities;
    }
    // Skip example directories (/examples/, /demos/, /tutorials/, etc.)
    if ((0, context_helpers_1.isExampleDirectory)(filePath)) {
        return vulnerabilities;
    }
    // Skip documentation/README files
    if (isDocumentationFile(filePath)) {
        return vulnerabilities;
    }
    const strings = extractStringLiterals(content);
    for (const { value, line, lineContent } of strings) {
        // Skip comments
        if ((0, context_helpers_1.isComment)(lineContent))
            continue;
        // Skip PEM headers/footers (they look high-entropy but aren't secrets)
        if (isPEMHeader(value))
            continue;
        // Skip encrypted content blocks (the payload, not the key)
        if (isEncryptedContent(value, lineContent))
            continue;
        // Skip JWT segments (handled by patterns.ts for specific detection)
        if (isJWTSegment(value))
            continue;
        // Skip inline styles (CSS/JSX style={{...}} or style="...")
        if (isInlineStyle(lineContent))
            continue;
        // Skip strings with CSS tokens (colors, gradients, units)
        if (hasCSSTokens(value))
            continue;
        // Skip environment variable placeholders (shell scripts, test files)
        if (isEnvVarPlaceholder(lineContent, value))
            continue;
        // Skip CSS calc patterns and dynamic expressions
        if (isCSSCalcOrExpression(value, lineContent))
            continue;
        // Skip safe patterns
        if (isSafePattern(value))
            continue;
        // Skip CSS/Tailwind class strings
        if (isCSSClasses(value))
            continue;
        // Skip CSS-in-JS patterns (styled-components, emotion)
        if (isCSSInJS(lineContent))
            continue;
        // Skip debug log statements (they often contain env var names which look high-entropy)
        if (isDebugLogContent(lineContent))
            continue;
        // Skip CLI command/usage snippets (flags/commands can look high-entropy)
        if (isCommandLineSnippet(value, lineContent))
            continue;
        // Skip SQL queries (SELECT, INSERT, etc. have high entropy but aren't secrets)
        if (isSQLQuery(value, lineContent))
            continue;
        // Skip i18n/translation strings
        if (isI18nString(value, lineContent))
            continue;
        // Skip CSS transforms/animations
        if (isCSSTransformOrAnimation(value))
            continue;
        // Skip URL templates and path patterns
        if (isURLTemplate(value))
            continue;
        // Skip blockchain addresses (public, not secrets)
        if (isBlockchainAddress(value))
            continue;
        // Skip shader/WebGL code (high entropy but not secrets)
        if (isShaderCode(value, lineContent))
            continue;
        // Skip regex/route matcher patterns
        if (isRegexPattern(value))
            continue;
        // Skip template literals with code expressions (they look high-entropy but aren't secrets)
        if (isTemplateWithCode(value, lineContent))
            continue;
        // Skip human-readable text/markdown content
        if (isHumanReadableContent(value))
            continue;
        // Skip UI strings (model names, descriptions, etc.)
        if (isUIString(value, lineContent))
            continue;
        // Skip JSX UI context (component props, JSX text - like "Synced ${hours}h ago")
        if (isJSXUIContext(lineContent))
            continue;
        // Skip natural language strings (high ratio of common English words)
        if (isNaturalLanguage(value))
            continue;
        // Calculate entropy
        const entropy = calculateEntropy(value);
        // Determine if this is a test file (lower severity)
        const inTestFile = (0, context_helpers_1.isTestOrMockFile)(filePath);
        // Two thresholds:
        // - entropy > 4.5 for strings > 20 chars (standard)
        // - entropy > 4.2 for strings 16-20 chars (slightly stricter to reduce FPs)
        const meetsThreshold = (entropy > 4.5 && value.length > 20) ||
            (entropy > 4.2 && value.length >= 16 && value.length <= 20);
        if (meetsThreshold) {
            // Additional check: should have mix of character types
            const hasLower = /[a-z]/.test(value);
            const hasUpper = /[A-Z]/.test(value);
            const hasDigit = /[0-9]/.test(value);
            const hasSpecial = /[^a-zA-Z0-9]/.test(value);
            const charTypes = [hasLower, hasUpper, hasDigit, hasSpecial].filter(Boolean).length;
            // Only flag if it has at least 2 character types (looks like a secret)
            if (charTypes >= 2) {
                // Final check: skip CSS-like strings that passed earlier filters
                const looksLikeCSS = /gradient|rgba?|hsla?|#[0-9a-f]{3,8}/i.test(value);
                if (looksLikeCSS)
                    continue;
                // Lower severity for test files
                const baseSeverity = entropy > 5.0 ? 'high' : 'medium';
                const severity = inTestFile ? 'low' : baseSeverity;
                const confidence = inTestFile ? 'low' : (entropy > 5.0 ? 'high' : 'medium');
                vulnerabilities.push({
                    id: `entropy-${filePath}-${line}`,
                    filePath,
                    lineNumber: line,
                    lineContent,
                    severity,
                    category: 'high_entropy_string',
                    title: 'Potential hardcoded secret detected',
                    description: `High-entropy string found (entropy: ${entropy.toFixed(2)}). This may be a hardcoded secret, API key, or password.${inTestFile ? ' (in test file)' : ''}`,
                    suggestedFix: 'Move this value to an environment variable and access it via process.env',
                    confidence,
                    layer: 1,
                    requiresAIValidation: true, // Entropy findings must be validated by AI
                });
            }
        }
    }
    return vulnerabilities;
}
//# sourceMappingURL=entropy.js.map