# Технический справочник системы EDR ## Endpoint Detection and Response — подробная документация реализации --- ## Содержание 1. [Архитектура системы](#1-архитектура-системы) 2. [Агент мониторинга](#2-агент-мониторинга) 3. [Модуль машинного обучения](#3-модуль-машинного-обучения) 4. [Серверная часть — API](#4-серверная-часть--api) 5. [База данных](#5-база-данных) 6. [Веб-интерфейс](#6-веб-интерфейс) 7. [Развёртывание (Docker)](#7-развёртывание-docker) 8. [Конфигурация и переменные окружения](#8-конфигурация-и-переменные-окружения) 9. [Расхождения с дипломным документом](#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` | Образ агента | ### Сбор метрик процессов ```python # Атрибуты, запрашиваемые через 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()`. Агент обходит это через: ```python 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 — библиотека требует временного интервала для расчёта дельты. При старте выполняется: ```python 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) ```json { "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.py` → `EnsembleDetector` (экспортируется как `AnomalyDetector` для обратной совместимости) ```python AnomalyDetector = EnsembleDetector # алиас в конце model.py ``` ### Вектор признаков ```python FEATURE_COLS = ["cpu_percent", "memory_mb", "open_files", "connections"] ``` | Признак | Тип | Индикатор компрометации | |---------|-----|------------------------| | `cpu_percent` | float, 0–100 | Криптомайнеры, брутфорс, 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"` и применяются при инференсе без повторного обучения. ### Алгоритмы ансамбля ```python 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-классификация пропускается. Полный список: ```python 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` присутствует): ```python 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) | ### Сохранение и загрузка модели ```python # Сохранение (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) ```bash 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 — пример ответа ```json { "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 — пример ответа ```json [ { "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 — запрос к БД ```sql 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 ```python 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`. При обнаружении **новой** аномалии (прошедшей дедупликацию) рассылается: ```json { "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-схемы ```python 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 ```sql 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 ```sql 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 ```sql CREATE TABLE file_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, path TEXT, event_type TEXT -- create | modify | delete ); ``` ### system_metrics ```sql 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) ```typescript 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 ```yaml 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: true` — `psutil.net_connections()` требует root для чтения `/proc/net/tcp` - Зависимость от `service_healthy` бэкенда — агент стартует только после того, как `/api/stats` возвращает 200 ### Запуск ```bash # Стандартный запуск 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).*