Files
brain/docs/superpowers/specs/2026-06-15-task4-security-protected-paths-spec.md
T

68 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Спека: 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 = ["}
]
```