Проблема длинного security baseline в том, что он всё равно может не дать ответа на один неудобный вопрос: какая атака упирается в какую границу? Когда после инцидента control list читают как доказательство, цена — ложная причинность. Наличие policy, scanner или заголовка задним числом превращается в «значит, путь был закрыт», хотя никто не зафиксировал, какой шаг должен был быть остановлен.
Механика review должна хранить цепочку, а не набор названий: threat model формулирует путь, control объявляет interruption, evidence разрешает ограниченное наблюдение, residual risk держит незакрытую часть. При разрыве цепи результат — stop. При полной synthetic цепи — только hand-off для следующего review. Это сознательно меньше, чем verdict о защищённости, зато вывод имеет проверяемую область.
Перечень теряет отношение между сущностями
Control list — это таблица существительных: CSP, encoding, review, scanner, token. У него обычно нет направленного ребра между причиной и следствием. Два controls могут претендовать на один и тот же шаг, но не закрывать соседний. Один evidence может быть собран для другой страницы или иной модели угроз. Без идентификаторов мы не замечаем подмену: «есть CSP» начинает выглядеть как «данный input не исполняется», хотя это разные утверждения.
Traceability возвращает отношения в явную форму. Threat id связывается с ordered path; control id — с intended interruption; evidence обязано связать оба id; residual record указывает то, что не выводится из наблюдения. Такая форма не делает security математически полной. Она делает неполноту видимой и локальной: reviewer видит конкретное отсутствующее ребро, а не получает расплывчатое ощущение недостаточной зрелости.
| Вопрос | Нужный объект | Допустимый вывод | Запрещённый скачок |
|---|---|---|---|
| что атакуется? | asset и precondition | модель имеет границу | все угрозы перечислены |
| как идёт путь? | ordered attackPath | шаги можно обсуждать | путь существует в production |
| где прерывание? | control interruption | control относится к шагу | control исправляет первопричину |
| что видели? | bound allowed evidence | наблюдение относится к паре ids | наблюдение доказывает весь security posture |
| что осталось? | residual risk и owner question | следующий review назван | риск исчез или принят |
Evidence — это тип разрешённого вывода
Полезно читать evidence не как файл, скриншот или лог, а как тип разрешённого вывода. В fixed record есть named-directives-present и synthetic-negative-script-is-not-authorized. Первое позволяет говорить о строках модели. Второе — о негативной ветке именно этой модели. Ни одно не позволяет сказать, что unsafe sink отсутствует, input valid, пользователь защищён или реальный browser получил конкретный header.
Граница evidence должна быть записана рядом с ним, а не в памяти автора. Иначе доказательство расширяется с каждым пересказом. W3C CSP3 особенно полезен именно как ограничитель риторики: его introduction говорит, что CSP снижает вред injection, но не заменяет careful input validation и output encoding. Значит, даже корректно сформулированный policy control не разрешает убрать из residual matrix строку про данные и sink без независимого основания.
Исполняемая проверка разрыва трассы
import { createFixedSyntheticSecurityReview, reviewFixedSecurityTraceability } from './upgrade-2026-04.mjs';
const record = createFixedSyntheticSecurityReview('evidence-without-binding-v1');
const outcome = reviewFixedSecurityTraceability(record);
console.log({ status: outcome.status, reasons: outcome.reasons, next: outcome.nextAction });
// { status: 'stop-unbound-evidence', reasons: ['evidence-does-not-bind-threat-and-control'], next: 'bind-allowed-observation-to-named-objects' }
Snippet демонстрирует не «ошибку CSP», а отсутствующую связь в data model. Evidence содержит наблюдение, но ссылается на другие ids, поэтому engine не пытается сопоставить его по имени или похожему тексту. Public function возвращает fail-closed status. Вызов не читает файл, не смотрит время, не пользуется telemetry и не проводит retest; это важно, потому что функция проверяет дисциплину утверждения, а не безопасность окружения.
Остаточный риск — обратная сторона mechanism
Control без residual risk выглядит законченным просто потому, что у карточки больше нет строк. Это структурная ошибка. Если control прерывает late step — например, авторизацию script — то ранние шаги могут остаться открыты: появление untrusted fragment, его преобразование, выбор render sink. Они не становятся безопасными от того, что downstream boundary ограничивает последствия. В хорошем review residual list даёт этим шагам отдельные имена.
Для каждого остатка нужен owner question, но не owner verdict. В fixed data вопрос звучит: какой отдельный review трассирует validation и encoding до этой browser-policy boundary? Он не назначает команду, не создаёт тикет и не гарантирует ответ. Такая скромность функциональна: model не может знать реальные владельцы, зато может не позволить результату пройти, если вопрос о следующей проверке отсутствует. Пустой residual record получает stop-unbounded-residual-risk.
Почему controls иногда конфликтуют без явной ошибки
Конфликт часто не в том, что один control выключен. Он появляется, когда два controls дают разные обещания для одного участка, а evidence привязан только к одному. Например, policy может ограничивать execution, а sanitization claim — говорить о преобразовании input. Если в карточке остался только policy, reviewer не вправе выдать вывод о transformation. Если оба перечислены без путей, они могут дублировать друг друга и одновременно пропустить иной sink. Нужны два отдельных interruption и два ограниченных evidence либо честное объединение одного механизма.
ASVS помогает замечать классы таких проверок: его frontend chapter говорит о CSP, safe rendering functions, cookie attributes, CORS и других browser-facing требованиях. Но standard не подставляет relation за конкретную архитектуру. Техническая зрелость М9 состоит не в пересказе большего числа требований, а в умении сказать: «вот этот requirement используется здесь, вот какая связь проверяется, а вот какие conclusions запрещены». Это сильнее checklist, потому что его можно оспорить предметно.
Порядок механической проверки
- Проверить identity. У threat, control и evidence должны быть отдельные стабильные ids внутри review.
- Проверить маршрут. Attack path должен иметь минимум три ordered step, а не ярлык класса уязвимости.
- Проверить interruption. Control обязан называть решение, не только инструмент или политику.
- Проверить binding. Evidence одновременно указывает threat id и control id; совпадение слов не считается.
- Проверить предел. Limits запрещают выводы о среде, которых named observation не даёт.
- Проверить остаток. Открытые части и owner question обязательны перед positive hand-off.
Положительная ветка тоже должна быть ограничена
Самая опасная строка в automation — та, что звучит как итоговое разрешение. В данном overlay даже полностью связанная запись не возвращает approval, release или deployment. Допустимое значение одно: bounded-security-review-handoff, а effect равен no-system-change. Это не косметика. Структура не даёт подсунуть безопасному-looking data set действие, для которого отсутствуют полномочия, среда и реальные наблюдения.
Проверка unsafe-positive-result-v1 намеренно подставляет release-fixed-security-change после корректных ids. Она всё равно получает stop. Тем самым fixture охраняет не только негативные случаи, но и семантику зелёной ветки. Если модель разрешает сильный вывод ради удобства, дальнейшая трассировка перестаёт быть доказательной: любой полный объект становится приглашением выполнить необоснованную операцию.
Две независимые оси неполноты
У traceability есть как минимум две разные неполноты. Первая — структурная: нет шага path, нет interruption, ids не связаны или не указан residual question. Её можно обнаружить внутри объекта и вернуть deterministic stop. Вторая — эпистемическая: запись полная, но на вопрос о реальном браузере, фактическом input или охвате остальных sinks у модели нет данных. Её нельзя исправить добавлением ещё одного boolean. Она должна остаться в limits и стать входом отдельного метода.
Смешивать эти оси вредно. Когда структурный дефект называют «недостатком уверенности», команда может продолжать с поломанной схемой. Когда границу знания пытаются закрыть структурой, synthetic fixture начинает изображать измерительный инструмент. Хорошее механическое правило сначала устраняет разрыв ids, затем сохраняет неизвестное как неизвестное. Только так зелёная ветка означает качество аргумента, а не степень уверенности в окружающем мире.
Границы механизма и следующий шаг
Эта модель не оценивает вероятность, ущерб, exploitability, coverage, latency и соответствие требованиям. Она не заменяет threat modeling, code review, тестирование, сканирование или независимую оценку; NISTIR 8397 перечисляет эти техники именно как разные элементы developer verification. Мы заимствуем дисциплину сравнения, а не объявляем собственный fixture стандартом безопасности.
Следующий шаг — взять одну существующую «зелёную» строку в security spreadsheet и попытаться провести четыре ребра. Если evidence не может назвать оба ids, не улучшайте формулировку: остановите запись. Если residual risk нельзя назвать без реальных данных, оставьте его неизвестным и вынесите в отдельный review. Трассировка полезна тем, что предпочитает честный пробел убедительному, но неразрешённому выводу.
Проверяемые источники
- W3C Content Security Policy Level 3 — версия: W3C Working Draft, 21 April 2026, dated immutable snapshot. CSP3 от 21.04.2026 описывает CSP как дополнительную защиту, не заменяющую validation и output encoding. Граница: Draft не определяет локальную threat model, sufficient evidence или residual-risk owner для этого synthetic review.
- OWASP Application Security Verification Standard 5.0.0 — версия: release tag v5.0.0_release, commit 5cf9b032440be53ce345ab3c130fda46ba1ce7a2, 30 May 2025, immutable commit pin. Pinned ASVS 5.0.0 содержит web-frontend verification requirements для CSP, safe rendering, cookie и cross-origin механизмов. Граница: Наличие requirement не связывает конкретный control с конкретным attack path без отдельной трассировки.
- NISTIR 8397: Guidelines on Minimum Standards for Developer Verification of Software — версия: NIST IR 8397, 6 October 2021, immutable DOI publication. NISTIR 8397 рекомендует разнообразные техники developer verification, включая threat modeling, tests и static analysis. Граница: NISTIR не задаёт verdict, при котором один result может быть выдан за approval или release.