PT Application Inspector (PT AI) — анализатор защищённости приложений
от Positive Technologies: статический анализ кода, поиск уязвимых компонентов, конфигурации и секретов (белый ящик)
и сканирование работающего сайта (чёрный ящик).
Интеграция с PT AI подключает проект к серверу PT AI Enterprise Server,
а политика выполнения сканирования добавляет в пайплайны проекта задачу ptai_scan и задаёт, что она сканирует.
Задача загружает исходный код коммита на сервер клиентом aictl,
ждёт окончания сканирования, читает вердикт политики безопасности PT AI и публикует отчёты.
Предварительные требования
Чтобы сканирование могло запуститься, убедитесь, что:
- администратор включил флаг функций
fe_security_scan_policiesтак, как описано на странице Ранний доступ; - сервер PT AI Enterprise Server версии от 5.0 до 7.0 доступен раннеру, и у вас есть для него токен доступа (см. Токен доступа PT AI);
- интеграция с PT AI настроена и активна для проекта или унаследована от группы выше него;
- проект политик безопасности привязан к проекту или к группе выше него;
- доступен GitLab Runner с Docker executor, и он может загрузить образ задачи сканирования.
Токен доступа PT AI
- Войдите в веб-интерфейс PT AI Enterprise Server под учётной записью администратора.
- Нажмите значок шестерёнки в левом нижнем углу, чтобы открыть раздел администрирования.
- Откройте Настройка системы → Токены доступа.
- Нажмите Создать токен.
- Задайте название токена, например
gitlab-ci-ptai, выберите разрешение Для легкого агента и плагинов CI/CD и срок действия. - Подтвердите создание и скопируйте токен.
После закрытия окна значение токена в PT AI больше не показывается. Потерянный токен отзовите и создайте новый.
Интеграция с PT AI
Настройте интеграцию на уровне проекта или группы в разделе «Настройки» → «Интеграции» → «Positive Technologies Application Inspector». Форма задаёт подключение к серверу, доверие его сертификату и образ задачи сканирования. Что сканируется, чёрный ящик и вход на сайт задаются в политике выполнения сканирования.
Подключение
| Поле | Что задаёт |
|---|---|
| «URL сервера» | Базовый URL сервера PT AI, например https://ptai.example.com. Обязательное |
| «API-токен» | Токен доступа PT AI, с которым задача входит на сервер. Обязательное |
Настройка TLS
| Поле | Что задаёт |
|---|---|
| «Доверие к сертификату» | Как задача сканирования решает, доверять ли серверу PT AI: «Доверять сертификатам образа джобы», «Доверять удостоверяющему центру ниже» или «Не проверять сертификат» |
| «Удостоверяющий центр» | PEM-сертификат центра, подписавшего сертификат сервера. Используется, только если выбрано «Доверять удостоверяющему центру ниже» |
Задача передаёт удостоверяющий центр клиенту aictl отдельным параметром. Хранилище сертификатов образа для остальных команд задачи не меняется.
Образ задачи сканирования
| Поле | Что задаёт |
|---|---|
| «Образ джобы сканирования» | Образ, в котором выполняется задача ptai_scan: полный адрес с тегом или дайджестом. Обязательное. Новая интеграция заполняется образом из поставки продукта, registry.flant.com/deckhouse/code/gitlab-custom/ptai:1.2.2 |
| «Переменная логина реестра» | Необязательно. Имя переменной CI/CD проекта или его группы с логином для реестра образа, например $SCANNER_REGISTRY_USER. Обычная переменная, не файловая |
| «Переменная пароля реестра» | Необязательно. Имя переменной CI/CD с паролем или токеном для этого реестра. Пароль не может содержать символы " и \ |
Собственный образ должен содержать aictl, ruby и mktemp, а если политика читает конфигурационный файл из другого проекта, то и curl. Задача, в образе которой нет одной из этих команд, завершается с ошибкой и называет её.
Поля раздела «Джобы сканирования» читаются только из интеграции группы, которой принадлежит политика, или ближайшей группы выше неё. Значения в интеграции проверяемого проекта ни на что не влияют. Адрес сервера, токен и доверие сертификату задача берёт из интеграции проверяемого проекта: собственной или унаследованной.
Нажмите «Проверить настройки», чтобы войти на сервер с токеном из формы и запросить версию PT AI. При ошибке форма показывает код ответа сервера или ошибку соединения. Нажмите «Сохранить изменения», чтобы сохранить форму.
Включение сканирования
Добавьте действие ptai в файл .deckhouse_code/security-policies/policy.yml проекта политик безопасности:
scan_execution_policy:
- name: PT AI everywhere
enabled: true
rules:
- type: pipeline
branches: ["*"]
actions:
- scan: ptai
variables:
FE_SCANS_PTAI_MODULES: Configuration,Components,PatternMatching,StaticCodeAnalysis
FE_SCANS_PTAI_LANGUAGES: Java,PythonЗатем привяжите проект политик: для проекта — в разделе «Безопасность» → «Политики», для группы — в разделе «Настройки» → «Политика безопасности».
Сканирование можно также включить в редакторе политик флажком у сканирования PT AI. Редактор предлагает его, только когда для проекта или группы выше него настроена активная интеграция с PT AI; до тех пор редактор показывает «Для использования этого сканирования в политиках безопасности требуется активная интеграция с PT AI.» со ссылкой на настройку интеграции. Настройки сканирования в редакторе разбиты на блоки «Сканирование» и «Чёрный ящик и автопроверка»; «Вход на сайт» и «Прокси-сервер» находятся внутри блока «Чёрный ящик и автопроверка».
Политика не может задавать параметры интеграции. Действие ptai, в котором названы FE_SCANS_PTAI_SERVER_URL,
FE_SCANS_PTAI_TOKEN, FE_SCANS_PTAI_TLS_MODE, FE_SCANS_PTAI_CA_CERT, FE_SCANS_PTAI_IMAGE,
FE_SCANS_REGISTRY_LOGIN_VARIABLE или FE_SCANS_REGISTRY_PASSWORD_VARIABLE, не сохраняется.
Порог FE_SECURITY_FAIL_ON для ptai тоже не принимается: задачу роняет политика безопасности на сервере PT AI
(см. Когда задача завершается с ошибкой).
Опции политики проверяются при сохранении и ещё раз при формировании задачи, поэтому политика, записанная в репозиторий без редактора, с недопустимым значением роняет задачу до обращения к серверу:
ERROR: this scan cannot run the configuration the policy asks for: <причины>После применения политики каждый подходящий под неё пайплайн получает задачу ptai_scan
на этапе пайплайна, который называет политика, по умолчанию — fe-security-scanner.
Сканирование
Блок «Сканирование» обязателен: в каждом сканировании работает хотя бы один модуль белого ящика, а чёрный ящик добавляется к нему.
| Поле | Переменная | Что задаёт |
|---|---|---|
| «Модули анализа» | FE_SCANS_PTAI_MODULES | Модули белого ящика, через запятую. По умолчанию — Configuration, Components, PatternMatching, StaticCodeAnalysis: с ними сервер PT AI создаёт новый проект |
| «Языки приложения» | FE_SCANS_PTAI_LANGUAGES | Языки проекта, через запятую. Обязательно, как только заданы модули, адрес сайта или «Не загружать зависимости» |
| «Не сканировать файлы и папки, указанные в .gitignore» | FE_SCANS_PTAI_SKIP_GITIGNORE | true исключает из сканирования результаты сборки и каталоги со сторонним кодом, если репозиторий их игнорирует |
| «Не загружать зависимости» | FE_SCANS_PTAI_SKIP_DEPENDENCY_DOWNLOAD | true запрещает ядрам анализа загружать пакеты проекта для всех языков. Включите, если у сервера PT AI нет доступа к реестрам пакетов: иначе загрузка роняет сканирование |
| «Конфигурационный файл» | FE_SCANS_PTAI_SETTINGS_FILE | Путь к собственному файлу настроек проекта (см. Конфигурационный файл) |
| «Проект с конфигурационным файлом» | FE_SCANS_PTAI_SETTINGS_PROJECT | Репозиторий с этим файлом, в виде group/project |
Модули в редакторе называются так же, как в документации PT AI:
| Значение в политике | Название в редакторе |
|---|---|
StaticCodeAnalysis | «Статический анализ кода» |
PatternMatching | «Поиск по шаблонам» |
Components | «Поиск уязвимых компонентов» |
SoftwareCompositionAnalysis | «Поиск уязвимых компонентов и их использования» |
Configuration | «Поиск уязвимостей конфигурации» |
MaliciousCodeDetection | «Поиск вредоносного кода» |
SecretDetection | «Поиск секретов в коде» |
BlackBox в списке нет: модуль добавляется сам, когда задан адрес сайта.
Если в политике нет ни одного модуля белого ящика, задача берёт модули по умолчанию.
SecretDetection сервер принимает только вместе со StaticCodeAnalysis или PatternMatching;
без них сканирование завершается ошибкой INVALID_MODULES_FOR_SECRET_DETECTION.
Языки: Java, CSharp (CSharp (Windows, Linux)), CSharpWindows (CSharp (Windows)), Php, JavaScript, Python,
ObjectiveC, Swift, CAndCPlusPlus, Go, Kotlin, Sql, Ruby, Solidity, Scala, OneC.
Задача загружает на сервер рабочее дерево коммита без каталога .git, каталогов reports и security-reports
и файла .aiproj.json в корне репозитория.
Подмодули и объекты LFS попадают в загрузку так, как их выгрузил раннер;
это настраивается переменными CI/CD GIT_SUBMODULE_STRATEGY и GIT_LFS_SKIP_SMUDGE проекта.
Чёрный ящик
Блок «Чёрный ящик и автопроверка» сканирует работающее приложение в той же задаче, что и исходный код. Сайт открывает сервер PT AI, поэтому адрес должен быть доступен ему; внутренние адреса допускаются.
| Поле | Переменная | Что задаёт |
|---|---|---|
| «Адрес сайта» | FE_SCANS_PTAI_BLACKBOX_SITE | URL сайта, http или https. Заполнение поля включает чёрный ящик. Любая другая опция чёрного ящика без адреса сайта не сохраняется |
| «Режим поиска» | FE_SCANS_PTAI_BLACKBOX_LEVEL | Насколько подробно исследуется сайт: Fast, Normal или Full. По умолчанию — Normal |
| «Область сканирования» | FE_SCANS_PTAI_BLACKBOX_SCOPE | Насколько далеко от адреса уходит сканирование: Domain («Домен»), Folder («Папка») или Path («Путь»). По умолчанию — Path |
| «Проверка безопасности HTTPS-соединения» | FE_SCANS_PTAI_BLACKBOX_SSL_CHECK | Проверяет ли сервер PT AI сертификат сайта: true (по умолчанию) или false. Для сайта с сертификатом частного удостоверяющего центра добавьте этот центр в хранилище сертификатов сервера PT AI |
| «Автопроверка уязвимостей по завершении сканирования» | FE_SCANS_PTAI_BLACKBOX_AUTOCHECK | true запускает эксплойты для находок статического анализа на работающем сайте, чтобы подтвердить или опровергнуть каждую. Редактор включает её для нового блока |
| «Дополнительные HTTP-заголовки» | FE_SCANS_PTAI_BLACKBOX_HEADERS | По одному в строке, в виде Name: value |
| «Белый список адресов» | FE_SCANS_PTAI_BLACKBOX_ALLOWLIST | По одному в строке. Адрес без суффикса — шаблон; адрес|ExactMatch — точное совпадение, адрес|RegExp — регулярное выражение |
| «Чёрный список адресов» | FE_SCANS_PTAI_BLACKBOX_DENYLIST | В том же виде. Сюда добавляют ссылку выхода и всё, по чему сканирование переходить не должно |
Политику читает каждый проект, который она охватывает, поэтому секреты в ней не хранятся.
Поля с паролем, cookie и паролем прокси принимают ссылку на переменную CI/CD проверяемого проекта или его группы:
$NAME или ${NAME}. Раннер подставляет значение в задаче.
Политика с литеральным значением не сохраняется; имена переменных, начинающиеся с FE_, CI_ и GITLAB_, не принимаются.
Заголовки Authorization, Proxy-Authorization, Cookie, X-Api-Key и X-Auth-Token задаются так же:
значением заголовка должна быть вся ссылка.
FE_SCANS_PTAI_BLACKBOX_HEADERS: |
X-Trace: on
Authorization: $PTAI_SITE_AUTHORIZATIONBearer $TOKEN не принимается: храните Bearer вместе с токеном в переменной PTAI_SITE_AUTHORIZATION.
Если переменная, на которую ссылается политика, не существует или пуста, задача завершается с ошибкой, например:
ERROR: the CI/CD variable the security policy names for the sign-in password holds no value.Вход на сайт
Без входа сканирование видит только страницы, открытые анонимному посетителю. Редактор показывает только поля выбранного типа аутентификации.
| Поле | Переменная | Для типов | Что задаёт |
|---|---|---|---|
| «Аутентификация» | FE_SCANS_PTAI_BLACKBOX_AUTH_TYPE | все | none («Без аутентификации», по умолчанию), form («Form-аутентификация»), http («HTTP-аутентификация»), cookie («Cookie-аутентификация») |
| «Логин» | FE_SCANS_PTAI_BLACKBOX_AUTH_LOGIN | form, http | Логин на сайте. Обязательное |
| «Переменная пароля» | FE_SCANS_PTAI_BLACKBOX_AUTH_PASSWORD | form, http | Ссылка на маскируемую переменную CI/CD с паролем. Обязательное |
| «Переменная cookie» | FE_SCANS_PTAI_BLACKBOX_AUTH_COOKIE | cookie | Ссылка на маскируемую переменную CI/CD с сессионным cookie. Обязательное |
| «Адрес проверки» | FE_SCANS_PTAI_BLACKBOX_AUTH_VALIDATION_ADDRESS | http, cookie | Страница, доступная только вошедшему пользователю. Обязательное |
| «Искомая строка» | FE_SCANS_PTAI_BLACKBOX_AUTH_VALIDATION_TEMPLATE | form, cookie | Текст, который есть на странице при выполненном входе и которого нет без него. Обязательное для cookie |
| «Адрес формы» | FE_SCANS_PTAI_BLACKBOX_AUTH_FORM_ADDRESS | form | URL страницы с формой входа |
| «Обнаружение формы» | FE_SCANS_PTAI_BLACKBOX_AUTH_FORM_DETECTION | form | Auto («Автоматически», по умолчанию) или Manual («По заданным параметрам») для формы, которую сканирование не находит само |
| «XPath формы», «Идентификатор для логина», «Идентификатор для пароля» | FE_SCANS_PTAI_BLACKBOX_AUTH_FORM_XPATH, FE_SCANS_PTAI_BLACKBOX_AUTH_LOGIN_KEY, FE_SCANS_PTAI_BLACKBOX_AUTH_PASSWORD_KEY | form | XPath формы и имена её полей логина и пароля, при Manual |
Пример действия со входом HTTP-аутентификацией:
actions:
- scan: ptai
variables:
FE_SCANS_PTAI_LANGUAGES: Java
FE_SCANS_PTAI_BLACKBOX_SITE: https://staging.example.com
FE_SCANS_PTAI_BLACKBOX_AUTH_TYPE: http
FE_SCANS_PTAI_BLACKBOX_AUTH_LOGIN: scanner
FE_SCANS_PTAI_BLACKBOX_AUTH_PASSWORD: $PTAI_SITE_PASSWORD
FE_SCANS_PTAI_BLACKBOX_AUTH_VALIDATION_ADDRESS: https://staging.example.com/profileЧтобы пример работал:
- Откройте проект или группу и перейдите в «Настройки» → «CI/CD» → «Переменные».
- Создайте переменную
PTAI_SITE_PASSWORDсо значением пароля сайта и сделайте её маскируемой.
Если вход не удался, сканирование проходит только по открытым страницам,
а задача печатает ошибки сканирования, которые вернул сервер, в том числе AuthFailed.
Прокси-сервер
| Поле | Переменная | Что задаёт |
|---|---|---|
| «Адрес» | FE_SCANS_PTAI_BLACKBOX_PROXY_HOST | Имя хоста или IP-адрес прокси, не URL. Заполнение поля включает прокси |
| «Порт» | FE_SCANS_PTAI_BLACKBOX_PROXY_PORT | Число от 1 до 65535 |
| «Тип» | FE_SCANS_PTAI_BLACKBOX_PROXY_TYPE | Http (по умолчанию), Socks4 или Socks5 |
| «Логин» | FE_SCANS_PTAI_BLACKBOX_PROXY_LOGIN | Логин на прокси. С ним обязательна переменная пароля |
| «Переменная пароля» | FE_SCANS_PTAI_BLACKBOX_PROXY_PASSWORD | Ссылка на маскируемую переменную CI/CD с паролем прокси |
Конфигурационный файл
Для настройки, которую PT AI принимает, а политика не предлагает, задайте собственный файл настроек проекта PT AI
(формат .aiproj). Задача загружает его на сервер как есть вместо файла, который она собирает из политики.
Пока поле «Конфигурационный файл» заполнено, редактор показывает остальные настройки блоков «Сканирование»
и «Чёрный ящик и автопроверка» неактивными: задача их не читает, и при сохранении они не проверяются.
actions:
- scan: ptai
variables:
FE_SCANS_PTAI_SETTINGS_FILE: .aiproj.json
FE_SCANS_PTAI_SETTINGS_PROJECT: security/ptai-settings«Конфигурационный файл» — путь от корня репозитория: относительный, без сегмента .., без / в конце,
только символы ASCII. Имя файла может быть любым.
«Проект с конфигурационным файлом» определяет, откуда берётся файл:
- пустое поле — из проверяемого репозитория, на собираемом коммите. Такой файл проект может изменить сам, в том числе в ветке запроса на слияние;
group/project— с ветки по умолчанию этого проекта, токеном задачи. Проверяемые проекты изменить такой файл не могут.
Чтобы задача читала файл из другого проекта:
- Откройте проект с файлом.
- Перейдите в «Настройки» → «CI/CD» → «Права доступа токена заданий CI/CD».
- Добавьте проверяемые проекты или их группу.
Задача завершается с ошибкой до сканирования, если файла нет по указанному пути, файл пустой, проект не пускает задачи проверяемого проекта (HTTP 401 или 403) или файла нет на ветке по умолчанию (HTTP 404). Пароли и cookie, записанные в файл, видит каждый, у кого есть доступ к репозиторию.
Файл .aiproj.json из корня проверяемого репозитория задача на сервер не загружает.
Чтобы использовать его, назовите его в поле «Конфигурационный файл».
Проект PT AI
Задача сама создаёт на сервере проект и ветку, если их нет.
Проект называется полным путём проекта Deckhouse Code, в котором / заменён на ::
group/subgroup/app пишет в проект group:subgroup:app. Имя длиннее 255 символов обрезается до 240,
и в конец добавляется -p<PROJECT_ID>, где <PROJECT_ID> — ID проекта. Ветка называется именем ветки или тега пайплайна.
Точное имя задача пишет в журнал:
PT AI: scanning into project group:subgroup:app.Имена проектов в PT AI не зависят от регистра. Переименование или перенос проекта в Deckhouse Code меняет путь: следующее сканирование создаёт новый проект PT AI, а прежний остаётся на сервере со своей историей. Проект можно создать заранее в веб-интерфейсе PT AI, чтобы до первого сканирования настроить для него политику безопасности; назовите его по тому же правилу.
Каждое сканирование заменяет настройки проекта на сервере настройками из политики или конфигурационного файла, наложенными на настройки по умолчанию сервера: модули, языки и настройки языков, флажки блока «Сканирование» и чёрный ящик целиком. Изменение этих настроек в веб-интерфейсе PT AI действует до следующего сканирования. Задача не меняет политику безопасности проекта, флажок «Применять политику безопасности», приоритет проекта и предпочитаемые агенты: они задаются в веб-интерфейсе PT AI.
Очередь сканирований
Сканирует агент PT AI на стороне сервера; задача ждёт результата и всё это время занимает раннер. Время задачи складывается из ожидания в очереди сервера и самого сканирования, поэтому задайте проекту тайм-аут задач с запасом.
Задачи ptai_scan одной ветки одного проекта выполняются по очереди: следующая ждёт в статусе waiting_for_resource,
не занимая раннер и не расходуя тайм-аут. Пайплайн ветки и пайплайн запроса на слияние из неё ждут друг друга.
Если ветку уже сканирует кто-то другой, например из веб-интерфейса PT AI, задача повторяет попытку раз в 30 секунд,
пока ветка не освободится или не истечёт тайм-аут задачи.
Отменённая задача, в том числе отменённая новым пайплайном, останавливает своё сканирование на сервере. Чтобы при нескольких отправках подряд сканировался только последний коммит, включите в проекте «Настройки» → «CI/CD» → «Пайплайны» → «Автоматическая отмена избыточных пайплайнов». Задача, остановленная по тайм-ауту, сканирование не останавливает: уберите его из очереди в веб-интерфейсе PT AI на вкладке Агенты сканирования.
Когда задача завершается с ошибкой
Порога по уровню находок у сканирования PT AI нет: все находки попадают в отчёты, а задачу роняет политика безопасности проекта на сервере PT AI. После завершённого сканирования задача читает её вердикт:
Rejected— политика нарушена, задача завершается с ошибкой «the PT AI security policy rejected the scan»;Confirmed— политика соблюдена;None— проверка политики у проекта выключена.
Сервер записывает вердикт через несколько секунд после сканирования, поэтому при включённой проверке задача ждёт его до 120 секунд. Вердикт, который не удалось получить или прочитать, роняет задачу с сообщением «the PT AI security policy verdict could not be read». Проверку политики включают и правила задают в веб-интерфейсе PT AI: «Настройка» → «Политика безопасности» проекта.
Задача также завершается с ошибкой, если:
- интеграции нет, она неактивна или в ней нет адреса или токена: «ERROR: the PT AI integration is missing or inactive: the job got no server address or token.»;
- первое обращение к серверу не удалось: задача печатает сообщение aictl и «Check the server address, the token and the certificate trust in the PT AI integration.»;
- сервер отклонил настройки сканирования: «ERROR: the PT AI server refused the settings aictl does not send: …» с ответом сервера;
- сканирование завершилось с ошибкой или отчёт не получен: «Failing job: the PT AI scan did not finish or produced no readable report.»; причину, которую назвал сервер, задача печатает после строки «PT AI: the server reports for this scan:».
Отчёты
Задача публикует артефакты:
security-reports/gitlab/ptai.json— отчёт безопасности SAST, отчёт PT AI в формате GitLab без изменений; его находки попадают в отчёт об уязвимостях;security-reports/junit/ptai.xml— отчёт о тестах для вкладки «Тесты» пайплайна;reports/ptai.jsonиreports/ptai.sarif— собственный JSON-отчёт PT AI и SARIF, как их отдал сервер.
На вкладке «Тесты» каждая находка — отдельный тест. Набор тестов — модуль PT AI,
имя теста — тип находки и место, например Cross-Site Scripting [js/server.js:28];
у находки чёрного ящика вместо места адрес, у уязвимого компонента — компонент, версия, CVE и манифест.
Уровень находки — свойство severity теста: Critical, High, Medium, Low или Info.
Подробности теста содержат CWE и OWASP, код и точку входа, CVE с оценкой CVSS, текущее и рекомендуемое значение конфигурации,
вызовы вредоносного кода, адрес и эксплойт чёрного ящика, признак новой находки и ссылку на сканирование в веб-интерфейсе PT AI.
Если политика безопасности PT AI отклонила сканирование, первым идёт отдельный тест с её вердиктом.
Модуль, код, CVE и адрес чёрного ящика есть только в отчёте о тестах.
Отчёты не маскируются: эксплойт находки чёрного ящика в reports/ptai.json и на вкладке «Тесты» содержит строку запроса,
заголовок Host и тело запроса.
Диагностика
Сканирование не появляется в пайплайне
Проверьте, что:
- интеграция с PT AI активна для проекта или унаследована от группы выше него;
- проект политик привязан к проекту или группе выше него, и правило политики подходит под пайплайн;
- в действии
ptaiполитики нет параметров интеграции и порогаFE_SECURITY_FAIL_ON.
Проверка настроек или первое обращение к серверу не проходит
Проверьте URL сервера, токен (без пробелов и переносов строк, не отозван, срок не истёк, разрешение Для легкого агента и плагинов CI/CD) и «Доверие к сертификату». «Проверить настройки» показывает код ответа сервера, например HTTP 401 для неверного токена.
Проекта нет в PT AI
Ищите полный путь проекта с : вместо /. Точное имя задача пишет в журнал строкой «PT AI: scanning into project …».
«cannot downgrade 1.11 -> 1.10»
Конфигурационный файл из поля «Конфигурационный файл» содержит Version 1.11, а сервер — PT AI 6.0.
Укажите в файле версию 1.9 или 1.10.
Отчёт не получен после повторного сканирования
Сканирование на сервере в состоянии Done, а задача завершается с ошибкой «the PT AI scan did not finish or produced no readable report»
и печатает код REPORT_GENERATE_FAILED.
Запустите полное сканирование: измените настройки сканирования в политике, и следующий запуск задачи просканирует ветку полностью.