diff --git a/config.yaml b/config.yaml index 0153ed0..f48daed 100644 --- a/config.yaml +++ b/config.yaml @@ -49,8 +49,10 @@ pt_sandbox: # Отключать проверку SSL-сертификата (для самоподписанных сертификатов) verify_ssl: true - # Endpoint для загрузки и запуска проверки файла - upload_endpoint: "/api/v1/scan/checkFile" + # Endpoint для загрузки файла во временное хранилище + upload_endpoint: "/api/v1/storage/uploadScanFile" + # Endpoint для запуска проверки загруженного файла + scan_endpoint: "/api/v1/analysis/createScanTask" # Заголовок и значение для авторизации в Sandbox auth_header: "X-API-Key" @@ -59,11 +61,6 @@ pt_sandbox: # Формат multipart-поля с файлом file_field_name: "file" - # Дополнительные поля, передаваемые с файлом (опционально) - extra_fields: - # Например: source: "gitflic" - source: "gitflic" - # Таймауты (секунды) timeout_connect: 30 timeout_read: 300 # 5 минут на передачу большого файла diff --git a/main.py b/main.py index 04bbe0b..31ffe51 100644 --- a/main.py +++ b/main.py @@ -355,11 +355,11 @@ class PTSandboxClient: def __init__(self, cfg: dict): sb_cfg = cfg["pt_sandbox"] self.base_url = sb_cfg["base_url"].rstrip("/") - self.upload_endpoint = sb_cfg.get("upload_endpoint", "/api/v1/scan/checkFile") + self.upload_endpoint = sb_cfg.get("upload_endpoint", "/api/v1/storage/uploadScanFile") + self.scan_endpoint = sb_cfg.get("scan_endpoint", "/api/v1/analysis/createScanTask") self.auth_header = sb_cfg.get("auth_header", "X-API-Key") self.api_key = sb_cfg.get("api_key", "") self.file_field_name = sb_cfg.get("file_field_name", "file") - self.extra_fields = sb_cfg.get("extra_fields", {}) self.timeout_connect = sb_cfg.get("timeout_connect", 30) self.timeout_read = sb_cfg.get("timeout_read", 300) self.verify_ssl = sb_cfg.get("verify_ssl", True) @@ -378,54 +378,94 @@ class PTSandboxClient: self._session.mount("https://", InsecureSslAdapter()) logger.warning("Проверка SSL-сертификата отключена для PT Sandbox") + def _request(self, method: str, url: str, **kwargs) -> requests.Response: + """Выполнить HTTP-запрос к API PT Sandbox.""" + try: + resp = self._session.request(method, url, **kwargs) + if resp.status_code == 401: + logger.error("Sandbox 401: неверный или неактивный токен доступа") + return resp + resp.raise_for_status() + return resp + except requests.exceptions.RequestException as exc: + logger.error("Ошибка запроса Sandbox %s: %s", url, exc) + raise + def upload_file(self, file_path: Path) -> Optional[str]: """ - Отправить файл в PT Sandbox на анализ (одним запросом — загрузка + запуск проверки). - Возвращает ID задачи (scan_id) или None при ошибке. + Двухшаговый流程: загрузить файл в Sandbox, затем запустить проверку. + Возвращает scan_id или None при ошибке. """ - params = { - "file_name": file_path.name, - "async_result": "true", - } - url = f"{self.base_url}{self.upload_endpoint}" - logger.info("Отправка файла в Sandbox: %s (%d bytes)", - file_path.name, file_path.stat().st_size) + # Шаг 1: загрузка файла + upload_url = f"{self.base_url}{self.upload_endpoint}" + logger.info("Шаг 1/2: загрузка файла %s в Sandbox", file_path.name) try: with open(file_path, "rb") as fh: files = {self.file_field_name: (file_path.name, fh)} - fields = dict(self.extra_fields) - resp = self._session.post( - url, params=params, files=files, data=fields, + upload_url, files=files, timeout=(self.timeout_connect, self.timeout_read), verify=self.verify_ssl, ) - if resp.status_code in (200, 201, 202): - result = resp.json() - data = result.get("data", {}) - scan_id = data.get("scan_id") or result.get("task_id") or result.get("id") - logger.info("Файл %s принят Sandbox. scan_id=%s", - file_path.name, scan_id) - return str(scan_id) if scan_id else None - else: - logger.error("Sandbox вернул %s: %s", + if resp.status_code not in (200, 201, 202): + logger.error("Sandbox ошибка загрузки: %s: %s", resp.status_code, resp.text[:500]) return None - except requests.exceptions.Timeout: - logger.error("Тайм-аут при отправке файла %s в Sandbox", - file_path.name) - return None - except requests.exceptions.RequestException as exc: - logger.error("Ошибка отправки файла %s в Sandbox: %s", + upload_data = resp.json().get("data", {}) + file_uri = upload_data.get("file_uri") + if not file_uri: + logger.error("Sandbox: не получен file_uri в ответе на загрузку") + return None + logger.debug("Файл загружен, file_uri=%s", file_uri) + + except requests.RequestException as exc: + logger.error("Ошибка загрузки файла %s в Sandbox: %s", file_path.name, exc) return None except OSError as exc: logger.error("Ошибка чтения файла %s: %s", file_path.name, exc) return None + # Шаг 2: запуск проверки + scan_url = f"{self.base_url}{self.scan_endpoint}" + logger.info("Шаг 2/2: запуск проверки файла %s", file_path.name) + + try: + body = { + "file_uri": file_uri, + "file_name": file_path.name, + "async_result": True, + "short_result": True, + } + resp = self._session.post( + scan_url, json=body, + timeout=(self.timeout_connect, self.timeout_read), + verify=self.verify_ssl, + ) + + if resp.status_code not in (200, 201, 202): + logger.error("Sandbox ошибка запуска проверки: %s: %s", + resp.status_code, resp.text[:500]) + return None + + scan_data = resp.json().get("data", {}) + scan_id = scan_data.get("scan_id") or scan_data.get("id") + if scan_id: + logger.info("Файл %s принят Sandbox. scan_id=%s", + file_path.name, scan_id) + return str(scan_id) + + logger.error("Sandbox: не получен scan_id в ответе") + return None + + except requests.RequestException as exc: + logger.error("Ошибка запуска проверки %s в Sandbox: %s", + file_path.name, exc) + return None + # ───────────────────────────────────────────────────────── # Вспомогательные функции