68 lines
5.3 KiB
Markdown
68 lines
5.3 KiB
Markdown
# Спека: Task 4 security — config-augment `protected_paths` (fail-CLOSED union)
|
||
|
||
**Дата:** 2026-06-15
|
||
**Статус:** Draft (Фаза 1 config-seam, security-часть Task 4)
|
||
**Канон:** design v6 §3.3 / §5 / §5.1; план Фазы 1; handoff №2 §2.
|
||
|
||
## Цель
|
||
|
||
Дать двум защитным гейтам движка (`enforce-normative-content-rules`, `shell-content-rules`)
|
||
config-управляемое РАСШИРЕНИЕ списка защищённых путей через ключ `protected_paths`
|
||
(из `.claude/brain.local.md`), по принципу **fail-CLOSED union**: базовая защита остаётся
|
||
хардкодом и неизменной, конфиг только ДОБАВЛЯЕТ пути и никогда не убирает. Пусто/нет конфига →
|
||
база защищает полностью. Поведение `claude-brain` не меняется (`protected_paths: []` → байт-в-байт).
|
||
Подключение значений в `main()` хуков — Задача 7 (здесь только чистый seam + дефолтный ключ).
|
||
|
||
## Контракт augment {#D1}
|
||
|
||
Три точечных дополнения, все обратно-совместимые (новый параметр со значением по умолчанию):
|
||
|
||
1. `tools/brain-config.mjs` — в `DEFAULTS` добавить ключ `protected_paths: []`. После этого
|
||
`resolveConfig({}).protected_paths` равно `[]`; произвольный список пробрасывается как есть.
|
||
2. `tools/enforce-normative-content-rules.mjs` — `isNormativePath(filePath, extraProtectedPaths = [])`:
|
||
результат = совпадение базы `NORMATIVE_PATTERNS` ИЛИ совпадение с любым непустым нормализованным
|
||
путём из `extraProtectedPaths` (substring по нормализованному `filePath`). Вызов с одним
|
||
аргументом → только база.
|
||
3. `tools/shell-content-rules.mjs` — `buildProtectedPatterns(configPaths = [])` возвращает массив
|
||
`[...DEFAULT_PROTECTED_PATTERNS, ...<config-паттерны>]`: базовые паттерны ВСЕГДА первые и не
|
||
удаляются; каждый config-путь экранируется и оборачивается в `(^|/)…` (case-insensitive).
|
||
|
||
## Принцип fail-CLOSED union {#D2}
|
||
|
||
- База (`NORMATIVE_PATTERNS`, `DEFAULT_PROTECTED_PATTERNS`) — хардкод, неизменна, всегда активна.
|
||
- Конфиг `protected_paths` — только UNION (добавление). Операции «снять защиту» нет.
|
||
- Пусто / не-массив / отсутствие ключа → augment пуст → защищает только база (полная защита).
|
||
- Невалидный вход (не-строка, пустая строка, пробелы) — отбрасывается, не роняет функцию, база
|
||
остаётся.
|
||
- Направление отказа безопасное: при любой неясности — защищаем (больше путей под гейтом).
|
||
|
||
## Крайние случаи и критерий {#D3}
|
||
|
||
Крайние случаи (обязательны в тестах):
|
||
|
||
- backward-compat: вызов с одним аргументом (`isNormativePath(path)`) / без аргумента
|
||
(`buildProtectedPatterns()`) — поведение байт-в-байт как до правки.
|
||
- `extraProtectedPaths = null` / не-массив → трактуется как пусто (только база), без исключения.
|
||
- `protected_paths` с пустыми строками / пробелами → отбрасываются (результат = только база).
|
||
- база сохраняется при непустом augment (`isNormativePath('CLAUDE.md', ['x'])` === true;
|
||
`isProtectedPath('CLAUDE.md', …, buildProtectedPatterns(['x']))` === true).
|
||
|
||
Критерий приёмки:
|
||
|
||
- Полный свод tools проходит: `npx vitest run --config vitest.config.tools.mjs` (авторитетный
|
||
прогон — в терминале владельца; через сессионный Bash воркеры нестабильны под нагрузкой).
|
||
- Регрессия: ни один существующий тест не падает (дефолты сохранили поведение).
|
||
- Новые тесты покрывают: дефолтный ключ, augment-добавление, fail-CLOSED пусто, база-сохранена,
|
||
невалидный вход.
|
||
|
||
Конвенция: имена параметров `extraProtectedPaths` / `configPaths`; стиль `tools/` (ESM, чистые
|
||
функции, без новых зависимостей); нормализация путей — `replace(/\\/g, '/')` как в существующем коде.
|
||
|
||
```verified-context-json
|
||
[
|
||
{"id":"D1","kind":"EXTRACTED","ref":"tools/brain-config.mjs","anchor":"const DEFAULTS = Object.freeze({"},
|
||
{"id":"D2","kind":"EXTRACTED","ref":"tools/enforce-normative-content-rules.mjs","anchor":"const NORMATIVE_PATTERNS = ["},
|
||
{"id":"D3","kind":"EXTRACTED","ref":"tools/shell-content-rules.mjs","anchor":"export const DEFAULT_PROTECTED_PATTERNS = ["}
|
||
]
|
||
```
|