Files
dipl-edr/docs/technical_reference.md
2026-05-29 17:36:21 +04:00

35 KiB
Raw Permalink Blame History

Технический справочник системы EDR

Endpoint Detection and Response — подробная документация реализации


Содержание

  1. Архитектура системы
  2. Агент мониторинга
  3. Модуль машинного обучения
  4. Серверная часть — API
  5. База данных
  6. Веб-интерфейс
  7. Развёртывание (Docker)
  8. Конфигурация и переменные окружения
  9. Расхождения с дипломным документом

1. Архитектура системы

Компоненты

┌──────────────────────────────────────────────────────────────────────┐
│  Linux-хост (объект мониторинга)                                     │
│                                                                      │
│  ┌──────────────────┐  HTTP POST /api/metrics                        │
│  │  Агент           │ ──────────────────────────▶ ┌───────────────┐ │
│  │  agent/main.py   │                             │  Бэкенд       │ │
│  │                  │                             │  FastAPI      │ │
│  │  • psutil        │                             │  :8000        │ │
│  │  • watchdog      │                             │               │ │
│  │  • EnsembleDetector                            │  • SQLite     │ │
│  └──────────────────┘                             │  • WebSocket  │ │
│                                                   └───────┬───────┘ │
│                                                           │          │
│                                                   WS /ws (push)      │
│                                                           │          │
│                                                   ┌───────▼───────┐ │
│                                                   │  Фронтенд     │ │
│                                                   │  React SPA    │ │
│                                                   │  :3000        │ │
│                                                   └───────────────┘ │
└──────────────────────────────────────────────────────────────────────┘

Поток данных (один цикл, 5 секунд)

psutil.process_iter()          watchdog inotify
        │                           │
        ▼                           ▼
  collect_processes()        _FSHandler._push()
        │                           │
        ▼                           │
  EnsembleDetector.predict()        │
        │                           │
        └──────────────┬────────────┘
                       ▼
                send_batch() → POST /api/metrics
                                      │
                        ┌─────────────┼───────────────┐
                        ▼             ▼               ▼
                 INSERT metrics  INSERT file_events  INSERT system_metrics
                                      │
                              аномалии найдены?
                                      │ да
                              ┌───────▼──────────┐
                              │ дедупликация 30с │
                              └───────┬──────────┘
                           новая? │       существующая? │
                                  ▼                     ▼
                           INSERT alerts         UPDATE alerts
                           + WS broadcast           count+1

2. Агент мониторинга

Файлы

Файл Назначение
agent/main.py Цикл сбора метрик, watchdog, отправка батчей
agent/model.py EnsembleDetector — ML-ансамбль
agent/train.py Standalone-скрипт для предварительного обучения
agent/requirements.txt Зависимости Python
agent/Dockerfile Образ агента

Сбор метрик процессов

# Атрибуты, запрашиваемые через psutil (psutil 6.x убрал 'connections' из attrs)
attrs = ["pid", "name", "cpu_percent", "memory_info", "open_files"]

# CPU нормализован на число логических ядер
cpu_percent_normalized = info["cpu_percent"] / _CPU_COUNT

Важно: нормализация CPU. psutil возвращает суммарную процессорную нагрузку — на 4-ядерной машине процесс может показывать 400%. Агент делит на psutil.cpu_count(logical=True), приводя значение к диапазону 0–100% от мощности всей машины. Это обеспечивает корректное сравнение признаков между хостами с разным числом ядер.

Совместимость net_connections

В psutil 6.x метод Process.connections() переименован в Process.net_connections(). Агент обходит это через:

def _proc_connections(proc: psutil.Process) -> int:
    for method in ("net_connections", "connections"):
        fn = getattr(proc, method, None)
        if fn is None:
            continue
        try:
            return len(fn())
        except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
            return 0
    return 0

Прогрев CPU

Первый вызов cpu_percent() в psutil всегда возвращает 0.0 — библиотека требует временного интервала для расчёта дельты. При старте выполняется:

psutil.cpu_percent(interval=None)           # системный вызов
for proc in psutil.process_iter(["cpu_percent"]):
    proc.cpu_percent(interval=None)         # на процесс
time.sleep(1)                               # интервал измерения

Мониторинг файловой системы

Директория Причина наблюдения
/etc Persistence-атаки через изменение конфигурации
/tmp Типичное место загрузки malware-полезной нагрузки
/var/log Попытки зачистки следов атаки

Буфер событий ограничен 500 записями (FIFO). При переполнении самые старые события вытесняются.

Формат батча (JSON → POST /api/metrics)

{
  "timestamp": "2025-05-28T14:30:00.000000+00:00",
  "processes": [
    {
      "pid": 1234,
      "name": "nginx",
      "cpu_percent": 2.1,
      "memory_mb": 45.3,
      "open_files": 22,
      "connections": 8,
      "is_anomaly": false
    }
  ],
  "system": {
    "cpu_percent": 12.5,
    "ram_percent": 34.2,
    "network_connections": 47
  },
  "file_events": [
    {
      "path": "/tmp/suspicious.sh",
      "event_type": "create",
      "timestamp": "2025-05-28T14:29:58.000000+00:00"
    }
  ]
}

Поле is_anomaly заполняется агентом на основе результата EnsembleDetector.predict() до отправки. Бэкенд дополнительно проверяет пороговые значения.


3. Модуль машинного обучения

Класс

agent/model.pyEnsembleDetector (экспортируется как AnomalyDetector для обратной совместимости)

AnomalyDetector = EnsembleDetector  # алиас в конце model.py

Вектор признаков

FEATURE_COLS = ["cpu_percent", "memory_mb", "open_files", "connections"]
Признак Тип Индикатор компрометации
cpu_percent float, 0100 Криптомайнеры, брутфорс, ransomware
memory_mb float, МБ Heap overflow, утечки памяти
open_files int Шифровальщики (mass file access)
connections int C2-коммуникации, DDoS, сканирование

Предобработка: RobustScaler

x' = \frac{x - \text{median}(X_{\text{train}})}{\text{IQR}(X_{\text{train}})}

Параметры сохраняются в data/model.pkl под ключом "scaler" и применяются при инференсе без повторного обучения.

Алгоритмы ансамбля

IsolationForest(
    n_estimators=100,    # число деревьев изоляции
    contamination=0.01,  # ожидаемая доля аномалий: 1%
    random_state=42,
    n_jobs=-1,           # параллельное обучение на всех ядрах
)

LocalOutlierFactor(
    n_neighbors=20,      # размер локального окружения k
    contamination=0.01,
    novelty=True,        # режим инференса на новых данных (обязательно!)
    n_jobs=-1,
)

OneClassSVM(
    kernel="rbf",        # гауссово ядро (RBF)
    nu=0.01,             # верхняя оценка доли аномалий
    gamma="scale",       # γ = 1 / (n_features * Var(X))
)

Правило голосования

Каждая модель возвращает v_i \in \{+1, -1\}:

  • +1 — нормальный процесс
  • -1 — аномальный
\text{is\_anomaly} = \left[\sum_{i=1}^{3} v_i \leq -1\right]

Сумма -1 означает 2 голоса «аномалия», -3 — единогласно. Один «выброс» от одного алгоритма не даёт алерт.

Доверенные процессы (TRUSTED_PROCESSES)

Для 30+ процессов (браузеры, Electron-приложения, IDE, медиаплееры) ML-классификация пропускается. Полный список:

TRUSTED_PROCESSES = frozenset({
    "firefox", "firefox-bin", "Web Content", "Isolated Web Co",
    "chrome", "chromium", "chromium-browser",
    "brave", "brave-browser", "opera",
    "electron", "Electron",
    "telegram", "Telegram", "telegram-desktop",
    "slack", "discord", "discord-bin",
    "code", "code-oss", "vscodium", "cursor",
    "zoom", "zoom-bin", "zoomus",
    "obsidian",
    "spotify", "vlc", "mpv",
    "libreoffice", "soffice",
    "thunderbird",
    "gnome-shell", "plasmashell", "kwin_wayland", "kwin_x11",
    "Xorg", "Xwayland",
})

Пороговые алерты бэкенда (CPU > 80%, RAM > 3 ГБ, TCP > 100) работают для доверенных процессов.

Обучающие данные

Режим 1 — UNSW-NB15 (если data/UNSW_NB15_training-set.csv присутствует):

df = pd.read_csv(DATASET_PATH)
numeric = df.select_dtypes(include=[np.number]).dropna()
X = numeric.iloc[:, :4].values.astype(np.float32)

Из датасета берутся первые 4 числовые колонки как приближение к вектору признаков.

Режим 2 — Синтетические данные (10 000 образцов, n=10_000):

Признак Сегмент Доля Распределение
CPU Фоновые 75% Exponential(scale=0.3)
CPU Лёгкие 20% Uniform[0.5, 15]
CPU Тяжёлые 5% Uniform[15, 60]
RAM Мелкие 50% Uniform[0.5, 50] МБ
RAM Средние 30% Uniform[50, 300] МБ
RAM Крупные 12% Uniform[300, 800] МБ
RAM Десктоп 8% Uniform[800, 2000] МБ
Файлы Немного 70% randint[0, 20)
Файлы Много 30% randint[20, 80)
TCP Мало 70% randint[0, 2)
TCP Больше 30% randint[2, 20)

Сохранение и загрузка модели

# Сохранение (joblib)
joblib.dump({
    "iforest": self.iforest,
    "lof":     self.lof,
    "ocsvm":   self.ocsvm,
    "scaler":  scaler,
}, MODEL_PATH)   # data/model.pkl

# Загрузка при старте (если pkl существует)
bundle = joblib.load(MODEL_PATH)
# legacy-проверка: если bundle не dict — переобучение

Предварительное обучение (standalone)

cd agent
python train.py
# Done. Model saved to data/model.pkl  (257000 training samples)

4. Серверная часть — API

Стек

Компонент Версия Роль
FastAPI ≥0.111 ASGI-фреймворк, REST + WebSocket
aiosqlite ≥0.20 Асинхронный драйвер SQLite
uvicorn ≥0.29 ASGI-сервер
Pydantic v2 встроен в FastAPI Валидация входных данных

OpenAPI документация: http://localhost:8000/docs

REST-эндпоинты (полный список)

Метод Эндпоинт Параметры Описание
GET /api/stats Агрегированная статистика для панели Overview
GET /api/metrics limit=200 Последние метрики процессов
GET /api/alerts limit=50 Последние алерты (с дедупликацией)
GET /api/file-events limit=100 Последние события файловой системы
GET /api/system-metrics limit=60 Системные CPU/RAM для графика (хронологически)
GET /api/active-anomalies Текущие аномальные процессы (last snapshot per PID)
POST /api/metrics тело: MetricsBatch Приём батча от агента

GET /api/stats — пример ответа

{
  "total_processes": 142,
  "anomalies_today": 3,
  "alerts_today": 1,
  "cpu_avg": 18.4,
  "ram_avg": 52.1
}

cpu_avg и ram_avg — средние по таблице system_metrics за сегодня.

GET /api/system-metrics — пример ответа

[
  {
    "id": 1001,
    "timestamp": "2025-05-28T14:29:55+00:00",
    "cpu_percent": 15.2,
    "ram_percent": 48.7,
    "net_connections": 42
  },
  ...
]

Возвращается в хронологическом порядке (ORDER BY id DESC → reversed) для прямой подачи в Recharts.

GET /api/active-anomalies — запрос к БД

SELECT m.*
FROM metrics m
INNER JOIN (
    SELECT pid, MAX(id) AS max_id FROM metrics GROUP BY pid
) latest ON m.id = latest.max_id
WHERE m.is_anomaly = 1
ORDER BY m.cpu_percent DESC
LIMIT 20

Возвращает последний снимок каждого PID, у которого is_anomaly=1.

POST /api/metrics — логика обработки

  1. Bulk INSERT всех процессов в metrics
  2. INSERT одной строки системных метрик в system_metrics
  3. Bulk INSERT событий ФС в file_events
  4. Пороговая проверка (независимо от ML):
    • cpu_percent > 80 ИЛИ memory_mb > 3000 ИЛИ connections > 100
    • Такие процессы помечаются is_anomaly=True даже если ML не сработал
  5. Дедупликация алертов (30-секундное окно):
    • Если алерт с этим PID+именем был менее 30 секунд назад — UPDATE alerts SET count=count+1
    • Иначе — INSERT alerts + WS broadcast

Severity и Reason

def _severity(proc: ProcessMetric) -> str:
    if proc.cpu_percent > 80 or proc.memory_mb > 2000:
        return "high"
    if proc.cpu_percent > 50 or proc.memory_mb > 1000:
        return "medium"
    return "low"

def _reason(proc: ProcessMetric) -> str:
    parts = []
    if proc.cpu_percent > 80:   parts.append(f"CPU {proc.cpu_percent:.1f}%")
    if proc.memory_mb > 2000:   parts.append(f"ОЗУ {proc.memory_mb:.0f} МБ")
    if proc.connections > 50:   parts.append(f"{proc.connections} TCP-соединений")
    if proc.open_files > 200:   parts.append(f"{proc.open_files} открытых файлов")
    detail = ", ".join(parts) if parts else "статистический выброс (ансамбль ML: IF + LOF + OC-SVM)"
    return f"Аномальное поведение: {detail}"

Если ни одного порогового признака нет — причина описывается как «статистический выброс ансамбля».

WebSocket

GET /ws  →  WebSocket upgrade

Сервер хранит список подключённых клиентов в _WsManager. При обнаружении новой аномалии (прошедшей дедупликацию) рассылается:

{
  "type": "alert",
  "pid": 4567,
  "process_name": "python3",
  "reason": "Аномальное поведение: CPU 95.2%",
  "severity": "high",
  "timestamp": "2025-05-28T14:30:05.123456+00:00"
}

Дедуплицированные алерты (инкремент count) не рассылаются по WS — клиент получает уведомление только при первом обнаружении.

Клиенты, разорвавшие соединение, автоматически удаляются из списка при следующей широковещательной рассылке.

Pydantic-схемы

class ProcessMetric(BaseModel):
    pid: int
    name: str
    cpu_percent: float
    memory_mb: float
    open_files: int
    connections: int
    is_anomaly: bool = False

class SystemMetric(BaseModel):
    cpu_percent: float
    ram_percent: float
    network_connections: int

class FileEvent(BaseModel):
    path: str
    event_type: str
    timestamp: str

class MetricsBatch(BaseModel):
    processes: list[ProcessMetric]
    system: SystemMetric
    file_events: list[FileEvent] = []
    timestamp: str

5. База данных

SQLite, файл: data/edr.db. Четыре таблицы.

metrics

CREATE TABLE metrics (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp   TEXT    NOT NULL,   -- ISO 8601 UTC
    pid         INTEGER,
    name        TEXT,
    cpu_percent REAL,               -- нормализовано на _CPU_COUNT
    memory_mb   REAL,               -- RSS в МБ
    open_files  INTEGER,
    connections INTEGER,
    is_anomaly  INTEGER DEFAULT 0   -- 0=норма, 1=аномалия (ML или порог)
);

alerts

CREATE TABLE alerts (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp    TEXT NOT NULL,     -- время первого обнаружения
    pid          INTEGER,
    process_name TEXT,
    reason       TEXT,              -- человекочитаемое описание
    severity     TEXT,              -- low | medium | high
    last_seen    TEXT,              -- время последнего повтора (для дедупликации)
    count        INTEGER DEFAULT 1  -- сколько раз алерт повторился
);

Дедупликация: при поступлении нового батча бэкенд ищет алерт с тем же PID и именем, у которого last_seen >= NOW() - 30s. Если найден — инкрементирует count и обновляет last_seen. Иначе создаёт новую запись.

file_events

CREATE TABLE file_events (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp  TEXT NOT NULL,
    path       TEXT,
    event_type TEXT    -- create | modify | delete
);

system_metrics

CREATE TABLE system_metrics (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp       TEXT NOT NULL,
    cpu_percent     REAL,           -- система в целом (psutil.cpu_percent)
    ram_percent     REAL,           -- psutil.virtual_memory().percent
    net_connections INTEGER         -- psutil.net_connections() count
);

Одна строка на батч (каждые 5 секунд). Используется для графика нагрузки в реальном времени и для расчёта cpu_avg/ram_avg в /api/stats.


6. Веб-интерфейс

Стек

Технология Версия Назначение
React 18.3 Декларативный UI-фреймворк
TypeScript 5.4 Статическая типизация
Vite 5.3 Сборщик, HMR
Tailwind CSS 3.4 Utility-first стили
Radix UI / shadcn/ui 1.x Headless-компоненты (доступность)
Recharts 2.12 Графики (обёртка над D3.js)
TanStack Query 5.x Серверный стейт, кэш, polling
Lucide React последняя Иконки

Структура компонентов

frontend/src/
├── App.tsx                 — корневой компонент, Tab-навигация
├── components/
│   ├── Overview.tsx        — вкладка «Обзор»
│   ├── Processes.tsx       — вкладка «Процессы»
│   ├── Alerts.tsx          — вкладка «Алерты» + WS
│   ├── FileEvents.tsx      — вкладка «События ФС»
│   ├── SystemChart.tsx     — Recharts LineChart (CPU + RAM)
│   └── ui/                 — shadcn/ui компоненты
├── hooks/
│   └── use-toast.ts        — Toast-уведомления
└── lib/
    └── api.ts              — типы и fetcher-функции

Вкладка Overview (Обзор)

Компонент: components/Overview.tsx

Карточки-счётчики (StatCard × 5)

Заголовок Источник Цвет акцента
Всего процессов stats.total_processes #00d4ff (cyan)
Аномалий сегодня stats.anomalies_today #ff6b35 (orange)
Алертов сегодня stats.alerts_today #ff3366 (red)
Средний CPU stats.cpu_avg % #00ff9d (green)
Средний RAM stats.ram_avg % #a78bfa (violet)

Панель «Активные угрозы» (ActiveAnomaliesList)

Источник данных: GET /api/active-anomalies (polling 5 сек)

Отображает список процессов с is_anomaly=1 в текущем снимке. Для каждого показывает:

  • Имя процесса + PID
  • CPU%, RAM МБ, TCP-соединения (цветные метки)
  • Пульсирующая красная точка-индикатор

При отсутствии аномалий показывает зелёный чекмарк.

График системной нагрузки (SystemChart)

Источник: GET /api/system-metrics?limit=60 (polling 5 сек)

Recharts LineChart с двумя линиями — CPU% и RAM%. Ось X — время (HH:MM:SS). Подписи и тултипы в стиле JetBrains Mono.

Располагается на 2/3 ширины рядом с «Активными угрозами».

Диаграмма серьёзности (SeverityDonut)

Источник: GET /api/alerts?limit=200 (polling 10 сек)

Recharts PieChart (кольцо, innerRadius=48, outerRadius=72). Три сегмента:

Сегмент Цвет
Высокий #ff3366
Средний #ff6b35
Низкий #00d4ff

Топ процессов по алертам (TopProcessesBar)

Источник: те же данные алертов, что и SeverityDonut.

Recharts BarChart (горизонтальный, layout="vertical"). Агрегирует count алертов по имени процесса, показывает топ-6 по убыванию. Цвет баров: #ff6b35.

Вкладка Processes (Процессы)

Компонент: components/Processes.tsx

Источник: GET /api/metrics?limit=200 (polling 5 сек)

Таблица «последний снимок» для каждого уникального PID (MAX(id) per PID), отсортированная по убыванию CPU%.

Колонка Значение
PID числовой идентификатор
Имя имя исполняемого файла
CPU% нормализованная нагрузка
RAM МБ RSS-память
TCP число соединений
Файлы число дескрипторов
Статус зелёный «Normal» / красный «Anomaly»

Строки с аномалиями: красная левая рамка (border-l-2 border-red-500), красный фон.

Вкладка Alerts (Алерты)

Компонент: components/Alerts.tsx

Источник: GET /api/alerts?limit=50 (polling 5 сек) + WebSocket /ws

Таблица алертов. Колонки: Время, PID, Процесс, Описание, Серьёзность, Счётчик.

Индикатор WS-подключения (зелёная/красная точка).

При получении WS-сообщения:

  1. Принудительная инвалидация кэша alerts и stats через queryClient.invalidateQueries
  2. Показ Toast-уведомления в правом нижнем углу

Severity-бейджи:

Уровень Цвет
high красный
medium оранжевый
low синий

Вкладка File Events (События ФС)

Компонент: components/FileEvents.tsx

Источник: GET /api/file-events?limit=100 (polling 5 сек)

Таблица: Время, Путь, Тип события. Цветные бейджи типов:

Тип Цвет
create зелёный
modify жёлтый
delete красный

Дизайн-система

Фон:             #0a0a0f  (почти чёрный)
Поверхность:     #111118
Акцент зелёный:  #00ff9d  (cyber green)
Акцент синий:    #00d4ff  (cyan)
Акцент красный:  #ff3366
Шрифт:           JetBrains Mono (monospace)
Стиль:           SOC-dashboard, тёмный, декоративная сетка

TypeScript-типы (frontend/src/lib/api.ts)

interface ProcessMetric {
  id: number
  timestamp: string
  pid: number
  name: string
  cpu_percent: number
  memory_mb: number
  open_files: number
  connections: number
  is_anomaly: number   // 0 | 1 (SQLite INTEGER, не boolean)
}

interface Alert {
  id: number
  timestamp: string
  last_seen: string | null
  count: number
  pid: number
  process_name: string
  reason: string
  severity: 'low' | 'medium' | 'high'
}

interface SystemMetricRow {
  id: number
  timestamp: string
  cpu_percent: number
  ram_percent: number
  net_connections: number
}

Polling-интервалы

Данные Интервал Эндпоинт
Stats 5 сек /api/stats
System metrics 5 сек /api/system-metrics
Active anomalies 5 сек /api/active-anomalies
Processes 5 сек /api/metrics
Alerts (polling) 5 сек /api/alerts
Alerts (push) мгновенно WebSocket /ws
File events 5 сек /api/file-events

7. Развёртывание (Docker)

docker-compose.yml

services:
  backend:
    build: ./backend
    volumes:
      - ./data:/app/data     # SQLite + model.pkl
    ports:
      - "8000:8000"
    environment:
      - DB_PATH=/app/data/edr.db
    healthcheck:
      test: python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/stats')"
      interval: 10s
      timeout: 5s
      retries: 10

  agent:
    build: ./agent
    volumes:
      - ./data:/app/data     # общий том с backend
    environment:
      - BACKEND_URL=http://backend:8000
      - COLLECT_INTERVAL=5
      - MODEL_PATH=/app/data/model.pkl
      - DATASET_PATH=/app/data/UNSW_NB15_training-set.csv
    depends_on:
      backend:
        condition: service_healthy
    pid: host          # видит PID-пространство хоста
    privileged: true   # требуется для psutil.net_connections()

  frontend:
    build: ./frontend  # multi-stage: node build → nginx
    ports:
      - "3000:3000"
    depends_on:
      - backend

Ключевые особенности агентского контейнера:

  • pid: host — агент видит реальные процессы хоста (без этого будут только контейнерные PID)
  • privileged: truepsutil.net_connections() требует root для чтения /proc/net/tcp
  • Зависимость от service_healthy бэкенда — агент стартует только после того, как /api/stats возвращает 200

Запуск

# Стандартный запуск
docker compose up --build

# С датасетом UNSW-NB15
cp /path/to/UNSW_NB15_training-set.csv data/
docker compose up --build

# Без Docker (разработка)
cd backend && uvicorn main:app --reload --port 8000
cd agent && python main.py
cd frontend && pnpm dev

8. Конфигурация и переменные окружения

Агент (agent/main.py)

Переменная По умолчанию Описание
BACKEND_URL http://localhost:8000 Адрес бэкенда
COLLECT_INTERVAL 5 Интервал сбора метрик (секунды)

Модель (agent/model.py)

Переменная По умолчанию Описание
MODEL_PATH data/model.pkl Путь для сохранения/загрузки ансамбля
DATASET_PATH data/UNSW_NB15_training-set.csv Путь к датасету для обучения

Бэкенд (backend/main.py)

Переменная По умолчанию Описание
DB_PATH ../data/edr.db Путь к SQLite-базе данных

9. Расхождения с дипломным документом

В файле diploma_practical_section.md обнаружены следующие расхождения с реальной кодовой базой:

9.1 Таблица Overview устарела

В документе:

«Пять карточек-счётчиков» + «График реального времени»

В коде (после коммита 4253552):

  • ActiveAnomaliesList — живой список текущих аномальных процессов
  • SeverityDonut — кольцевая диаграмма распределения алертов по серьёзности
  • TopProcessesBar — горизонтальный bar-chart топ-6 процессов по числу алертов

Все три компонента опрашивают /api/active-anomalies и /api/alerts.

9.2 Отсутствует таблица system_metrics

Документ описывает 3 таблицы. Реально их 4: metrics, alerts, file_events, system_metrics.

9.3 Отсутствуют колонки alerts.last_seen и alerts.count

Документ не упоминает дедупликацию алертов. Реально таблица alerts содержит last_seen TEXT и count INTEGER DEFAULT 1. Дедупликационное окно — 30 секунд.

9.4 Отсутствуют два API-эндпоинта

Документ описывает 5 эндпоинтов. Реально их 7:

  • GET /api/system-metrics — данные для графика нагрузки
  • GET /api/active-anomalies — текущие аномальные процессы

9.5 Не описана нормализация CPU

Агент делит cpu_percent на psutil.cpu_count(logical=True). Без этого на многоядерных хостах значения CPU > 100% приводят к ложным алертам.

9.6 WebSocket срабатывает только на новые алерты

Документ: «при поступлении батча с аномальными процессами бэкенд транслирует каждому клиенту JSON».

Реально: WS-сообщение отправляется только при INSERT alerts (первое обнаружение). Повторные срабатывания в течение 30 секунд дают только UPDATE count без WS-рассылки.

9.7 Порог для включения RAM в reason

Документ: RAM > 2 ГБseverity=high.
Реально _severity() проверяет memory_mb > 2000 для "high", но _reason() добавляет строку ОЗУ тоже при memory_mb > 2000. Пороговый алерт (без ML) срабатывает при memory_mb > 3000. Документ эту разницу не разграничивает.


Справочник отражает состояние кодовой базы на коммит 4253552 (ветка master).