v0.8.6 Universal Command Guard 8 мин 26 сентября 2026, 17:15 — 17:55

Акт IX: Броня боевых серверов — Universal Command Guard и релиз v0.8.6

История глубокого аудита защиты серверов: почему тумблер Production Guard молчал на обычных хостах, как история Bash обходила перехват по стрелке вверх, рентген активного буфера xterm, спасение редакторов nano/vim и релиз v0.8.6.

BUG-037 BUG-038 BUG-039 BUG-040 BUG-041 BUG-042 #security #command-guard #prod-guard #xterm #bash-history #v0.8.6 #tui
Контекст акта: Живое тестирование на боевых серверах выявило парадокс: включение тумблера «Production Command Guard» на хосте с окружением по умолчанию не давало никакого визуального эффекта и не защищало сервер. Расследование вскрыло цепочку из шести уязвимостей — от фатального обхода перехвата через историю команд Bash (стрелка вверх) до поломки TUI-редакторов `nano` и `vim`. Результатом стал масштабный рефакторинг, создание системы Universal Command Guard и публикация релиза v0.8.6.

Запись 47. Броня для боевых серверов: Universal Command Guard, укрощение истории Bash и рентген буфера xterm

⏱️ Время: 26 сентября 2026, 17:45 (~40 минут)

1. Повод: когда защитный тумблер щелкает в пустоту

В процессе живой проверки Shellit я зашел в карточку рабочего сервера со средой по умолчанию (defaultEnv), открыл редактирование и щелкнул тумблер: «Production Command Guard».

Казалось бы, намерение прозрачно: я явно приказал приложению блокировать деструктивные команды на этом узле. Сохраняю хост, подключаюсь по SSH, консоль разворачивается… и тишина. Никакой тревожной рамки, ни предупреждающего бейджа, ни перехвата команд. Я вбиваю опасную команду — и она без малейшего сопротивления улетает на сервер.

Диалог с нейросетью был предельно конкретным:

«проверь реализацию prod guard»
«не вижу никакого визуального отличия, в дополнение к выявленным тобой багам»
«все обнаруженные баги зафиксируй! потом обсудим план»
«надо как-то в описании самого тумблера указать на это. Кроме того, объясни мне, на что вообще влияет выбор пункта из этого выпадающего списка?»


2. Подводные камни: разрыв логики и каскад уязвимостей

Когда мы начали копать глубже одного визуального бага, наружу всплыл целый каскад критических архитектурных и эргономических проблем:

  1. Разрыв между тумблером и средой (BUG-037): Рамка ProdGuardBorder и условие в терминале проверяли исключительно host.environment == HostEnvironment.production. Если сервер находился в defaultEnv, staging или development, тумблер защиты превращался в «мертвый код» — настройки сохранялись в зашифрованную базу, но в терминале наглухо игнорировались.
  2. Фатальный обход через историю Bash (BUG-038, P0 Blocker): Классический буфер ввода _commandLineBuffer посимвольно накапливал печатные знаки из потока ввода, отбрасывая управляющие ANSI escape-коды. Стоило нажать Стрелку вверх (Up Arrow), чтобы достать из истории шелла недавнюю команду вроде rm -rf /var/cache/*, как _commandLineBuffer оставался девственно пустым! При нажатии Enter опасная команда моментально улетала на выполнение без единого диалога.
  3. Дырки в регулярных выражениях (BUG-039): Детектор деструктивных команд жил во внешнем UI-пакете terminal_ui вместо доменного ядра и проверял \brm\s+. Команды с абсолютными путями (/bin/rm), обратным порядком флагов (rm -f -r) или современные системные утилиты (wipefs, truncate -s 0, systemctl poweroff/reboot, chmod -R 777) пролетали сквозь защиту.
  4. Кошмар внутри nano и vim (BUG-040): При редактировании файлов в полноэкранных TUI-редакторах каждое нажатие Enter продолжало анализироваться парсером шелла. Если в конфиге попадалась подстрока с rm, всплывал диалог подтверждения, а при его отмене терминал посылал сигнал Ctrl+C (\x03), немедленно ломая сеанс редактирования.
  5. Рассинхрон с AI Gateway MCP (BUG-041): В коде MCP-шлюза для внешних агентов стояла примитивная проверка contains('rm -rf'), расходящаяся с терминальным клиентом.
  6. Нарушение Zero Hardcoded Strings и неинформативные вкладки (BUG-042): Тексты баннеров были захардкожены на английском, а на вкладках сессий не было видно, в каком окружении работает открытый терминал.

3. Архитектурные решения и реализация

План исправления потребовал слаженной переработки нескольких слоев приложения:

1. Универсальный Command Guard и цветовая дифференциация

Мы превратили «Prod Guard» в полноценный Universal Command Guard:

  • Если сервер объявлен как Production (HostEnvironment.production), защита деструктивных действий активна всегда по умолчанию, а окно терминала обрамляется контрастной красной рамкой (2.5px, #FF3333) с бейджем [⚠️ PRODUCTION ENVIRONMENT].
  • Если сервер находится в любом другом окружении (defaultEnv, staging, development), но пользователь сознательно активировал тумблер «Command Guard», окно терминала выделяется защитной янтарной рамкой (2.0px, #FFB020) с бейджем [🛡️ COMMAND GUARD ACTIVE].
  • В форме хоста тумблер переименован в «Command Guard (Destructive Protection)» и снабжен динамическим подзаголовком: он открыто сообщает, что для серверов Production защита активна всегда, а для остальных сред включается индивидуально.

2. Рентген буфера xterm и очистка системного промпта

Чтобы исключить обход защиты стрелками истории Bash, мы применили элегантный прием: при нажатии Enter терминал выполняет резервный снимок активной экранной строки:

final screenLine = _terminal.buffer.lines[_terminal.buffer.cursorY].toString();
final candidate = DangerousCommandChecker.cleanPromptAndExtractCommand(screenLine);

Новый статический метод cleanPromptAndExtractCommand виртуозно отсекает системные промпты любой сложности (user@server:~$ , root@srv:~# , (venv) [user@host /var]$ ), оставляя чистую команду для проверки. Если в строке обнаружена деструктивная операция, терминал мгновенно блокирует ввод и вызывает модальное подтверждение.

3. Уважение к Alternate Screen Buffer (TUI)

В терминальных эмуляторах при запуске vim, nano, htop или less происходит переключение в альтернативный буфер экрана (\x1b[?1049h). Библиотека xterm.dart предоставляет нативное свойство _terminal.isUsingAltBuffer. В самое начало обработчика ввода мы добавили лаконичную строку:

if (_terminal.isUsingAltBuffer) return;

Внутри редакторов защита благоразумно отходит в сторону, позволяя спокойно нажимать Enter при правке файлов.

4. Доменное ядро и унификация в core_foundation

Класс DangerousCommandChecker переехал в packages/core_foundation/lib/src/security/dangerous_command_checker.dart. Регулярные выражения получили поддержку путей (?:[^\s]*\/)?, произвольного порядка ключей и полный арсенал опасных системных утилит (wipefs, truncate, dd, mkfs, systemctl, init 0, chmod -R). Этот же валидатор подключен в MCP Server Gateway.

5. Бейджи на табах и чистота локализации

В заголовках вкладок top_bar_tabs.dart появились цветные чипы окружений (PROD красный, STAGE янтарный, DEV синий), а рядом — индикатор [🛡️ GUARD]. Все пользовательские сообщения вынесены в context.tr('prod_guard.*') с полным русским переводом.


4. Итог: Релиз v0.8.6

  • Закрыты все 6 инцидентов: BUG-037 — BUG-042.
  • Написаны 48 тестов детектора деструктивных команд в ядре и 9 регрессионных тестов в интерфейсе.
  • Полный прогон тестов дал 100% зеленый результат:
    • core_foundation: 27/27 успешно.
    • terminal_ui: 126/126 успешно.
    • desktop_plugin_sdk: 62/62 успешно.
    • apps/shellit: 61/61 успешно.
  • Статический анализ flutter analyze показал 0 issues found.
  • Версия программы повышена до v0.8.6.

Защита боевых серверов в Shellit теперь работает на уровне интуиции: видна глазами, перехватывает команды независимо от способа ввода и не мешает повседневной работе в консольных редакторах.

📖 Типографика статьи выровнена по алгоритму Кнута — Пласса с помощью mcp-typography-server на базе Justif.
Knuth-Plass • TeX Style