Документация / Руководство пользователя / Объекты
Импорт отчётов
Core принимает отчёты сканеров тремя путями: автоматически из Radar, вручную из интерфейса и через API из CI или скриптов. Все три пути ведут в один эндпоинт POST /api/v1/scan/import/ и обрабатываются одинаково.
Как обрабатывается отчёт
- Core проверяет запрос: имя сканера, продукт, ассеты, аудит. Ошибка - сразу ответ 400.
- Core выбирает продукт, создаёт недостающие продукт, тип продукта, ассеты и аудит.
- Core ставит отчёт в очередь обработчика импорта и отвечает 202.
- Обработчик импорта разбирает отчёт логикой разбора, выбранной для сканера, и записывает уязвимости. Что происходит дальше, описано в Жизненный цикл уязвимости.
Разбор асинхронный: ответ 202 значит "отчёт принят", а не "уязвимости записаны".
Сканеры и логика разбора
Поле scanner_name (в интерфейсе "Сканер") - имя сканера из "Глобальные настройки" → "Сканеры". Имя должно совпадать точно, с учётом регистра и пробелов. Неизвестное имя - ответ 400.
Как разбирается отчёт, задаёт настройка сканера "Настройка логики разбора отчётов":
| Режим | Что значит |
|---|---|
| "Нет" | Логика разбора не задана |
| "Встроенный" | Встроенный разборщик. Доступен, если он есть у сканера |
| "Вендорский" | Разборщик, поставляемый с обработчиком импорта. Доступен, если он есть у сканера |
| "Свой" | Конфигурация из мастера разбора |
"Своё сопоставление критичности" сканера применяется только во встроенном разборе. Настройки сканера описаны в Настройки сканеров в Core.
Сканеры, которые есть в Core после установки:
| Тип сканера | Имена для scanner_name |
|---|---|
| SAST | Bandit Scan, Checkov Scan, CodeQL Scan (SARIF), ESLint Scan, GitLab Bandit, GitLab ESLint, GitLab KICS, GitLab Semgrep, Gosec Scanner, Hadolint, Mobsfscan, PHPCodeSniffer, Semgrep JSON Report, Snyk, Terrascan Scan |
| SCA | Dependency-Track, GitLab Gemnasium, Retire.js Scan, Trivy Scan, Vulners Trivy |
| Секреты | Gitleaks Scan, GitLab Gitleaks, Trufflehog3 Scan |
| DAST | Acunetix Scan, Arachni Scan, Burp Enterprise Scan, GitLab OWASP Zap, Nessus, Nuclei Scan |
| Инфра | Naabu, Subfinder |
| Облако | AWS Security Hub Scan, Prowler |
| Не указан | Manual - уязвимости, созданные вручную; SecurityFlow - отчёт другой установки Core |
Какой разборщик есть у конкретного сканера, видно в его карточке: "Встроенный парсер", "Парсер вендора", "Свой парсер". Какой сканер какой формат отчёта выдаёт и какой джобой Radar запускается - в Справочник сканеров.
Импорт из интерфейса
Где: "Продукты" → продукт → "Импорт скана". Нужно право "Уязвимости" - "Изменение".
| Поле | Обязательное | Что указать |
|---|---|---|
| "Сканер" | да | Сканер из списка |
| "Файл отчёта" | да | Файл отчёта сканера |
| Поля ассетов | нет | "SSH URL репозитория", Docker-образ, домен, хост, облачный аккаунт |
| "Ветка" | нет | Ветка репозитория |
| "Тег" | нет | Тег Docker-образа |
| "Своё поле" | нет | Произвольная метка до 300 символов, например номер сборки |
- Откройте продукт и вкладку "Импорт скана".
- Выберите "Сканер".
- Перетащите файл в поле "Файл отчёта" или нажмите "Выбрать файл".
- Укажите хотя бы один ассет, если нужен аудит.
- Отправьте форму.
Core ответит "Отчёт принят в очередь импорта, скоро обработается". Дальше - "Импортировать другой отчет" или "Вернуться на страницу продукта".
Аудит при импорте
"Импорт отчёта создаст запись об аудите. Без указания ассета статистика аудита будет неточной." Если ни один ассет не указан, аудит не создаётся: форма показывает "Аудит недоступен".
Смена продукта в форме очищает уже заполненные значения ассетов.
Импорт через API
Запрос
POST /api/v1/scan/import/ HTTP/1.1
Host: core.example.ru
Authorization: Token <токен>
Content-Type: multipart/form-data
Аутентификация: заголовок Authorization: Token <токен> или cookie access интерфейса. Токен берётся в "Профиль" → "Токен аутентификации API", для этого у роли должно быть право "API токен" - "Просмотр".
Пользователю, от имени которого идёт импорт, нужно право "Уязвимости" - "Изменение".
Служебная учётная запись
Для CI заведите отдельного пользователя с ролью "Уязвимости" - "Изменение", "API токен" - "Просмотр" и областью только нужных продуктов. Токен храните в защищённой переменной CI.
Поля формы
| Поле | Обязательное | Описание |
|---|---|---|
scanner_name |
да | Имя сканера из Core, см. таблицу выше |
file |
file или report |
Файл отчёта. Читается как текст в UTF-8 |
report |
file или report |
Текст отчёта строкой вместо файла |
product_name |
вместе с product_type |
Имя продукта. Нет такого - создаётся |
product_type |
вместе с product_name |
Тип продукта. Нет такого - создаётся |
repository |
нет | SSH URL репозитория |
docker_image |
нет | Образ без тега |
domain |
нет | Домен без схемы |
host |
нет | Хост |
cloud_account |
нет | Имя облачного аккаунта |
branch |
нет | Ветка репозитория |
docker_image_tag |
нет | Тег образа |
custom_field |
нет | Своё поле, до 300 символов |
audit_id |
нет | Номер существующего аудита, в который записать уязвимости |
Указанные ассеты создаются в выбранном продукте, если их там нет.
Как Core выбирает продукт
- Заданы оба поля
product_nameиproduct_type- используется этот продукт. Если задано только одно из двух, оно игнорируется. - Продукт не задан, указан ровно один ассет - используется продукт этого ассета. Если ассет есть в нескольких продуктах - ошибка 400. Если ассета нет нигде - используется продукт по умолчанию.
- Продукт не задан и указано несколько ассетов - ошибка 400.
- Продукт не задан, ассетов нет - ошибка 400. Продукт по умолчанию в этом случае не используется: он подставляется только для единственного ассета, которого нет ни в одном продукте.
При включённых уникальных ассетах product_name и product_type игнорируются всегда: продукт выбирается по шагам 2-4 (Уникальные ассеты).
Аудит
| Запрос | Результат |
|---|---|
Передан audit_id |
Уязвимости записываются в этот аудит. Аудита нет - ошибка 400 |
audit_id нет, указан хотя бы один ассет |
Создаётся новый аудит, его номер - в ответе |
audit_id нет, ассетов нет |
Аудит не создаётся, в ответе "audit_id": null |
Чтобы собрать отчёты нескольких сканеров одного прогона в один аудит, передайте первому импорту ассет, возьмите audit_id из ответа и передайте его остальным.
Примеры
Отчёт Gitleaks по репозиторию, продукт задан явно:
curl -sS -X POST "https://core.example.ru/api/v1/scan/import/" \
-H "Authorization: Token <токен>" \
-F "scanner_name=Gitleaks Scan" \
-F "product_name=payments" \
-F "product_type=Common" \
-F "repository=git@gitlab.example.ru:team/payments.git" \
-F "branch=main" \
-F "file=@gitleaks.json"
Отчёт Trivy по образу, продукт определяется по ассету:
curl -sS -X POST "https://core.example.ru/api/v1/scan/import/" \
-H "Authorization: Token <токен>" \
-F "scanner_name=Trivy Scan" \
-F "docker_image=registry.example.ru/team/payments" \
-F "docker_image_tag=1.4.2" \
-F "file=@trivy-image.json"
Отчёт текстом в поле report (curl подставляет содержимое файла после <):
curl -sS -X POST "https://core.example.ru/api/v1/scan/import/" \
-H "Authorization: Token <токен>" \
-F "scanner_name=Semgrep JSON Report" \
-F "repository=git@gitlab.example.ru:team/payments.git" \
-F "audit_id=1842" \
-F "report=<semgrep.json"
Ответ
Успех - 202 Accepted:
{
"product_name": "payments",
"product_type": "Common",
"scanner_name": "Gitleaks Scan",
"branch": "main",
"docker_image_tag": "",
"custom_field": "",
"audit_id": 1842,
"assets": {
"repository": "git@gitlab.example.ru:team/payments.git",
"docker_image": "",
"domain": "",
"cloud_account": "",
"host": ""
},
"user_name": "ci-import",
"user_id": 12
}
product_name и product_type в ответе - продукт, который Core выбрал фактически.
Ошибки
| Код | Когда | Тело ответа | Что сделать |
|---|---|---|---|
| 400 | Неизвестный сканер | {"scanner_name": ["\"Gitleak\" is not a valid choice."]} |
Сверьте имя с "Глобальные настройки" → "Сканеры" |
| 400 | Нет ни file, ни report |
{"non_field_errors": ["At least one of the 'file' or 'report' fields is required."]} |
Передайте отчёт |
| 400 | Аудита с таким номером нет | {"audit_id": ["Audit with id 15 does not exist"]} |
Проверьте audit_id или не передавайте его |
| 400 | Несколько ассетов без продукта | {"detail": "Either specify product and product type or a single asset"} |
Передайте product_name и product_type или один ассет |
| 400 | Ассет есть в нескольких продуктах | {"detail": "Only one specified asset should exist"} |
Передайте продукт явно |
| 400 | Продукт не определён | ["Specify product name and product type or set default product"] |
Передайте product_name и product_type или один ассет. Для нового ассета без продукта назначьте продукт по умолчанию |
| 400 | custom_field длиннее 300 символов |
{"custom_field": ["Ensure this field has no more than 300 characters."]} |
Сократите значение |
| 401 | Нет заголовка Authorization |
{"detail": "No credentials provided"} |
Добавьте заголовок |
| 401 | Неверный токен | {"detail": "Invalid token."} |
Возьмите актуальный токен в профиле, после ротации старый не работает |
| 403 | Нет права "Уязвимости" - "Изменение" | {"detail": "You do not have permission to perform this action."} |
Добавьте роль пользователю |
| 413 | Тело запроса больше лимита прокси | ответ прокси | Уменьшите отчёт или согласуйте лимит прокси, см. Размер отчёта |
Размер отчёта
Отчёт целиком передаётся в очередь RabbitMQ аргументом задачи. Поэтому размер ограничен двумя лимитами: размером сообщения брокера (max_message_size, в RabbitMQ 3.13 по умолчанию 128 МиБ) и лимитом тела запроса на прокси перед Core. Лимит прокси держите ниже лимита брокера: в типовой установке ingress принимает до 100 МБ (Системные требования).
Файл должен быть в кодировке UTF-8.
Как узнать результат
- Откройте аудит из ответа: "Аудиты" → аудит, или список уязвимостей с фильтром
affected_by__content_type__model=audit&affected_by__object_id=<audit_id>. - Включите уведомление "Уведомление об импортированных уязвимостях" (Уведомления).
Ошибки разбора отчёта пишутся в журнал обработчика импорта (Мониторинг и журналы). Если аудит остался без уязвимостей, проверьте этот журнал.
Импорт из CI
Готовые шаблоны GitLab CI и Jenkins - в Загрузка отчётов из CI. Проверка мерж-реквеста с запуском Radar - в Security checker.
Мастер разбора своего формата
Мастер настраивает разбор JSON-отчёта сканера, для которого в Core нет разборщика. Нужны платная лицензия и право "Глобальные настройки" - "Изменение".
Перед началом
- Получите пример отчёта сканера в JSON, лучше с несколькими уязвимостями разных типов.
- Создайте сканер: "Глобальные настройки" → "Сканеры" → вкладка "Свои сканеры" → "Создать сканер". Укажите "Название" (это и есть
scanner_nameпри импорте), "Тип сканера", "Теги". - В карточке сканера нажмите "Настроить свою логику разбора". Откроется "Конфигурация импортера".
Свой разборщик можно настроить и для сканера со встроенным разбором. Интерфейс предупреждает: "Меняйте с осторожностью: небольшое различие даст дубликаты" - другие значения полей дают уязвимостям другой отпечаток (Дедупликация).
Шаги мастера
| Шаг | Что делать |
|---|---|
| "Шаг 1. Выберите отчёт в формате JSON" | Загрузите пример отчёта (.json). Если файл соответствует схеме SARIF, мастер предложит "Применить стандартную структуру" |
| "Шаг 2. Укажите путь к списку уязвимостей" | Пройдите по объекту отчёта до массива уязвимостей: кнопками ключа и индекса выберите поле объекта или элемент массива. Если ключи объекта сами являются значениями поля, отметьте это отдельной кнопкой. Итог - "Путь к уязвимостям" |
| "Шаг 3. Путь к описанию правил" | Нужен, если отчёт хранит правила отдельно от уязвимостей, как SARIF. Укажите "Путь к списку правил", "Имя ключа", "Путь к идентификатору правила" и "Путь к идентификатору правила в уязвимости". Для простых отчётов шаг пропустите |
| "Шаг 4. Поля уязвимости" | Сопоставьте поля уязвимости Core с путями в отчёте. "Шаблон описания" собирает описание из нескольких полей, Markdown допускается. "Критичность" сопоставляет значения сканера уровням Core ("Критичность от сканера" → "Критичность"). "Список CWE" и "Поиск подстроки CWE" извлекают CWE. Несколько значений объединяются кнопкой "Объединить" с выбранным "Разделитель". Своё поле - "Добавить своё поле" |
| "Шаг 5. Проверка и сохранение" | "Загрузить предпросмотр": мастер разбирает пример и показывает уязвимости, "Ассеты для добавления из полученных уязвимостей: {N}" и ошибки ("Парсинг отчёта завершился с {N} ошибками"). Исправьте ошибки и сохраните |
"Включить поиск ассетов" - разборщик ищет продукты, в которых есть ассет из уязвимости. Доступно, только если в структуре указано ровно одно поле ассета.
Кнопки "Перейти в пошаговый режим" и "Выйти из пошагового режима" включают и выключают пошаговый режим мастера.
Конфигурация, поставляемая с обработчиком импорта, открывается только для просмотра: "Это конфигурация импортера, предоставленная вендором, доступна только для просмотра."
После сохранения
- В карточке сканера выберите для своего разборщика "Использовать по умолчанию" - режим разбора станет "Свой".
- Импортируйте пример отчёта через "Импорт скана" и проверьте уязвимости.
- Если отчёт приходит из Radar, заведите джобу с
SCAN_TYPE, равным имени сканера (Свой и коммерческий сканер).
API мастера
| Метод и путь | Назначение |
|---|---|
POST /api/v1/custom-importer/parse-report/ |
Предпросмотр: тело {"report_config": {...}, "report": {...}}, ответ - найденные уязвимости и ассеты, ошибки структуры и полей, errors_count |
GET, POST /api/v1/custom-importer/ |
Конфигурации разбора, фильтр ?scanner=<id> |
PATCH, DELETE /api/v1/custom-importer/{id}/ |
Изменение и удаление. Вендорскую конфигурацию изменить нельзя |