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)
Просмотр зарегистрированных модулей¶
{
"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 при старте.
Модули фронта требуют явного импорта (автообнаружения на стороне
браузера нет).