Closes#18 · мержить после #45 (ветка от feat/16-logs, до мержа #45 в диффе видны и его коммиты)
Конвейер (SPEC §4.3.1, ADR-007, ADR-013)
промт ─► prefilter ─✗─► отказ + 🛡 админам
│✓
▼
guard (LLM, JSON {verdict, reason}) ─ attack / мусор ─► отказ + 🛡
│safe
▼
planner (LLM, JSON AgentLogQuery) ─ не прошёл валидацию ─► «не смог построить запрос»
│
▼
grep (#16) ─► ответ: построенная команда grep + строки
prefilter (config/agent_rules.yaml): длина ≤ 500; просьбы выполнить команду; shell-метасимволы с намерением исполнения; системные файлы и секреты (.env, ~/.ssh, /etc/, api key); prompt injection («игнорируй инструкции», «ты теперь…», «system prompt»). Слова вроде password или token не режутся: «password authentication failed» — законная строка лога, такие запросы оценивает страж.
guard: отдельный вызов с узким промптом (config/prompts/guard.md). Невалидный JSON или неизвестный вердикт считаются атакой. Недоступный LLM — это «попробуйте позже», не атака.
planner (config/prompts/planner.md): получает только вопрос, список контейнеров и время; логов не видит. Ответ — строго AgentLogQuery: контейнер из белого списка, период ≤ 7 дней, шаблон ≤ 200 символов компилируется и не содержит вложенных квантификаторов ((a+)+, (a|aa)*), контекст ≤ 5, лимит ≤ 500, лишние поля запрещены.
Бот: «🤖 Спросить агента» в карточке контейнера логов; ответ показывает grep -iE '…', контейнер, период и строки. При атаке пользователь получает вежливый отказ без деталей, а администраторы — 🛡 с текстом, автором и причиной.
Квота 30 вопросов в сутки; журнал agent_requests (вердикт, причина, запрос, токены) и log_queries (mode=agent).
При LLM_PROVIDER=mock агент выключен, кнопка отвечает «Агент не настроен»: mock возвращает стражу {}, и любой вопрос был бы атакой.
Как проверено
273 теста (51 новый): атаки (injection, «покажи .env», rm -rf, $(curl …), ~/.ssh, system prompt, > 500 символов), ложные срабатывания (обычные вопросы, «password authentication failed»), мусор от стража и планировщика, чужой контейнер, ReDoS-шаблон от планировщика, недоступный LLM, квота, алерт админам, а не участнику.
Покрытие 92%, ruff, mypy strict.
Нужно решить
Какой LLM подключить на сервере: нужна модель с structured output (response_format: json_schema). Без него агент на сервере выключен, остальное работает.
Closes #18 · **мержить после #45** (ветка от `feat/16-logs`, до мержа #45 в диффе видны и его коммиты)
## Конвейер (SPEC §4.3.1, ADR-007, ADR-013)
```
промт ─► prefilter ─✗─► отказ + 🛡 админам
│✓
▼
guard (LLM, JSON {verdict, reason}) ─ attack / мусор ─► отказ + 🛡
│safe
▼
planner (LLM, JSON AgentLogQuery) ─ не прошёл валидацию ─► «не смог построить запрос»
│
▼
grep (#16) ─► ответ: построенная команда grep + строки
```
- **prefilter** (`config/agent_rules.yaml`): длина ≤ 500; просьбы выполнить команду; shell-метасимволы с намерением исполнения; системные файлы и секреты (`.env`, `~/.ssh`, `/etc/`, api key); prompt injection («игнорируй инструкции», «ты теперь…», «system prompt»). Слова вроде `password` или `token` не режутся: «password authentication failed» — законная строка лога, такие запросы оценивает страж.
- **guard**: отдельный вызов с узким промптом (`config/prompts/guard.md`). Невалидный JSON или неизвестный вердикт считаются атакой. Недоступный LLM — это «попробуйте позже», не атака.
- **planner** (`config/prompts/planner.md`): получает только вопрос, список контейнеров и время; **логов не видит**. Ответ — строго `AgentLogQuery`: контейнер из белого списка, период ≤ 7 дней, шаблон ≤ 200 символов компилируется и не содержит вложенных квантификаторов (`(a+)+`, `(a|aa)*`), контекст ≤ 5, лимит ≤ 500, лишние поля запрещены.
- **Бот**: «🤖 Спросить агента» в карточке контейнера логов; ответ показывает `grep -iE '…'`, контейнер, период и строки. При атаке пользователь получает вежливый отказ без деталей, а администраторы — 🛡 с текстом, автором и причиной.
- **Квота** 30 вопросов в сутки; журнал `agent_requests` (вердикт, причина, запрос, токены) и `log_queries` (`mode=agent`).
- При `LLM_PROVIDER=mock` агент выключен, кнопка отвечает «Агент не настроен»: mock возвращает стражу `{}`, и любой вопрос был бы атакой.
## Как проверено
- 273 теста (51 новый): атаки (injection, «покажи .env», `rm -rf`, `$(curl …)`, `~/.ssh`, system prompt, > 500 символов), ложные срабатывания (обычные вопросы, «password authentication failed»), мусор от стража и планировщика, чужой контейнер, ReDoS-шаблон от планировщика, недоступный LLM, квота, алерт админам, а не участнику.
- Покрытие 92%, ruff, mypy strict.
## Нужно решить
Какой LLM подключить на сервере: нужна модель с structured output (`response_format: json_schema`). Без него агент на сервере выключен, остальное работает.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- app/logs/grep.py: подмножество grep (шаблон, -i -v -E -c -C N), разбор
shlex без исполнения, BRE → regex, таймаут на строку и бюджет на поиск,
лимиты 200 символов / 500 строк вывода
- app/logs/journal.py: каждый поиск в log_queries (L-6)
- бот: выбор контейнера, хвост 50/200 строк или 5 мин/1 ч/24 ч, свой grep
по 24 ч, длинный результат — файлом .log (L-5)
- логи Docker: не больше 50 000 строк и 5 МБ, сохраняются новейшие
- fix: кнопки меню и /menu, /help больше не проглатываются шагами ввода
(имя файла, содержимое, сообщение коммита, grep) — фильтр free_input
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- prefilter: правила из config/agent_rules.yaml (длина, команды, shell,
секреты и системные файлы, prompt injection) — без LLM
- guard: отдельный вызов, JSON {verdict, reason}; невалидный ответ = attack,
недоступный LLM — «попробуйте позже», а не атака
- planner: только AgentLogQuery (контейнер из белого списка, ≤ 7 д, шаблон
≤ 200 без вложенных квантификаторов, лимит ≤ 500); логи в LLM не идут
- бот: «🤖 Спросить агента» → показывает построенный grep и строки;
атака — вежливый отказ и 🛡 администраторам; квота 30 в сутки
- журнал agent_requests + log_queries (mode=agent)
- при LLM_PROVIDER=mock агент выключен
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #18 · мержить после #45 (ветка от
feat/16-logs, до мержа #45 в диффе видны и его коммиты)Конвейер (SPEC §4.3.1, ADR-007, ADR-013)
config/agent_rules.yaml): длина ≤ 500; просьбы выполнить команду; shell-метасимволы с намерением исполнения; системные файлы и секреты (.env,~/.ssh,/etc/, api key); prompt injection («игнорируй инструкции», «ты теперь…», «system prompt»). Слова вродеpasswordилиtokenне режутся: «password authentication failed» — законная строка лога, такие запросы оценивает страж.config/prompts/guard.md). Невалидный JSON или неизвестный вердикт считаются атакой. Недоступный LLM — это «попробуйте позже», не атака.config/prompts/planner.md): получает только вопрос, список контейнеров и время; логов не видит. Ответ — строгоAgentLogQuery: контейнер из белого списка, период ≤ 7 дней, шаблон ≤ 200 символов компилируется и не содержит вложенных квантификаторов ((a+)+,(a|aa)*), контекст ≤ 5, лимит ≤ 500, лишние поля запрещены.grep -iE '…', контейнер, период и строки. При атаке пользователь получает вежливый отказ без деталей, а администраторы — 🛡 с текстом, автором и причиной.agent_requests(вердикт, причина, запрос, токены) иlog_queries(mode=agent).LLM_PROVIDER=mockагент выключен, кнопка отвечает «Агент не настроен»: mock возвращает стражу{}, и любой вопрос был бы атакой.Как проверено
rm -rf,$(curl …),~/.ssh, system prompt, > 500 символов), ложные срабатывания (обычные вопросы, «password authentication failed»), мусор от стража и планировщика, чужой контейнер, ReDoS-шаблон от планировщика, недоступный LLM, квота, алерт админам, а не участнику.Нужно решить
Какой LLM подключить на сервере: нужна модель с structured output (
response_format: json_schema). Без него агент на сервере выключен, остальное работает.🤖 Generated with Claude Code
- prefilter: правила из config/agent_rules.yaml (длина, команды, shell, секреты и системные файлы, prompt injection) — без LLM - guard: отдельный вызов, JSON {verdict, reason}; невалидный ответ = attack, недоступный LLM — «попробуйте позже», а не атака - planner: только AgentLogQuery (контейнер из белого списка, ≤ 7 д, шаблон ≤ 200 без вложенных квантификаторов, лимит ≤ 500); логи в LLM не идут - бот: «🤖 Спросить агента» → показывает построенный grep и строки; атака — вежливый отказ и 🛡 администраторам; квота 30 в сутки - журнал agent_requests + log_queries (mode=agent) - при LLM_PROVIDER=mock агент выключен Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>