fix(build): исключить SIGPIPE из релизных гейтов под pipefail
Поиск с флагом -q прекращает чтение на первом совпадении и закрывает свой конец
канала. Продюсер, которому осталось что писать, получает SIGPIPE и завершается
кодом 141, а `set -o pipefail` делает 141 статусом всей конструкции:
совпадение НАЙДЕНО -> продюсер оборван -> статус 141 -> «не найдено»
Для утвердительных проверок это ложный FAIL. Для отрицательных — «такой
конструкции в коде нет» — ложный PASS: запрещённая конструкция найдена, а гейт
зелёный. Отрицательными проверками закреплена половина инвариантов приёмки,
включая запрет обхода тестов и запрет `pnpm audit --prod`.
Порог резкий: пока вывод продюсера помещается в буфер канала (64 KiB на Linux),
он не блокируется и успевает завершиться раньше, чем потребитель начнёт читать.
Замер, 60 прогонов на размер: до 60 KiB — 0 отказов, ровно на 64 KiB — 58/60,
от 96 KiB — 60/60. То есть проверка выглядит исправной ровно до первого
источника крупнее буфера, а такие файлы в репозитории уже есть.
- 56 мест переведены на here-string: `grep -q PATTERN <<<"$content"`;
- продюсеры-команды (ss|awk, dpkg-query, /proc/cpuinfo, systemctl
list-unit-files) сначала читаются в переменную;
- введён code_has: десять отрицательных сканов держались на `|| true` внутри
code_without_comments, гасившем 141, — то есть на побочном эффекте
подавления ошибок, а не на заявленном свойстве;
- несуществующий путь в скане больше не означает успех: `2>/dev/null || true`
превращал опечатку в пустой вывод, а пустой вывод для проверки «этого в коде
нет» — это PASS. Проверка явная, а не через set -e: в контексте `! code_has`
bash отключает errexit на весь вызов;
- возврат пайплайна запрещён отдельной приёмкой.
This commit is contained in:
@@ -42,6 +42,49 @@ Hardening-проход перед релизом `1.0.0`. Основная те
|
||||
асинхронного исполнителя, в трёх местах принимал за доказательство отсутствие
|
||||
наблюдения.
|
||||
|
||||
Восьмой проход — сами релизные гейты. Проверка, которая инвертирует свой
|
||||
результат на достаточно большом входе, опаснее отсутствующей: отсутствующая
|
||||
ничего не обещает.
|
||||
|
||||
### Исправлено — гейты сборки
|
||||
|
||||
- **Пайплайн в поиск с флагом `-q` под `pipefail` инвертирует смысл проверки.**
|
||||
Такой поиск прекращает чтение на первом совпадении и закрывает свой конец
|
||||
канала; продюсер, которому осталось что писать, получает `SIGPIPE` и
|
||||
завершается кодом 141, а `pipefail` делает 141 статусом всей конструкции:
|
||||
|
||||
```text
|
||||
совпадение НАЙДЕНО -> продюсер оборван -> статус 141 -> «не найдено»
|
||||
```
|
||||
|
||||
Для утвердительных проверок это ложный FAIL. Для отрицательных — «такой
|
||||
конструкции в коде нет» — **ложный PASS**: запрещённая конструкция найдена, а
|
||||
гейт зелёный. Именно отрицательными проверками закреплена половина инвариантов
|
||||
приёмки, включая запрет обхода тестов и запрет `pnpm audit --prod`.
|
||||
|
||||
Порог резкий и измеримый: пока вывод продюсера помещается в буфер канала
|
||||
(64 KiB на Linux), он не блокируется и успевает завершиться раньше, чем
|
||||
потребитель начнёт читать, — 0 отказов на 60 прогонах для любого размера до
|
||||
60 KiB и 58/60 ровно на 64 KiB. То есть проверка выглядит исправной ровно до
|
||||
первого источника крупнее буфера, а такие файлы в репозитории уже есть.
|
||||
|
||||
Все 56 мест переведены на here-string (`grep -q PATTERN <<<"$content"`);
|
||||
продюсеры-команды (`ss | awk`, `dpkg-query`, `/proc/cpuinfo`,
|
||||
`systemctl list-unit-files`) сначала читаются в переменную. Возврат пайплайна
|
||||
запрещён отдельной приёмкой.
|
||||
|
||||
- **Корректность десяти отрицательных сканов держалась на `|| true`.** В
|
||||
`code_without_comments` подавление статуса гасило и SIGPIPE-141, поэтому
|
||||
сканы работали — но по причине, которую никто не заявлял и которую снял бы
|
||||
первый же, кто убрал бы маскировку ошибок. Введён `code_has`, читающий код в
|
||||
переменную отдельным оператором.
|
||||
|
||||
- **Несуществующий путь в скане означал успех.** `2>/dev/null || true` в
|
||||
`code_without_comments` превращал опечатку в имени файла в пустой вывод, а
|
||||
пустой вывод для проверки «запрещённой конструкции здесь нет» — это PASS.
|
||||
Теперь отсутствующий файл останавливает сборку. Проверка явная, а не через
|
||||
`set -e`: в контексте `! code_has …` bash отключает `errexit` на весь вызов.
|
||||
|
||||
### Исправлено — барьер покоя и контракт транзиентного таймера
|
||||
|
||||
- **Отказ запроса к systemd выдавался за отсутствие guard'а.** Листинг
|
||||
|
||||
Reference in New Issue
Block a user