Перейти к содержанию

Гайд разработчика модулей Umbryx

Этот гайд описывает создание модулей расширений для платформы Umbryx.

Быстрый старт

Создайте каталог в /modules/ с manifest.yaml:

modules/
  my-module/
    manifest.yaml
    backend/
      routes.py      # Опционально: кастомные API-маршруты
    README.md

Модуль обнаруживается автоматически при старте бэкенда. Регистрационный код не нужен.

Справочник по манифесту

manifest.yaml — единственный источник истины о возможностях модуля.

Обязательные поля

id: my-module            # lowercase, дефисы, [a-z0-9-]{2,63}
name: My Module          # отображаемое имя
version: 1.0.0           # семвер

Метаданные

description: >
  Что делает этот модуль.
author: Your Name
license: MIT
icon: server             # имя иконки из компонента Icon

Зависимости

depends_on:

  - topology             # id других обязательных модулей
conflicts_with:

  - old-module           # несовместимые модули

Права

Объявите, к каким данным модулю нужен доступ:

permissions:

  - read:hosts           # чтение хостов
  - write:findings       # создание/обновление находок
  - exec:jobs            # исполнение задач
  - write:object         # запись в универсальную объектную модель

Валидные режимы: read, write, exec Валидные scope: hosts, creds, findings, scopes, loot, jobs, notes, networks, tasks, object, settings

Типы объектов

Типизированные объекты в универсальной таблице core_objects:

objects:

  - type: vulnerability      # уникальный идентификатор типа
    name: Vulnerability      # отображаемое имя
    icon: bug
    list_columns: [title, severity, status]   # колонки авто-списка
    fields:
      - name: title
        type: string
        label: Title
        required: true
      - name: severity
        type: enum
        label: Severity
        options: [low, medium, high, critical]
        default: medium
      - name: cvss_score
        type: float
        label: CVSS Score
      - name: description
        type: text
        label: Description
      - name: is_exploitable
        type: boolean
        label: Exploitable
        default: false
      - name: remediation
        type: text
        label: Remediation
      - name: related_finding
        type: reference
        label: Related Finding
        reference_type: finding     # ссылка на другой тип объектов
      - name: metadata
        type: json
        label: Metadata

Типы полей: string, text, integer, float, boolean, enum, datetime, json, reference

Объекты доступны через универсальный CRUD API:

Метод Эндпоинт Описание
GET /api/modules/objects?type=vulnerability Список объектов
POST /api/modules/objects Создать объект
GET /api/modules/objects/{id} Получить объект
PATCH /api/modules/objects/{id} Обновить объект
DELETE /api/modules/objects/{id} Удалить объект

Точки расширения

Регистрация UI-расширений:

extensions:
  sidebar.nav:
    - id: my-view
      label: My View
      icon: bolt
      view: myView

  project.tab:
    - id: project-items
      label: Items
      icon: list

  dashboard.widget:
    - id: stats-widget
      label: Statistics
      component: StatsWidget

  host.tab:
    - id: host-details
      label: My Tab

  settings.section:
    - id: my-settings
      label: Module Settings

  report.section:
    - id: my-report
      label: Custom Section

  actions.host:
    - id: scan-host
      label: Scan
      icon: target

  importer:
    - id: csv-import
      label: Import CSV
      accept: .csv

  search.provider:
    - id: search-items
      label: Search Items

Известные точки расширения: sidebar.nav, project.tab, host.tab, network.tab, dashboard.widget, settings.section, report.section, actions.host, actions.finding, actions.cred, actions.object, importer, search.provider

Размещение в навигации (обязательно для sidebar.nav)

Сайдбар использует двухуровневую иерархию семантических групп. Группы определены централизованно в frontend/src/constants.js::TAB_GROUPS — сверяйтесь с этим файлом как авторитетным списком:

Группа Назначение Текущие секции
(Overview) Выбор проекта и дашборд Projects, Overview
Recon Разведка и сканирование Hosts, Scope, ROE, Watch, Network, Attack Path, Attack Graph
Exploit Evidence и доступ Creds, Findings, Loot, Notes
Automate Автоматизация и исполнение Jobs, Playbooks, Automations, Cheatsheet
Infra Управление инфраструктурой Platform Ops, Workstations, Connections
Output Отчётность и трекинг Access, Objectives, Checklist, KB, Timeline, Report, Purple

Правила:

  1. Встраивание лучше новых пунктов навигации. Прежде чем добавить sidebar.nav, подумайте, нельзя ли выразить функциональность как host.tab, dashboard.widget, actions.host или inline-кнопку в существующей секции. Кнопка на карточке нужного объекта почти всегда лучше новой страницы верхнего уровня.

  2. Соответствуйте ментальной модели пользователя. Выбирайте группу, где пользователь будет искать функцию; пункт без подходящей зарегистрированной группы не отрисуется.

  3. Новые группы требуют обоснования. Новая группа верхнего уровня — существенное UX-изменение. Допустимо, когда:

  4. модуль вводит принципиально новую фазу workflow (не покрытую существующими группами)
  5. есть 3+ связанные секции, не помещающиеся ни в одну группу
  6. лид модуля + UX-ревьюер одобрили

  7. Один пункт на модуль. Модуль даёт максимум один sidebar.nav; остальное — через другие точки (host.tab, dashboard.widget и т.д.).

  8. Предпочитайте контекстную интеграцию. Для AI/ML-функций: добавляйте кнопки «Ask AI» через actions.* на карточках объектов, а не отдельную AI-секцию. Отдельная секция оправдана только для полноценного workspace (интерфейс чата, оркестрация агентов и т.п.).

Визуальная согласованность

Весь UI модулей обязан использовать общий глоссарий цветов и статусов из src/constants.js:

Импорт Назначение
NODE_STATUS Цвета статусов хостов (unknown/alive/scanned/access/pwned/owned)
SEVERITY Цвета severity находок (critical/high/medium/low/info)
FINDING_STATUS Цвета жизненного цикла находок
OBJECTIVE_STATUS Цвета статусов objectives
ACTIVITY_TYPES / ACTIVITY_STATUS Из utils/hosts.js — цвета журнала активностей

Никогда не хардкодьте hex-значения. Импортируйте из constants.js или utils/hosts.js. Нужен новый уровень статуса/severity — сначала добавьте в канонический источник, потом импортируйте.

Имена иконок должны совпадать с ключами в src/components/Icon.jsx. Указывайте icon: в манифесте для пунктов навигации и типов объектов.

Процесс ревью навигации

Структуру сайдбара (TAB_GROUPS в src/constants.js) ревьюят:

  • после каждых 3 новых модулей
  • или раз в квартал (что раньше)

Чек-лист ревью: 1. В какой-то группе больше 5 пунктов? Рассмотрите разделение. 2. Есть пункты, которые пользователи не находят? Смотрите тикеты/отзывы. 3. Есть группы из 1 пункта? Рассмотрите слияние со смежной группой. 4. Появилась новая фаза workflow, не попадающая ни в одну группу?

Настройки

Объявление конфигурируемых настроек:

settings:

  - key: max_items
    label: Maximum Items
    type: integer
    default: 100
    description: Maximum number of items per project
  - key: api_key
    label: API Key
    type: string
    placeholder: "Enter your API key"
  - key: enabled_features
    label: Features
    type: enum
    options: [basic, advanced, enterprise]
    default: basic

Роуты бэкенда

Кастомные API-эндпоинты:

route_prefix: /api/ext/my-module
backend_entry: routes    # резолвится в backend/routes.py

Создайте backend/routes.py:

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.core_object import CoreObject

router = APIRouter(prefix="/api/ext/my-module", tags=["my-module"])

@router.get("/custom-endpoint")
def my_endpoint(db: Session = Depends(get_db)):
    return {"status": "ok"}

# Загрузчик ищет этот атрибут
FASTAPI_ROUTER = router

Роутер монтируется в приложение FastAPI автоматически при старте.

Универсальная объектная модель

Таблица core_objects даёт schema-driven хранилище без кастомных таблиц:

from app.models.core_object import CoreObject
from app.core.modules import obj_type_registry

# Валидация данных по схеме
errors = obj_type_registry.validate_data("vulnerability", data)

# Создание объекта
obj = CoreObject(
    id="vuln_001",
    type="vulnerability",
    module_id="my-module",
    name=data.get("title"),
    data=data,
)
db.add(obj)
db.commit()

Связи

Объекты могут иметь типизированные связи:

# Добавить связь
obj.relations["affects"] = [{"id": "host_123", "weight": "high"}]
db.commit()

# Запрос через API
GET /api/modules/objects/{id}/relations/affects

Интеграция фронта

Компонент ExtensionPoint

Рендер расширений, зарегистрированных модулями:

import ExtensionPoint from '../modules/ExtensionPoint.jsx';

// Все виджеты дашборда из модулей
<ExtensionPoint point="dashboard.widget" projectId={pid} />

ExtensionPoint находит зарегистрированные записи точки расширения и рендерит компонент каждого модуля, обёрнутый в error boundary.

Error boundaries

Компоненты модулей автоматически оборачиваются в error boundary компонентом ExtensionPoint. Чтобы изолировать вручную:

import ModuleErrorBoundary from '../modules/ModuleErrorBoundary.jsx';

<ModuleErrorBoundary moduleName="my-module">
  <MyComponent />
</ModuleErrorBoundary>

Справочник API

Управление модулями

Метод Эндпоинт Auth Описание
GET /api/modules любой Список всех модулей
POST /api/modules/{id}/enable admin Включить модуль
POST /api/modules/{id}/disable admin Выключить модуль
GET /api/modules/{id}/health любой Health-check

Типы объектов

Метод Эндпоинт Описание
GET /api/modules/object-types Все схемы типов
GET /api/modules/object-types/{type} Схема конкретного типа

Расширения

Метод Эндпоинт Описание
GET /api/modules/extensions Все записи расширений
GET /api/modules/extensions/{point} Записи конкретной точки

CRUD объектов

Метод Эндпоинт Описание
GET /api/modules/objects Список (фильтры type, project_id, status)
POST /api/modules/objects Создать
GET /api/modules/objects/{id} Получить
PATCH /api/modules/objects/{id} Обновить
DELETE /api/modules/objects/{id} Удалить
POST /api/modules/objects/{id}/relations Добавить связь
GET /api/modules/objects/{id}/relations/{type} Получить связи

Граница core и расширений

Core (не извлекается)

  • Аутентификация и авторизация (auth, core/deps, core/security)
  • Управление пользователями и организациями (users, organizations, projects)
  • БД и сессии (database.py, alembic)
  • Система событий (core/events, ws)
  • Настройки и конфигурация (core/config, core/enums)
  • Логирование (core/logging_setup)
  • Обработка ошибок (core/errors)
  • Сам фреймворк модулей (core/modules)

Кандидаты на извлечение

  • Provisioning инфраструктуры (core/infra/*)
  • AI-ассистент (routers/ai)
  • Граф атак и топология (routers/attack_graph*, routers/topology)
  • Управление DNS (routers/dns)
  • Импортёры сканов (routers/import_scanners)
  • Интеграция C2 (routers/c2)
  • Плейбуки (routers/playbooks)
  • Отчёты (routers/report)
  • Маркетплейс (routers/marketplace)

Флоу обнаружения модулей

  1. Бэкенд сканирует /modules/*/manifest.yaml при старте
  2. Каждый манифест парсится и валидируется
  3. Проверяются зависимости и конфликты
  4. Типы объектов регистрируются в obj_type_registry
  5. Точки расширения регистрируются в ext_registry
  6. Импортируются entry points бэкенда (монтируется FASTAPI_ROUTER)
  7. Фронт при аутентификации запрашивает /api/modules/extensions
  8. Компоненты ExtensionPoint рендерят зарегистрированные расширения

Жизненный цикл

Модули включаются/выключаются на лету через админ-API:

# Выключить
curl -X POST -b "rt_auth=TOKEN" http://localhost:3000/api/modules/my-module/disable

# Включить
curl -X POST -b "rt_auth=TOKEN" http://localhost:3000/api/modules/my-module/enable

# Health-check
curl -b "rt_auth=TOKEN" http://localhost:3000/api/modules/my-module/health

Выключение разрегистрирует все типы объектов и записи расширений модуля. Существующие данные в core_objects сохраняются.

Реальные примеры миграции

Паттерн 1: модуль только с настройками (AI Assistant)

Для возможностей, у которых код уже есть и нужна лишь формальная регистрация:

# modules/ai-assistant/manifest.yaml
id: ai-assistant
name: AI Assistant
version: 1.0.0
description: AI-powered security assistant with multi-provider LLM support.
permissions:

  - read:hosts
  - read:findings
extensions:
  settings.section:
    - id: ai-providers
      label: AI Providers
      component: AISection
settings:

  - key: provider
    label: Default Provider
    type: enum
    options: [openai, anthropic, ollama]
    default: anthropic

Что произошло: код бэкенда (routers/ai.py, core/ai_manager.py) уже был подключён в main.py. Манифест декларирует только метаданные, права, настройки и точки расширения. Код не двигался. Модуль виден в System → Modules, его настройки обнаруживаемы.

Паттерн 2: декларация типа объекта (DNS Management)

Для возможностей со структурированными данными под универсальную модель:

# modules/dns-management/manifest.yaml
objects:

  - type: dns_record
    name: DNS Record
    list_columns: [hostname, record_type, value, provider]
    fields:
      - name: hostname
        type: string
        required: true
      - name: record_type
        type: enum
        options: [A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR]
        default: A
      - name: value
        type: string
        required: true
      - name: provider
        type: enum
        options: [manual, cloudflare]
        default: manual

Что произошло: у DNS-записей была кастомная таблица (dns_records). Манифест декларирует тип dns_record. Новые записи создаются через универсальный CRUD API (POST /api/modules/objects) и рендерятся через GenericObjectView. Старая таблица остаётся для совместимости на время миграции.

Паттерн 3: подключение точки расширения (ROE)

Для возможностей, дающих проектный таб:

# modules/roe/manifest.yaml
id: roe
name: Rules of Engagement
version: 1.0.0
extensions:
  project.tab:
    - id: roe
      label: ROE
      icon: scroll

Что произошло: у ROE уже были роутер бэкенда и вью фронта (RoeView.jsx). Манифест декларирует точку project.tab, чтобы система модулей знала: модуль даёт таб уровня проекта. Подключение таба в AppTabRouter.jsx на переходный период не менялось.

Чек-лист миграции

При переводе существующей возможности в модуль:

  • [ ] Создать modules/<name>/manifest.yaml с метаданными, правами, объектами, расширениями, настройками
  • [ ] Проверить появление модуля в панели System → Modules
  • [ ] Проверить типы объектов в GET /api/modules/object-types
  • [ ] Проверить точки расширения в GET /api/modules/extensions
  • [ ] Прогнать валидатор: python3 -c "from app.core.modules import *; from app.core.modules.manifest import Manifest; m = Manifest.from_file(Path('modules/<name>/manifest.yaml')); from app.core.modules.validator import validate_module; print(validate_module(m))"
  • [ ] При кастомных API-маршрутах создать backend/routes.py с FASTAPI_ROUTER
  • [ ] Протестировать жизненный цикл: POST /api/modules/<name>/disable, затем /enable
  • [ ] Дополнить этот гайд использованным паттерном миграции