Плейбуки и автоматизации с нуля
Маршрут: /playbooks (редактор /playbooks/:playbookId, новый
/playbooks/new); автоматизации /automations · Права: playbooks.manage
(авторить, редактировать, импортировать, версионировать, удалять плейбук),
playbooks.execute (запуск / возобновление / отмена), automations.create ·
automations.edit (авторить скрипты) · Модуль: cases
Почему это важно. Плейбук — это где реагирование перестаёт быть чек-листом, который аналитик прогоняет вручную, и становится повторяемым графом, который платформа гоняет одинаково каждый раз. Эта глава — руководство по авторингу: как движок выполняет граф, как данные текут от одной задачи к следующей, как писать скрипты, которые становятся задачами, и как протестировать всё это до того, как оно коснётся живого кейса. Руководство аналитика (§6) показывает, как запускать готовый плейбук; здесь вы его собираете.
Эта глава предполагает, что вы прочитали главы 3–4 (классификаторы и мапперы) — плейбук читает форму инцидента, которую произвёл маппер, так что эти два проектируются вместе.
8.1. Ключевые концепции
Плейбук — это направленный ацикличный граф (DAG) из задач (узлов), соединённых рёбрами. Когда плейбук запускается, кастомный движок на основе BullMQ — движок PlaybookV2 — идёт по графу задача за задачей на выделенном playbook-воркере. Под ним нет стороннего workflow-продукта; движок, язык выражений и каждый тип задачи, описанный здесь, — собственные у SOARForge.
Подсистему составляют две поверхности авторинга, и их важно держать порознь:
| Поверхность | Что это | Где вы её собираете |
|---|---|---|
| Playbook | Граф оркестрации — последовательность, ветки, согласования и циклы. | Редактор плейбуков (/playbooks) |
| Automation | Переиспользуемый скрипт (Python или JavaScript), который может вызвать задача плейбука или запустить обогащение. | Страница Automations (/automations) |
Задача плейбука сама по себе никогда не содержит бизнес-логики сверх управления потоком: собственно работа — вызов интеграции, преобразование данных, вынесение вердикта — происходит в задаче Action (команда коннектора или хранимая автоматизация) или задаче Script (inline-код). Всё остальное — условия, циклы, согласования — это движок, рулящий между этими рабочими задачами.
Модель выполнения в одном абзаце. Когда плейбук запускается, движок создаёт
запись PlaybookExecution, загружает данные инцидента кейса в контекст выполнения и
ставит в очередь узел Start. Каждая задача выполняется в собственной задаче очереди;
вывод задачи записывается, сливается в контекст, и движок ставит в очередь
преемников задачи. Задачи взаимодействия с человеком (approval, ask, data
collection, human task, sub-playbook) приостанавливают выполнение — задача
заканчивается, а более поздняя задача возобновления подхватывает её. Выполнение
заканчивается completed, failed, cancelled или (пока ожидает) waiting_input.
Почему это важно. Поскольку каждая задача — отдельная задача очереди, плейбук, который час ждёт согласования, не держит поток открытым — он по-настоящему запаркован. Вот почему таймауты, повторы и «continue on error» — настройки на задачу, а не один глобальный переключатель.
8.2. Как запускается плейбук
Каждый плейбук несёт Trigger Type — manual, incident_created или
scheduled. Трактуйте это поле как классификацию и фильтр списка, которое
документирует, как плейбук должен запускаться; само по себе оно не подключает
плейбук к событию. (Четвёртое значение, webhook, убрано из выбора в редакторе и
из фильтра списка, так как у него никогда не было диспетчеризации; плейбуки,
сохранённые с ним ранее, сохраняют значение и отображают его только для чтения.)
Конкретные способы, которыми плейбук фактически стартует:
| Как он стартует | Где настраивается | Рантайм-путь |
|---|---|---|
| Вручную на кейсе | Аналитик открывает кейс → панель Work Plan или War Room Playbook Console → выбор плейбука → Run. | POST /cases/:caseId/playbooks/run |
| Автоматически на новых инцидентах | У типа инцидента есть Default Playbook и включено Run playbook automatically (гайд администратора, Objects Setup). Любой кейс, созданный с этим типом, авто-запускает его. | Жизненный цикл инцидента → runPlaybook() |
| По расписанию | Jobs & Schedules → New Schedule, Job Type playbook, cron / interval / once. | Планировщик → runPlaybook() |
| Массово | Список кейсов → выбор кейсов → запуск плейбука по всем ним. | POST /cases/batch-run-playbook |
Почему это важно. Самая частая ошибка «мой плейбук никогда не срабатывает» — ожидать, что тип триггера
incident_createdавто-запустит его. Авто-запуск управляется привязкой Default Playbook типа инцидента, а не выпадающим списком в редакторе. Задайте привязку на типе инцидента — и авто-запуск случается; тип триггера лишь помечает плейбук и фильтрует список.
Где наблюдать за прогоном. Три поверхности показывают одно и то же выполнение под разными углами:
- Work Plan (на кейсе) — разворачиваемый статус, логи и выводы по шагам, с элементом Resume, когда плейбук ждёт ввода.
- War Room → Playbook Console — история прогонов и живой статус по мере прихода событий.
- Editor → Debugger — инспектор прогона на плейбук (см. §8.11).
8.3. Редактор с высоты птичьего полёта
Откройте существующий плейбук из списка (ссылка Name) или создайте новый через New Playbook. Новый плейбук стартует с единственным, неудаляемым узлом Start и неактивен, пока вы его не включите.
Панель инструментов.
| Элемент | Что делает |
|---|---|
| Back | Вернуться в список плейбуков. |
| Name | Имя плейбука (обязательно для сохранения). |
| ID tag | Показан после сохранения; кликните, чтобы скопировать ID плейбука (нужен для ссылок на сабплейбуки и расписаний). |
| Trigger Type | manual / incident_created / scheduled. Синхронизируется с узлом Start. |
| Validate (бейдж) | Гоняет валидацию графа; бейдж показывает число блокирующих ошибок. |
| Auto Layout | Перекомпоновывает узлы сверху вниз. |
| Undo / Redo | Шагать по истории правок (также Ctrl/Cmd+Z / Ctrl/Cmd+Y). |
| Version History | Выдвижная панель сохранённых версий с Restore (только существующие плейбуки). |
| Clone | Сохранить копию как новый плейбук (только существующие плейбуки). |
| Shortcuts | Справочник горячих клавиш. |
| Debugger | Переключить панель инспектора прогона справа. Она изменяема по размеру — тяните её левый край; ширина запоминается на браузер. |
| Save | Сохранить плейбук. Недоступно, пока нет несохранённых изменений. |
Регионы рабочего пространства. Слева направо: Node Palette (перетаскиваемые типы задач, сгруппированные и искомые), панель Playbook Inputs (объявленные параметры прогона), Canvas (граф) и — когда открыт — Debugger. Выбор узла открывает плавающую панель Properties, заякоренную слева от холста; у неё есть кнопка Expand для большего места и она закрывается через OK / Cancel (обе просто закрывают — правки применяются вживую по мере набора).
Взаимодействия с холстом.
| Действие | Как |
|---|---|
| Добавить задачу | Перетащить элемент палитры на холст. |
| Соединить задачи | Тянуть от ручки-выхода узла к ручке-входу другого узла. |
| Выделить | Кликнуть узел или ребро; Shift+drag обводит группу; Shift+click добавляет к выделению. |
| Двигать | Тянуть узел; холст защёлкивается к сетке 15 px. |
| Пан / зум / fit | Элементы холста снизу-слева плюс мини-карта. |
| Копировать / вставить | Ctrl/Cmd+C / Ctrl/Cmd+V (узел Start никогда не копируется). |
| Удалить | Выделить и нажать Delete / Backspace; удаление ребра спрашивает подтверждение. |
Рёбра несут смысл. Кликните ребро, чтобы задать его Label, Semantic
Type (default / success / error / conditional / approved / rejected) и
Animation. Для ветвящихся задач подпись не косметична — движок идёт по ребру,
чья подпись совпадает с веткой, которую выбрала задача (напр. true, approved,
значение опции Ask). См. §8.7.
Валидация, сохранение, версионирование, активация.
- Validate помечает отсутствующий узел start, цикл или узел Condition с менее чем двумя исходящими рёбрами как ошибки (они блокируют Save); отсоединённый узел — предупреждение (Save всё равно разрешён).
- Save на существующем плейбуке спрашивает change note и пишет новую версию; объявленные входы узла Start и конфигурация каждой задачи хранятся в DAG. Если задача Script ссылается на автоматизацию, которая отключена или отсутствует, Save предупреждает до продолжения.
- Version History перечисляет версии с автором и заметкой; Restore откатывает холст к выбранной версии (сама записывается как новая версия).
- Активация. У плейбука нет отдельного состояния draft/publish — переключатель Active на странице списка — это то, что его «публикует». Новые плейбуки создаются неактивными. Привяжите активный плейбук к типу инцидента, чтобы он авто-запускался.
Импорт. Import на странице списка принимает плейбук в YAML (формат upstream
demisto/content). Импорт показывает превью — статистику задач и предупреждения
конверсии — до того, как создаёт неактивный PlaybookV2, который вы можете
просмотреть и отредактировать.
8.4. Входы плейбука и узел Start
Узел Start — точка входа (метаданные триггера живут здесь, зеркалятся в панель
инструментов). Его настоящая задача в рантайме — привязать входы: для каждого
объявленного входа движок берёт значение, поданное запросом прогона (или умолчание
входа), и делает его доступным как ${inputs.<name>} по всему графу.
Объявляйте входы в панели Playbook Inputs над холстом (Add Input). У каждого входа есть имя, источник и опциональные умолчание и описание. Обязательный вход без значения и без умолчания роняет выполнение на старте с ясным сообщением «Missing required inputs» — так что помечайте вход обязательным, только когда плейбук действительно не может без него запуститься.
Почему это важно. Входы — это как один и тот же плейбук обслуживает вызов сабплейбука, ручной прогон и прогон по расписанию, не хардкодя значения. Объявленные входы сабплейбука — это ровно то, что родительская задача Sub-Playbook предлагает как типизированные поля привязки (§8.8).
8.5. Типы задач
Палитра группирует задачи в Actions, Advanced / AI, Logic, Human и Flow Control. Панель Properties каждой задачи имеет вкладки Configuration (специфична для типа, ниже), Advanced (timeout, on-error, retry — см. §8.10) и Appearance (цвет, переопределение иконки и Lock, предотвращающий перемещение или редактирование узла) — кроме узлов Note и Section Header, которые ничего не выполняют и потому опускают Advanced. Это полный инвентарь задач, как его предлагает редактор:
| Задача | Ключ типа | Категория | Назначение в одну строку |
|---|---|---|---|
| Action | action |
Actions | Запустить команду коннектора или хранимую автоматизацию. |
| Script | script |
Actions | Запустить inline Python / JavaScript. |
| Set Incident Field | set_incident_field |
Actions | Записать значение в поле инцидента. |
| Close Incident | close_incident |
Actions | Закрыть кейс с классификацией и сводкой. |
| Extract Indicators | extract_indicators |
Actions | Извлечь IOC из текста и связать их с кейсом. |
| Update Workbook Step | workbook_step_update |
Actions | Пометить шаг Investigation Workbook done/skipped/blocked. |
| AI Agent | ai_agent |
Advanced / AI | Вызвать LLM-провайдера с инструментами и промптами. |
| Condition | condition |
Logic | Ветвиться по правилам. |
| Loop | loop |
Logic | Итерировать по массиву. |
| Parallel Fork/Join | parallel |
Logic | Разветвиться на несколько веток и пере-объединиться. |
| Set Variable | set_variable |
Logic | Сохранить значение в контексте плейбука. |
| Ask | ask |
Human | Задать человеку вопрос с кнопками-опциями. |
| Wait for Approval | wait_approval |
Human | Пауза, пока кто-то не одобрит или отклонит. |
| Human Task | human_task |
Human | Создать задачу кейса и ждать её. |
| Data Collection | data_collection |
Human | Отправить многовопросную форму и собрать ответы. |
| Polling | polling |
Human | Пере-запускать команду, пока условие не выполнится. |
| Start | start |
Flow Control | Точка входа; привязывает входы. |
| Section Header | section_header |
Flow Control | Визуальная подпись группировки (без рантайм-эффекта). |
| Note | note |
Flow Control | Стикер на холсте (без рантайм-эффекта). |
| Timer / Delay | timer |
Flow Control | Задержать ветку на N секунд. |
| Sub-Playbook | sub_playbook |
Flow Control | Запустить другой плейбук как дочерний. |
Подразделы ниже разбирают Configuration каждой задачи подробно.
8.5.1. Action
Рабочая лошадка. Выпадающий список Automation перечисляет всё, что можно
вызвать: команды интеграций из установленных, активных инстансов коннекторов
(сгруппированные по интеграции) плюс встроенные Playbook scripts (Set,
Print, CreateList, AddToList, IsIntegrationAvailable). Команда появляется,
только когда для неё существует хотя бы один активный инстанс коннектора.
Configuration — четыре вкладки:
- Inputs — по одному полю на аргумент выбранной команды. Обязательные аргументы
отмечены звёздочкой;
?показывает описание аргумента; аргументы с предопределёнными значениями отрисовываются как комбо-бокс. Кнопка{ }на поле открывает Source Picker, чтобы вставить значение из полей инцидента, вывода предыдущей задачи или рукописного выражения, а оттуда — мастер Filters & Transformers (общий с маппером — см.mapper-operators-reference.ru.md), чтобы сформировать значение. - Outputs — Context Output Mode управляет тем, как вывод этой задачи
сливается в Context Data кейса: Extend (дописать, по умолчанию), Key
(дедуп по названному вами ключевому полю) или Overwrite (заместить ключ).
Также здесь: Ignore outputs и Extend context — маппинг
ContextKey=outputField(через запятую), который копирует конкретные поля в ваши собственные ключи контекста. Ниже перечислены объявленные пути контекста вывода команды, чтобы вы знали, что могут прочитать задачи ниже по потоку. - Mapping — смаппить путь вывода на поле инцидента (
Account.Email → …); движок пишет эти поля, когда задача завершается. - Advanced — Using (выбрать конкретный инстанс коннектора, когда их несколько; оставьте пустым, чтобы использовать дефолтный), таймаут выполнения, повторы (число + интервал), Mark results as note, Mark results as evidence (также сохраняет вывод как файл-доказательство), Skip this branch if this automation/playbook is unavailable и Quiet Mode (подавить логирование War Room / весь побочный вывод).
IsIntegrationAvailable — встроенная проверка, а не команда коннектора: она читает
аргумент brandname, проверяет, что у интеграции есть активный инстанс, и
маршрутизирует через ребро yes или no.
8.5.2. Script
Гоняет код, который вы пишете на самом узле. Configuration: Language (JavaScript или Python), Timeout (1–3600 с, по умолчанию 300) и редактор Code. Два опциональных сворачиваемых раздела — Output Transformers и Output Filters — постобрабатывают вывод скрипта до его слияния в контекст.
Пустой редактор показывает запускаемый стартер для выбранного языка. Контракт:
- JavaScript получает
args,context.incident,context.variables,context.inputs;returnJSON-сериализуемое значение. - Python получает
args/incident, и вы возвращаете черезforge.results(...). - Любой язык вызывает
forge.setContext("Path.Key", value), чтобы записать явные значения контекста.
Возвращаемое значение задачи Script хранится в variables["script.<nodeId>"], а её
{ data, stdout, stderr } становятся выводом задачи. Используйте задачу Script
только для inline, разовой логики; переиспользуемая логика принадлежит хранимой
автоматизации (§8.9), выбираемой из задачи
Action.
Почему это важно. JavaScript-скрипты гоняются в песочнице Node VM; Python-скрипты гоняются как подпроцесс в рантайме интеграции с ForgeBot SDK. Вот почему хелперы
forge.*доступны в Python, но точка входа JavaScript — простая формаargs/context/return.
8.5.3. Set Incident Field
Пишет одно поле. Configuration: Field name и Field value (литерал или
выражение). Ядровые поля валидируются: severity принимает
critical/high/medium/low/informational; status принимает
new/open/in_progress/pending_approval/resolved/closed (установка closed
штампует время закрытия). title, description и assignedToId пишутся напрямую;
любое другое имя трактуется как кастомное поле инцидента. Невалидное значение enum
пропускается с причиной, а не роняет прогон.
8.5.4. Close Incident
Закрывает кейс. Configuration: Close reason (хранится как классификация
закрытия) и Close notes (сводка закрытия); оба принимают выражения. Задача
ставит статус closed и штампует время закрытия.
8.5.5. Extract Indicators
Гоняет общий движок извлечения по текстовому источнику. Configuration: Source
path (текст для сканирования, обычно выражение вроде ${incident.description}) и
опциональный фильтр types. Пустой список types извлекает каждый тип индикатора,
распознаваемый regex, настроенный под Settings → Object Setup → Indicators.
Задача также вытягивает SHA-256 хеши из любых контекстных записей File[], создаёт
индикаторы, связывает их с кейсом и возвращает ExtractedIndicators плюс счётчики
по типам.
8.5.6. Update Workbook Step
Закрывает шаг в Investigation Workbook кейса. Configuration: опциональное выражение
кейса, step selector (текущий шаг / ключ шага / id шага — ровно один), target
status (completed / skipped / blocked), заметки, ссылки на доказательства и
причину skip/block. Селектор по умолчанию — шаг, который триггернул плейбук.
Подключайте эту задачу после шага обогащения или сдерживания, чтобы просигналить,
что шаг workbook выполнен.
8.5.7. AI Agent
Вызывает провайдера большой языковой модели как агента. Configuration — пять вкладок:
- Model — провайдер (Anthropic, OpenAI, Azure OpenAI, Google, Ollama, xAI Grok, Qwen, OpenRouter), настроенный инстанс провайдера, модель, Agent Type (Single Turn / Tool Use / Conversational / ReAct), температура, max tokens и — для агентов, использующих инструменты, — max tool iterations.
- Prompts — System Prompt (роль) и User Prompt (рантайм-промпт, с
переменными
${incident.field}/${steps.stepName.result}). - Tools — переключить встроенные SOAR-инструменты (поиск кейсов, получить индикаторы, запустить действие плейбука, добавить комментарий, создать задачу, обновить поле кейса, …) и выбрать MCP-серверы.
- Memory — none / session / persistent, с размером окна.
- Output — text / JSON / structured (со JSON-схемой), Context Data Mode
(агент сливается под
AI.<node name>) и опциональный путь extract-to-context.
Инстанс провайдера должен быть настроен и активен в Settings → Connectors до того, как эта задача сможет запуститься.
8.5.8. Condition, Loop, Parallel, Set Variable (Logic)
- Condition — ветвится по правилам; полностью разобрано в §8.7.
- Loop — Configuration: Iterate over (массив или
${path}, резолвящийся в один), Max iterations (по умолчанию 100) и опциональное Exit condition. Движок гоняет тело цикла один раз на элемент и пере-входит в узел цикла между итерациями; подключите тело цикла от узла цикла и выведите завершение из выхода#loop_done#цикла. Пустой массив пропускает тело. - Parallel Fork/Join — разветвляется на всех подключённых преемников сразу; нижестоящая задача join ждёт каждую ветку, которая действительно отработала, до того как продолжить.
- Set Variable — Configuration: Variable name и Variable value. Значение
пишется в
${variables.<name>}и зеркалится в Context Data кейса. Единичное значение${…}сохраняет свой тип; простая строка, которая парсится как JSON, парсится (так"123"становится числом123).
8.5.9. Ask, Wait for Approval, Human Task, Data Collection, Polling (Human)
Эти задачи приостанавливают выполнение, пока человек (или опрашиваемое условие) их не разрешит.
- Ask — Configuration: Message, Response Options (значение каждой опции
становится подписью исходящего ребра), Timeout (таймаут разрешается как
rejected) и опциональный назначенец. Раздел Ask an external person выдаёт
одноразовую one-click ссылку на каждого получателя по каналу доставки (email или
настроенный отправитель Slack/Telegram), чтобы кто-то без логина в SOARForge мог
ответить на публичной странице
/respond/:token. - Wait for Approval — Configuration: Message и Timeout. Разрешается
через Approve / Reject в Quick Actions кейса, следуя ребру
approvedилиrejected. Таймаут разрешается как rejected. - Human Task — создаёт реальную Task кейса (title, description, назначенец)
и ждёт её; по завершении следует
approved/rejected. - Data Collection — отправляет многовопросную форму (text, number, date,
checkbox, select, multi-select). Configuration: заголовок формы, инструкции,
таймаут ответа, Context Path для ответов (по умолчанию
DataCollection.Responses), вопросы и та же опция внешней аудитории, что у Ask. Ответы попадают по настроенному пути контекста. - Polling — пере-запускает команду, пока не выполнится условие DT Filter.
Каждая попытка — одна задача очереди, которая пере-ставит себя после interval,
пока условие не выполнится, не достигнут timeout или max attempts или
задача не будет отменена. Когда исчерпано, следует ребро
on_error. Фильтр оценивается как настоящий булев (напр.${DBotScore.Score} >= 3— числовое сравнение, а не проверка на truthiness).
8.5.10. Start, Timer, Sub-Playbook, Section Header, Note (Flow Control)
- Start — см. §8.4.
- Timer / Delay — Configuration: Delay (seconds). Движок ставит следующую задачу в очередь с этой задержкой.
- Sub-Playbook — см. §8.8.
- Section Header / Note — только аннотации холста. Они пропускаются в рантайме, не производят вывода и никогда не появляются в Context Data или War Room.
8.6. Контекстные данные и выражения
Всё, что задача читает или пишет, течёт через контекст выполнения. Читайте из
него выражениями ${…}; пишите в него неявно (вывод задачи сливается) или явно (Set
Variable, Set Incident Field, forge.setContext).
Корни. Путь начинается с одного из этих корней или с голого верхнеуровневого ключа контекста:
| Корень | Резолвится в |
|---|---|
incident |
Каноническое представление инцидента (то же ${incident.*}, что видит War Room CLI). Встроенные поля инцидента живут здесь. |
inputs |
Объявленные входы плейбука. |
variables |
Значения, записанные Set Variable / скриптами. |
steps |
Выводы по задачам, ключами по узлам. |
execution |
Собственные метаданные выполнения (id, caseId, playbookV2Id). |
context |
Виртуальное представление, экспонирующее вышеперечисленное плюс верхнеуровневые ключи. |
| голый ключ | Выводы обогащения, слитые в корень — напр. IP, File, DBotScore. |
Почему это важно. Поля инцидента под
incident.*; выводы обогащения (IPот командыip,DBotScore,File, …) сидят в корне, а не подincident. Так что поле source IP —${incident.sourceip}, а score репутации, произведённый обогащением, —${DBotScore.Score}. Source Picker это отражает: «Incident Built-ins» подincident.*, выводы команд как их собственные пути.
Синтаксис выражений. ${...} интерполирует. Поле, составленное из единичного
${path}, сохраняет тип разрешённого значения (число, массив, объект); выражение с
текстом вокруг ссылки рендерится в строку. Внутри пути можно использовать полный
язык выражений DT:
| Форма | Пример |
|---|---|
| Точечная нотация | IP.Address |
| Индекс массива (допустим отрицательный) | DBotScore.[0].Vendor |
| Фильтр массива | DBotScore.(val.Score > 0).Vendor |
| Вызов метода | IP.Address.join(",") |
| Цепочка | Emails.(val.type == "to").address.uniq() |
| Конвейер трансформеров | IP.Address=stringify(.) |
Движок поставляет большую библиотеку методов — string (toUpperCase, split,
replace, base64Encode, sha256, …), array (join, uniq, pluck, groupBy,
sum, first/last, …), object (keys, pick, omit, getByPath, …) и
числовые методы. Если путь не может быть разрешён, он даёт undefined (пустую
строку при интерполяции), а не бросает — выражения падают мягко.
Как вывод задачи попадает в контекст. Каждая производящая данные задача, чей
вывод — объект, автоматически сливается в Context Data кейса и в
${variables.*}, используя Context Output Mode задачи (Extend / Key /
Overwrite). Задачи управления потоком и AI Agent исключены из этого универсального
слияния (AI Agent сливает себя под AI.*). Два escape-hatch на задачу это уточняют:
Extend context (ContextKey=outputField) копирует выбранные поля в ваши
собственные ключи, а вкладка Mapping пишет выбранные выводы прямо в поля
инцидента.
Переиспользование значений во входах. Операторы маппера из главы 5 — те же
операторы, что мастер Filters & Transformers экспонирует на входе задачи, так
что значение, которое вы научились формировать в маппере, формируется идентично
здесь. Дойдите до мастера из Source Picker { } на любом аргументе Action.
8.7. Условия и ветвление
Задача Condition держит упорядоченный список branches; у каждой ветки —
набор rules. Движок идёт по веткам по порядку и берёт первую не-дефолтную ветку,
чьи правила все совпадают, следуя ребру, чья подпись равна id этой ветки. Ветка
без правил никогда не совпадает. Если ничего не совпало, движок берёт ветку,
помеченную Default (обычно ветку false / else).
Стройте ветки во вкладке Configuration: у каждой ветки есть подпись и чекбокс Default; каждое правило — left — operator — right, где left и right — выражения или литералы. Доступные операторы (список подаётся с платформы, со статическим fallback):
==, !=, contains, starts with, ends with, is empty, is not empty,
>, <, >=, <=, regex, exists. Строковые сравнения нечувствительны к
регистру; > / < и родственные приводят обе стороны к числам.
Прокладка рёбер. Condition нужны хотя бы два исходящих ребра (Validate это
навязывает), и каждое ребро должно быть подписано id ветки — true и false
для двух дефолтных веток или ваши id веток, если вы добавили больше. Подпись ребра —
это как выбранная движком ветка отображается на путь на холсте.
Поведение при сбое. Поскольку резолвинг выражений падает мягко, правило, чей путь отсутствует, сравнивает пустое значение и обычно не совпадает — так что сломанный или отсутствующий вход рулит Condition к его ветке Default, а не роняет прогон. Проектируйте ветку Default безопасным путём (напр. «трактовать как не-вредоносное, эскалировать к человеку»), чтобы отсутствующее обогащение не уводило молча по агрессивному маршруту.
Заметка — это задача плейбука Condition. У SOARForge также есть отдельная, намеренно fail-closed предикатная грамматика, используемая оркестратором авто-расследования (ADR-0015): там неверно сформированное или неразрешимое условие помечает паттерн невалидным, и он исключается (никогда не диспетчеризуется), а evaluator никогда не бросает. Та грамматика (
{field, op, value}, укоренённая вincident.) — это не задача плейбука Condition и не авторится в редакторе плейбуков — не путайте эти два. В редакторе плейбуков «fail-safe» означает провалиться в ветку Default, так что сделайте эту ветку консервативной.
8.8. Сабплейбуки
Задача Sub-Playbook запускает другой плейбук как дочернее выполнение; родитель приостанавливается, пока дочерний не закончит, затем возобновляется. Configuration:
- Playbook — выбрать из существующих плейбуков (пикер недоступен для неактивных). Бэкенд резолвит сначала по ID, затем по имени.
- Separate context (по умолчанию включено) — дочерний видит только данные
инцидента. Выключите, чтобы также унаследовать
${variables.*}родителя. - Input Bindings — когда выбранный плейбук объявляет входы, они отрисовываются
как типизированные поля (имя, маркер обязательности, источник, умолчание, редактор
выражений). Привязки принимают простые значения или выражения вроде
${variables.foo},${steps.action1.result},${incident.name}. Продвинутые пользователи могут переключить JSON, чтобы редактировать карту привязок напрямую. - Outputs reference — объявленные переменные сабплейбука показаны read-only,
чтобы вы знали, что будет доступно как
${variables.<name>}после его возврата.
Когда дочерний завершается, его вывод сливается обратно в родителя под
SubPlaybook.<label> и как верхнеуровневые ключи, а также в Context Data кейса.
Ограждения. Движок обнаруживает рекурсию (плейбук не может появиться дважды в собственной родословной) и ограничивает глубину вложенности 10; любое из двух возвращает вывод-ошибку, а не зацикливается вечно. Встроенного переключателя «зациклить этот сабплейбук по списку» нет — чтобы итерировать, оберните задачу Sub-Playbook внутрь Loop (§8.5.8).
8.9. Автоматизации (скрипты) с нуля
Автоматизации — это переиспользуемые скрипты, которые вызывает задача Action
плейбука. Они живут на странице Automations (/automations); создайте одну
через New Automation (automations.create). Встроенные автоматизации read-only
— Clone одну, чтобы кастомизировать.
Редактор. Поля заголовка: Name (machine-имя), Display Name, Description, Language (Python или JavaScript), Scope, Timeout (1–3600 с). Затем четыре вкладки:
| Вкладка | Содержимое |
|---|---|
| Script | Редактор кода (Monaco). |
| Arguments | Объявленные входы: имя, тип (string/number/boolean/array/object), required, default, описание. Они становятся полями Inputs задачи Action. |
| Outputs | Объявленные пути контекста вывода, чтобы вкладка Outputs задачи Action и Source Picker могли предлагать их ниже по потоку. |
| Test | Введите JSON-объект аргументов и нажмите Run Test, чтобы выполнить сохранённый скрипт автоматизации на бэкенде (POST /automations/:id/test, право automations.execute). Панель результата показывает тег успех/ошибка, длительность, возвращённые данные и — для runtime-скриптов — сворачиваемые stdout/stderr и текст ошибки. Сначала сохраните автоматизацию: тест прогоняет её сохранённый скрипт, а не несохранённые правки. |
Под вкладками: Tags (пользовательские фильтры каталога) и Enabled.
Scope решает, где предлагается скрипт. Scope, важный для плейбуков, —
playbook-action: автоматизация playbook-action появляется в выпадающем
списке Automation задачи Action под «Playbook scripts». Другие scope подключают
тот же вид скрипта к другим поверхностям (операторы/действия processing-rule и
скрипты dynamic-section, отрисовывающие виджет layout-а — см. ниже).
Рантайм. Python-автоматизации гоняются как подпроцесс в рантайме интеграции с
ForgeBot SDK; JavaScript-автоматизации гоняются в песочнице Node VM. Точки
входа SDK зеркалят upstream demisto/content mock-модуль, так что импортированные
паки, которые import demistomock as demisto, авто-переписываются в import forgebot as forge при импорте — вы никогда не поставляете shim demistomock.
Анатомия скрипта (Python / ForgeBot). Аргументы внутрь, результаты наружу:
| Вызов | Назначение |
|---|---|
forge.args() |
Аргументы команды/задачи (ваши объявленные Arguments). |
forge.params() |
Конфигурация инстанса коннектора (для интеграционных скриптов). |
forge.command() |
Имя текущей команды. |
forge.results(data) |
Вернуть результаты (список разворачивается в отдельные записи). |
forge.error(msg) |
Вернуть ошибку. |
forge.setContext(key, value) / forge.getContext() |
Записать / прочитать явный контекст. |
forge.incident() |
Данные текущего инцидента. |
forge.executeCommand(cmd, args) |
Вызвать другую команду интеграции через платформу. |
forge.createIndicators([...]) |
Создать индикаторы. |
forge.getFilePath(entryId) / forge.fileResult(name, bytes) |
Прочитать файл-доказательство / вернуть файл. |
forge.getLastRun() / forge.setLastRun(obj) |
Персистить состояние между прогонами. |
forge.info(msg) / forge.debug(msg) |
Строки лога, всплывающие в SOARForge. |
Минимальная playbook-action Python-автоматизация:
import forgebot as forge
def main():
args = forge.args()
ip = args.get("ip", "")
verdict = "malicious" if ip.startswith("10.") else "unknown"
forge.setContext("MyCheck.Verdict", verdict)
forge.results({"ip": ip, "verdict": verdict})
if __name__ in ("__main__", "builtins", "__builtin__"):
main()
От автоматизации к задаче плейбука. Как только автоматизация включена со scope
playbook-action, бросьте задачу Action, выберите её из выпадающего списка
Automation, заполните её Inputs (ваши объявленные Arguments), и её выводы
текут в контекст согласно Context Output Mode задачи. Встроенные playbook-скрипты
(Set, Print, CreateList, AddToList) доступны тем же способом и не нуждаются в
коннекторе.
Динамические виджеты layout-а. Автоматизация со scope dynamic-section
(a.k.a. layout-dynamic) стоит за виджетом Dynamic Section на layout-е кейса:
layout ссылается на автоматизацию по имени, платформа гоняет её для кейса, и её
вывод (HTML, Markdown или таблица) отрисовывается в виджете, авто-обновляясь при
изменении контекста или полей кейса. Размещение виджета — задача layout-а (гайд
администратора); написание скрипта — тот же поток авторинга, что выше.
8.10. Обработка ошибок, таймауты и повторы
Вкладка Advanced каждой задачи управляет тем, что происходит при сбое или долгом прогоне:
- Timeout (seconds, 0 = no limit) — задача прерывается с ошибкой таймаута, если гоняется дольше.
- On error — Stop execution (по умолчанию; прогон падает), Continue to
next node (зафиксировать сбой, но продолжить) или Follow error path edges
(маршрутизировать по рёбрам
on_error— настройте такое ребро, чтобы это что-то делало). - Retry on failure — политика повторов с числом попыток и фиксированным или экспоненциальным бэкоффом; экспоненциальный удваивает задержку каждую попытку.
Задачи Action добавляют два дополнительных элемента управления сбоем: Skip this branch if this automation/playbook is unavailable (молча пропустить, когда интеграция не установлена) и Quiet Mode (0 = выкл, 1 = подавить записи War Room, 2 = подавить весь побочный вывод и слияние контекста для шумных подшагов).
Почему это важно. Сбои логируются в War Room с сообщением об ошибке, так что «Continue to next node» безопасен для best-effort обогащения, но опасен для шага сдерживания — там предпочитайте Stop или явный путь ошибки плюс согласование. Повторы с экспоненциальным бэкоффом — правильный инструмент для капризного внешнего API, а не для настоящего «не найдено».
8.11. Отладчик
Переключите Debugger в панели инструментов, чтобы открыть инспектор прогона справа. Он изменяем по размеру (тяните левый край; ширина сохраняется на браузер). Он требует сохранённого плейбука и показывает прогоны только после того, как плейбук хотя бы раз выполнился против кейса — это пост-фактум инспектор и реплеер записанных выполнений, а не живой break-and-step отладчик, который паузит движок.
Раскладка, сверху вниз:
- Recent Runs — выполнения этого плейбука, каждое показывает номер кейса, статус и время; кликните одно, чтобы инспектировать его. Кнопка refresh пере-опрашивает.
- Execution header — статус и кейс выбранного прогона, со степпером: Prev, Play / Pause (авто-продвигается по записанным шагам), Next и Run to Breakpoint.
- Steps — записанный список задач с статусом на шаг, типом, временем старта и любой ошибкой; шаг помечен Breakpoint, если вы пометили его узел. Добавьте или уберите breakpoint кнопкой flag в заголовке Properties этого узла.
- Detail — для сфокусированного шага, четыре представления (сегментированные):
- Step I/O — Input, Output и Error шага.
- Context — переигранный контекст выполнения вплоть до выбранного шага: Inputs, Variables, Incident, Step Outputs.
- Watch — добавьте выражения вроде
variables.fooилиsteps.node_1.outputи смотрите их значение в реплее. - Case Context — живая Context Data кейса прогона.
Пока прогон ещё активен, отладчик опрашивает и обновляется; выводы шагов переигрываются вплоть до выбранного шага, так что шаг назад и вперёд показывает, как строился контекст. Breakpoint-ы и степпер Play оперируют на этом записанном таймлайне — они не останавливают живое выполнение.
8.12. Разобранный пример — плейбук триажа «Acme SIEM»
Цель: когда кейс прибывает из Acme SIEM, обогатить его source IP, ветвиться по вердикту, эскалировать плохие на согласование и авто-закрывать благонадёжные с заметкой. Пять функциональных задач после Start.
1. Start. Триггер incident_created. Объявите один вход sourceip (источник:
incident), чтобы ручной прогон мог его переопределить; по умолчанию он берёт
сопоставленное поле инцидента.
2. Action — «Enrich IP». Выберите команду ip вашего threat-intel коннектора.
- Вход
ip→${incident.sourceip}(через Source Picker). - Вкладка Outputs: оставьте Context Output Mode Key с ключом дедупа
Address, чтобы пере-прогоны обновляли, а не дублировали. Команда пишетIPиDBotScoreв корень контекста.
3. Condition — «Malicious?».
- Ветка Malicious (не default), одно правило:
${DBotScore.Score}>=3. - Ветка Benign — пометьте Default.
- Нарисуйте два ребра: подпишите ребро к задаче 4
Malicious, а ребро к задаче 6Benign.
4. Set Incident Field — «Raise severity». На ребре Malicious.
- Field name
severity, значениеhigh.
5. Wait for Approval — «Confirm containment». После задачи 4.
- Message:
IP ${IP.Address} scored ${DBotScore.Score}. Approve containment? - На Approve (ребро
approved) продолжайте к вашему Action сдерживания (напр. команда блокировки на файрволе, использующая${IP.Address}); реброrejectedможет маршрутизировать к Human Task для ручного разбора. Таймаут считается как rejected.
6. Close Incident — «Auto-close benign». На ребре Benign.
- Close reason
false_positive. - Close notes:
Auto-closed: ${IP.Address} scored ${DBotScore.Score} (below malicious threshold).
Валидируйте (у Condition два подписанных ребра, нет цикла, есть узел Start),
сохраните с change note, переключите плейбук Active и привяжите его как Default
Playbook типа инцидента Acme SIEM с включённым Run playbook automatically.
Прогоните его один раз против реального кейса Acme, затем откройте Debugger и
подтвердите на шаге Enrich IP, что DBotScore.Score заполнен и Condition взял
ожидаемую вами ветку.
8.13. Распространённые ошибки
- Тип триггера — подпись, а не провод. Авто-запуск приходит из привязки Default Playbook типа инцидента, а не из выпадающего списка Trigger Type в редакторе (§8.2).
- Неверный корень для вывода обогащения.
${incident.DBotScore}пуст; выводы обогащения в корне —${DBotScore.Score}. Поля инцидента подincident.*(§8.6). - Рёбра Condition не подписаны. Ребро без подписи (или с подписью, не совпадающей с id ветки) никогда не следуется; каждому Condition нужны хотя бы два подписанных ребра, а ветка без правил никогда не совпадает.
- Отсутствующие данные тихо берут ветку Default. Выражения падают мягко, так что сломанное обогащение рулит Condition к Default — сделайте Default безопасным путём, а не агрессивным.
- «Continue on error» на шаге сдерживания. Best-effort обогащение может продолжить; деструктивное действие должно Stop или маршрутизировать явный путь ошибки с согласованием (§8.10).
- Бизнес-логика в задаче Script. Inline-скрипты разовые; переиспользуемая логика
принадлежит хранимой
playbook-action-автоматизации, чтобы она была версионирована, тестируема и общая (§8.9). - Команда отсутствует в выпадающем списке Action. Команда коннектора появляется,
только когда у интеграции есть активный инстанс; иначе сначала
установите/включите её или используйте
IsIntegrationAvailable, чтобы ветвиться по доступности. - Сабплейбук не итерирует сам по себе. Переключателя сабплейбука на элемент нет; оберните его в Loop. Рекурсия и глубина (макс. 10) огорожены и возвращают вывод-ошибку.
- Вкладка Test в редакторе прогоняет сохранённый скрипт. Вкладка Test в
модальном окне Automations теперь работает — задайте JSON-объект аргументов и
нажмите Run Test (нужно право
automations.execute). Она выполняет сохранённый скрипт автоматизации в песочнице, поэтому сохраните правки перед тестом; панель вернёт успех, длительность, данные и (для runtime-скриптов)stdout/stderr. - Debugger не паузит живой прогон. Он переигрывает записанные шаги; breakpoint-ы и Play оперируют на записанном таймлайне, а не на движке.
- Редактирование встроенной автоматизации. Встроенные read-only — Clone, чтобы кастомизировать, затем направьте задачу Action на клон.