bizprocdesigner 26.600.0: публикация схемы сверяет права с шаблоном, внешние ИИ-агенты получили REST v3
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
В bizprocdesigner 26.600.0 публикация схемы проверяет права по типу документа самого шаблона. До этого пользователь с правом создавать процессы для одного типа документа мог опубликовать схему поверх шаблона другого типа, а по ответу контроллера понять, существует ли шаблон с нужным ID. Вендор не упомянул в описании второе крупное изменение, выключенное по умолчанию подключение внешнего ИИ-агента (Claude Code, Codex или любого другого) к конкретному шаблону через REST v3 и временный вебхук.
Обновитесь, чтобы закрыть перезапись чужих шаблонов
Дыра была в publicateAction() и publicateDraftAction() контроллера Bitrix\BizprocDesigner\Infrastructure\Controller\Diagram. Права проверялись только по documentTypeSigned из запроса (метод prepareDiagramData), а templateId брался из того же JSON и с типом документа шаблона не сверялся. Разработчики Битрикса в комментариях к исправлению называют обе проблемы: «cross-type overwrite» и «existence/type oracle».
Теперь право CreateWorkflow проверяется по типу документа целевого шаблона, этим занимается новый приватный canWriteTemplate(). Несуществующий шаблон для пользователя без прав администратора выглядит так же, как чужой, причём и при публикации, и в getAction() (через getTemplateData()).
До обновления контроллер отдавал эти ошибки через getError() с кодом 0, и удалённый шаблон от чужого отличался только текстом сообщения. Теперь у них есть коды ACCESS_DENIED и TEMPLATE_NOT_FOUND, и по TEMPLATE_NOT_FOUND редактор показывает экран удалённого шаблона. Обычный пользователь этот код не получит, на несуществующий ID ему придёт ACCESS_DENIED. В getAction() и публикации ошибки доступа теперь идут через getCodedError(). Новые действия агента отдают их через getError() с кодом 0 и сообщают TEMPLATE_NOT_FOUND любому пользователю.
Если у вас есть свой контроллер или сервис, который меняет шаблон бизнес-процесса по ID из запроса, проверяйте права тем же способом, по типу документа из записи шаблона:
<?php declare(strict_types=1);
namespace Vendor\Module\Application\Service;
use Bitrix\Bizproc\Workflow\Template\Entity\WorkflowTemplateTable;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;
final class TemplateWriteGuard
{
public function check(int $templateId, int $userId): Result
{
Loader::requireModule('bizproc');
$template = WorkflowTemplateTable::getRow([
'select' => ['MODULE_ID', 'ENTITY', 'DOCUMENT_TYPE'],
'filter' => ['=ID' => $templateId],
]);
// тип документа берём из записи шаблона, documentType из запроса здесь не участвует
$allowed = $template !== null && \CBPDocument::canUserOperateDocumentType(
\CBPCanUserOperateOperation::CreateWorkflow,
$userId,
[$template['MODULE_ID'], $template['ENTITY'], $template['DOCUMENT_TYPE']],
);
$result = new Result();
if (!$allowed) {
// один ответ на «нет шаблона» и «нет прав», чтобы перебор ID не выдавал чужие шаблоны
$result->addError(new Error('Access denied', 'ACCESS_DENIED'));
}
return $result;
}
}
Передавайте properties в ActionDictionaryEntryDto по имени
В конструктор Bitrix\BizprocDesigner\Infrastructure\Dto\Activity\Complex\ActionDictionaryEntryDto добавили ?string $group = null перед ?array $properties. Если вы передавали properties четвёртым позиционным аргументом, массив теперь уйдёт в $group и вызов упадёт с TypeError, потому что массив в ?string не проходит даже без strict_types. С именованными аргументами порядок параметров перестаёт иметь значение:
<?php declare(strict_types=1);
use Bitrix\BizprocDesigner\Infrastructure\Dto\Activity\Complex\ActionDictionaryEntryDto;
$entry = new ActionDictionaryEntryDto(
id: 'vendor_send_invoice',
title: 'Отправить счёт',
handlesDocument: true,
properties: $properties,
);
Заберите все найденные элементы из _all
Фильтр в комплексной ноде (Internal\Command\Activity\Complex\ConvertRuleCommand) раньше регистрировал одно возвращаемое свойство <filterId>, и флаг Multiple у него копировался из свойства документа. Теперь свойств два. <filterId> всегда идёт с Multiple => false и подписью «— первый найденный», у нового <filterId>_all стоят Multiple => true и подпись «— все найденные». Там, где результат фильтра нужен списком, берите <filterId>_all.
В том же ConvertRuleCommand к порту теперь подключаются все rule-входы. Раньше подключался только первый, в коде так и было написано: /* temporary only first input activity */. Когда на порту несколько правил, inputNames[порт] становится массивом, когда одно, остаётся строкой. Код, который читает inputNames, должен понимать оба варианта.
Пройдитесь по остальным несовместимостям
Почти все они в пространствах Internal и Infrastructure. Если вы на них опирались:
- У
Diagram::getAction()удалён параметр?string $editBlockвместе с веткой//todo: proto, которая подменяла граф дочерними активити выбранного блока. Шаблон компонентаbizprocdesigner.editorбольше не передаёт в приложениеinitEditBlockиз$_GET['editBlock']. Diagram::updateTemplateAction()при ошибкеUpdateWorkflowTemplateCommandвозвращаетnullи ошибки. Раньше он отдавал->run()->getData()без проверки результата.Internal\Entity\Block::toArray()отдаёт порты плоским списком черезBitrix\Bizproc\Activity\Dto\NodePorts::fromArray()->toArray(), группировки{input, output}больше нет.AiAvailabilityService,DocumentFieldServiceиDocumentAccessServiceизlib/Internal/Integration/AiAssistant/Service/сталиfinal,DocumentAccessServiceещё иreadonly, аLastWorkflowServiceизfinalсталfinal readonly. УDocumentFieldServiceубран конструктор, семантический поиск создаётся лениво.Internal\Integration\AiAssistant\Entity\Draftсталfinal, а с публичных свойств его конструктора снялиreadonly, объект стал изменяемым. УValidator\SaveWorkflowValidatorreadonlyпереехал со свойств на класс, свойства по-прежнему только для чтения.
Остальные изменения сигнатур добавляют необязательные параметры в конец списка и старые вызовы не ломают. Например, у Activity::getNodeFilterMetadataAction() появился bool $includeRelatedEntityTypes = false для фильтрации по связям. Отдельно NodeCatalogItemDtoFactory::makeDefaultSettingsByType() из private static стал public static.
Не вызывайте InstallEvents() у bizprocdesigner
Из класса bizprocdesigner в install/index.php удалили собственные InstallEvents() и UnInstallEvents(), которые регистрировали pull OnGetDependentModule и main OnAfterRegisterModule. Обработчики теперь описаны в install/migrations/events.php, где к ним добавился rest OnRestServiceBuildDescription. InstallDB() и UnInstallDB() вызывают installMigrations() и uninstallMigrations($dropTables) с учётом savedata, прямой вызов \CAgent::RemoveModuleAgents('bizprocdesigner') из удаления модуля убрали.
Одноимённые методы остались у базового CModule, и они пустые. Скрипт, который чинил обработчики через CModule::CreateModuleObject('bizprocdesigner')->InstallEvents(), отработает без ошибок и ничего не зарегистрирует. Сверять и восстанавливать обработчики теперь нужно по списку из install/migrations/events.php, при установке его применяет CModule::installMigrations() из InstallDB().
Новых таблиц и полей нет. Остальное про установку:
install/migrations/agents.phpрегистрируетAgentTokenGardenerAgent::executeс интервалом 86400,isPeriod=falseиexecDelay=3600;install/updater.phpпри обновлении регистрирует только обработчик restOnRestServiceBuildDescriptionна\Bitrix\BizprocDesigner\RestService;- появился
migration_config.jsonсinstallDirectoriesMappingдляcomponents,jsиtools; - новые опции модуля:
external_ai_agent_available(N),expression_builder_available(N) иagent_token_ttl(86400 секунд, значение не больше нуля сбрасывается на 86400).
Подключите к шаблону Claude Code или Codex
У шаблона в редакторе появилась кнопка подключения агента (shared/ui/connect-agent-button/). За ней стоят действия Diagram::connectAgentAction, revokeAgentAction, regenerateAgentAction и agentStatusAction с параметром int $templateId, из JS они вызываются как bizprocdesigner.v2.Diagram.*. Ответ connect и regenerate содержит connectUrl вида /rest/api/<user>/<token>/bizprocdesigner.agent.connect?templateId=… и готовые команды запуска commands.claude, commands.codex и commands.universal.
Дальше агент работает с REST v3 в scope bizprocdesigner. Контроллеры лежат в lib/Infrastructure/Rest/Controller/, методы такие:
bizprocdesigner.agent.connectотдаёт агенту инструкцию в markdown;bizprocdesigner.catalog.listвозвращает типы блоков или схему настроек блока сdefaultValuesпресета;bizprocdesigner.document.fieldsвозвращает поля документа, а если вConfigurationзаданsemantic_search.url, ищет по ним семантически;bizprocdesigner.template.list,.getи.validateчитают шаблон и проверяют граф без сохранения;bizprocdesigner.template.draft.pushвалидирует граф, сохраняет черновик и через pull отправляет его в открытый редактор.
Публикует шаблон пользователь сам, метода публикации у агента нет.
Токен агента выпускает AgentWebhookService. Это System-вебхук модуля rest, созданный через CreateSystemIncomingWebhookCommand, с атрибутами bizprocdesigner=Y, template_id и expire_at. Живёт он agent_token_ttl секунд, по умолчанию сутки. Перевыпуск идёт под блокировкой bizprocdesigner:agent_token:{user}:{template}, и новый токен создаётся раньше, чем отключаются старые. Раз в сутки AgentTokenGardenerAgent отключает просроченные токены, а отключённые токены удаляет совсем, когда с последнего использования или создания прошло больше 7 дней.
Токен привязан к одному шаблону, поэтому bizprocdesigner.template.list возвращает только его. Привязку AbstractAgentRestController::resolveAndAuthorize() проверяет до поиска шаблона, чтобы по разнице между 404 и 403 нельзя было узнать о чужих шаблонах. Граф от агента ограничен: AgentBlocksValidator::MAX_BLOCKS = 256, AgentConnectionsValidator::MAX_CONNECTIONS = 512. В комментариях это названо «DoS guard», потому что раскладка полиномиальна по числу блоков.
По комментариям в AgentTokenGuard.php и AbstractAgentRestController.php, цепочка авторизации REST кеширует валидность токена до суток, и отозванный или просроченный токен может пройти транспортную авторизацию. Поэтому AgentTokenGuard перечитывает токен через IncomingWebhookRepository на каждый вызов. Если ваш модуль отдаёт данные по вебхуку и вы рассчитываете, что его отключение сразу закроет доступ, проверяйте токен в контроллере так же.
Включается всё опцией external_ai_agent_available, по умолчанию N:
<?php declare(strict_types=1);
use Bitrix\Main\Config\Option;
Option::set('bizprocdesigner', 'external_ai_agent_available', 'Y');
// срок жизни токена агента в секундах, 0 и меньше модуль заменит на 86400
Option::set('bizprocdesigner', 'agent_token_ttl', '3600');
Кроме опции, Feature::isExternalAiAgentAccessible() проверяет через ServiceLocator AiAgentsFeature и RegionAvailabilityService, а фильтр AiAvailabilityFilter у REST-методов требует модуль aiassistant и «реального» пользователя. В «Управлении сайтом» модуля aiassistant нет, значит, все REST-методы агента там ответят AccessDenied. При этом Diagram::authorizeAgentTemplate() в контроллере редактора наличие aiassistant не проверяет.
Сверьте правила комплексных нод с каталогом
ValidateSingleRuleCommand теперь сверяет тип конструкции и activityData.Type с каталогом ноды. Правило с недоступным блоком получит ошибку ERR-BLOCK-NOT-AVAILABLE, вторая базовая конструкция на ноду получит ERR-BASE-SETTINGS-DUPLICATE. Сам каталог допустимых блоков и действий отдаёт новый Complex::getCapabilityCatalogAction(ActivityData, array $documentType) в виде CapabilityCatalogResponseDto и AvailableBlocksDto.
Базовая конструкция — это новый ConstructionType::BASE_SETTINGS ('base-settings') с Rule\BaseSettingsExpressionDto. Без actionId её свойства вливаются в саму ноду, и такая конструкция на ноду может быть только одна. С actionId она превращается в дочернюю активити. Ещё правила получили именованные группы (RuleDto::$groupTitle), а NodeCatalogItemDto знает про relationsAvailable и подложку (contentBlock, contentBlockProducer, contentBlockConsumer через Bitrix\Bizproc\Activity\ContentBlockResolver). SaveCommandHandler записывает в активити ключ ContentBlock. Каталог фильтрует группы через Bitrix\Bizproc\Public\Service\ActivityGroup\GroupVisibilityServiceInterface, если тот зарегистрирован в ServiceLocator.
В JS редактора появились таблицы данных ноды со слиянием двух источников по ключу и группировкой с итогами SUM/COUNT/AVG/MIN/MAX (features/data-view-editor/), конструктор выражений с функциями, калькулятором и модификаторами, текст и файлы в подложке. Таблицы данных читают опцию bizproc dataview_enabled, конструктор выражений закрыт expression_builder_available, и об обоих вендор не пишет. Включены ли функции, фронт проверяет через Feature.isAvailable() из расширения bizprocdesigner.feature с кодами dataTables, externalAiAgent и expressionBuilder. Новый Feature.isLocked() возвращает true только для externalAiAgent, когда опция включена, а тариф или регион агента не пускают.
Мелочи и находки
- Команды для Claude Code и Codex лежат прямо в языковом файле
lang/ru/lib/Infrastructure/Controller/Diagram.php. Команда для Claude начинается сclaude "Выполни в терминале: curl -s '#URL#'…и запрещает агенту встроенный WebFetch, потому что «запрос должен идти с твоей машины». Команда для Codex даже в русском файле написана по-английски. - Инструкция из
bizprocdesigner.agent.connect— это фразаBIZPROCDESIGNER_AGENT_CONNECT_INSTRUCTION, промпт с форматом графа, портамиoN/iN, замыканием цикловForEach/Whileи порядком работы get → validate → draft.push. Отдельно агента предупреждают, чтоvalidateиdraft.pushполностью заменяют граф. Английской версии фразы в релизе нет,build()в этом случае берёт'ru'. ActivityVisibilityFilterпрячет от ИИ старые контейнеры:IfElseActivity,ParallelActivity,StateMachineWorkflowActivity,ListenActivity,EventDrivenActivityи ещё семь. По комментарию, конвертер не умеет разложить их на ноды с портами, и структура «молча потерялась бы».- Порт возврата цикла распознаётся по заголовку
'->>'(BlockTypeDetail::LOOPBACK_PORT_TITLE). УForEachэто входi1, уWhileмаркер стоит на выходе. LegacyAgentTokenMigratorAgentдолжен отключать «старые» User-токены со scope ровно[bizprocdesigner], которые выдавал «former AgentWebhookService». НоAgentWebhookServiceв bus появился только в этом релизе, а сам агент нигде не регистрируется, его нет ни вinstall/migrations/agents.php, ни вinstall/updater.php.- Контроллер
Activityпередаёт третий аргумент$includeRelatedEntityTypesв$className::getNodeFilterMetadata(), хотя в интерфейсеBitrix\Bizproc\Public\Activity\Interface\NodeFilterMetadataProviderна этом коммите (bizproc 26.1200.0) параметров по-прежнему два. - Ссылка
>>> OPEN NODE DESIGNER <<<вbizproc.workflow.editтеперь показывается для любого шаблона, раньше только дляStateMachineWorkflowActivity. С константойBitrix\Bizproc\Dev\ENVкаталог получает группуdevс захардкоженным заголовком'В разработке'. - В комментариях встречаются ссылки на внутренние спецификации «DTO-01…DTO-04» и «ADR invariant». Докблок
lib/Internal/Entity/AgentPortDefault.phpнаписан по-русски, остальной код по-английски. VERSION_DATEвinstall/version.phpпошла назад. У 26.600.0 там2026-07-02 10:14:00, у предыдущей 26.550.0 было2026-08-07 09:34:35.
Что делать
- Обновите модуль, если схемы в редакторе публикуют пользователи без прав администратора.
- Если ваш код разбирает ошибки контроллера
Diagramпо тексту, переведите его на кодыACCESS_DENIEDиTEMPLATE_NOT_FOUNDи учтите, что обычный пользовательTEMPLATE_NOT_FOUNDне получает. - Найдите
new ActionDictionaryEntryDto(и переведите вызовы на именованные аргументы. - Проверьте схемы, где результат фильтра комплексной ноды используется как список, и переключите их на
<filterId>_all. - Уберите вызовы
InstallEvents()у модуля bizprocdesigner из своих скриптов восстановления и сверяйте обработчики поinstall/migrations/events.php.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
Bitrix\BizprocDesigner\Infrastructure\Controller\Diagram::getAction($editBlock)
|
Удалено |
Bitrix\BizprocDesigner\Infrastructure\Controller\Diagram::getAction()
|