From 64a55d1f7ed5156ae5e776c7fba4e7edb5563477 Mon Sep 17 00:00:00 2001 From: byrsapty Date: Sat, 29 Aug 2026 01:10:58 +0300 Subject: [PATCH] =?UTF-8?q?HISTORY:=20=D1=96=D1=81=D1=82=D0=BE=D1=80=D1=96?= =?UTF-8?q?=D1=8F=20=D0=B2=D1=85=D0=BE=D0=B4=D1=96=D0=B2,=20=D0=B0=D1=83?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=20=D0=B0=D0=BB=D0=B5=D1=80=D1=82=D1=96=D0=B2?= =?UTF-8?q?=20=D1=96=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=B8,=20?= =?UTF-8?q?=D1=96=20=D0=BE=D0=B4=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=BC=D0=B8?= =?UTF-8?q?=D0=BB=D0=BA=D0=BE=D0=B2=D0=B0=20=D1=82=D1=80=D0=B8=D0=B2=D0=BE?= =?UTF-8?q?=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 --- HISTORY.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index d8f214c..4c2b583 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -8056,3 +8056,63 @@ ROADMAP називав три причини, з яких ламається с пісочниці народжується чистою. І `журнал аудиту 0 → 0` — проба з правилом `min`, тобто нуль у ній нічого не доводить; чому після повної установки аудит порожній, варто подивитись окремо. + +--- + +## 2026-08-29 — Хто заходив і хто що вимкнув + +Дві сліпі зони, знайдені питанням «чому в пісочниці аудит порожній». +Відповідь на саме питання виявилась нудною (установник робить лише те, +що в переліку сліпих зон), а от дорога до неї — ні. + +### Історія входів: 93 записи були в базі й ніде не показувались + +`core.login_attempts` наповнювалась на кожній спробі входу від першого +дня. Читалась рівно в одному місці — для стримування підбору. Ні +ендпойнта, ні сторінки, ні згадки у вебі. Тобто на питання «хто заходив +у мій моніторинг» продукт мав відповідь у базі й не мав способу її +показати. Для B2B це перше, що питає служба безпеки клієнта. + +**Головним була не верстка.** У таблиці немає `tenant_id` — саме тому ці +події й не в аудиті (журнал вимагає кабінет, а на момент перевірки +пароля кабінет ще невідомий). Показати «як є» означало б віддати одному +кабінету спроби входу чужих людей. Прив'язка непряма: спроба → +користувач → членство, трьома шляхами одночасно (`user_id`, `username`, +`email`) в одному `JOIN LATERAL` з `tenant_id` усередині. Саме `JOIN`, а +не `LEFT JOIN`: без збігу рядок ПРИБИРАЄТЬСЯ, а не лишається без імені. + +На бойових даних це виявилось не теорією: у власника **немає пошти**, +тож за `email` не прив'язується ЖОДНА з 94 спроб. Шлях через `username` +дає 93. Тобто зіставлення «як усі роблять» показало б порожню сторінку — +і виглядало б справним. + +Невдала спроба з неіснуючим логіном не належить нікому. Обрано: +**рядком не показувати, але порахувати** — «ще N спроб з M адрес не +показані». Сигнал «логіни перебирають» лишається, чужа людина в чужий +кабінет не потрапляє. Однозначно правильного варіанта тут немає, і в +коді так і написано. + +### Аудит: алерти й склад команди + +За цілий день роботи (драбина, тригери, канал, правила відповідності, +політика відкату) у журналі не з'явилось нічого. Тепер пишуться правила +алертів, канали, драбини, правила відповідності, а також додавання +людини в кабінет, зміна ролі, вилучення й скидання пароля. + +Два рішення про зміст: + +* **вимкнення видно з НАЗВИ дії** (`alr.rule.disable`), а не з різниці + подробиць. Питання «хто вимкнув правило, за яким приходив алерт» + має відповідатись переліком, а не порівнянням двох записів; +* **`config` каналу не їде в запис ВЗАГАЛІ.** Там не лише токен бота: у + webhook-каналі лежить адреса (доступ на запис у чужий чат) і заголовок + `Authorization`. Замість вмісту — прапорець `secret_changed`. + +Перевірено наживо: створення й видалення каналу лягли в журнал, токен у +ньому не з'явився. + +### Помилкова тривога, названа для чесності + +Я вирішив, що сім подій у `core.event_outbox` «висять». Ні: усі сім +позначені доставленими, а `pruneLoop` щогодини прибирає доставлені, +старші за граничний вік. Найстаршій було 11 годин. Механізм справний.