UNPKG

@inso_web/els-mcp

Version:

MCP-сервер поверх INSO Error Logs Service. Read-only tools (search, analytics, fingerprinting, correlations) для подключения Claude Desktop/Code и ChatGPT к логам ошибок. Streamable HTTP transport + stdio для npx-запуска.

97 lines 3.28 kB
/** * Рекурсивная редакция структуры args / arbitrary JSON value. * * Для каждого string-value применяем `redactString` (regex PII). Для ключей * password/token/secret/apiKey/authorization → drop value (заменяем на * `[REDACTED]`). Не модифицируем исходный объект — возвращаем глубокий * клон с применёнными правками. * * Ограничения: * - Защита от циклов через Set<seen>. При обнаружении цикла подставляем * строку `[CIRCULAR]`. * - Защита от глубоких структур: depth-limit 16, дальше — `[TRUNCATED]`. */ import { redactString } from './fields.js'; const SENSITIVE_KEYS = new Set([ 'password', 'passwd', 'pwd', 'token', 'access_token', 'refresh_token', 'secret', 'apikey', 'api_key', 'authorization', 'cookie', 'set-cookie', 'session', 'csrf', 'private_key', 'client_secret', 'ssn', 'cardnumber', 'card_number', 'cvv', 'cvc', // PII ключи — даже если значение «обычное», drop, чтобы не утекало // в LLM-контекст через структурированный args. PII regex в текстовых // полях обработает inline-вхождения (например, email в сообщении лога). 'email', 'mail', 'phone', 'ip', 'remoteaddr', 'remote_addr', ]); const MAX_DEPTH = 16; const REDACTED_VALUE = '[REDACTED]'; function isSensitiveKey(key) { return SENSITIVE_KEYS.has(key.toLowerCase()); } function redactInternal(input, fieldsHit, seen, depth) { if (depth > MAX_DEPTH) return '[TRUNCATED]'; if (input === null || input === undefined) return input; if (typeof input === 'string') { const { value, fieldsHit: hits } = redactString(input); for (const f of hits) fieldsHit.add(f); return value; } if (typeof input === 'number' || typeof input === 'boolean' || typeof input === 'bigint') { return input; } if (Array.isArray(input)) { if (seen.has(input)) return '[CIRCULAR]'; seen.add(input); return input.map((v) => redactInternal(v, fieldsHit, seen, depth + 1)); } if (typeof input === 'object') { if (seen.has(input)) return '[CIRCULAR]'; seen.add(input); const out = {}; for (const [k, v] of Object.entries(input)) { if (isSensitiveKey(k)) { fieldsHit.add(`key:${k.toLowerCase()}`); out[k] = REDACTED_VALUE; continue; } out[k] = redactInternal(v, fieldsHit, seen, depth + 1); } return out; } return input; } /** * Главный entrypoint для редакции произвольных args / JSON value. */ export function redactValue(input) { const fieldsHit = new Set(); const value = redactInternal(input, fieldsHit, new WeakSet(), 0); return { value, fieldsHit }; } //# sourceMappingURL=argsRedactor.js.map