classifyre-cli 0.4.7__tar.gz → 0.4.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/.turbo/turbo-build.log +1 -1
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/PKG-INFO +1 -1
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/package.json +1 -1
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/pyproject.toml +1 -1
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/rest.py +71 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_outputs.py +3 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/uv.lock +182 -150
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/.gitignore +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/.python-version +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/README.md +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/main.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/config.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_feature_extraction.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/main.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/models/generated_input.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/console.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/outputs/file.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sandbox/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sandbox/runner.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/telemetry.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/src/utils/validation.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/conftest.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.4.7 → classifyre_cli-0.4.9}/tests/utils/test_file_parser.py +0 -0
|
@@ -1,17 +1,85 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import random
|
|
4
5
|
from typing import Any, Literal, cast
|
|
5
6
|
from urllib.parse import urljoin
|
|
6
7
|
|
|
7
8
|
import requests # type: ignore[import-untyped]
|
|
8
9
|
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
from requests.adapters import HTTPAdapter
|
|
11
|
+
from urllib3.util.retry import Retry # type: ignore[import-untyped]
|
|
9
12
|
|
|
10
13
|
from .base import OutputRuntimeContext, OutputType
|
|
11
14
|
|
|
12
15
|
logger = logging.getLogger(__name__)
|
|
13
16
|
|
|
14
17
|
|
|
18
|
+
class _JitteredRetry(Retry):
|
|
19
|
+
"""urllib3 Retry subclass that adds ±25 % multiplicative jitter to the
|
|
20
|
+
computed backoff so that multiple concurrent CLI jobs do not all retry
|
|
21
|
+
at exactly the same moment (thundering-herd mitigation).
|
|
22
|
+
|
|
23
|
+
The jitter is applied *after* the standard exponential backoff formula
|
|
24
|
+
and the backoff_max cap, so it never pushes the delay above
|
|
25
|
+
backoff_max * 1.25.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_JITTER_FACTOR: float = 0.25
|
|
29
|
+
|
|
30
|
+
def get_backoff_time(self) -> float: # type: ignore[override]
|
|
31
|
+
base = super().get_backoff_time()
|
|
32
|
+
if base == 0:
|
|
33
|
+
return 0.0
|
|
34
|
+
lo = base * (1 - self._JITTER_FACTOR)
|
|
35
|
+
hi = base * (1 + self._JITTER_FACTOR)
|
|
36
|
+
return random.uniform(lo, hi)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Retry policy for CLI → API REST calls.
|
|
40
|
+
#
|
|
41
|
+
# What we retry and why:
|
|
42
|
+
# connect=8 — pod restarted / not yet ready (RemoteDisconnected, ConnectionReset,
|
|
43
|
+
# ConnectTimeout). Request never reached the application.
|
|
44
|
+
# read=8 — API is under load and slow to respond (ReadTimeout). Safe to retry
|
|
45
|
+
# because all endpoints are idempotent (bulk ingest is upsert-based,
|
|
46
|
+
# status/findings updates are set-operations).
|
|
47
|
+
# status=8 — transient HTTP errors from an overloaded or restarting API:
|
|
48
|
+
# 408 Request Timeout - API-level timeout
|
|
49
|
+
# 429 Too Many Requests - rate-limited / backpressure
|
|
50
|
+
# 502 Bad Gateway - proxy has no upstream yet
|
|
51
|
+
# 503 Service Unavail. - under-pressure / pod not ready
|
|
52
|
+
# 504 Gateway Timeout - upstream took too long
|
|
53
|
+
#
|
|
54
|
+
# backoff_factor=2, backoff_max=60: exponential cap at 60 s, with ±25 % jitter
|
|
55
|
+
# (see _JitteredRetry). Approximate wait schedule between attempts:
|
|
56
|
+
# attempt 1 → immediate (0 s)
|
|
57
|
+
# attempt 2 → ~2 s
|
|
58
|
+
# attempt 3 → ~4 s
|
|
59
|
+
# attempt 4 → ~8 s
|
|
60
|
+
# attempt 5 → ~16 s
|
|
61
|
+
# attempt 6 → ~32 s
|
|
62
|
+
# attempt 7 → ~60 s (capped)
|
|
63
|
+
# attempt 8 → ~60 s (capped)
|
|
64
|
+
# Total extra wait: ~182 s (~3 min) — covers extended load spikes on a
|
|
65
|
+
# single-node VPS before event-loop pressure drops. Worst-case a single
|
|
66
|
+
# call costs 8 * 120 s + 182 s = ~18 min, acceptable for long-running scans.
|
|
67
|
+
#
|
|
68
|
+
# POST and PATCH are explicitly allowed: without this urllib3 only retries
|
|
69
|
+
# idempotent methods (GET/HEAD) by default.
|
|
70
|
+
_RETRY_POLICY = _JitteredRetry(
|
|
71
|
+
total=8,
|
|
72
|
+
connect=8,
|
|
73
|
+
read=8,
|
|
74
|
+
status=8,
|
|
75
|
+
backoff_factor=2,
|
|
76
|
+
backoff_max=60,
|
|
77
|
+
status_forcelist={408, 429, 502, 503, 504},
|
|
78
|
+
allowed_methods={"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"},
|
|
79
|
+
raise_on_status=False,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
15
83
|
def _drop_none_recursive(value: Any) -> Any:
|
|
16
84
|
if isinstance(value, dict):
|
|
17
85
|
return {key: _drop_none_recursive(item) for key, item in value.items() if item is not None}
|
|
@@ -63,6 +131,9 @@ class RestOutputSink:
|
|
|
63
131
|
self.base_url = base_url.rstrip("/")
|
|
64
132
|
self.timeout_sec = timeout_sec
|
|
65
133
|
self.session = requests.Session()
|
|
134
|
+
adapter = HTTPAdapter(max_retries=_RETRY_POLICY)
|
|
135
|
+
self.session.mount("http://", adapter)
|
|
136
|
+
self.session.mount("https://", adapter)
|
|
66
137
|
self._runner_id = context.runner_id
|
|
67
138
|
self._seen_hashes: set[str] = set()
|
|
68
139
|
|
|
@@ -158,6 +158,9 @@ class _FakeSession:
|
|
|
158
158
|
self.responses = responses
|
|
159
159
|
self.calls: list[dict[str, Any]] = []
|
|
160
160
|
|
|
161
|
+
def mount(self, prefix: str, adapter: Any) -> None: # noqa: ARG002
|
|
162
|
+
"""Absorb HTTPAdapter.mount() calls from RestOutputSink.__init__."""
|
|
163
|
+
|
|
161
164
|
def request(
|
|
162
165
|
self,
|
|
163
166
|
method: str,
|