Files
portal/tools/receipt-key-config.mjs
T
Дмитрий 478a9fd8d4 feat(m5): запрет env-fallback ключа + сужение node-whitelist (Пакет 4, Блок 2 RISK, Δ4)
4.1 env-fallback УБРАН (receipt-key-config + judge-key-config):
- resolveReceiptKey/resolveJudgeKey читают ТОЛЬКО keychain; ROUTER_MENTOR_*_KEY env
  больше не читается (анти-казуальная утечка через env/логи; шов под HSM). `env` убран
  из сигнатуры (extra-проп игнорируется); KEY_ENV сохранён в _internals (domain-тест R-31).
- Критерий: env задан, keychain пуст → null.

4.2 node-whitelist сужен (enforce-router-gate, RISK):
- широкое `node <любой-файл>` правило удалено из SAFE_EXACT; новая ветвь classifyWhitelist
  через nodeScriptAllowed: allow только плоский tools/<file>.{mjs,cjs,js} + vitest-runner
  + node --version/-v; иначе default-deny. Инспекция ТОКЕНА пути (shell-quote сохраняет
  бэкслеши Windows-пути — инвариант запиннен тестом), нормализация \\ → /.
- verify (живой): после правки `node vitest.mjs` отработал по всем 147 тест-файлам —
  тул-чейн не сломан (canary). Гейт жив в сессии (powershell+router фаеры подтвердили).

Δ4 честно (§7): сужение — анти-казуальная мера, НЕ полный close. Остаток «контроллер
commit'ит разрешённый tools-скрипт и запускает» принят; file-watcher блокирует
session-edited до commit. Полная изоляция ключа — только HSM/YubiKey (v4.2).

TDD RED-GREEN per задача; tools-only регрессия 2677 passed + 2 skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 12:19:02 +03:00

44 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
/**
* receipt-key-config — резолв HMAC-ключа подписи расписок роутер-наставника.
* Пакет 4.1 (Δ4): ТОЛЬКО keychain (service router-mentor-receipts). env-fallback УБРАН —
* ROUTER_MENTOR_RECEIPT_KEY больше не читается (анти-казуальная утечка ключа через env/логи;
* шов под HSM). Ключ провижинит владелец. Без ключа — null → подпись невозможна
* → неподписанная расписка невалидна (fail-closed на стороне verifyReceipt).
*/
import { createRequire } from 'node:module';
const KEY_ENV = 'ROUTER_MENTOR_RECEIPT_KEY';
const KEYCHAIN_SERVICE = 'router-mentor-receipts';
const KEYCHAIN_ACCOUNT = 'default';
/**
* Резолв ключа: ТОЛЬКО keychain (env-fallback убран, Пакет 4.1 Δ4). `env` в args
* больше не используется (extra-проп в вызовах/тестах молча игнорируется);
* KEY_ENV сохранён в _internals как имя теперь-игнорируемой переменной.
* @param {object} [args]
* @param {Function} [args.keychainGet] - () => string|null (инъекция для тестов)
* @returns {string|null}
*/
export function resolveReceiptKey({ keychainGet = defaultKeychainGet } = {}) {
try {
const v = keychainGet();
return v ? String(v) : null;
} catch {
return null;
}
}
/** Lazy keytar-ридер; null если keytar отсутствует или записи нет. Никогда не бросает. */
export function defaultKeychainGet() {
try {
const require = createRequire(import.meta.url);
const keytar = require('keytar');
return (keytar.getPasswordSync?.(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT)) || null;
} catch {
return null;
}
}
export const _internals = { KEY_ENV, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT };