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

Umbryx — Разработка модулей

Обзор

Umbryx поддерживает drop-in модули бэкенда и фронта. Положите Python-файл в backend/app/plugins/modules/ и JS-модуль, вызывающий moduleRegistry.register() на стороне фронта. Модули бэкенда обнаруживаются автоматически при старте; модули фронта должны импортироваться бандлом фронта.


Модуль бэкенда

Минимальный пример

Создайте backend/app/plugins/modules/my_plugin.py:

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.plugins.types import BackendModule
from app.core.deps import get_current_user
from app.database import get_db

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

@router.get("/hello")
def hello(user=Depends(get_current_user)):
    return {"hello": user.username}

MODULE = BackendModule(
    name="my_plugin",
    version="1.0.0",
    description="My custom plugin",
    router=router,
)

Загрузчик сам находит MODULE и регистрирует его роутер в приложении.

Пример парсера сканов

Чтобы добавить новый формат (например, Masscan JSON):

import json

def parse_masscan_json(content: str) -> list[dict]:
    data = json.loads(content)
    hosts = []
    for entry in data:
        hosts.append({
            "ip": entry["ip"],
            "hostname": "",
            "os": "Unknown",
            "ports": [
                {"port": p["port"], "proto": p["proto"], "state": "open", "service": p.get("reason", "")}
                for p in entry.get("ports", [])
            ],
        })
    return hosts

MODULE = BackendModule(
    name="masscan_parser",
    version="1.0.0",
    description="Masscan JSON scan parser",
    scan_parsers={"masscan": parse_masscan_json},
)

Зарегистрированные scan_parsers видны через GET /api/modules (пригодятся фронтовым тулзам и импортёрам). Учтите: эндпоинт предпросмотра топологии сейчас принимает nmap как source_type — модуль, чей парсер должен работать во встроенном построителе топологии, должен дополнительно интегрироваться с пакетом роутеров топологии.

Контракт BackendModule

@dataclass
class BackendModule:
    name: str
    version: str
    description: str = ""
    enabled: bool = True
    router: APIRouter | None = None
    scan_parsers: dict[str, Callable] = field(default_factory=dict)
    export_contributors: list[Callable] = field(default_factory=list)
    report_contributors: list[Callable] = field(default_factory=list)
    search_providers: list[Callable] = field(default_factory=list)
    startup_hooks: list[Callable] = field(default_factory=list)
    shutdown_hooks: list[Callable] = field(default_factory=list)

Просмотр зарегистрированных модулей

GET /api/modules
{
  "modules": [
    {
      "name": "my_plugin",
      "version": "1.0.0",
      "description": "My custom plugin",
      "enabled": true,
      "has_router": true,
      "scan_parsers": []
    }
  ]
}

Модуль фронта

Минимальный пример — пункт сайдбара и таб хоста

import { moduleRegistry } from './features/plugins/registry.js';
import MyHostTab from './components/MyHostTab.jsx';

moduleRegistry.register({
  id: 'my-plugin',
  title: 'My Plugin',
  version: '1.0.0',
  description: 'Adds a custom host tab',
  enabled: true,

  menuItems: [{
    id: 'my-menu',
    label: 'My Plugin',
    icon: 'target',
    tab: 'my-plugin',
  }],

  hostTabs: [{
    id: 'my-tab',
    label: 'My Tab',
    component: MyHostTab,
  }],
});

Все точки расширения

Поле Тип Где появляется
menuItems [{ id, label, icon, tab }] Навигация сайдбара
routes [{ path, component }] Маршруты React Router
projectTabs [{ id, label, component }] Табы на странице проекта
hostTabs [{ id, label, component }] Табы в карточке хоста
networkTabs [{ id, label, component }] Табы в панели узла сети
reportSections [{ id, label, component }] Доп. секции при генерации отчёта
importers [{ id, label, accept, component }] Пункты UI импорта
dashboardWidgets [{ id, component }] Слоты виджетов дашборда
actions.hosts [{ id, label, handler }] Контекстные действия на строках хостов
actions.findings [{ id, label, handler }] Контекстные действия на находках
actions.creds [{ id, label, handler }] Контекстные действия на учётных данных
actions.networkNodes [{ id, label, handler }] Контекстные действия на узлах топологии

Чтение агрегированных точек расширения

import { moduleRegistry } from './features/plugins/registry.js';

const hostTabs   = moduleRegistry.getHostTabs();
const menuItems  = moduleRegistry.getMenuItems();
const importers  = moduleRegistry.getImporters();

Размещение файлов

backend/app/plugins/modules/   ← кладите .py сюда (автозагрузка)
frontend/src/features/plugins/ ← реестр + хелперы точек расширения
frontend/src/...               ← импортируйте файл модуля из активного бандла фронта

Модули бэкенда обнаруживаются через pkgutil.iter_modules при старте. Модули фронта требуют явного импорта (автообнаружения на стороне браузера нет).