/**
 * OWASP MCP03 rug-pull detection — canonical tools/list fingerprinting.
 */
import { createHash } from 'crypto';
import { Logger } from '../utils/logger.js';
import { StructuredLogger } from '../utils/structured-logger.js';
import * as Metrics from '../utils/metrics.js';
export function canonicalizeToolsList(tools) {
    const canonical = JSON.stringify(tools
        .map((t) => ({
        name: t.name,
        description: t.description,
        inputSchema: t.inputSchema,
    }))
        .sort((a, b) => String(a.name).localeCompare(String(b.name))));
    return createHash('sha256').update(canonical).digest('hex').slice(0, 16);
}
export function hashToolsFromResult(result) {
    if (!result || typeof result !== 'object')
        return null;
    const tools = result.tools;
    if (!Array.isArray(tools) || tools.length === 0)
        return null;
    return canonicalizeToolsList(tools);
}
/**
 * Update fingerprint from a tools/list payload (JSON-RPC response or notification).
 * Returns true if a new rug-pull mismatch was detected this call.
 */
export function applyToolFingerprint(state, tools, ctx) {
    if (!tools.length)
        return false;
    const hash = canonicalizeToolsList(tools);
    const prefix = ctx.logPrefix ?? `[proxy:${ctx.serverName}]`;
    if (!state.fingerprint) {
        state.fingerprint = hash;
        Logger.debug(`${prefix} Tool fingerprint registered: ${hash} (${tools.length} tools)`);
        return false;
    }
    if (state.fingerprint === hash)
        return false;
    const prev = state.fingerprint;
    state.blocked = true;
    const alert = `${prefix} 🚨 RUG-PULL DETECTED (OWASP MCP03): tool definitions changed mid-session. Previous: ${prev}, New: ${hash}`;
    Logger.error(alert);
    StructuredLogger.info({
        event: 'rug_pull_detected',
        serverName: ctx.serverName,
        previousFingerprint: prev,
        currentFingerprint: hash,
        toolCount: tools.length,
    });
    Metrics.rugpullDetectedTotal.inc(Metrics.withTenantMetricLabels({ server_name: ctx.serverName }, ctx.tenantId));
    Metrics.recordProxyBlock({
        server_name: ctx.serverName,
        block_reason: 'rug_pull',
        rule: 'tool-fingerprint-mismatch',
        tenant_id: ctx.tenantId,
    }, 'rug_pull');
    void ctx.onMismatch?.({
        serverName: ctx.serverName,
        tenantId: ctx.tenantId,
        previousFingerprint: prev,
        currentFingerprint: hash,
        toolCount: tools.length,
    });
    return true;
}
export function applyToolFingerprintFromResult(state, result, ctx) {
    if (!result || typeof result !== 'object')
        return false;
    const tools = result.tools;
    if (!Array.isArray(tools) || tools.length === 0)
        return false;
    return applyToolFingerprint(state, tools, ctx);
}
//# sourceMappingURL=tool-fingerprint.js.map