ДокументацияИнженерия контента8. Плейбуки и автоматизации с нуля

Плейбуки и автоматизации с нуля

Маршрут: /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 Typemanual, 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), чтобы сформировать значение.
  • OutputsContext Output Mode управляет тем, как вывод этой задачи сливается в Context Data кейса: Extend (дописать, по умолчанию), Key (дедуп по названному вами ключевому полю) или Overwrite (заместить ключ). Также здесь: Ignore outputs и Extend context — маппинг ContextKey=outputField (через запятую), который копирует конкретные поля в ваши собственные ключи контекста. Ниже перечислены объявленные пути контекста вывода команды, чтобы вы знали, что могут прочитать задачи ниже по потоку.
  • Mapping — смаппить путь вывода на поле инцидента (Account.Email → …); движок пишет эти поля, когда задача завершается.
  • AdvancedUsing (выбрать конкретный инстанс коннектора, когда их несколько; оставьте пустым, чтобы использовать дефолтный), таймаут выполнения, повторы (число + интервал), 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; return JSON-сериализуемое значение.
  • 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.
  • PromptsSystem 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; каждое правило — leftoperatorright, где 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 errorStop 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/OInput, 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, а ребро к задаче 6 Benign.

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 на клон.