@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
JavaScript
/**
* Рекурсивная редакция структуры 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