bizproc 26.1300.0 + bizprocdesigner 26.700.0: дизайнер проверяет права на присланный documentType, внешнего AI-агента вернули владельцу его копии
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
bizprocdesigner 26.700.0 ставится одним шагом с bizproc 26.1300.0 и закрывает экшен, который без проверки прав отдавал категории и стадии любого присланного клиентом типа документа. Внешнего AI-агента на псевдотипе Workflow, которого с bizproc 26.1200 подключал только администратор, снова может подключать владелец собственной запущенной копии, чужой templateId при этом не пройдёт. В bizproc перенос шаблона на месте (SequentialToNodeWorkflow::migrateTemplate()) теперь снимает автозапуск, который дублирует новый стартовый триггер, а узлы автомата состояний спрятали за выключенной по умолчанию опцией.
Обновите bizprocdesigner, чтобы настройки действия не раскрывали стадии
В 26.600 экшен getSettingsControlsAction() контроллера Bitrix\BizprocDesigner\Infrastructure\Controller\Activity брал documentType из запроса и права на него не проверял. Этот тип доходит до getPropertiesMap(), а контролы действия кладут в Options метаданные документа, например категории и стадии. Пользователь дизайнера мог прислать тип документа, к которому у него нет доступа, и получить эти списки. В 26.700 экшен требует CreateWorkflow на присланный тип, так же как соседний getNodeFilterMetadataAction(). Заодно documentType теперь передаётся в CBPActivity::createConfigurator().
Если ваш контроллер тоже принимает тип документа из запроса, проверьте права на него раньше, чем тип попадёт в сервисы. Хватит одного вызова \CBPDocument::canUserOperateDocumentType() и небольшой чистки входного массива. Вызов стоит в try: провайдер типа документа может бросить исключение на незнакомый тип, и без перехвата клиент вместо ACCESS_DENIED получил бы ошибку с текстом исключения. Ядро в Complex::loadSettingsAction() делает так же.
<?php declare(strict_types=1);
namespace Vendor\Module\Infrastructure\Controller;
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Engine\CurrentUser;
use Bitrix\Main\Engine\JsonController;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
final class DocumentFields extends JsonController
{
public function configureActions(): array
{
return [
'list' => [
'prefilters' => [
new ActionFilter\Authentication(),
new ActionFilter\Csrf(),
new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
],
],
];
}
public function listAction(array $documentType): ?array
{
Loader::requireModule('bizproc');
$documentType = $this->normalizeDocumentType($documentType);
try {
$allowed = $documentType !== null && \CBPDocument::canUserOperateDocumentType(
\CBPCanUserOperateOperation::CreateWorkflow,
(int)CurrentUser::get()->getId(),
$documentType,
);
} catch (\Throwable) {
// провайдер типа может бросить исключение на незнакомый тип
$allowed = false;
}
if (!$allowed) {
// кривой и чужой тип получают один ответ, перебором типы не выяснить
$this->addError(new Error('Access denied', 'ACCESS_DENIED'));
return null;
}
return \CBPRuntime::getRuntime()->getDocumentService()->getDocumentFields($documentType);
}
/** @return array{string, string, string}|null */
private function normalizeDocumentType(array $raw): ?array
{
if (!array_is_list($raw) || count($raw) !== 3) {
return null;
}
[$moduleId, $entity, $type] = array_map(
static fn(mixed $part): string => is_string($part) ? trim($part) : '',
$raw,
);
$isValid = preg_match('/^[a-z0-9_.]+$/D', $moduleId) === 1
&& preg_match('/^[A-Za-z0-9_\\\\]+$/D', $entity) === 1
&& $type !== '';
return $isValid ? [$moduleId, $entity, $type] : null;
}
}
Модификатор D у регулярок не даёт $ пропустить строку с переводом строки в конце. В этом же релизе его добавили в регулярку имени агента в CLI-командах генератора AI-агентов bizproc: '/^[a-z0-9_]+$/D'.
Учтите, что владелец копии снова подключает внешнего агента
Подключение внешнего агента к шаблону мы разбирали в bizprocdesigner 26.600.0. В 26.700 для него сменилась проверка прав. Экшены connectAgentAction, revokeAgentAction, regenerateAgentAction и agentStatusAction контроллера Diagram проходят через общий authorizeAgentTemplate(). Он и AbstractAgentRestController раньше спрашивали DocumentAccessService::canCreate(). С bizproc 26.1200 CreateWorkflow на псевдотипе Workflow есть только у администратора, поэтому не-админ получал отказ на любом templateId, в том числе на своей копии агента. Теперь обе точки вызывают новый canManageDocument($userId, $documentType, $templateId, $isUserAdmin).
Для реальных типов документа проверка прежняя, нужно CreateWorkflow. На псевдотипе Workflow пропускают администратора или владельца собственной запущенной копии AI-агента (canManageLaunchedTemplate(..., requireStarted: true)). Docblock поясняет, что так законный владелец копии возвращает себе доступ, а подставить чужой templateId (IDOR) при этом нельзя. Что поменялось с правами на псевдодокумент WORKFLOW в прошлой версии bizproc, смотрите в разборе bizproc 26.1200.0.
REST-метод bizprocdesigner.template.list отдаёт только шаблон, к которому привязан токен агента, и теперь проверяет его через тот же canManageDocument(). На псевдотипе Workflow не-админ получит этот шаблон, только если это его собственная запущенная копия, для реальных типов по-прежнему решает CreateWorkflow.
Сверьте, кто может писать константы через мастер агента
В bizproc права на запись констант шаблона вынесли в политику. WorkflowTemplateService::setConstants() спрашивает canWrite(int $templateId, array $documentType, int $userId) у объекта TemplateConstantsAccessPolicyInterface, который передаётся четвёртым необязательным параметром конструктора. Без него подставляется PublishTemplateConstantsAccessPolicy, то есть прежняя проверка canPersist(..., publish: true), и код, который создаёт сервис по-старому, работает как раньше.
Мастер настройки AI-агента (CBPSetupTemplateActivity) теперь пишет константы через SetupTemplateConstantsService с политикой AgentSetupTemplateConstantsAccessPolicy. Шаблон должен существовать, принадлежать тому же типу документа и быть копией AI-агента (узловой шаблон без SYSTEM_CODE), а пользователь должен быть администратором или владельцем этой копии (ACTIVATED_BY). Это проверяет AiAgentProvider::canManageLaunchedTemplate(). Право публиковать шаблон мастер больше не спрашивает.
Сотрудник с правом публикации, но без админских прав и без своей копии агента, записать константы через мастер не сможет, а владелец копии без права публикации сможет.
Проверьте AUTO_EXECUTE, если вызывали migrateTemplate() сами
SequentialToNodeWorkflow::migrateTemplate() переносит последовательный шаблон в новый дизайнер на месте. До 26.1300 он оставлял шаблону прежний AUTO_EXECUTE. Если после конвертации у шаблона появлялся стартовый триггер создания или изменения документа, процесс стартовал бы дважды, по автозапуску и по триггеру. Об этом в коде висел TODO, из которого видно, что метод готовили под active migration. Штатный код migrateTemplate() не вызывает. Конвертация, которую дизайнер делает при открытии шаблона (TemplateToNodes), ставит ручной стартовый триггер ManualStartTrigger, и фикс её не касается.
Теперь при переносе getRemainingAutoExecute() снимает бит CBPDocumentEventType::Create или CBPDocumentEventType::Edit, если для этого события появился триггер. Срабатывает это только в момент переноса, а миграций и изменений схемы БД в релизе нет.
Если вы вызывали SequentialToNodeWorkflow::migrateTemplate() из своего кода до обновления, проверьте, не остался ли у перенесённых шаблонов автозапуск на событие, для которого уже есть стартовый триггер.
Включите узлы автомата состояний опцией, если они уже в схемах
StateNode и SetStateNode появились в bizproc 26.1100.0, а в 26.1300 их убрали из списка действий: оба получили setExcluded(!WorkflowStateGroupFeature::isAvailable()). Группу ActivityGroup::WORKFLOW_STATE прячет правило OptionGroupRule в bizproc/.settings.php. По умолчанию опция bizproc:workflow_state_group_available равна N, и пока её не переключат в Y, узлов в дизайнере не видно.
Уже сохранённые блоки вид не теряют. TemplateToNodes перестал отбрасывать исключённые действия, когда собирает карты иконок, поэтому у блока остаются иконка и цвет, а подпись по умолчанию приходит в defaultTitle.
Чтобы добавлять такие узлы в новые схемы, включите опцию:
<?php declare(strict_types=1);
use Bitrix\Main\Config\Option;
Option::set('bizproc', 'workflow_state_group_available', 'Y');
Рядом в коде стоит комментарий «Still under internal rollout», так что включаете вы функциональность, которую Битрикс пока раскатывает у себя.
Пересмотрите скрипты вокруг bizproc:ai-agent-reverse
Это касается тех, кто собирает AI-агентов генератором bizproc. Команда bizproc:ai-agent-reverse теперь сначала собирает шаблон из полученного template.source.json в памяти и сравнивает с исходным. Если они расходятся, файл не пишется. Записать его всё равно можно с новым флагом --allow-lossy, тогда в источник добавляется "_partial": true, и bizproc:ai-agent-generate о такой пометке предупредит. У --verify сменился смысл. Заметки проверки печатаются всегда, а флаг просит вывести отчёт, даже когда сказать нечего. Ещё bizproc:ai-agent-generate теперь сообщает, если откат сгенерированных файлов не удался.
Поправьте вызовы внутренних классов
Публичные сигнатуры вне Internal в bizproc не менялись. Если вы опирались на внутренние классы или контроллеры дизайнера:
- В генераторе AI-агентов (
Internal\AI\Agent\Generator\...) удалёнRoundTripReport, вместо негоEquivalenceReport. УLinkGraph::__construct(array $links, array $knownNodes)второй параметр стал обязательным, уParsedSetupBlockиWizardMetaпоявился обязательныйconstantElements. ВConstantConfigзначение$defaultпо умолчанию теперь''вместоnull, а$optionsстал?array = nullвместоarray = [].ActivityNodeBuilder::build()принимает$descriptor,$innerActivityTypeи$activityTail. Controller\Activity\Complex::loadSettingsAction()принимает ещёarray $sources = []иarray $documentType = []. В ответеLoadSettingsResponseDtoпоявилисьautofillMap,relationActionиrelationPortRuleDtoDictionary, аfilterSupportedповторяетavailableBlocks.filter.availableчерезNodeFilterAvailability. Старому клиенту это не мешает.- Внутренний пайплайн AI-ассистента дизайнера получил параметр
RequestSource $source(по умолчаниюRestилиMarta, в зависимости от метода) вAgentDraftService::saveAndPush(),AiAssistantDraftConverterService::covertFromAgentInput(),AiAssistantWorkflowTemplateConverterService::convertFromTemplateArrayToAgentTemplate()и валидаторах. УAgentBlockновые поляmemberBlockIds,frameColorNameиframeContent, уBlockпоявилось?FrameData $frameData.
Не стройте логику на парах взаимозаменяемых триггеров
При сохранении узла Public\Command\Activity\Settings\SaveCommandHandler в bizprocdesigner разрешает сменить тип триггера только для пар, которые модуль-владелец документа объявил в ModuleSettings::getInterchangeableTriggerPairs(). На остальные вернётся ошибка TRIGGER_TYPE_TRANSITION_NOT_ALLOWED. Тип приводится к написанию из пары, чтобы в CBPActivity::createInstance не получилось CBPCBP.... Опираться на эту схему я бы не стал, потому что в bizprocdesigner 26.800 её заменили односторонним апгрейдом (подробности в разборе bizproc 26.1400.0).
Новое
bizproc
- Страница грида хранилища обходится одним
CUser::GetListвместо запроса на каждую ячейку. Это делаетInternal\Service\User\UserProviderсо статическим кешем пользователей (get(),prefetch()), на него же переведёнCBPHelper. Internal\Service\DocumentField\FieldValueFormatter(forProperty(),forType(),format(),prefetchUsers()) печатает значение одинаково в гриде хранилищаbizproc.storage.item.listи в предпросмотре таблицы данных. Даты из хранилища он приводит к времени зрителя черезValueFieldConverter::fromStorage().- У провайдеров DataView
CompositeDataSourceProvider,StorageDataSourceProviderиVariablesDataSourceProviderпоявилсяpresent(SourceRef $source, iterable $rows). Public\Service\ActivityGroup\OptionGroupRuleреализуетHideableGroupRuleInterfaceи прячет группу действий, пока опция модуля неY. Docblock советует регистрировать по экземпляру на группу вbizproc/.settings.php. Флаг группыworkflow_stateчитается в одном месте,Internal\Config\WorkflowStateGroupFeature::isAvailable(), чтобы группа и её узлы не расходились в видимости.- Проверку «можно ли сохранить или опубликовать» вынесли из
WorkflowTemplateServiceвInternal\Service\WorkflowTemplate\TemplatePersistAccessService. Для узловых шаблонов она смотрит ACL шаблона (canPublish,canEdit), для остальныхcanCreateWorkflow, а строку шаблона кеширует. - Генератор AI-агентов пополнился классами
ActivityDescriptorConfig,ComplexActivityRules,DescriptorRegistryDrift,StrictActivityResolver,EquivalenceReport,NodeDescriptorExtractor,WizardConstantElementиTemplateComparatorна 1 313 строк, уAgentTemplateGeneratorестьgetDescriptorWarnings(). Для трёх штатных агентов,bitrix_ai_coach,bitrix_ai_day_plannerиbitrix_ai_project_pulse, в поставке лежат исходникиtemplate.source.json.
bizprocdesigner
Controller\Diagram::getLastRunValuesAction(int $templateId)отдаёт значения последнего завершённого запуска шаблона по ключам выражений инспектора. Пустой результат приходит JSON-объектом, без ошибки. На отсутствующий шаблон не-админ получаетACCESS_DENIED, «не найден» ему не показывают.getAction()теперь возвращаетdraftSavedAt, время последнего сохранения черновика.- В
Internal\Config\Featureдва новых флага.isReadableExpressionsAvailable()читает опциюbizprocdesigner:readable_expressions_available(по умолчаниюN). УisLastRunValuesAvailable()своей опции нет, он повторяетLastValuesService::isAvailable()из bizproc. Коды фичreadableExpressionsиlastRunValues. Internal\Entity\FrameDataиNodeType::Frameдобавляют подложку, блок схемы с цветом, выравниванием, текстом и файлами. Для внешнего REST-агента к ним приложены проверки членства блоков, геометрии после раскладки и пересечений подложек:FrameStyleSanitizer,FramePostLayoutChecker,AgentFrameMembershipValidator,FrameBlockMatcher,UndirectedConnectivity,AgentBlockGeometry,FrameGeometry.FramePostLayoutCheckerвызывается и вtemplate.validate, и вtemplate.draft.push, чтобы вердикты совпадали.- Поля создаваемого связанного документа заполняются из выходов блоков-предков. Карту строит
Internal\Service\RelationAutofillMatcher::buildAutofillMap(), доступность фильтра решает сервисbizprocdesigner.activity.node.filter.availability(NodeFilterAvailability) из.settings.php. - В JS появились режим реакции на изменение элемента (
common-node-settings/ui/reaction-mode-selector/), «читаемые выражения» (readable-expression-*,readable-expressions-veneer.js), подсказки последних значений в инспекторе данных иautofill-form.js. Вconfig.phpрасширения добавленыui.draganddrop.draggable,ui.system.radiobuttonиui.system.radiobutton.vue.
Мелочи и находки
CBPMailActivity::getConfirmedEmails()считает подтверждённым адрес, которыйBitrix\Mail\Public\Service\Mailbox\AddressResolverсводит к активному IMAP-ящику, так что в «Отправить письмо» годятся дополнительные адреса ящика. Метод вызывается, только когда подключается модульbitrix24. Код готов к старому mail:class_exists, потомmethod_existsс откатом наfindBindings()и на прямой запрос кMailboxTableпачками по 100.- У «Тренера знаний» (
bitrix_ai_coach) вtemplate.source.jsonесть потокrestartс триггеромAiAgentRestartTrigger. Он состоит из мастераSetupTemplateActivityи повторного создания ботаImBotCreateBotActivity.template.jsonагента перегенерирован, метка вinstaller.phpсменилась на1786348402. describeSourceWarning()вAiAgentReverseпредупреждает о цене перегенерации. Ревизия агента считается хешем всего поставляемого шаблона, поэтому новыйtemplate.jsonпредложит обновление каждой запущенной копии, а подтверждение перезапишет то, что настроили на портале. В комментариях там же встречается внутренний код задачиTPL-06.- Санитайзер BBCode подложки
FrameStyleSanitizerможно брать за образец для любого BBCode, который приходит от REST-клиента или нейросети. Разрешены теги редактораb, i, u, s, list, *, url, quote, code, img, остальные[tag]превращаются в инертный текст через[. Вurlиimgсхемыjavascript:,vbscript:иdata:обезвреживаются и в атрибуте, и в теле тега, причём после удаления символов\x00-\x20. Длина ограничена 20 000 символов, и обрезка не разрывает тег или HTML-сущность. Заголовок подложки сервер намеренно не экранирует, иначе, как сказано в docblock, «R&D» дошёл бы до пользователя какR&D. PresentableProviderв docblock просит отдавать подпись значения простым текстом, экранирует тот, кто выводит. Иначе получится двойное экранирование.PreviewService::applyPresenterOverlay()глотает исключение презентера и оставляет сырые значения, если источник стал недоступен.ActivityVisibilityFilterпоказывает подложки (EmptyBlockActivityсNODE_TYPE=FRAME) внешнему REST-агенту и прячет их от внутреннего ассистента Марты. В комментарии это названо «the single deliberate divergence» двух предикатов.- В
Complex::loadSettingsAction()автозаполнение fail-open, а права fail-closed. БезCreateWorkflowпользователь получает пустую карту автозаполнения, и настройки грузятся без ошибки. Права проверяются на тип документа, который определил сервер, ещё до чтения метаданных связей. Идентификаторы блоков ограничены[A-Za-z0-9_], и блоки с id видаMartaGeneratedId-*автозаполнения не получают, это прямо записано в комментарии. ValidateSingleRuleCommandполучил белый список типов конструкций для карточек связей. Без него прямой запрос мог положитьbase-settingsилиfilterна порт, где таких блоков нет.- Старые admin-попапы bizprocdesigner
admin/bizproc_activity_settings.phpиadmin/bizproc_wf_settings.phpподключаютmain/include/epilog_admin_js.php, чтобы отложенные head-блоки попадали в ответ попапа (в комментарии «Task 722886»). Из списка доступных типов вbizproc_wf_settings.phpубралиFieldType::CONDITIONGROUP.
Что делать
- Обновите bizprocdesigner до 26.700. В 26.600
getSettingsControlsAction()отдавал категории и стадии по любому присланному типу документа. - Найдите в своих контроллерах
documentTypeиз запроса и убедитесь, что права на него проверяются до обращения к сервисам. - Если своих AI-агентов подключают сотрудники без прав администратора, учтите, что после обновления владелец собственной запущенной копии снова может подключить к ней внешнего агента, а чужие копии ему по-прежнему закрыты.
- Если вы вызывали
SequentialToNodeWorkflow::migrateTemplate()из своего кода до обновления, посмотритеAUTO_EXECUTEу перенесённых шаблонов. - Если в схемах уже есть
StateNodeилиSetStateNode, решите, включать лиworkflow_state_group_available.