Почему ложная тревога дороже пропуска
Соблазн при срабатывании детектора персональных данных ровно один: прогнать автозамену и забыть. Тогда из текста молча исчезнут площадь, цена и половина заголовка — скраб подставляет псевдоним вместо находки, а не проверяет её. Обратный соблазн — «детектор врёт, отключу» — снимает защиту там, где адрес однажды окажется настоящим.
Между этими двумя дверями лежит третья, и она единственная рабочая: починить то, что прибор получает на вход. Ниже — три источника мусора на входе, каждый пойман у себя.
Источник первый: разметка внутри текста
Правило поиска адреса устроено просто: маркер жилья плюс число рядом. Разметка ломает его тремя разными способами.
| Что увидел прибор | Что было в тексте |
|---|---|
| «дом» и число рядом | «Дом 100 м²» — площадь читается как номер дома |
| «…прав на дом … 54» | Две соседние ячейки таблицы: очистка тегов режет их пробелом, конец одной встаёт вплотную к числу из другой |
| «квартира» и цифра | Слово-маркер последним в заголовке, цифра приехала из закрывающего тега h2 |
Починка — сегментация перед гейтом: закрывающие блочные теги заменяются на точку с пробелом. Проверили на живых текстах, какой разделитель обрывает поиск, а какой нет.
Отдельное следствие для собственных генераторов: приёмка идёт по собранной странице, где теги — часть текста, а не по исходнику. Исходник бывает чист, а собранный HTML красный; если генератор гоняет гейт только по исходнику, «у меня собралось» разойдётся с «гейт красный» на приёмке у другого человека.
Источник второй: подстрока вместо слова
Второй способ прочитать не тот текст — искать без границ слова. Два случая одного дня:
- Скан следов на чужих сайтах: 484 «находки» на ровном месте. Трёхбуквенная аббревиатура искалась без границ слова и с игнорированием регистра, поэтому ловила «сроки» и «срочно». После правки — 0 находок по всем 560 адресам, и это правильный ноль.
- Гейт утечек в клиентских документах: 5 чистых страниц из 6 получили красное. Стоп-слова искались подстрокой: «бан» нашлось внутри «банк», «обход» — внутри «обходится», «внутрен» — во фразе «внутреннюю кухню вырезаем».
Короткий токен без границ слова — гарантированный шум. Аббревиатуры искать только заглавными и как отдельное слово; списки стоп-слов держать в одном месте и импортировать, а не копировать в каждый гейт — иначе починка границ доедет до одного из трёх.
Источник третий: символы, которых не видно
Самая обидная серия ложных тревог за заезд: сверка инструментов с общей веткой показала, что расходится почти половина файлов. Оценка ушла дальше как «89 расходящихся из 189». Ошибка была в самом сравнении: содержимое из ветки отдаётся с одним видом перевода строки, файл с диска — с другим. Побайтово разные, содержательно идентичные.
Поймал это не автор замера, а получатель, прогнавший ту же розданную команду: сравнение выдало «1,214c1,214» — отличаются все строки до единой — на файле, который он только что синхронизировал. После нормализации переводов строк реальная цифра оказалась 37 из 191, из них 5 с настоящими локальными правками.
Цена такой ошибки не в цифре, а в доверии. Проверка свежести со стопроцентной ложной тревогой перестаёт читаться через день, и настоящие расхождения тонут вместе с шумом. Любое сравнение содержимого между системами с разными переводами строк нормализуется явно — флагом сравнения или заменой символа перед сверкой.
Один глиф в двух значениях — тоже неверный вход
Четвёртый случай не про регулярные выражения, а про соглашение. В наших аудитах галочка использовалась в двух смыслах: «проверено, вот источник» и «да, так можно». Гейт доказательности считает каждую галочку утверждением и требует пруф в той же строке — поэтому статус-ячейки вида «✅ можно», «✅ устойчиво» подняли долю недоказанного до 61% при пороге 35%, хотя ни одного бездоказательного факта на странице не было.
- ✅ — только «проверено», и только со ссылкой, датой или номером в той же строке.
- 🟢 — «да, можно»: вердикт выполнимости, пруфа не требует.
- ⚠️ — частично · 🔴 — не проверено · ⛔ — гарантировать нельзя. Источник назван словами, но ссылки нет — метка честно понижается до ⚠️, а не подпирается словом «проверено».
Вторая половина той же грабли: гейт ищет пруф построчно, он писался под текстовый формат. Таблица, собранная в одну длинную строку HTML, читается им как одно утверждение — одна ячейка без источника роняет всю таблицу, и наоборот, одна ссылка «прикрывает» десяток недоказанных. Рендерить строки таблицы с новой строки файла — иначе прибор меряет не то, что кажется.
Чек-лист для своих детекторов
- Прогнать негативный контроль. Заведомо чистый корпус обязан давать ноль. Пять красных страниц из шести чистых — это отчёт о приборе, а не о тексте.
- Нормализовать вход до правил. Сегментация по блочным тегам, границы слова, единый перевод строки, один регистр — всё это до первого правила.
- Развести значения символов. Один глиф — одно значение; иначе и гейт, и читатель считают разное.
- Разбирать срабатывание, а не гасить. Решение по каждой находке — прочитать строку с контекстом и записать вывод туда, где его найдут.
Ложная тревога — это дефект прибора той же тяжести, что и пропуск. Разница в том, что пропуск ищут, а на ложную тревогу привыкают не смотреть.