# Спека: якорь host-терминатора в commit-message lookahead (security-фикс) **Дата:** 2026-06-15. Слой: движок (`tools/url-whitelist-rules.mjs`). Источник: автоматическое security-ревью (MEDIUM, Substring/Unanchored Allowlist Bypass). ## Цель Закрыть обход allowlist в `buildCommitMessageUrlPattern`: negative-lookahead без host-терминатора пропускает subdomain-спуф (`https://liderra.ru.evil.com/exfil` не флагуется → канал утечки). Зеркалить уже-корректный `buildNavigateWhitelistPatterns`. Существующие allow/block-кейсы сохранить. ## Дефект {#D1} Текущий возврат: `new RegExp('\\bhttps?:\\/\\/(?!' + frags.join('|') + ')\\S+', 'i')`. Фрагменты (`liderra\.ru`, `github\.com/liderra`, …) в lookahead не закрыты терминатором → `liderra.ru.evil.com` совпадает по префиксу `liderra\.ru` → lookahead гасится → URL НЕ флагуется (считается своим). Пре-существующий дефект (был в оригинальном `SUSPICIOUS_MESSAGE_PATTERNS[0]`), перенесён в билдер. ## Фикс {#D2} Обернуть альтернацию и добавить общий host-терминатор после неё: `new RegExp('\\bhttps?:\\/\\/(?!(?:' + frags.join('|') + ')(?:[:/?#]|$))\\S+', 'i')`. Терминатор `(?:[:/?#]|$)` требует, чтобы за разрешённым доменом шёл порт/путь/запрос/фрагмент или конец — `.` (как в `…ru.evil.com`) и `-` (как в `…liderra-evil`) его не удовлетворяют → спуф флагуется. Зеркало границы `buildNavigateWhitelistPatterns`. ## Инвариант (backward-compat) {#D3} Легитимные кейсы не меняются: `liderra.ru/x` (после домена `/` ∈ `[:/?#]` → allow), `docs.anthropic.com/x` (allow), `evil.example.com/p` (нет фрагмента → flag/block). Меняется только спуф: `liderra.ru.evil.com` теперь флагуется (block) — это и есть цель. Существующие тесты `commit-message-scanner.test.mjs` (anthropic/liderra allow, external block, hex/base64/script) остаются GREEN. ## Крайние случаи {#D4} Спуф-варианты режутся терминатором: `liderra.ru.evil.com` (`.` после `ru`), `liderra-evil.com` (нет фрагмента вовсе). Path-домен `github.com/liderra` + терминатор: `github.com/liderra/repo` → `/` ∈ → allow; `github.com/liderra-evil` → `-` ∉ → flag. fail-CLOSED не затронут: пустой whitelist → проектных фрагментов нет → `liderra.ru` любой формы флагуется. ## Критерий {#D5} Новый тест: `buildCommitMessageUrlPattern(['liderra.ru','github.com/liderra'])` → `.test('see https://liderra.ru.evil.com/x')` === `true` (флагуется). Существующие билдер-тесты (`liderra.ru/x` false, `docs.anthropic.com/x` false, `evil.example.com/p` true, empty→liderra true) — GREEN. Целевой тест-файл `url-whitelist-rules.test.mjs` GREEN; полный `tools/`-свод — терминал владельца. ```verified-context-json [ {"id":"fx1","kind":"EXTRACTED","ref":"tools/url-whitelist-rules.mjs","anchor":"buildCommitMessageUrlPattern"}, {"id":"fx2","kind":"EXTRACTED","ref":"tools/url-whitelist-rules.mjs","anchor":"buildNavigateWhitelistPatterns"}, {"id":"fx3","kind":"EXTRACTED","ref":"tools/url-whitelist-rules.mjs","anchor":"BASE_COMMIT_MSG_FRAGS"} ] ```