bizproc 26.1000.0: активности научились проверять права на целевой документ, ScopeTokenService удалён
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Самый содержательный из трёх подряд релизов бизнес-процессов. Появились публичные контракты, по которым активность резолвит «над каким документом я работаю» и проверяет права на него. Удалён ScopeTokenService, версия API активностей поднята с 2 до 3, у хранилища появились лимиты.
Целевой документ и проверка прав — главное
Новые публичные контракты: TargetDocumentResolver и TargetDocumentAccessGuard со своими реестрами, плюс трейты TargetDocumentResolverTrait и ChecksResolvedTargetAccessTrait. Интерфейс гарда предельно прямой:
interface TargetDocumentAccessGuard
{
public function canUpdate(array $rootDocumentId, array $resolvedDocumentId, int $actorId): ?bool;
public function canRead(array $rootDocumentId, array $resolvedDocumentId, int $actorId): ?bool;
public function canDelete(array $rootDocumentId, array $resolvedDocumentId, int $actorId): ?bool;
}
Обратите внимание на два разных документа в сигнатуре: rootDocumentId — документ, на котором запущен процесс, resolvedDocumentId — тот, до которого активность реально дотянулась.
Именно в этом зазоре и была проблема. Процесс, запущенный на одной сделке, через связи и множественные поля мог добраться до другого документа и изменить его — а прав на этот второй документ никто не спрашивал, потому что права проверялись при запуске процесса. Теперь CBPSetFieldActivity и активности создания/удаления документа резолвят цель и зовут canUpdateResolvedTarget / canDeleteResolvedTarget.
Если вы пишете свои активности, работающие с чужими документами, — это тот контракт, который надо реализовать. Не «желательно», а «иначе ваша активность останется единственной дырой в этой схеме».
ACTIVITY_API_VERSION: 2 → 3
Константа CBPRuntime::ACTIVITY_API_VERSION поднята до 3. В отчётах это выглядит как ломающее изменение, но по коду — наоборот: константа используется ровно в одном месте, в ActivityFilterChecker:
if ($type === 'MIN_API_VERSION') {
if ((int)$rules > \CBPRuntime::ACTIVITY_API_VERSION) {
return false; // активность скрыта
}
}
То есть активность в своём .description.php может объявить FILTER['MIN_API_VERSION'], и рантайм её спрячет, если версия ниже требуемой. Подъём до 3 не ломает старые активности — он открывает те, что требовали третью версию. Практический вывод для вас: если пишете активность, использующую новые контракты (content-block, target document), объявляйте MIN_API_VERSION = 3 — на старом ядре она корректно не покажется вместо того, чтобы упасть.
Content-block: описание того, что активность показывает
Ещё один набор публичных контрактов: ActivityContentBlockProviderInterface, ContentBlockScopeConsumerInterface / ProducerInterface, ContentBlockResolver и DTO ContentBlock, ContentBlockContext, ContentBlockScope. Storage-активности (create/read/write/delete) их уже реализуют.
Это про дизайнер: карточка активности в редакторе перестаёт быть жёстко зашитой разметкой и становится набором блоков, которые активность объявляет сама. Цвет блока приехал в предыдущем релизе (ActivityContentBlockColor), теперь — содержимое.
Туда же: NodeFilterMetadataProvider, FilterResultPropertyResolver с реестром, ReturnDocumentTrait, BaseComplexActivity::execute() и initializeFilterResultProperties().
Что сломается
Bitrix\Bizproc\Integration\ScopeTokenServiceудалён целиком (getToken,setScope,tokenizeUrl). Замена —QuickAccessFileParam::add. Следом удалёнWorkflowUserView::setScopeForTokenService(), аCBPViewHelperпереведён на новый способ токенизации URL файлов.CBPCreateDocumentTriggerпотерялgetPropertiesDialogValues()иcreateApplyRules(). В API появилсяBaseTrigger::preparePropertiesDialogValues— логика поднялась в базовый класс.- Интерфейс
Internal\Entity\Activity\Interface\FixedDocumentComplexActivityопустошён и теперь наследует публичныйPublic\Activity\Interface\FixedDocumentComplexActivity. Правильное движение (контракт переехал из Internal в Public), но если вы импортировали Internal-версию — переключайтесь на публичную. DeleteStorageItemCommandпринимает массив id:new DeleteStorageItemCommand($ids). Второй раз за два релиза меняется — в 26.900.0 у неё убрали$storageTypeId.- Удалены
install/db/{mysql,pgsql}/{install,uninstall}.sql— схема только вinstall/migrations/tables.php(переезд начался в 26.900.0).
Совместимо: StorageTypeProvider::getCount(?FilterInterface $filter = null), DocumentsResolver::resolvePayloadItem и ResolvedDocumentDto с ?int $categoryId = null, MakeTemplatePackageDto с bool $pullAiPrompts = false.
Лимиты хранилища
StorageLimitsService вводит потолки: 50 полей и 300 хранилищ, опции очистки storage_items_cleanup_days и storage_items_cleanup_size. Плюс StorageSizeCleanupService::cleanupOldStorageItemsBySize() и StorageItemRepository::findOldestStorageItemIds() — чистка не только по возрасту, но и по объёму.
В гриде хранилищ появились массовое удаление и фильтр (Storage::deleteItemsAction / deleteListAction, StorageListFilter, row DeleteAction).
Смысл понятен: хранилище процессов — это место, куда данные пишутся автоматически и никогда не удаляются, пока кто-нибудь не спохватится. Лимиты плюс чистка по размеру превращают его из растущей навсегда таблицы в управляемую.
Отдельно в install/migrations/tables.php у b_bp_task.NAME длина поднята с varchar(128) до varchar(255). Это определение в create(), а не ALTER существующей таблицы — на работающем портале колонка сама не расширится.
Ещё по мелочи
CBPForEachActivityумеет прокидывать return-свойство документа из множественногоDOCUMENT.- Старт процессов:
Starter\Template\Start\{CollectRequest, CollectorService, StartableTemplate, StartableTemplateCollection}иcategoryIdв компонентахbizproc.workflow.start/start.list. AutoExecuteFilter::getFilterValue()— матчAUTO_EXECUTEвынесен изCBPWorkflowTemplateLoaderв отдельный класс.CBPDocument::_ReplaceTaskURL():CHTTP::URN2URIзаменён наUri::toAbsolute— та же замена идёт по всему батчу в разных модулях.NodeAvailabilityService+GroupVisibilityServiceпрячут группы нодAIиMCP;AiAgent\BaseController::processBeforeActionотказывает по региону (ERROR_CODE_UNAVAILABLE_BY_REGION).
Что делать
- Свои активности, работающие с документами помимо корневого, — реализуйте
TargetDocumentAccessGuard. Это главное в релизе. - Ищите
ScopeTokenServiceв коде — класса нет, переходите наQuickAccessFileParam. - Проверьте вызовы
DeleteStorageItemCommand— сигнатура снова изменилась. - Новым активностям на новых контрактах ставьте
MIN_API_VERSION = 3.