/**
 * Zero-Day Hunter Agent
 *
 * AI agent that reasons about code semantics to find novel vulnerabilities.
 * Uses Claude to analyze code for patterns that pattern-based scanners miss.
 *
 * Focus areas:
 * - Logic Flaws: Inconsistent state handling, broken assumptions
 * - Authentication Bypasses: Ways to skip auth checks
 * - Authorization Issues: Privilege escalation paths
 * - Cryptographic Weaknesses: Weak algorithms, key management
 * - Injection Vectors: Novel injection patterns beyond OWASP
 *
 * @module agents/zero-day-hunter
 */
import { readFile } from "fs/promises";
import * as path from "path";
import { glob } from "glob";
// ============================================================================
// Code Context Building
// ============================================================================
/**
 * Detect language from file extension
 */
function detectLanguage(file) {
    const ext = path.extname(file).toLowerCase();
    const langMap = {
        ".ts": "typescript",
        ".tsx": "typescript",
        ".js": "javascript",
        ".jsx": "javascript",
        ".py": "python",
        ".go": "go",
        ".rs": "rust",
        ".java": "java",
        ".rb": "ruby",
        ".php": "php",
        ".cs": "csharp",
        ".c": "c",
        ".cpp": "cpp",
        ".h": "c",
        ".hpp": "cpp",
    };
    return langMap[ext] || "unknown";
}
/**
 * Extract function signatures from code
 */
function extractFunctionSignatures(content, language) {
    const signatures = [];
    const patterns = {
        typescript: [
            /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)/g,
            /(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
            /(?:public|private|protected)?\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/g,
        ],
        javascript: [
            /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)/g,
            /(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
        ],
        python: [
            /def\s+(\w+)\s*\([^)]*\)\s*(?:->.*)?:/g,
            /async\s+def\s+(\w+)\s*\([^)]*\)\s*(?:->.*)?:/g,
        ],
        go: [
            /func\s+(?:\([^)]+\)\s+)?(\w+)\s*\([^)]*\)/g,
        ],
        java: [
            /(?:public|private|protected)\s+(?:static\s+)?(?:\w+\s+)?(\w+)\s*\([^)]*\)/g,
        ],
    };
    const langPatterns = patterns[language] || patterns.javascript;
    for (const pattern of langPatterns) {
        let match;
        while ((match = pattern.exec(content)) !== null) {
            signatures.push(match[0]);
        }
    }
    return signatures.slice(0, 50); // Limit to 50 signatures
}
/**
 * Extract imports from code
 */
function extractImports(content, language) {
    const imports = [];
    const patterns = {
        typescript: /import\s+(?:(?:\{[^}]+\}|[\w*]+)\s+from\s+)?['"]([^'"]+)['"]/g,
        javascript: /(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g,
        python: /(?:from\s+(\S+)\s+import|import\s+(\S+))/g,
        go: /import\s+(?:\([^)]+\)|"[^"]+")/g,
        java: /import\s+([\w.]+);/g,
    };
    const pattern = patterns[language] || patterns.javascript;
    let match;
    while ((match = pattern.exec(content)) !== null) {
        imports.push(match[1] || match[0]);
    }
    return imports;
}
/**
 * Determine focus areas based on code content
 */
function determineFocusAreas(content, imports) {
    const areas = [];
    const contentLower = content.toLowerCase();
    const importsStr = imports.join(" ").toLowerCase();
    // Authentication
    if (contentLower.includes("password") ||
        contentLower.includes("login") ||
        contentLower.includes("authenticate") ||
        contentLower.includes("jwt") ||
        contentLower.includes("session") ||
        importsStr.includes("passport") ||
        importsStr.includes("auth")) {
        areas.push("auth");
    }
    // Cryptography
    if (contentLower.includes("encrypt") ||
        contentLower.includes("decrypt") ||
        contentLower.includes("hash") ||
        contentLower.includes("crypto") ||
        contentLower.includes("hmac") ||
        importsStr.includes("crypto")) {
        areas.push("crypto");
    }
    // Injection
    if (contentLower.includes("exec") ||
        contentLower.includes("eval") ||
        contentLower.includes("query") ||
        contentLower.includes("sql") ||
        contentLower.includes("command") ||
        contentLower.includes("shell")) {
        areas.push("injection");
    }
    // Access Control
    if (contentLower.includes("role") ||
        contentLower.includes("permission") ||
        contentLower.includes("authorize") ||
        contentLower.includes("isadmin") ||
        contentLower.includes("acl")) {
        areas.push("access-control");
    }
    // Data Validation
    if (contentLower.includes("validate") ||
        contentLower.includes("sanitize") ||
        contentLower.includes("request.body") ||
        contentLower.includes("req.params") ||
        contentLower.includes("user input")) {
        areas.push("data-validation");
    }
    // Session Management
    if (contentLower.includes("session") ||
        contentLower.includes("cookie") ||
        contentLower.includes("token")) {
        areas.push("session");
    }
    // API
    if (contentLower.includes("api") ||
        contentLower.includes("endpoint") ||
        contentLower.includes("route") ||
        contentLower.includes("controller")) {
        areas.push("api");
    }
    // State management
    if (contentLower.includes("state") ||
        contentLower.includes("store") ||
        contentLower.includes("global") ||
        contentLower.includes("singleton")) {
        areas.push("state");
    }
    // Logic (catch-all for complex code)
    if (areas.length === 0 || contentLower.includes("if") && contentLower.includes("else")) {
        areas.push("logic");
    }
    return [...new Set(areas)];
}
/**
 * Check if file is security-relevant
 */
function isSecurityRelevant(content, imports) {
    const securityKeywords = [
        "password", "secret", "key", "token", "auth", "login", "session",
        "encrypt", "decrypt", "hash", "sign", "verify", "validate",
        "admin", "role", "permission", "access", "grant", "deny",
        "sql", "query", "exec", "eval", "command", "shell",
        "certificate", "ssl", "tls", "https", "cors",
        "sanitize", "escape", "encode", "decode",
        "credit", "ssn", "pii", "gdpr", "hipaa",
    ];
    const contentLower = content.toLowerCase();
    const importsLower = imports.join(" ").toLowerCase();
    return securityKeywords.some(kw => contentLower.includes(kw) || importsLower.includes(kw));
}
/**
 * Build code context for analysis
 */
async function buildCodeContext(projectPath, config) {
    const contexts = [];
    const absolutePath = path.resolve(projectPath);
    const defaultInclude = [
        "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx",
        "**/*.py", "**/*.go", "**/*.java", "**/*.rb",
    ];
    const defaultExclude = [
        "**/node_modules/**", "**/vendor/**", "**/dist/**",
        "**/build/**", "**/*.test.*", "**/*.spec.*",
        "**/test/**", "**/tests/**", "**/__tests__/**",
    ];
    const includePatterns = config.includePatterns || defaultInclude;
    const excludePatterns = config.excludePatterns || defaultExclude;
    const files = await glob(includePatterns, {
        cwd: absolutePath,
        ignore: excludePatterns,
        nodir: true,
    });
    // Limit files based on analysis depth
    const maxFiles = config.maxFilesToAnalyze ||
        (config.analysisDepth === "quick" ? 20 :
            config.analysisDepth === "standard" ? 50 : 100);
    // Prioritize security-relevant files
    const prioritizedFiles = [];
    for (const file of files) {
        const fullPath = path.join(absolutePath, file);
        try {
            const content = await readFile(fullPath, "utf-8");
            const language = detectLanguage(file);
            const imports = extractImports(content, language);
            let priority = 0;
            if (isSecurityRelevant(content, imports))
                priority += 10;
            if (file.includes("auth"))
                priority += 5;
            if (file.includes("api"))
                priority += 3;
            if (file.includes("route"))
                priority += 3;
            if (file.includes("controller"))
                priority += 3;
            if (file.includes("service"))
                priority += 2;
            prioritizedFiles.push({ file, priority });
        }
        catch {
            // Skip unreadable files
        }
    }
    // Sort by priority and take top N
    prioritizedFiles.sort((a, b) => b.priority - a.priority);
    const selectedFiles = prioritizedFiles.slice(0, maxFiles);
    for (const { file } of selectedFiles) {
        const fullPath = path.join(absolutePath, file);
        try {
            const content = await readFile(fullPath, "utf-8");
            const language = detectLanguage(file);
            const imports = extractImports(content, language);
            const signatures = extractFunctionSignatures(content, language);
            const focusArea = determineFocusAreas(content, imports);
            const securityRelevant = isSecurityRelevant(content, imports);
            contexts.push({
                file,
                language,
                content: content.slice(0, 50000), // Limit content size
                functionSignatures: signatures,
                imports,
                securityRelevant,
                focusArea,
            });
        }
        catch {
            // Skip unreadable files
        }
    }
    return contexts;
}
const VULN_PATTERNS = [
    // Auth bypasses
    {
        id: "zdh-auth-001",
        title: "Admin check using string comparison",
        pattern: /(?:req|request)\.\w+(?:\.\w+)*\s*===?\s*['"]admin['"]/gi,
        severity: "high",
        category: "auth",
        cweIds: ["CWE-287"],
        description: "Direct string comparison for admin check can be bypassed with role manipulation.",
        recommendation: "Use role-based access control with proper session validation.",
    },
    {
        id: "zdh-auth-002",
        title: "JWT without expiration check",
        pattern: /jwt\.(?:verify|decode)\s*\([^)]*\)(?![\s\S]{0,200}exp)/gi,
        severity: "high",
        category: "auth",
        cweIds: ["CWE-613"],
        description: "JWT verification without expiration check allows replay attacks.",
        recommendation: "Always verify token expiration with maxAge or exp claim validation.",
    },
    // Crypto weaknesses
    {
        id: "zdh-crypto-001",
        title: "Weak hash algorithm (MD5/SHA1)",
        pattern: /(?:createHash|hashlib\.)(?:md5|sha1)\s*\(/gi,
        severity: "high",
        category: "crypto",
        cweIds: ["CWE-328"],
        description: "MD5 and SHA1 are cryptographically broken for password hashing.",
        recommendation: "Use bcrypt, argon2, or SHA-256+ with proper salting.",
    },
    {
        id: "zdh-crypto-002",
        title: "Hardcoded encryption key",
        pattern: /(?:key|secret|password)\s*[:=]\s*['"][A-Za-z0-9+/=]{16,}['"]/gi,
        severity: "critical",
        category: "crypto",
        cweIds: ["CWE-321"],
        description: "Hardcoded encryption key can be extracted from source code.",
        recommendation: "Use environment variables or secure key management system.",
    },
    // Injection
    {
        id: "zdh-injection-001",
        title: "Dynamic query construction",
        pattern: /(?:query|execute|exec)\s*\([^)]*\$\{[^}]+\}|(?:query|execute|exec)\s*\([^)]*\+\s*\w+/gi,
        severity: "critical",
        category: "injection",
        cweIds: ["CWE-89"],
        description: "Dynamic SQL query construction is vulnerable to injection.",
        recommendation: "Use parameterized queries or prepared statements.",
    },
    {
        id: "zdh-injection-002",
        title: "Command injection via template literal",
        pattern: /exec(?:Sync)?\s*\(`[^`]*\$\{[^}]+\}/gi,
        severity: "critical",
        category: "injection",
        cweIds: ["CWE-78"],
        description: "User input in command execution enables command injection.",
        recommendation: "Use parameterized APIs or strict input validation.",
    },
    // Logic flaws
    {
        id: "zdh-logic-001",
        title: "Race condition in balance check",
        pattern: /if\s*\(\s*\w*balance\w*\s*[><=]+[^{]+\{[\s\S]{0,200}balance\s*[-+]?=/gi,
        severity: "high",
        category: "logic",
        cweIds: ["CWE-367"],
        description: "Balance check followed by modification is vulnerable to race conditions.",
        recommendation: "Use atomic transactions or optimistic locking.",
    },
    {
        id: "zdh-logic-002",
        title: "Integer overflow in calculation",
        pattern: /\w+\s*\*\s*\w+\s*[+\-\*]\s*\w+(?!\s*>>>)/gi,
        severity: "medium",
        category: "logic",
        cweIds: ["CWE-190"],
        description: "Arithmetic operations may overflow without proper bounds checking.",
        recommendation: "Use BigInt for large numbers or add overflow checks.",
    },
    // Access control
    {
        id: "zdh-acl-001",
        title: "Missing authorization check in endpoint",
        pattern: /app\.(?:get|post|put|delete)\s*\([^,]+,\s*(?:async\s*)?\([^)]*\)\s*=>\s*\{(?![\s\S]{0,100}(?:isAuth|checkAuth|requireAuth|authorize))/gi,
        severity: "high",
        category: "access-control",
        cweIds: ["CWE-862"],
        description: "API endpoint without apparent authorization check.",
        recommendation: "Add authentication middleware to protected routes.",
    },
    // Data validation
    {
        id: "zdh-valid-001",
        title: "Unchecked file path from user input",
        pattern: /(?:readFile|writeFile|unlink|open)\s*\([^)]*(?:req|request)\.[^)]+\)/gi,
        severity: "critical",
        category: "data-validation",
        cweIds: ["CWE-22"],
        description: "File path from user input without validation enables path traversal.",
        recommendation: "Validate and sanitize file paths, use allowlist of permitted directories.",
    },
];
/**
 * Run pattern-based zero-day detection
 */
function runPatternAnalysis(contexts) {
    const findings = [];
    let findingId = 1;
    for (const ctx of contexts) {
        for (const vuln of VULN_PATTERNS) {
            vuln.pattern.lastIndex = 0;
            let match;
            while ((match = vuln.pattern.exec(ctx.content)) !== null) {
                // Find line number
                const upToMatch = ctx.content.slice(0, match.index);
                const lineNumber = upToMatch.split("\n").length;
                // Extract code snippet (3 lines context)
                const lines = ctx.content.split("\n");
                const startLine = Math.max(0, lineNumber - 2);
                const endLine = Math.min(lines.length, lineNumber + 2);
                const snippet = lines.slice(startLine, endLine).join("\n");
                findings.push({
                    id: `zdh-${String(findingId++).padStart(3, "0")}`,
                    title: vuln.title,
                    description: vuln.description,
                    severity: vuln.severity,
                    confidence: 80,
                    category: vuln.category,
                    file: ctx.file,
                    line: lineNumber,
                    codeSnippet: snippet,
                    attackScenario: generateAttackScenario(vuln.category, vuln.id),
                    cweIds: vuln.cweIds,
                    recommendation: vuln.recommendation,
                    exploitability: "likely",
                    aiReasoning: `Pattern-based detection: ${vuln.pattern.toString().slice(0, 100)}`,
                });
            }
        }
    }
    return findings;
}
/**
 * Generate attack scenario description
 */
function generateAttackScenario(category, vulnId) {
    const scenarios = {
        "zdh-auth-001": "Attacker manipulates role field in request to gain admin access without proper authentication.",
        "zdh-auth-002": "Attacker captures a JWT and replays it indefinitely since expiration is not validated.",
        "zdh-crypto-001": "Attacker uses rainbow tables or collision attacks to crack MD5/SHA1 hashed passwords.",
        "zdh-crypto-002": "Attacker extracts hardcoded key from source/binary to decrypt sensitive data.",
        "zdh-injection-001": "Attacker injects SQL payload via user input to extract or modify database contents.",
        "zdh-injection-002": "Attacker injects shell commands to achieve remote code execution on the server.",
        "zdh-logic-001": "Attacker exploits race condition to perform double-spend or bypass balance checks.",
        "zdh-logic-002": "Attacker causes integer overflow to bypass security checks or corrupt application state.",
        "zdh-acl-001": "Attacker accesses protected endpoint directly without authentication, exposing sensitive data.",
        "zdh-valid-001": "Attacker uses ../../../ sequences to read arbitrary files from the server filesystem.",
    };
    return scenarios[vulnId] || `Attacker exploits ${category} vulnerability to compromise application security.`;
}
// ============================================================================
// AI Analysis (when API key available)
// ============================================================================
/**
 * Build AI analysis prompt
 */
function buildAnalysisPrompt(ctx, focusAreas) {
    return `You are a security researcher analyzing code for novel vulnerabilities.

## File: ${ctx.file}
## Language: ${ctx.language}
## Focus Areas: ${focusAreas.join(", ")}

## Code:
\`\`\`${ctx.language}
${ctx.content.slice(0, 10000)}
\`\`\`

## Instructions:
Analyze this code for security vulnerabilities, focusing on:

1. **Logic Flaws**: Inconsistent state handling, broken assumptions, edge cases
2. **Authentication Bypasses**: Ways to skip or circumvent auth checks
3. **Authorization Issues**: Privilege escalation, missing access controls
4. **Cryptographic Weaknesses**: Weak algorithms, key management issues
5. **Injection Vectors**: Novel injection patterns, template injection, etc.
6. **Race Conditions**: TOCTOU, concurrent access issues
7. **Data Exposure**: Information leakage, verbose errors

For each potential vulnerability found:
- Provide exact line numbers
- Include the vulnerable code snippet
- Explain the attack scenario
- Rate confidence (0-100)
- Map to CWE if applicable

Output as JSON array:
[{
  "title": "Vulnerability title",
  "line": 42,
  "endLine": 45,
  "codeSnippet": "vulnerable code",
  "severity": "critical|high|medium|low",
  "confidence": 85,
  "category": "auth|crypto|injection|logic|access-control|data-validation|session|api|state",
  "attackScenario": "How an attacker would exploit this",
  "cweIds": ["CWE-XXX"],
  "recommendation": "How to fix",
  "reasoning": "Why this is a vulnerability"
}]

Return empty array [] if no vulnerabilities found.`;
}
// ============================================================================
// Main Entry Point
// ============================================================================
/**
 * Run zero-day hunting on a project
 */
export async function runZeroDayHunter(projectPath, config) {
    // 1. Build code context
    const contexts = await buildCodeContext(projectPath, config);
    if (contexts.length === 0) {
        return {
            filesAnalyzed: 0,
            findings: [],
            analysisDepth: config.analysisDepth,
            focusAreas: config.focusAreas,
            modelUsed: "pattern-only",
            recommendations: ["No analyzable source files found."],
        };
    }
    // 2. Filter contexts by focus areas if specified
    const filteredContexts = config.focusAreas.length > 0
        ? contexts.filter(ctx => ctx.focusArea.some(area => config.focusAreas.includes(area)))
        : contexts;
    // 3. Run pattern-based analysis (always runs)
    const patternFindings = runPatternAnalysis(filteredContexts);
    // 4. AI analysis would go here if API key is configured
    // For now, we rely on pattern-based detection
    // TODO: Integrate with Claude API when available
    // 5. Generate recommendations
    const recommendations = [];
    const criticalCount = patternFindings.filter(f => f.severity === "critical").length;
    const highCount = patternFindings.filter(f => f.severity === "high").length;
    if (criticalCount > 0) {
        recommendations.push(`CRITICAL: ${criticalCount} critical vulnerabilities require immediate attention.`);
    }
    if (highCount > 0) {
        recommendations.push(`HIGH: ${highCount} high-severity issues should be fixed before deployment.`);
    }
    // Category-specific recommendations
    const categories = [...new Set(patternFindings.map(f => f.category))];
    if (categories.includes("injection")) {
        recommendations.push("Implement input validation and use parameterized queries throughout the codebase.");
    }
    if (categories.includes("auth")) {
        recommendations.push("Review authentication flow for bypass opportunities. Consider multi-factor authentication.");
    }
    if (categories.includes("crypto")) {
        recommendations.push("Audit cryptographic implementations. Use established libraries and avoid custom crypto.");
    }
    return {
        filesAnalyzed: filteredContexts.length,
        findings: patternFindings,
        analysisDepth: config.analysisDepth,
        focusAreas: config.focusAreas,
        modelUsed: "pattern-analysis",
        recommendations,
    };
}
/**
 * Convert zero-day findings to certification findings
 */
export function zeroDayToFindings(result) {
    return result.findings.map((f) => ({
        id: f.id,
        severity: f.severity,
        category: mapZeroDayCategory(f.category),
        description: `${f.title}: ${f.description}`,
        evidence: `File: ${f.file}:${f.line}\nCode: ${f.codeSnippet.slice(0, 200)}\nAttack: ${f.attackScenario}`,
        confidence: f.confidence,
        verifications: [],
        created_at: new Date().toISOString(),
        scanner_source: "zero-day-hunter",
        metadata: {
            cweIds: f.cweIds,
            recommendation: f.recommendation,
            exploitability: f.exploitability,
            aiReasoning: f.aiReasoning,
        },
    }));
}
/**
 * Map zero-day category to finding category
 */
function mapZeroDayCategory(category) {
    const mapping = {
        auth: "authentication",
        crypto: "cryptography",
        injection: "injection",
        logic: "logic-flaw",
        state: "logic-flaw",
        "access-control": "authorization",
        "data-validation": "input-validation",
        session: "session-management",
        api: "api-security",
    };
    return mapping[category] || "code-quality";
}
//# sourceMappingURL=zero-day-hunter.js.map