From 104fe5eae176525068894b90f33ea83ecadc66f6 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Wed, 3 Dec 2025 09:32:42 +0000 Subject: [PATCH 1/8] create GTI modules --- GoogleThreatIntelligence/CHANGELOG.md | 8 + GoogleThreatIntelligence/Dockerfile | 16 + GoogleThreatIntelligence/README.md | 56 + .../action_get_comments.json | 109 + .../action_get_file_behaviour.json | 250 ++ .../action_get_ioc_report.json | 129 + .../action_get_passive_dns.json | 171 + ...action_get_vulnerability_associations.json | 305 ++ .../action_get_vulnerability_report.json | 153 + .../action_scan_file.json | 89 + GoogleThreatIntelligence/action_scan_url.json | 89 + GoogleThreatIntelligence/actions.csv | 10 + .../googlethreatintelligence/__init__.py | 0 .../googlethreatintelligence/client.py | 511 +++ .../googlethreatintelligence/get_comments.py | 60 + .../get_file_behaviour.py | 34 + .../get_ioc_report.py | 60 + .../get_passive_dns.py | 30 + .../get_vulnerability_associations.py | 32 + .../get_vulnerability_report.py | 32 + .../googlethreatintelligence/sample.exe | 0 .../googlethreatintelligence/scan_file.py | 41 + .../googlethreatintelligence/scan_url.py | 41 + .../googlethreatintelligence/script_v2.py | 608 +++ .../vt_test_results.json | 129 + GoogleThreatIntelligence/logo.svg | 1 + GoogleThreatIntelligence/main.py | 24 + GoogleThreatIntelligence/manifest.json | 29 + GoogleThreatIntelligence/poetry.lock | 3876 +++++++++++++++++ GoogleThreatIntelligence/pyproject.toml | 53 + GoogleThreatIntelligence/tests/__init__.py | 0 GoogleThreatIntelligence/tests/conftest.py | 16 + .../tests/test_get_comments.py | 118 + .../tests/test_get_file_behaviour.py | 98 + .../tests/test_get_ioc_report.py | 313 ++ .../tests/test_get_passive_dns.py | 107 + .../test_get_vulnerability_associations.py | 109 + .../tests/test_get_vulnerability_report.py | 78 + .../tests/test_scan_file.py | 156 + .../tests/test_scan_url.py | 178 + 40 files changed, 8119 insertions(+) create mode 100644 GoogleThreatIntelligence/CHANGELOG.md create mode 100644 GoogleThreatIntelligence/Dockerfile create mode 100644 GoogleThreatIntelligence/README.md create mode 100644 GoogleThreatIntelligence/action_get_comments.json create mode 100644 GoogleThreatIntelligence/action_get_file_behaviour.json create mode 100644 GoogleThreatIntelligence/action_get_ioc_report.json create mode 100644 GoogleThreatIntelligence/action_get_passive_dns.json create mode 100644 GoogleThreatIntelligence/action_get_vulnerability_associations.json create mode 100644 GoogleThreatIntelligence/action_get_vulnerability_report.json create mode 100644 GoogleThreatIntelligence/action_scan_file.json create mode 100644 GoogleThreatIntelligence/action_scan_url.json create mode 100644 GoogleThreatIntelligence/actions.csv create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/__init__.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/client.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_comments.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/sample.exe create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/scan_file.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/scan_url.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/script_v2.py create mode 100644 GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json create mode 100644 GoogleThreatIntelligence/logo.svg create mode 100644 GoogleThreatIntelligence/main.py create mode 100644 GoogleThreatIntelligence/manifest.json create mode 100644 GoogleThreatIntelligence/poetry.lock create mode 100644 GoogleThreatIntelligence/pyproject.toml create mode 100644 GoogleThreatIntelligence/tests/__init__.py create mode 100644 GoogleThreatIntelligence/tests/conftest.py create mode 100644 GoogleThreatIntelligence/tests/test_get_comments.py create mode 100644 GoogleThreatIntelligence/tests/test_get_file_behaviour.py create mode 100644 GoogleThreatIntelligence/tests/test_get_ioc_report.py create mode 100644 GoogleThreatIntelligence/tests/test_get_passive_dns.py create mode 100644 GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py create mode 100644 GoogleThreatIntelligence/tests/test_get_vulnerability_report.py create mode 100644 GoogleThreatIntelligence/tests/test_scan_file.py create mode 100644 GoogleThreatIntelligence/tests/test_scan_url.py diff --git a/GoogleThreatIntelligence/CHANGELOG.md b/GoogleThreatIntelligence/CHANGELOG.md new file mode 100644 index 000000000..a4af7e839 --- /dev/null +++ b/GoogleThreatIntelligence/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased diff --git a/GoogleThreatIntelligence/Dockerfile b/GoogleThreatIntelligence/Dockerfile new file mode 100644 index 000000000..16a2ddfd6 --- /dev/null +++ b/GoogleThreatIntelligence/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.11 + +WORKDIR /app + +RUN pip install poetry + +# Install dependencies +COPY poetry.lock pyproject.toml /app/ +RUN poetry config virtualenvs.create false && poetry install --only main + +COPY . . + +RUN useradd -ms /bin/bash sekoiaio-runtime +USER sekoiaio-runtime + +ENTRYPOINT [ "python", "./main.py" ] diff --git a/GoogleThreatIntelligence/README.md b/GoogleThreatIntelligence/README.md new file mode 100644 index 000000000..5003c4219 --- /dev/null +++ b/GoogleThreatIntelligence/README.md @@ -0,0 +1,56 @@ +# README + +## **Key Features** + +### 1. **Security Enhancements** +- API key loaded from environment variable (`VT_API_KEY`) +- Validation to prevent running with hardcoded keys +- Proper file existence checks before scanning + +### 2. **Error Handling** +- Try-catch blocks for all API calls +- Specific exception handling for `vt.APIError`, `FileNotFoundError`, `IOError` +- Detailed error logging and tracking +- Graceful degradation when tests fail + +### 3. **Official vt-py Library Usage** +- `client.scan_url()` for URL scanning +- `client.scan_file()` for file scanning +- `client.get_object()` for retrieving reports +- `client.get_json()` for raw JSON responses +- `client.iterator()` for paginated results +- Proper context manager usage (`with vt.Client()`) + +### 4. **Structured Results** +- `TestResult` dataclass for type-safe result storage +- JSON output with detailed success/error information +- Summary statistics at the end + +### 5. **Comprehensive Coverage** +All your required actions are covered: +- ✅ Scan File +- ✅ Get IOC Report (IP, URL, domain, file) +- ✅ Get Comments +- ✅ Get Vulnerability Associations +- ✅ Get File Sandbox Report +- ✅ Scan URL +- ✅ Get Passive DNS Data +- ✅ Get Vulnerability Report + +## **Usage** + +```bash +# Install vt-py +pip install vt-py + +# Set your API key +export VT_API_KEY='your_actual_api_key' + +# Run the script +python script.py + +# Optional: test with a file +# Modify main() to include: tester.run_all_tests(test_file_path="your_file.png") +``` + +The script will generate `vt_test_results.json` with detailed results and print a summary to the console. \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_comments.json b/GoogleThreatIntelligence/action_get_comments.json new file mode 100644 index 000000000..5c048c034 --- /dev/null +++ b/GoogleThreatIntelligence/action_get_comments.json @@ -0,0 +1,109 @@ +{ + "uuid": "a8e3b5f1-3f4a-4b2d-8f7e-1b2f6c9d5e11", + "name": "Get Comments", + "description": "Retrieve recent comments associated with a domain or IP from Google Threat Intelligence", + "docker_parameters": "get_comments", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "ip": { + "type": "string", + "description": "IP address to query (e.g., 8.8.8.8)", + "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "domain": { + "type": "string", + "description": "Domain name to query (e.g., google.com)", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + }, + "url": { + "type": "string", + "description": "URL to query (e.g., https://example.com/path)", + "format": "uri" + }, + "file_hash": { + "type": "string", + "description": "File hash to query (MD5, SHA1, or SHA256)", + "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + } + }, + "oneOf": [ + {"required": ["ip"]}, + {"required": ["domain"]}, + {"required": ["url"]}, + {"required": ["file_hash"]} + ] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Get Comments Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "Container for the comments data", + "properties": { + "comments_count": { + "type": "integer", + "description": "Total number of comments retrieved" + }, + "entity": { + "type": "string", + "description": "The entity (domain, IP, URL, or file hash) that was queried" + }, + "comments": { + "type": "array", + "description": "Array of comment objects", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of the object (always 'comment')", + "enum": ["comment"] + }, + "id": { + "type": "string", + "description": "Unique identifier for the comment" + }, + "text": { + "type": "string", + "description": "The comment text content" + }, + "date": { + "type": ["integer", "string"], + "description": "Comment timestamp (Unix timestamp or ISO 8601 string)" + }, + "votes": { + "type": "object", + "description": "Vote statistics for the comment", + "properties": { + "positive": { + "type": "integer", + "description": "Number of positive votes" + }, + "negative": { + "type": "integer", + "description": "Number of negative votes" + } + }, + "required": ["positive", "negative"] + }, + "author": { + "type": ["string", "null"], + "description": "Username of the comment author (may be null for anonymous)" + } + }, + "required": ["text", "date", "votes"] + } + } + }, + "required": ["comments_count", "entity", "comments"] + } + + } + }, + "slug": "get_comments_in_gti" +} diff --git a/GoogleThreatIntelligence/action_get_file_behaviour.json b/GoogleThreatIntelligence/action_get_file_behaviour.json new file mode 100644 index 000000000..acfa048c5 --- /dev/null +++ b/GoogleThreatIntelligence/action_get_file_behaviour.json @@ -0,0 +1,250 @@ +{ + "uuid": "d4b8a1e4-9c7f-44c1-9a53-2f0b8a3d99c3", + "name": "Get File Behaviour", + "description": "Retrieve sandbox behaviour for a file hash from Google Threat Intelligence", + "docker_parameters": "get_file_behaviour", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "file_hash": { + "type": "string", + "description": "File hash to query (MD5, SHA1, or SHA256)", + "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + } + }, + "required": ["file_hash"] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Get File Behaviour Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "Container for the file behaviour data", + "properties": { + "behaviours_count": { + "type": "integer", + "description": "Total number of behaviour reports retrieved" + }, + "file_hash": { + "type": "string", + "description": "The file hash that was queried" + }, + "behaviours": { + "type": "array", + "description": "Array of behaviour report objects from different sandboxes", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of the object (always 'file_behaviour')", + "enum": ["file_behaviour"] + }, + "id": { + "type": "string", + "description": "Unique identifier for the behaviour report (hash_sandboxname)" + }, + "sandbox_name": { + "type": "string", + "description": "Name of the sandbox that analyzed the file (e.g., 'C2AE', 'CAPA', 'VirusTotal Jujubox')" + }, + "analysis_date": { + "type": "integer", + "description": "Unix timestamp of when the analysis was performed" + }, + "last_modification_date": { + "type": "integer", + "description": "Unix timestamp of the last modification to this report" + }, + "behash": { + "type": "string", + "description": "Behavioral hash of the file execution" + }, + "has_html_report": { + "type": "boolean", + "description": "Indicates if an HTML report is available for download" + }, + "has_pcap": { + "type": "boolean", + "description": "Indicates if a PCAP network capture is available for download" + }, + "has_evtx": { + "type": "boolean", + "description": "Indicates if Windows event logs (EVTX) are available" + }, + "has_memdump": { + "type": "boolean", + "description": "Indicates if a memory dump is available for download" + }, + "verdicts": { + "type": "array", + "description": "List of verdicts from the sandbox analysis", + "items": { + "type": "string" + } + }, + "processes_tree": { + "type": "array", + "description": "Tree structure of processes created during execution", + "items": { + "type": "object", + "properties": { + "process_id": { + "type": "string", + "description": "Process ID (PID)" + }, + "name": { + "type": "string", + "description": "Process name and command line" + }, + "children": { + "type": "array", + "description": "Child processes spawned by this process" + } + } + } + }, + "processes_created": { + "type": "integer", + "description": "Number of processes created during execution" + }, + "processes_terminated": { + "type": "array", + "description": "List of processes terminated during execution", + "items": { + "type": "string" + } + }, + "command_executions": { + "type": "array", + "description": "List of command-line executions observed", + "items": { + "type": "string" + } + }, + "files_opened": { + "type": "array", + "description": "List of files accessed during execution", + "items": { + "type": "string" + } + }, + "files_written": { + "type": "integer", + "description": "Number of files written to disk" + }, + "files_deleted": { + "type": "integer", + "description": "Number of files deleted during execution" + }, + "registry_keys_opened": { + "type": "array", + "description": "List of Windows registry keys accessed", + "items": { + "type": "string" + } + }, + "registry_keys_set": { + "type": "integer", + "description": "Number of registry keys modified" + }, + "modules_loaded": { + "type": "array", + "description": "List of DLLs and modules loaded during execution", + "items": { + "type": "string" + } + }, + "ip_traffic": { + "type": "array", + "description": "Network traffic observed during execution", + "items": { + "type": "object", + "properties": { + "destination_ip": { + "type": "string", + "description": "Destination IP address" + }, + "destination_port": { + "type": "integer", + "description": "Destination port number" + }, + "transport_layer_protocol": { + "type": "string", + "description": "Protocol used (TCP/UDP)", + "enum": ["TCP", "UDP", "ICMP"] + } + } + } + }, + "dns_lookups": { + "type": "integer", + "description": "Number of DNS queries performed" + }, + "mitre_attack_techniques": { + "type": "array", + "description": "MITRE ATT&CK techniques observed in the behavior", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "MITRE technique ID (e.g., 'T1055')" + }, + "signature_description": { + "type": "string", + "description": "Description of the observed technique" + } + } + } + }, + "tags": { + "type": "array", + "description": "Tags categorizing the observed behavior", + "items": { + "type": "string" + } + }, + "text_highlighted": { + "type": "array", + "description": "Important strings or patterns highlighted in the analysis", + "items": { + "type": "string" + } + }, + "sigma_analysis_results": { + "type": "array", + "description": "Results from Sigma rule matching", + "items": { + "type": "object", + "properties": { + "rule_title": { + "type": "string" + }, + "rule_level": { + "type": "string" + }, + "match_context": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + }, + "required": ["type", "id", "sandbox_name", "analysis_date"] + } + } + }, + "required": ["behaviours_count", "file_hash", "behaviours"] + } + } + }, + "slug": "get_file_behaviour_in_gti" +} diff --git a/GoogleThreatIntelligence/action_get_ioc_report.json b/GoogleThreatIntelligence/action_get_ioc_report.json new file mode 100644 index 000000000..ac5097ce2 --- /dev/null +++ b/GoogleThreatIntelligence/action_get_ioc_report.json @@ -0,0 +1,129 @@ +{ + "uuid": "4b6ebd76-076f-4f35-9cd1-36953f14df88", + "name": "Get IoC Report", + "description": "Get threat intelligence report for an IP address, domain, URL, or file hash from Google Threat Intelligence (VirusTotal)", + "docker_parameters": "get_ioc_report", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Arguments", + "properties": { + "ip": { + "type": "string", + "description": "IP address to query (e.g., 8.8.8.8)", + "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "domain": { + "type": "string", + "description": "Domain name to query (e.g., google.com)", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + }, + "url": { + "type": "string", + "description": "URL to query (e.g., https://example.com/path)", + "format": "uri" + }, + "file_hash": { + "type": "string", + "description": "File hash to query (MD5, SHA1, or SHA256)", + "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + } + }, + "oneOf": [ + {"required": ["ip"]}, + {"required": ["domain"]}, + {"required": ["url"]}, + {"required": ["file_hash"]} + ] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Results", + "properties": { + "success": { + "type": "boolean", + "description": "Whether the operation was successful" + }, + "data": { + "type": "object", + "description": "IoC report data", + "properties": { + "entity_type": { + "type": "string", + "description": "Type of entity queried (ip_addresses, domains, urls, files)" + }, + "entity": { + "type": "string", + "description": "The entity value that was queried" + }, + "id": { + "type": "string", + "description": "VirusTotal object ID" + }, + "reputation": { + "type": "integer", + "description": "Reputation score" + }, + "last_analysis_stats": { + "type": "object", + "description": "Statistics from last analysis", + "properties": { + "harmless": {"type": "integer"}, + "malicious": {"type": "integer"}, + "suspicious": {"type": "integer"}, + "undetected": {"type": "integer"} + } + }, + "detections": { + "type": "object", + "description": "Detection summary", + "properties": { + "harmless": {"type": "integer"}, + "malicious": {"type": "integer"}, + "suspicious": {"type": "integer"}, + "undetected": {"type": "integer"} + } + }, + "country": { + "type": "string", + "description": "Country code (for IPs)" + }, + "asn": { + "type": "integer", + "description": "Autonomous System Number (for IPs)" + }, + "as_owner": { + "type": "string", + "description": "AS owner name (for IPs)" + }, + "categories": { + "type": "object", + "description": "Domain categories" + }, + "sha256": { + "type": "string", + "description": "SHA256 hash (for files)" + }, + "md5": { + "type": "string", + "description": "MD5 hash (for files)" + }, + "sha1": { + "type": "string", + "description": "SHA1 hash (for files)" + }, + "size": { + "type": "integer", + "description": "File size in bytes (for files)" + }, + "type_description": { + "type": "string", + "description": "File type description (for files)" + } + } + } + } + }, + "slug": "get_ioc_report_in_gti" +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_passive_dns.json b/GoogleThreatIntelligence/action_get_passive_dns.json new file mode 100644 index 000000000..ada0bec1a --- /dev/null +++ b/GoogleThreatIntelligence/action_get_passive_dns.json @@ -0,0 +1,171 @@ +{ + "uuid": "e3a9b7c5-2d4f-4b1e-a6f8-8f0d9b7c6a88", + "name": "Get Passive DNS", + "description": "Retrieve passive DNS data for a domain from Google Threat Intelligence", + "docker_parameters": "get_passive_dns", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "domain": { + "type": "string", + "description": "Domain name to query (e.g., google.com)", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + } + }, + "required": ["domain"] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Passive DNS Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "Container for passive DNS resolution data", + "properties": { + "resolutions_count": { + "type": "integer", + "description": "Total number of DNS resolutions retrieved" + }, + "unique_ips_count": { + "type": "integer", + "description": "Number of unique IP addresses found in resolutions (for domain queries)" + }, + "unique_domains_count": { + "type": "integer", + "description": "Number of unique domains found in resolutions (for IP queries)" + }, + "entity": { + "type": "string", + "description": "The entity (domain or IP) that was queried" + }, + "entity_type": { + "type": "string", + "description": "Type of entity queried", + "enum": ["domains", "ip_addresses"] + }, + "unique_ips": { + "type": "array", + "description": "List of unique IP addresses (for domain queries)", + "items": { + "type": "string" + } + }, + "unique_domains": { + "type": "array", + "description": "List of unique domains (for IP queries)", + "items": { + "type": "string" + } + }, + "resolutions": { + "type": "array", + "description": "Array of DNS resolution records", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of the object (always 'resolution')", + "enum": ["resolution"] + }, + "id": { + "type": "string", + "description": "Unique identifier for the resolution (format: ip_address+hostname)" + }, + "ip_address": { + "type": "string", + "description": "The resolved IP address", + "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "host_name": { + "type": "string", + "description": "The domain name associated with the IP" + }, + "date": { + "type": ["integer", "string"], + "description": "Timestamp when the resolution was observed (Unix timestamp or ISO 8601 string)" + }, + "resolver": { + "type": "string", + "description": "Source of the DNS resolution data (e.g., 'VirusTotal', 'PassiveTotal')" + }, + "host_name_last_analysis_stats": { + "type": "object", + "description": "Analysis statistics for the hostname", + "properties": { + "harmless": { + "type": "integer", + "description": "Number of engines marking as harmless" + }, + "malicious": { + "type": "integer", + "description": "Number of engines marking as malicious" + }, + "suspicious": { + "type": "integer", + "description": "Number of engines marking as suspicious" + }, + "undetected": { + "type": "integer", + "description": "Number of engines with no detection" + }, + "timeout": { + "type": "integer", + "description": "Number of engines that timed out" + } + } + }, + "ip_address_last_analysis_stats": { + "type": "object", + "description": "Analysis statistics for the IP address", + "properties": { + "harmless": { + "type": "integer", + "description": "Number of engines marking as harmless" + }, + "malicious": { + "type": "integer", + "description": "Number of engines marking as malicious" + }, + "suspicious": { + "type": "integer", + "description": "Number of engines marking as suspicious" + }, + "undetected": { + "type": "integer", + "description": "Number of engines with no detection" + }, + "timeout": { + "type": "integer", + "description": "Number of engines that timed out" + } + } + } + }, + "required": ["type", "id", "date"] + } + }, + "links": { + "type": "object", + "description": "Pagination links for additional results", + "properties": { + "self": { + "type": "string", + "description": "Link to the current page of results" + }, + "next": { + "type": "string", + "description": "Link to the next page of results (if available)" + } + } + } + }, + "required": ["resolutions_count", "entity", "entity_type", "resolutions"] + } + } + }, + "slug": "get_passive_dns_in_gti" +} diff --git a/GoogleThreatIntelligence/action_get_vulnerability_associations.json b/GoogleThreatIntelligence/action_get_vulnerability_associations.json new file mode 100644 index 000000000..73f2a49e6 --- /dev/null +++ b/GoogleThreatIntelligence/action_get_vulnerability_associations.json @@ -0,0 +1,305 @@ +{ + "uuid": "c2f9b6a2-7a4c-4d9d-bb2f-5f9ef6b0a222", + "name": "Get Vulnerability Associations", + "description": "Retrieve vulnerabilities associated with an IP from Google Threat Intelligence", + "docker_parameters": "get_vulnerability_assocations", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "ip": { + "type": "string", + "description": "IP address to query (e.g., 8.8.8.8)", + "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + }, + "domain": { + "type": "string", + "description": "Domain name to query (e.g., google.com)", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + }, + "url": { + "type": "string", + "description": "URL to query (e.g., https://example.com/path)", + "format": "uri" + }, + "file_hash": { + "type": "string", + "description": "File hash to query (MD5, SHA1, or SHA256)", + "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + } + }, + "oneOf": [ + {"required": ["ip"]}, + {"required": ["domain"]}, + {"required": ["url"]}, + {"required": ["file_hash"]} + ] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Vulnerability Associations Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "Container for vulnerability association data", + "properties": { + "vulnerabilities_count": { + "type": "integer", + "description": "Total number of vulnerabilities associated with the entity" + }, + "unique_cves_count": { + "type": "integer", + "description": "Number of unique CVE identifiers found" + }, + "high_severity_count": { + "type": "integer", + "description": "Number of HIGH or CRITICAL severity vulnerabilities" + }, + "entity": { + "type": "string", + "description": "The entity (IP, domain, or file) that was queried" + }, + "entity_type": { + "type": "string", + "description": "Type of entity queried", + "enum": ["ip_addresses", "domains", "files"] + }, + "cve_ids": { + "type": "array", + "description": "List of unique CVE identifiers found", + "items": { + "type": "string", + "pattern": "^CVE-[0-9]{4}-[0-9]+$" + } + }, + "vulnerabilities": { + "type": "array", + "description": "Array of vulnerability objects", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of the object (always 'vulnerability')", + "enum": ["vulnerability"] + }, + "id": { + "type": "string", + "description": "Unique identifier for the vulnerability (typically the CVE ID)" + }, + "cve_id": { + "type": "string", + "description": "Common Vulnerabilities and Exposures identifier", + "pattern": "^CVE-[0-9]{4}-[0-9]+$" + }, + "title": { + "type": "string", + "description": "Title or summary of the vulnerability" + }, + "description": { + "type": "string", + "description": "Detailed description of the vulnerability" + }, + "published_date": { + "type": ["integer", "string"], + "description": "Date when the vulnerability was published (Unix timestamp or ISO 8601 string)" + }, + "last_modification_date": { + "type": ["integer", "string"], + "description": "Date of the last modification to the vulnerability record" + }, + "cvss": { + "type": "object", + "description": "Common Vulnerability Scoring System information", + "properties": { + "score": { + "type": "number", + "description": "CVSS base score (0.0-10.0)", + "minimum": 0, + "maximum": 10 + }, + "severity": { + "type": "string", + "description": "Severity rating based on CVSS score", + "enum": ["NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL"] + }, + "vector_string": { + "type": "string", + "description": "CVSS vector string (e.g., CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)" + }, + "version": { + "type": "string", + "description": "CVSS version used", + "enum": ["2.0", "3.0", "3.1", "4.0"] + }, + "base_metrics": { + "type": "object", + "description": "Detailed CVSS base metrics", + "properties": { + "attack_vector": { + "type": "string", + "description": "Attack Vector (NETWORK, ADJACENT, LOCAL, PHYSICAL)" + }, + "attack_complexity": { + "type": "string", + "description": "Attack Complexity (LOW, HIGH)" + }, + "privileges_required": { + "type": "string", + "description": "Privileges Required (NONE, LOW, HIGH)" + }, + "user_interaction": { + "type": "string", + "description": "User Interaction (NONE, REQUIRED)" + }, + "scope": { + "type": "string", + "description": "Scope (UNCHANGED, CHANGED)" + }, + "confidentiality_impact": { + "type": "string", + "description": "Confidentiality Impact (NONE, LOW, HIGH)" + }, + "integrity_impact": { + "type": "string", + "description": "Integrity Impact (NONE, LOW, HIGH)" + }, + "availability_impact": { + "type": "string", + "description": "Availability Impact (NONE, LOW, HIGH)" + } + } + } + }, + "required": ["score", "severity"] + }, + "cvss_v2": { + "type": "object", + "description": "CVSS v2 score information (legacy)", + "properties": { + "score": { + "type": "number", + "minimum": 0, + "maximum": 10 + }, + "vector_string": { + "type": "string" + } + } + }, + "cvss_v3": { + "type": "object", + "description": "CVSS v3 score information", + "properties": { + "score": { + "type": "number", + "minimum": 0, + "maximum": 10 + }, + "vector_string": { + "type": "string" + } + } + }, + "cwe_id": { + "type": "string", + "description": "Common Weakness Enumeration identifier", + "pattern": "^CWE-[0-9]+$" + }, + "references": { + "type": "array", + "description": "External references and resources about the vulnerability", + "items": { + "type": "string", + "format": "uri" + } + }, + "vendor": { + "type": "string", + "description": "Affected vendor name" + }, + "product": { + "type": "string", + "description": "Affected product name" + }, + "affected_versions": { + "type": "array", + "description": "List of affected product versions", + "items": { + "type": "string" + } + }, + "exploitability": { + "type": "object", + "description": "Information about exploit availability", + "properties": { + "exploit_available": { + "type": "boolean", + "description": "Whether a known exploit exists" + }, + "exploit_types": { + "type": "array", + "description": "Types of available exploits", + "items": { + "type": "string" + } + }, + "epss_score": { + "type": "number", + "description": "Exploit Prediction Scoring System score (0.0-1.0)", + "minimum": 0, + "maximum": 1 + } + } + }, + "tags": { + "type": "array", + "description": "Tags categorizing the vulnerability", + "items": { + "type": "string" + } + }, + "mitre_attack_techniques": { + "type": "array", + "description": "MITRE ATT&CK techniques associated with exploitation", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "MITRE technique ID (e.g., 'T1190')" + }, + "name": { + "type": "string", + "description": "Name of the technique" + } + } + } + } + }, + "required": ["type", "id", "cve_id"] + } + }, + "links": { + "type": "object", + "description": "Pagination links for additional results", + "properties": { + "self": { + "type": "string", + "description": "Link to the current page of results" + }, + "next": { + "type": "string", + "description": "Link to the next page of results (if available)" + } + } + } + }, + "required": ["vulnerabilities_count", "entity", "entity_type", "vulnerabilities"] + } + } + }, + "slug": "get_vulnerability_associations_in_gti" +} diff --git a/GoogleThreatIntelligence/action_get_vulnerability_report.json b/GoogleThreatIntelligence/action_get_vulnerability_report.json new file mode 100644 index 000000000..876c06f16 --- /dev/null +++ b/GoogleThreatIntelligence/action_get_vulnerability_report.json @@ -0,0 +1,153 @@ +{ + "uuid": "b7c2d8e9-5f3a-4d2f-8e1b-7a6c5b4d3f55", + "name": "Get Vulnerability Report", + "description": "Retrieve a CVE vulnerability report from Google Threat Intelligence", + "docker_parameters": "get_vulnerability_report", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "cve": { + "type": "string", + "description": "CVE query (e.g. CVE-2021-34527)" + } + }, + "required": ["cve"] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Vulnerability Report Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Vulnerability identifier in format 'vulnerability--cve-XXXX-XXXXX'" + }, + "type": { + "type": "string", + "description": "Object type, always 'vulnerability'" + }, + "collection_type": { + "type": "string", + "description": "Collection type identifier, value is 'vulnerability'" + }, + "name": { + "type": "string", + "description": "CVE identifier (e.g., CVE-2021-34527)" + }, + "description": { + "type": "string", + "description": "Human-readable description of the vulnerability" + }, + "creation_date": { + "type": "integer", + "description": "Vulnerability object creation date (UTC timestamp)" + }, + "last_modification_date": { + "type": "integer", + "description": "Last modification timestamp (UTC timestamp)" + }, + "risk_rating": { + "type": "string", + "enum": ["Critical", "High", "Medium", "Low"], + "description": "Google TI's assessment of vulnerability severity" + }, + "exploitation_state": { + "type": "string", + "enum": ["Wide", "Confirmed", "Reported", "Suspected", "No Known"], + "description": "Current exploitation status based on GTI intelligence" + }, + "exploit_availability": { + "type": "string", + "description": "Status of exploit code availability (e.g., 'Publicly Available', 'Privately Held', 'No Known')" + }, + "exploited_as_zero_day": { + "type": "boolean", + "description": "Whether the vulnerability was exploited before a patch was available" + }, + "exploited_in_the_wild": { + "type": "boolean", + "description": "Whether active real-world exploitation has been observed" + }, + "cvss": { + "type": "object", + "properties": { + "base_score": { + "type": "number", + "description": "CVSS base score" + }, + "temporal_score": { + "type": "number", + "description": "CVSS temporal score adjusted for exploit availability" + }, + "vector_string": { + "type": "string", + "description": "CVSS vector string" + }, + "version": { + "type": "string", + "description": "CVSS version (e.g., '3.1')" + } + } + }, + "counters": { + "type": "object", + "properties": { + "iocs": { + "type": "integer", + "description": "Number of IoCs associated with this vulnerability" + }, + "files": { + "type": "integer", + "description": "Number of files associated with this vulnerability" + }, + "domains": { + "type": "integer", + "description": "Number of domains associated with this vulnerability" + }, + "ip_addresses": { + "type": "integer", + "description": "Number of IP addresses associated with this vulnerability" + }, + "urls": { + "type": "integer", + "description": "Number of URLs associated with this vulnerability" + }, + "attack_techniques": { + "type": "integer", + "description": "Number of MITRE ATT&CK techniques associated with this vulnerability" + }, + "subscribers": { + "type": "integer", + "description": "Number of users subscribed to this vulnerability" + } + } + }, + "origin": { + "type": "string", + "description": "Source of information ('Google Threat Intelligence', 'Partner', or 'Crowdsourced')" + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "Tags associated with the vulnerability" + }, + "first_published_date": { + "type": "integer", + "description": "When the CVE was initially published (UTC timestamp)" + }, + "intelligence_source": { + "type": "string", + "description": "Organization that curated the intelligence (e.g., 'Google Threat Intelligence')" + } + }, + "required": ["id", "type", "name", "description"] + } + } + }, + "slug": "get_vulnerability_report_in_gti" +} diff --git a/GoogleThreatIntelligence/action_scan_file.json b/GoogleThreatIntelligence/action_scan_file.json new file mode 100644 index 000000000..ea7137227 --- /dev/null +++ b/GoogleThreatIntelligence/action_scan_file.json @@ -0,0 +1,89 @@ +{ + "uuid": "fe3aa9b9-e922-4dcf-a3d5-c05a491f1c74", + "name": "Scan File", + "description": "Upload a file for scanning to Google Threat Intelligence (VirusTotal)", + "docker_parameters": "scan_file", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "file_path": { + "type": "string", + "description": "Local path to the file to scan" + } + }, + "required": ["file_path"] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Scan File Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "VirusTotal FILE analysis result", + "properties": { + "file_path": { + "type": "string", + "description": "The FILE PATH submitted by the user" + }, + "analysis_stats": { + "type": "object", + "description": "Summary statistics of the FILE scan", + "properties": { + "harmless": { "type": "integer" }, + "malicious": { "type": "integer" }, + "suspicious": { "type": "integer" }, + "undetected": { "type": "integer" }, + "timeout": { "type": "integer" }, + "failure": { "type": "integer" }, + "confirmed-timeout": { "type": "integer" }, + "type-unsupported": { "type": "integer" } + }, + "required": [ + "harmless", + "malicious", + "suspicious", + "undetected", + "timeout", + "failure", + "confirmed-timeout", + "type-unsupported" + ] + }, + "analysis_results": { + "type": "object", + "description": "Per-engine detailed results", + "additionalProperties": { + "type": "object", + "properties": { + "category": { + "type": "string", + "enum": [ + "confirmed-timeout", + "timeout", + "failure", + "harmless", + "undetected", + "suspicious", + "malicious", + "type-unsupported" + ] + }, + "engine_name": { "type": "string" }, + "engine_version": { "type": ["string", "null"] }, + "engine_update": { "type": ["string", "null"] }, + "method": { "type": ["string", "null"] }, + "result": { "type": ["string", "null"] } + }, + "required": ["category", "engine_name"] + } + } + }, + "required": ["file_path", "analysis_stats", "analysis_results"] + } + } + }, + "slug": "scan_file_in_gti" +} diff --git a/GoogleThreatIntelligence/action_scan_url.json b/GoogleThreatIntelligence/action_scan_url.json new file mode 100644 index 000000000..8ca814c2f --- /dev/null +++ b/GoogleThreatIntelligence/action_scan_url.json @@ -0,0 +1,89 @@ +{ + "uuid": "f1b2c3d4-e5f6-4789-9a1b-0c2d3e4f5a66", + "name": "Scan URL", + "description": "Submit a URL for scanning to Google Threat Intelligence (VirusTotal)", + "docker_parameters": "scan_url", + "arguments": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "URL to scan" + } + }, + "required": ["url"] + }, + "results": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Scan URL Results", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "VirusTotal URL analysis result", + "properties": { + "url": { + "type": "string", + "description": "The URL submitted by the user" + }, + "analysis_stats": { + "type": "object", + "description": "Summary statistics of the URL scan", + "properties": { + "harmless": { "type": "integer" }, + "malicious": { "type": "integer" }, + "suspicious": { "type": "integer" }, + "undetected": { "type": "integer" }, + "timeout": { "type": "integer" }, + "failure": { "type": "integer" }, + "confirmed-timeout": { "type": "integer" }, + "type-unsupported": { "type": "integer" } + }, + "required": [ + "harmless", + "malicious", + "suspicious", + "undetected", + "timeout", + "failure", + "confirmed-timeout", + "type-unsupported" + ] + }, + "analysis_results": { + "type": "object", + "description": "Per-engine detailed results", + "additionalProperties": { + "type": "object", + "properties": { + "category": { + "type": "string", + "enum": [ + "confirmed-timeout", + "timeout", + "failure", + "harmless", + "undetected", + "suspicious", + "malicious", + "type-unsupported" + ] + }, + "engine_name": { "type": "string" }, + "engine_version": { "type": ["string", "null"] }, + "engine_update": { "type": ["string", "null"] }, + "method": { "type": ["string", "null"] }, + "result": { "type": ["string", "null"] } + }, + "required": ["category", "engine_name"] + } + } + }, + "required": ["url", "analysis_stats", "analysis_results"] + } + } + }, + "slug": "scan_url_in_gti" +} diff --git a/GoogleThreatIntelligence/actions.csv b/GoogleThreatIntelligence/actions.csv new file mode 100644 index 000000000..bfbf897c9 --- /dev/null +++ b/GoogleThreatIntelligence/actions.csv @@ -0,0 +1,10 @@ +Get IOC Report (API action) - IP address, URL, domain, or file;GET /api/v3/{entity_type}/{entity} +Get Comments (API action) - IP address, URL, domain, or file;GET /api/v3/{entity_type}/{entity}/comments +Get Vulnerability Associations - IP address, URL, domain, or file(API action);"GET /api/v3/{entity_type}/{entity}/vulnerabilities +Example https://www.virustotal.com/api/v3/ip_addresses/45.182.189.102/vulnerabilities" +Get File Sandbox Report (API action);GET /api/v3/files/{file_id}/behaviours +Scan Url (API action);"POST /api/v3/urls +POST /api/v3/private/urls" +Get Curated Associations (API action);GET /api/v3/{entity_type}/{entity} (avec entity_type=collections, cti, … selon vos collections VT) +Get Passive DNS Data (API action);GET /api/v3/{entity_type}/{entity}/resolutions +Get Vulnerability Report (API action);GET /api/v3/collections/vulnerability--cve-2024-6284 diff --git a/GoogleThreatIntelligence/googlethreatintelligence/__init__.py b/GoogleThreatIntelligence/googlethreatintelligence/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/GoogleThreatIntelligence/googlethreatintelligence/client.py b/GoogleThreatIntelligence/googlethreatintelligence/client.py new file mode 100644 index 000000000..cedecc8a5 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/client.py @@ -0,0 +1,511 @@ +""" +Google Threat Intelligence (VirusTotal) API Testing Script +Uses vt-py official library with comprehensive error handling + +ACTIONS MAPPING (from requirements): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Action | API Endpoint | Method(s) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Scan File | POST /api/v3/files | scan_file() + | POST /api/v3/private/files | +Get IOC Report | GET /api/v3/{entity_type}/{entity} | get_ioc_report() + - IP address | GET /api/v3/ip_addresses/{ip} | get_ip_report() + - URL | GET /api/v3/urls/{url_id} | get_url_report() + - Domain | GET /api/v3/domains/{domain} | get_domain_report() + - File | GET /api/v3/files/{hash} | get_file_report() +Get Comments | GET /api/v3/{entity_type}/{entity}/comments | get_comments() +Get Vulnerability Associations | GET /api/v3/{entity_type}/{entity}/vulnerabilities | get_vulnerability_associations() +Get File Sandbox Report | GET /api/v3/files/{hash}/behaviours | get_file_behaviour() +Scan URL | POST /api/v3/urls | scan_url() + | POST /api/v3/private/urls | +Get Curated Associations | GET /api/v3/{entity_type}/{entity} | get_ioc_report() +Get Passive DNS Data | GET /api/v3/{entity_type}/{entity}/resolutions | get_passive_dns() +Get Vulnerability Report | GET /api/v3/collections/{vuln_id} | get_vulnerability_report() +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +""" + +import vt +import base64 +from pathlib import Path +from typing import List, Optional, Any +from dataclasses import dataclass +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") +logger = logging.getLogger(__name__) + +# Security: Load API key from environment variable +import os + +API_KEY = os.getenv("VT_API_KEY", "REDACTED") + +if API_KEY == "REDACTED": + logger.warning("API_KEY not set! Set VT_API_KEY environment variable") + + +@dataclass +class Result: + """Structure to hold test results""" + + name: str + method: str + endpoint: str + status: str + response: Any + error: Optional[str] = None + + +class VTAPIConnector: + """VirusTotal API Connector Class using vt-py""" + + def __init__( + self, + api_key: str, + domain: Optional[str] = None, + ip: Optional[str] = None, + url: Optional[str] = None, + file_hash: Optional[str] = None, + cve: Optional[str] = None, + ): + """ + Initialize the VT API Connector + + Args: + api_key: VirusTotal API key + domain: Domain to query (optional, defaults to google.com) + ip: IP address to query (optional, defaults to 8.8.8.8) + url: URL to query (optional, defaults to sekoia.io) + file_hash: File hash to query (optional, defaults to EICAR test file) + cve: CVE ID to query (optional, defaults to CVE-2021-34527) + """ + self.api_key = api_key + self.results: List[Result] = [] + + # Use provided values when available, otherwise fall back to sensible defaults + self.domain = domain or "google.com" + self.ip = ip or "8.8.8.8" + self.url = url or "https://www.sekoia.io/en/homepage/" + self.file_hash = file_hash or "44d88612fea8a8f36de82e1278abb02f" # EICAR test file + self.cve = cve or "CVE-2021-34527" + + # Headers used by the internal _request helper + self.headers = {"x-apikey": self.api_key} + + """ + Usage Example: + + connector._add_result( + "SCAN_URL", "POST", "/api/v3/urls", "SUCCESS", {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "url": self.url} + ) + """ + + def _add_result( + self, name: str, method: str, endpoint: str, status: str, response: Any, error: Optional[str] = None + ): + """Add a result""" + # Convert VT objects to JSON-serializable format + if response is not None: + response = self._make_serializable(response) + + result = Result(name, method, endpoint, status, response, error) + self.results.append(result) + + if error: + logger.error(f"[{status}] {name}: {error}") + else: + logger.info(f"[{status}] {name}: Success") + + def _make_serializable(self, obj: Any) -> Any: + """Convert VT objects to JSON-serializable format""" + if isinstance(obj, dict): + return {k: self._make_serializable(v) for k, v in obj.items()} + elif isinstance(obj, (list, tuple)): + return [self._make_serializable(item) for item in obj] + elif hasattr(obj, "__dict__"): + return str(obj) + else: + return obj + + def test_connectivity(self, client: vt.Client): + """Test API connectivity""" + try: + # Use get_object instead of get_json for /me endpoint + user = client.get_object("/users/me") + self._add_result( + "TEST_CONNECTIVITY", + "GET", + "/api/v3/users/me", + "SUCCESS", + {"user_id": user.id if hasattr(user, "id") else None}, + ) + except vt.APIError as e: + self._add_result("TEST_CONNECTIVITY", "GET", "/api/v3/users/me", "ERROR", None, str(e)) + + def get_ioc_report(self, client: vt.Client, entity_type: str, entity: str): + """ + Generic IOC Report - handles IP, URL, domain, or file + GET /api/v3/{entity_type}/{entity} + """ + try: + # Handle URL encoding for URLs + if entity_type == "urls": + entity = base64.urlsafe_b64encode(entity.encode()).decode().strip("=") + + ioc_obj = client.get_object(f"/{entity_type}/{entity}") + + response_data = { + "entity_type": entity_type, + "entity": entity, + "id": ioc_obj.id if hasattr(ioc_obj, "id") else None, + "reputation": ioc_obj.reputation if hasattr(ioc_obj, "reputation") else None, + } + + # Add type-specific attributes + if hasattr(ioc_obj, "last_analysis_stats"): + response_data["last_analysis_stats"] = dict(ioc_obj.last_analysis_stats) + if hasattr(ioc_obj, "country"): + response_data["country"] = ioc_obj.country + if hasattr(ioc_obj, "categories"): + response_data["categories"] = dict(ioc_obj.categories) if ioc_obj.categories else None + + self._add_result( + f"GET_IOC_REPORT_{entity_type.upper()}", + "GET", + f"/api/v3/{entity_type}/{entity}", + "SUCCESS", + response_data, + ) + except vt.APIError as e: + self._add_result( + f"GET_IOC_REPORT_{entity_type.upper()}", + "GET", + f"/api/v3/{entity_type}/{entity}", + "ERROR", + None, + str(e), + ) + + def get_ip_report(self, client: vt.Client): + """Get IP address report""" + self.get_ioc_report(client, "ip_addresses", self.ip) + + def get_domain_report(self, client: vt.Client): + """Get domain report""" + self.get_ioc_report(client, "domains", self.domain) + + def get_url_report(self, client: vt.Client): + """Get URL report""" + self.get_ioc_report(client, "urls", self.url) + + def get_file_report(self, client: vt.Client): + """Get file report""" + self.get_ioc_report(client, "files", self.file_hash) + + def scan_url(self, client: vt.Client): + """Scan a URL + This returns an Analysis ID. The analysis can be retrieved by using the Analysis endpoint. + -> https://docs.virustotal.com/reference/analysis + + """ + try: + analysis = client.scan_url(self.url, wait_for_completion=True) + print("Analysis completed") + # print(analysis.stats) + # print(analysis.results) + self._add_result( + "SCAN_URL", + "POST", + "/api/v3/urls", + "SUCCESS", + {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "url": self.url}, + ) + except vt.APIError as e: + self._add_result("SCAN_URL", "POST", "/api/v3/urls", "ERROR", None, str(e)) + return None + + def scan_file(self, client: vt.Client, file_path: str): + """Scan a file""" + try: + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + + with open(file_path, "rb") as f: + analysis = client.scan_file(f, wait_for_completion=True) + print("Analysis completed") + + self._add_result( + "SCAN_FILE", + "POST", + "/api/v3/files", + "SUCCESS", + {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "file_path": file_path}, + ) + except (vt.APIError, FileNotFoundError, IOError) as e: + self._add_result("SCAN_FILE", "POST", "/api/v3/files", "ERROR", None, str(e)) + return None + + def get_analysis(self, client: vt.Client, analysis_id: str): + """Get analysis results""" + try: + analysis = client.get_object(f"/analyses/{analysis_id}") + self._add_result( + "GET_ANALYSIS", + "GET", + f"/api/v3/analyses/{analysis_id}", + "SUCCESS", + {"status": analysis.status, "stats": analysis.stats if hasattr(analysis, "stats") else None}, + ) + except vt.APIError as e: + self._add_result("GET_ANALYSIS", "GET", f"/api/v3/analyses/{analysis_id}", "ERROR", None, str(e)) + + def get_file_behaviour(self, client: vt.Client): + """Get file sandbox behavior""" + try: + # Use iterator for behaviours and FULLY consume it + behaviours_it = client.iterator(f"/files/{self.file_hash}/behaviours", limit=5) + + # IMPORTANT: Fully consume the iterator to test it properly + behaviours = [] + for behaviour in behaviours_it: + behaviour_data = { + "sandbox_name": behaviour.sandbox_name if hasattr(behaviour, "sandbox_name") else None, + } + + # Extract detailed behaviour information + if hasattr(behaviour, "processes_created"): + behaviour_data["processes_created"] = len(behaviour.processes_created) + if hasattr(behaviour, "files_written"): + behaviour_data["files_written"] = len(behaviour.files_written) + if hasattr(behaviour, "files_deleted"): + behaviour_data["files_deleted"] = len(behaviour.files_deleted) + if hasattr(behaviour, "registry_keys_set"): + behaviour_data["registry_keys_set"] = len(behaviour.registry_keys_set) + if hasattr(behaviour, "dns_lookups"): + behaviour_data["dns_lookups"] = len(behaviour.dns_lookups) + if hasattr(behaviour, "ip_traffic"): + behaviour_data["ip_traffic"] = len(behaviour.ip_traffic) + + behaviours.append(behaviour_data) + + self._add_result( + "GET_FILE_SANDBOX", + "GET", + f"/api/v3/files/{self.file_hash}/behaviours", + "SUCCESS", + {"behaviours_count": len(behaviours), "behaviours": behaviours}, + ) + except vt.APIError as e: + # This endpoint requires Premium API - log as warning not error + logger.warning(f"File behaviours not available (may require Premium API): {e}") + self._add_result( + "GET_FILE_SANDBOX", + "GET", + f"/api/v3/files/{self.file_hash}/behaviours", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_comments(self, client: vt.Client, entity_type: str, entity: str): + """Get comments for an entity - FULLY tests the iterator""" + try: + # Special case: URLs must be base64url encoded without "=" padding + if entity_type == "urls": + import base64 + + entity = base64.urlsafe_b64encode(entity.encode()).decode().strip("=") + + path = f"/{entity_type}/{entity}/comments" + + comments_it = client.iterator(path, limit=10) + + comments = [] + for comment in comments_it: + comments.append( + { + "text": getattr(comment, "text", None), + "date": str(getattr(comment, "date", None)), + "votes": { + "positive": ( + getattr(getattr(comment, "votes", {}), "positive", 0) + if hasattr(comment, "votes") + else 0 + ), + "negative": ( + getattr(getattr(comment, "votes", {}), "negative", 0) + if hasattr(comment, "votes") + else 0 + ), + }, + "author": getattr(comment, "author", None), + } + ) + + self._add_result( + "GET_COMMENTS", + "GET", + f"/api/v3/{entity_type}/{entity}/comments", + "SUCCESS", + {"comments_count": len(comments), "entity": entity, "comments": comments}, + ) + + except vt.APIError as e: + self._add_result("GET_COMMENTS", "GET", f"/api/v3/{entity_type}/{entity}/comments", "ERROR", None, str(e)) + + def get_passive_dns(self, client: vt.Client): + """Get passive DNS resolutions - FULLY tests the iterator""" + try: + # IMPORTANT: Fully consume the iterator to test it properly + resolutions_it = client.iterator(f"/domains/{self.domain}/resolutions", limit=40) + + resolutions = [] + unique_ips = set() + + for resolution in resolutions_it: + resolution_data = { + "ip_address": resolution.ip_address if hasattr(resolution, "ip_address") else None, + "host_name": resolution.host_name if hasattr(resolution, "host_name") else None, + "date": str(resolution.date) if hasattr(resolution, "date") else None, + "resolver": resolution.resolver if hasattr(resolution, "resolver") else None, + } + resolutions.append(resolution_data) + + # Track unique IPs + if resolution_data["ip_address"]: + unique_ips.add(resolution_data["ip_address"]) + + self._add_result( + "PASSIVE_DNS", + "GET", + f"/api/v3/domains/{self.domain}/resolutions", + "SUCCESS", + { + "resolutions_count": len(resolutions), + "unique_ips_count": len(unique_ips), + "unique_ips": list(unique_ips), + "resolutions": resolutions, + }, + ) + + logger.info( + f"Retrieved and processed {len(resolutions)} DNS resolutions with {len(unique_ips)} unique IPs" + ) + + except vt.APIError as e: + logger.warning(f"Passive DNS not available (may require Premium API): {e}") + self._add_result( + "PASSIVE_DNS", + "GET", + f"/api/v3/domains/{self.domain}/resolutions", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_vulnerability_report(self, client: vt.Client): + """Get vulnerability report""" + try: + # Correct path for vulnerability collections + # `https://www.virustotal.com/api/v3/collections/vulnerability--cve-2010-3765` + vuln = client.get_object(f"/collections/vulnerability--{self.cve}") + + vuln_data = { + "cve": self.cve, + "id": vuln.id if hasattr(vuln, "id") else None, + } + + # Extract additional vulnerability details + if hasattr(vuln, "title"): + vuln_data["title"] = vuln.title + if hasattr(vuln, "description"): + vuln_data["description"] = ( + vuln.description[:200] + "..." if len(vuln.description) > 200 else vuln.description + ) + if hasattr(vuln, "cvss"): + vuln_data["cvss"] = vuln.cvss + + self._add_result( + "VULN_REPORT", + "GET", + f"/api/v3/intelligence/vulnerability_collections/{self.cve}", + "SUCCESS", + vuln_data, + ) + except vt.APIError as e: + logger.warning(f"OUCH! Vulnerability report not available (may require Premium API): {e}") + self._add_result( + "VULN_REPORT", + "GET", + f"/api/v3/intelligence/vulnerability_collections/{self.cve}", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_vulnerability_associations(self, client: vt.Client): + """Get vulnerability associations for an entity - FULLY tests the iterator""" + try: + # IMPORTANT: Fully consume the iterator to test it properly + vulns_it = client.iterator(f"/ip_addresses/{self.ip}/vulnerabilities", limit=20) + + vulnerabilities = [] + cve_ids = set() + high_severity_count = 0 + + for vuln in vulns_it: + vuln_data = { + "id": vuln.id if hasattr(vuln, "id") else None, + } + + # Extract CVE information + if hasattr(vuln, "cve_id"): + vuln_data["cve_id"] = vuln.cve_id + cve_ids.add(vuln.cve_id) + + if hasattr(vuln, "cvss"): + if isinstance(vuln.cvss, dict): + vuln_data["cvss_score"] = vuln.cvss.get("score") + vuln_data["cvss_severity"] = vuln.cvss.get("severity") + if vuln.cvss.get("severity") in ["HIGH", "CRITICAL"]: + high_severity_count += 1 + + if hasattr(vuln, "description"): + vuln_data["description"] = ( + vuln.description[:150] + "..." if len(vuln.description) > 150 else vuln.description + ) + + if hasattr(vuln, "published_date"): + vuln_data["published_date"] = str(vuln.published_date) + + vulnerabilities.append(vuln_data) + + self._add_result( + "VULN_ASSOCIATIONS", + "GET", + f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", + "SUCCESS", + { + "vulnerabilities_count": len(vulnerabilities), + "unique_cves_count": len(cve_ids), + "high_severity_count": high_severity_count, + "cve_ids": list(cve_ids), + "vulnerabilities": vulnerabilities, + }, + ) + + logger.info( + f"Retrieved and processed {len(vulnerabilities)} vulnerability associations ({len(cve_ids)} unique CVEs)" + ) + + except vt.APIError as e: + logger.warning(f"Vulnerability associations not available (may require Premium API): {e}") + self._add_result( + "VULN_ASSOCIATIONS", + "GET", + f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py new file mode 100644 index 000000000..1520d00b9 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py @@ -0,0 +1,60 @@ +""" +Sekoia Automation Action: Get Comments from Google Threat Intelligence +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIGetComments(Action): + """ + Retrieve comments for a given domain or IP + """ + + def run(self, arguments: dict): + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + domain = arguments.get("domain", "") + ip = arguments.get("ip", "") + url = arguments.get("url", "") + file_hash = arguments.get("file_hash", "") + + connector = VTAPIConnector(api_key, domain=domain, ip=ip, url=url, file_hash=file_hash) + with vt.Client(api_key) as client: + # Determine which entity to query for comments + # Priority: domain > ip > url > file_hash + # Only one can be provided at a time because of input constraints + entity_type = None + entity_name = None + + if domain != "": + entity_type = "domains" + entity_name = domain + elif ip != "": + entity_type = "ip_addresses" + entity_name = ip + elif url != "": + entity_type = "urls" + entity_name = url + elif file_hash != "": + entity_type = "files" + entity_name = file_hash + else: + # Use default domain + entity_type = "domains" + entity_name = domain + + print(f"Getting comments for {entity_type}: {entity_name}") + # connector.get_comments(client, entity_type) + connector.get_comments(client, entity_type, entity_name) + + result = connector.results[-1] + print("Comments:", result.response) + + return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py b/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py new file mode 100644 index 000000000..1fb78d675 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py @@ -0,0 +1,34 @@ +""" +Sekoia Automation Action: Get File Behaviour from Google Threat Intelligence +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIGetFileBehaviour(Action): + """ + Retrieve sandbox (dynamic analysis) behaviour for a file hash + This action uses is a particular use case of get_ioc_reports from VTAPIConnector with entity_type = files and entity = file_hash, like: + file = client.get_object("/files/44d88612fea8a8f36de82e1278abb02f") + Reference: https://virustotal.github.io/vt-py/quickstart.html#get-information-about-a-file + """ + + def run(self, arguments: dict): + api_key = self.module.configuration.get("api_key") + file_hash = arguments.get("file_hash", "") + + if not api_key: + return {"success": False, "error": "API key not configured"} + + try: + connector = VTAPIConnector(api_key, domain="", ip="", url="", file_hash=file_hash) + with vt.Client(api_key) as client: + connector.get_file_behaviour(client) + result = connector.results[-1] + + return {"success": result.status == "SUCCESS", "data": result.response} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py b/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py new file mode 100644 index 000000000..25afc41d4 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py @@ -0,0 +1,60 @@ +""" +Sekoia Automation Action: Get Comments from Google Threat Intelligence +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt +from typing import Any + + +class GTIIoCReport(Action): + """ + Action to get IoC reports from Google Threat Intelligence (VirusTotal) + Supports: IP addresses, domains, URLs, and file hashes + """ + + def run(self, arguments: dict[str, Any]) -> dict[str, Any]: + """ + Execute the IoC report action + + Arguments: + ip (str, optional): IP address to query + domain (str, optional): Domain to query + url (str, optional): URL to query + file_hash (str, optional): File hash (MD5, SHA1, or SHA256) to query + + Returns: + dict: IoC report data or error information + """ + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + domain = arguments.get("domain", "") + ip = arguments.get("ip", "") + url = arguments.get("url", "") + file_hash = arguments.get("file_hash", "") + + connector = VTAPIConnector(api_key, domain=domain, ip=ip, url=url, file_hash=file_hash) + with vt.Client(api_key) as client: + if domain != "": + connector.get_domain_report(client) + elif ip != "": + connector.get_ip_report(client) + elif url != "": + connector.get_url_report(client) + elif file_hash != "": + connector.get_file_report(client) + else: + return { + "success": False, + "error": "At least one of domain, ip, url, or file_hash must be provided", + } + + result = connector.results[-1] + + return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py b/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py new file mode 100644 index 000000000..127596cf4 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py @@ -0,0 +1,30 @@ +""" +Sekoia Automation Action for Google Threat Intelligence: Get Passive DNS +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIGetPassiveDNS(Action): + """ + Retrieve passive DNS records for a domain + """ + + def run(self, arguments: dict): + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + domain = arguments.get("domain", "") + + connector = VTAPIConnector(api_key, domain=domain, ip="", url="", file_hash="") + with vt.Client(api_key) as client: + connector.get_passive_dns(client) + result = connector.results[-1] + + return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py new file mode 100644 index 000000000..f19ce65b1 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py @@ -0,0 +1,32 @@ +""" +Sekoia Automation Action: Get Vulnerability Associations from Google Threat Intelligence +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIGetVulnerabilityAssociations(Action): + """ + Retrieve vulnerabilities linked to an IP or domain + """ + + def run(self, arguments: dict): + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + # TODO: Check README about parameters -> needs to be domain or ip, fix client.py accordingly + # entity_type = arguments.get("entity_type", "domains") + ip = arguments.get("ip", "") + + try: + connector = VTAPIConnector(api_key, ip=ip, domain="", url="", file_hash="") + with vt.Client(api_key) as client: + connector.get_vulnerability_associations(client) + result = connector.results[-1] + + return {"success": getattr(result, "status", "").strip().upper() == "SUCCESS", "data": result.response} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py new file mode 100644 index 000000000..6f4ed3d58 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py @@ -0,0 +1,32 @@ +""" +Sekoia Automation Action for Google Threat Intelligence: Get Vulnerability Report +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIGetVulnerabilityReport(Action): + """ + Retrieve CVE vulnerability report from Google Threat Intelligence + """ + + def run(self, arguments: dict): + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + cve = arguments.get("cve") + if not cve: + return {"success": False, "error": "No CVE provided"} + + connector = VTAPIConnector(api_key, cve=cve, domain="", ip="", url="", file_hash="") + with vt.Client(api_key) as client: + connector.get_vulnerability_report(client) + result = connector.results[-1] + + return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/sample.exe b/GoogleThreatIntelligence/googlethreatintelligence/sample.exe new file mode 100644 index 000000000..e69de29bb diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py new file mode 100644 index 000000000..f15ea17ab --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py @@ -0,0 +1,41 @@ +""" +Sekoia Automation Action for Google Threat Intelligence: Scan File +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt +from pathlib import Path + + +class GTIScanFile(Action): + """ + Upload a file to Google Threat Intelligence (VirusTotal) for scanning + """ + + def run(self, arguments: dict): + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + file_path = arguments.get("file_path") + if not file_path or not Path(file_path).exists(): + return {"success": False, "error": f"File not found: {file_path}"} + + connector = VTAPIConnector(api_key, url="", domain="", ip="", file_hash="", cve="") + with vt.Client(api_key) as client: + + connector.scan_file(client, file_path) + analysis = connector.results[-1].response + print(f"API call response: {analysis}") # Debugging line + + return { + "success": True, + "data": { + "analysis_stats": analysis.get("analysis_stats"), + "analysis_results": analysis.get("analysis_results"), + "file_path": analysis.get("file_path", file_path), + }, + } + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py new file mode 100644 index 000000000..6d0d81c0d --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py @@ -0,0 +1,41 @@ +""" +Sekoia Automation Action for Google Threat Intelligence: Scan URL +""" + +from sekoia_automation.action import Action +from .client import VTAPIConnector +import vt + + +class GTIScanURL(Action): + """ + Submit a URL for scanning to Google Threat Intelligence + """ + + def run(self, arguments: dict): + try: + api_key = self.module.configuration.get("api_key") + if not api_key: + return {"success": False, "error": "API key not configured"} + url = arguments.get("url") + if not url: + return {"success": False, "error": "No URL provided"} + + connector = VTAPIConnector(api_key, url=url, domain="", ip="", file_hash="", cve="") + with vt.Client(api_key) as client: + + connector.scan_url(client) + analysis = connector.results[-1].response + print(f"API call response: {analysis}") # Debugging line + + return { + "success": True, + "data": { + "analysis_stats": analysis.get("analysis_stats"), + "analysis_results": analysis.get("analysis_results"), + "url": analysis.get("url", url), + }, + } + + except Exception as e: + return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py b/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py new file mode 100644 index 000000000..5dd724bf3 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py @@ -0,0 +1,608 @@ +""" +Google Threat Intelligence (VirusTotal) API Connector +Production-ready connector using vt-py official library +""" + +import vt +import json +import time +import base64 +from pathlib import Path +from typing import Dict, List, Optional, Any +from dataclasses import dataclass, asdict +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") +logger = logging.getLogger(__name__) + +# Security: Load API key from environment variable +import os + +API_KEY = os.getenv("VT_API_KEY", "REDACTED") + +if API_KEY == "REDACTED": + logger.warning("API_KEY not set! Set VT_API_KEY environment variable") + + +@dataclass +class Result: + """Structure to hold test results""" + + name: str + method: str + endpoint: str + status: str + response: Any + error: Optional[str] = None + + +class VTAPIConnector: + """VirusTotal API Connector Class using vt-py""" + + def __init__( + self, + api_key: str, + domain: Optional[str] = None, + ip: Optional[str] = None, + url: Optional[str] = None, + file_hash: Optional[str] = None, + cve: Optional[str] = None, + ): + """ + Initialize the VT API Connector + + Args: + api_key: VirusTotal API key + domain: Domain to query (optional, defaults to google.com) + ip: IP address to query (optional, defaults to 8.8.8.8) + url: URL to query (optional, defaults to sekoia.io) + file_hash: File hash to query (optional, defaults to EICAR test file) + cve: CVE ID to query (optional, defaults to CVE-2021-34527) + """ + self.api_key = api_key + self.results: List[Result] = [] + + # Use provided values when available, otherwise fall back to sensible defaults + self.domain = domain or "google.com" + self.ip = ip or "8.8.8.8" + self.url = url or "https://www.sekoia.io/en/homepage/" + self.file_hash = file_hash or "44d88612fea8a8f36de82e1278abb02f" # EICAR test file + self.cve = cve or "CVE-2021-34527" + + # Headers used by the internal _request helper + self.headers = {"x-apikey": self.api_key} + + def _add_result( + self, name: str, method: str, endpoint: str, status: str, response: Any, error: Optional[str] = None + ): + """Add a test result""" + # Convert VT objects to JSON-serializable format + if response is not None: + response = self._make_serializable(response) + + result = Result(name, method, endpoint, status, response, error) + self.results.append(result) + + if error: + logger.error(f"[{status}] {name}: {error}") + else: + logger.info(f"[{status}] {name}: Success") + + def _make_serializable(self, obj: Any) -> Any: + """Convert VT objects to JSON-serializable format""" + if isinstance(obj, dict): + return {k: self._make_serializable(v) for k, v in obj.items()} + elif isinstance(obj, (list, tuple)): + return [self._make_serializable(item) for item in obj] + elif hasattr(obj, "__dict__"): + return str(obj) + else: + return obj + + def test_connectivity(self, client: vt.Client): + """Test API connectivity""" + try: + # Use get_object instead of get_json for /me endpoint + user = client.get_object("/users/me") + self._add_result( + "TEST_CONNECTIVITY", + "GET", + "/api/v3/users/me", + "SUCCESS", + {"user_id": user.id if hasattr(user, "id") else None}, + ) + except vt.APIError as e: + self._add_result("TEST_CONNECTIVITY", "GET", "/api/v3/users/me", "ERROR", None, str(e)) + + def get_ioc_report(self, client: vt.Client, entity_type: str, entity: str): + """ + Generic IOC Report - handles IP, URL, domain, or file + GET /api/v3/{entity_type}/{entity} + """ + try: + # Handle URL encoding for URLs + if entity_type == "urls": + entity = base64.urlsafe_b64encode(entity.encode()).decode().strip("=") + + ioc_obj = client.get_object(f"/{entity_type}/{entity}") + + response_data = { + "entity_type": entity_type, + "entity": entity, + "id": ioc_obj.id if hasattr(ioc_obj, "id") else None, + "reputation": ioc_obj.reputation if hasattr(ioc_obj, "reputation") else None, + } + + # Add type-specific attributes + if hasattr(ioc_obj, "last_analysis_stats"): + response_data["last_analysis_stats"] = dict(ioc_obj.last_analysis_stats) + if hasattr(ioc_obj, "country"): + response_data["country"] = ioc_obj.country + if hasattr(ioc_obj, "categories"): + response_data["categories"] = dict(ioc_obj.categories) if ioc_obj.categories else None + + self._add_result( + f"GET_IOC_REPORT_{entity_type.upper()}", + "GET", + f"/api/v3/{entity_type}/{entity}", + "SUCCESS", + response_data, + ) + except vt.APIError as e: + self._add_result( + f"GET_IOC_REPORT_{entity_type.upper()}", + "GET", + f"/api/v3/{entity_type}/{entity}", + "ERROR", + None, + str(e), + ) + + def get_ip_report(self, client: vt.Client): + """Get IP address report""" + self.get_ioc_report(client, "ip_addresses", self.ip) + + def get_domain_report(self, client: vt.Client): + """Get domain report""" + self.get_ioc_report(client, "domains", self.domain) + + def get_url_report(self, client: vt.Client): + """Get URL report""" + self.get_ioc_report(client, "urls", self.url) + + def get_file_report(self, client: vt.Client): + """Get file report""" + self.get_ioc_report(client, "files", self.file_hash) + + def scan_url(self, client: vt.Client): + """Scan a URL + This returns an Analysis ID. The analysis can be retrieved by using the Analysis endpoint. + -> https://docs.virustotal.com/reference/analysis + + """ + try: + analysis = client.scan_url(self.url, wait_for_completion=True) + print("Analysis completed") + # print(analysis.stats) + # print(analysis.results) + self._add_result( + "SCAN_URL", + "POST", + "/api/v3/urls", + "SUCCESS", + {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "url": self.url}, + ) + except vt.APIError as e: + self._add_result("SCAN_URL", "POST", "/api/v3/urls", "ERROR", None, str(e)) + return None + + def scan_file(self, client: vt.Client, file_path: str): + """Scan a file""" + try: + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + + with open(file_path, "rb") as f: + analysis = client.scan_file(f, wait_for_completion=True) + + self._add_result( + "SCAN_FILE", + "POST", + "/api/v3/files", + "SUCCESS", + {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "file": file_path}, + ) + return analysis.id + except (vt.APIError, FileNotFoundError, IOError) as e: + self._add_result("SCAN_FILE", "POST", "/api/v3/files", "ERROR", None, str(e)) + return None + + def get_analysis(self, client: vt.Client, analysis_id: str): + """Get analysis results""" + try: + analysis = client.get_object(f"/analyses/{analysis_id}") + self._add_result( + "GET_ANALYSIS", + "GET", + f"/api/v3/analyses/{analysis_id}", + "SUCCESS", + {"status": analysis.status, "stats": analysis.stats if hasattr(analysis, "stats") else None}, + ) + except vt.APIError as e: + self._add_result("GET_ANALYSIS", "GET", f"/api/v3/analyses/{analysis_id}", "ERROR", None, str(e)) + + def get_file_behaviour(self, client: vt.Client): + """Get file sandbox behavior""" + try: + # Use iterator for behaviours and FULLY consume it + behaviours_it = client.iterator(f"/files/{self.file_hash}/behaviours", limit=5) + + # IMPORTANT: Fully consume the iterator to test it properly + behaviours = [] + for behaviour in behaviours_it: + behaviour_data = { + "sandbox_name": behaviour.sandbox_name if hasattr(behaviour, "sandbox_name") else None, + } + + # Extract detailed behaviour information + if hasattr(behaviour, "processes_created"): + behaviour_data["processes_created"] = len(behaviour.processes_created) + if hasattr(behaviour, "files_written"): + behaviour_data["files_written"] = len(behaviour.files_written) + if hasattr(behaviour, "files_deleted"): + behaviour_data["files_deleted"] = len(behaviour.files_deleted) + if hasattr(behaviour, "registry_keys_set"): + behaviour_data["registry_keys_set"] = len(behaviour.registry_keys_set) + if hasattr(behaviour, "dns_lookups"): + behaviour_data["dns_lookups"] = len(behaviour.dns_lookups) + if hasattr(behaviour, "ip_traffic"): + behaviour_data["ip_traffic"] = len(behaviour.ip_traffic) + + behaviours.append(behaviour_data) + + self._add_result( + "GET_FILE_SANDBOX", + "GET", + f"/api/v3/files/{self.file_hash}/behaviours", + "SUCCESS", + {"behaviours_count": len(behaviours), "behaviours": behaviours}, + ) + except vt.APIError as e: + # This endpoint requires Premium API - log as warning not error + logger.warning(f"File behaviours not available (may require Premium API): {e}") + self._add_result( + "GET_FILE_SANDBOX", + "GET", + f"/api/v3/files/{self.file_hash}/behaviours", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_comments(self, client: vt.Client, entity_type: str = "domains"): + """Get comments for an entity - FULLY tests the iterator""" + try: + entity = self.domain if entity_type == "domains" else self.ip + + # IMPORTANT: Fully consume the iterator to test it properly + comments_it = client.iterator(f"/{entity_type}/{entity}/comments", limit=10) + + comments = [] + for comment in comments_it: + comment_data = { + "text": comment.text if hasattr(comment, "text") else None, + "date": str(comment.date) if hasattr(comment, "date") else None, + "votes": ( + { + "positive": comment.votes.get("positive", 0) if hasattr(comment, "votes") else 0, + "negative": comment.votes.get("negative", 0) if hasattr(comment, "votes") else 0, + } + if hasattr(comment, "votes") + else {"positive": 0, "negative": 0} + ), + "author": comment.author if hasattr(comment, "author") else None, + } + comments.append(comment_data) + + self._add_result( + "GET_COMMENTS", + "GET", + f"/api/v3/{entity_type}/{entity}/comments", + "SUCCESS", + {"comments_count": len(comments), "entity": entity, "comments": comments}, + ) + + logger.info(f"Retrieved and processed {len(comments)} comments") + + except vt.APIError as e: + self._add_result("GET_COMMENTS", "GET", f"/api/v3/{entity_type}/{entity}/comments", "ERROR", None, str(e)) + + def get_passive_dns(self, client: vt.Client): + """Get passive DNS resolutions - FULLY tests the iterator""" + try: + # IMPORTANT: Fully consume the iterator to test it properly + resolutions_it = client.iterator(f"/domains/{self.domain}/resolutions", limit=40) + + resolutions = [] + unique_ips = set() + + for resolution in resolutions_it: + resolution_data = { + "ip_address": resolution.ip_address if hasattr(resolution, "ip_address") else None, + "host_name": resolution.host_name if hasattr(resolution, "host_name") else None, + "date": str(resolution.date) if hasattr(resolution, "date") else None, + "resolver": resolution.resolver if hasattr(resolution, "resolver") else None, + } + resolutions.append(resolution_data) + + # Track unique IPs + if resolution_data["ip_address"]: + unique_ips.add(resolution_data["ip_address"]) + + self._add_result( + "PASSIVE_DNS", + "GET", + f"/api/v3/domains/{self.domain}/resolutions", + "SUCCESS", + { + "resolutions_count": len(resolutions), + "unique_ips_count": len(unique_ips), + "unique_ips": list(unique_ips), + "resolutions": resolutions, + }, + ) + + logger.info( + f"Retrieved and processed {len(resolutions)} DNS resolutions with {len(unique_ips)} unique IPs" + ) + + except vt.APIError as e: + logger.warning(f"Passive DNS not available (may require Premium API): {e}") + self._add_result( + "PASSIVE_DNS", + "GET", + f"/api/v3/domains/{self.domain}/resolutions", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_vulnerability_report(self, client: vt.Client): + """Get vulnerability report""" + try: + # Correct path for vulnerability collections + print("Getting vuln", self.cve) + # `https://www.virustotal.com/api/v3/collections/vulnerability--cve-2010-3765` + vuln = client.get_object(f"/collections/vulnerability--{self.cve}") + print("VULN is:", vuln) + + vuln_data = { + "cve": self.cve, + "id": vuln.id if hasattr(vuln, "id") else None, + } + + # Extract additional vulnerability details + if hasattr(vuln, "title"): + vuln_data["title"] = vuln.title + if hasattr(vuln, "description"): + vuln_data["description"] = ( + vuln.description[:200] + "..." if len(vuln.description) > 200 else vuln.description + ) + if hasattr(vuln, "cvss"): + vuln_data["cvss"] = vuln.cvss + + self._add_result( + "VULN_REPORT", + "GET", + f"/api/v3/intelligence/vulnerability_collections/{self.cve}", + "SUCCESS", + vuln_data, + ) + except vt.APIError as e: + logger.warning(f"OUCH! Vulnerability report not available (may require Premium API): {e}") + self._add_result( + "VULN_REPORT", + "GET", + f"/api/v3/intelligence/vulnerability_collections/{self.cve}", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def get_vulnerability_associations(self, client: vt.Client): + """Get vulnerability associations for an entity - FULLY tests the iterator""" + try: + # IMPORTANT: Fully consume the iterator to test it properly + vulns_it = client.iterator(f"/ip_addresses/{self.ip}/vulnerabilities", limit=20) + + vulnerabilities = [] + cve_ids = set() + high_severity_count = 0 + + for vuln in vulns_it: + vuln_data = { + "id": vuln.id if hasattr(vuln, "id") else None, + } + + # Extract CVE information + if hasattr(vuln, "cve_id"): + vuln_data["cve_id"] = vuln.cve_id + cve_ids.add(vuln.cve_id) + + if hasattr(vuln, "cvss"): + if isinstance(vuln.cvss, dict): + vuln_data["cvss_score"] = vuln.cvss.get("score") + vuln_data["cvss_severity"] = vuln.cvss.get("severity") + if vuln.cvss.get("severity") in ["HIGH", "CRITICAL"]: + high_severity_count += 1 + + if hasattr(vuln, "description"): + vuln_data["description"] = ( + vuln.description[:150] + "..." if len(vuln.description) > 150 else vuln.description + ) + + if hasattr(vuln, "published_date"): + vuln_data["published_date"] = str(vuln.published_date) + + vulnerabilities.append(vuln_data) + + self._add_result( + "VULN_ASSOCIATIONS", + "GET", + f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", + "SUCCESS", + { + "vulnerabilities_count": len(vulnerabilities), + "unique_cves_count": len(cve_ids), + "high_severity_count": high_severity_count, + "cve_ids": list(cve_ids), + "vulnerabilities": vulnerabilities, + }, + ) + + logger.info( + f"Retrieved and processed {len(vulnerabilities)} vulnerability associations ({len(cve_ids)} unique CVEs)" + ) + + except vt.APIError as e: + logger.warning(f"Vulnerability associations not available (may require Premium API): {e}") + self._add_result( + "VULN_ASSOCIATIONS", + "GET", + f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", + "NOT_AVAILABLE", + None, + f"May require Premium API: {str(e)}", + ) + + def run_all_tests(self, test_file_path: Optional[str] = None): + """Run all API tests""" + logger.info("Starting VirusTotal API tests...") + logger.info( + f"Using: domain={self.domain}, ip={self.ip}, url={self.url}, file_hash={self.file_hash}, cve={self.cve}" + ) + + with vt.Client(self.api_key) as client: + # Additional data - FULLY test iterators + logger.info("Testing iterators (comments, passive DNS, vulnerability associations)...") + + # Get comments - default to domain + self.get_comments(client, "domains") # Use plural "domains" + print("Comment (domain):", self.results[-1].response) + time.sleep(0.5) + + # Get vulnerability report + self.get_vulnerability_report(client) + print("VULN REPORT:", self.results[-1].response) + time.sleep(0.5) + + logger.info("All tests completed!") + + # Alternative version that queries based on what's provided: + def run_all_tests_smart(self, test_file_path: Optional[str] = None): + """Run all API tests - intelligently choose entity type""" + logger.info("Starting VirusTotal API tests...") + logger.info( + f"Using: domain={self.domain}, ip={self.ip}, url={self.url}, file_hash={self.file_hash}, cve={self.cve}" + ) + + with vt.Client(self.api_key) as client: + logger.info("Testing iterators (comments, passive DNS, vulnerability associations)...") + + self.scan_url(client) + print("SCAN URL:", self.results[-1].response) + time.sleep(0.5) + + if test_file_path is not None: + self.scan_file(client, test_file_path) + print("SCAN FILE:", self.results[-1].response) + time.sleep(0.5) + + # Determine which entity to query for comments + # Priority: domain > ip > url > file_hash + entity_type = None + entity_name = None + + if self.domain: + entity_type = "domains" + entity_name = self.domain + elif self.ip: + entity_type = "ip_addresses" + entity_name = self.ip + elif self.url: + entity_type = "urls" + entity_name = self.url + elif self.file_hash: + entity_type = "files" + entity_name = self.file_hash + else: + # Use default domain + entity_type = "domains" + entity_name = self.domain + + print(f"Getting comments for {entity_type}: {entity_name}") + self.get_comments(client, entity_type) + print("Comments:", self.results[-1].response) + time.sleep(0.5) + + # Get vulnerability report + # self.get_vulnerability_report(client) + # print("VULN REPORT:", self.results[-1].response) + # time.sleep(0.5) + # logger.info("All tests completed!") + + def save_results(self, output_file: str = "vt_test_results.json"): + """Save test results to JSON file""" + results_dict = [asdict(r) for r in self.results] + + with open(output_file, "w", encoding="utf-8") as f: + json.dump(results_dict, f, indent=2, ensure_ascii=False) + + logger.info(f"Results saved to {output_file}") + + # Print summary + success_count = sum(1 for r in self.results if r.status == "SUCCESS") + error_count = sum(1 for r in self.results if r.status == "ERROR") + not_available_count = sum(1 for r in self.results if r.status == "NOT_AVAILABLE") + + print(f"\n{'='*60}") + print(f"TEST SUMMARY") + print(f"{'='*60}") + print(f"Total tests: {len(self.results)}") + print(f"Successful: {success_count}") + print(f"Failed: {error_count}") + print(f"Not Available (Premium API): {not_available_count}") + print(f"{'='*60}\n") + + +def main(): + """Main execution function""" + # Security check + if API_KEY == "REDACTED": + print("ERROR: Please set VT_API_KEY environment variable") + print("Example: export VT_API_KEY='your_api_key_here'") + return + + # Initialize connector with custom values (or use defaults) + connector = VTAPIConnector( + api_key=API_KEY, + domain="google.com", # Optional: override default + ip="8.8.8.8", # Optional: override default + url="https://www.sekoia.io/en/homepage/", # Optional: override default + file_hash="44d88612fea8a8f36de82e1278abb02f", # Optional: override default + cve="CVE-2021-34527", # Optional: override default + ) + + # Run tests (optionally provide a test file path) + # connector.run_all_tests(test_file_path="upload.png") + # connector.run_all_tests() + connector.run_all_tests_smart("sample.exe") + + # Save results + connector.save_results() + + +if __name__ == "__main__": + main() diff --git a/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json b/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json new file mode 100644 index 000000000..956cb76d0 --- /dev/null +++ b/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json @@ -0,0 +1,129 @@ +[ + { + "name": "SCAN_URL", + "method": "POST", + "endpoint": "/api/v3/urls", + "status": "SUCCESS", + "response": { + "analysis_stats": "{'malicious': 0, 'suspicious': 0, 'undetected': 30, 'harmless': 68, 'timeout': 0}", + "analysis_results": "{'Artists Against 419': {'method': 'blacklist', 'engine_name': 'Artists Against 419', 'category': 'harmless', 'result': 'clean'}, 'Acronis': {'method': 'blacklist', 'engine_name': 'Acronis', 'category': 'harmless', 'result': 'clean'}, 'Abusix': {'method': 'blacklist', 'engine_name': 'Abusix', 'category': 'harmless', 'result': 'clean'}, 'ADMINUSLabs': {'method': 'blacklist', 'engine_name': 'ADMINUSLabs', 'category': 'harmless', 'result': 'clean'}, 'Lionic': {'method': 'blacklist', 'engine_name': 'Lionic', 'category': 'harmless', 'result': 'clean'}, 'Criminal IP': {'method': 'blacklist', 'engine_name': 'Criminal IP', 'category': 'harmless', 'result': 'clean'}, 'AILabs (MONITORAPP)': {'method': 'blacklist', 'engine_name': 'AILabs (MONITORAPP)', 'category': 'harmless', 'result': 'clean'}, 'AlienVault': {'method': 'blacklist', 'engine_name': 'AlienVault', 'category': 'harmless', 'result': 'clean'}, 'alphaMountain.ai': {'method': 'blacklist', 'engine_name': 'alphaMountain.ai', 'category': 'undetected', 'result': 'unrated'}, 'AlphaSOC': {'method': 'blacklist', 'engine_name': 'AlphaSOC', 'category': 'undetected', 'result': 'unrated'}, 'Antiy-AVL': {'method': 'blacklist', 'engine_name': 'Antiy-AVL', 'category': 'harmless', 'result': 'clean'}, 'ArcSight Threat Intelligence': {'method': 'blacklist', 'engine_name': 'ArcSight Threat Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'AutoShun': {'method': 'blacklist', 'engine_name': 'AutoShun', 'category': 'undetected', 'result': 'unrated'}, 'Axur': {'method': 'blacklist', 'engine_name': 'Axur', 'category': 'undetected', 'result': 'unrated'}, 'benkow.cc': {'method': 'blacklist', 'engine_name': 'benkow.cc', 'category': 'harmless', 'result': 'clean'}, 'Bfore.Ai PreCrime': {'method': 'blacklist', 'engine_name': 'Bfore.Ai PreCrime', 'category': 'undetected', 'result': 'unrated'}, 'BitDefender': {'method': 'blacklist', 'engine_name': 'BitDefender', 'category': 'harmless', 'result': 'clean'}, 'Bkav': {'method': 'blacklist', 'engine_name': 'Bkav', 'category': 'undetected', 'result': 'unrated'}, 'BlockList': {'method': 'blacklist', 'engine_name': 'BlockList', 'category': 'harmless', 'result': 'clean'}, 'Blueliv': {'method': 'blacklist', 'engine_name': 'Blueliv', 'category': 'harmless', 'result': 'clean'}, 'Certego': {'method': 'blacklist', 'engine_name': 'Certego', 'category': 'harmless', 'result': 'clean'}, 'ChainPatrol': {'method': 'blacklist', 'engine_name': 'ChainPatrol', 'category': 'undetected', 'result': 'unrated'}, 'Chong Lua Dao': {'method': 'blacklist', 'engine_name': 'Chong Lua Dao', 'category': 'harmless', 'result': 'clean'}, 'CINS Army': {'method': 'blacklist', 'engine_name': 'CINS Army', 'category': 'harmless', 'result': 'clean'}, 'Snort IP sample list': {'method': 'blacklist', 'engine_name': 'Snort IP sample list', 'category': 'harmless', 'result': 'clean'}, 'Cluster25': {'method': 'blacklist', 'engine_name': 'Cluster25', 'category': 'undetected', 'result': 'unrated'}, 'CMC Threat Intelligence': {'method': 'blacklist', 'engine_name': 'CMC Threat Intelligence', 'category': 'harmless', 'result': 'clean'}, 'Xcitium Verdict Cloud': {'method': 'blacklist', 'engine_name': 'Xcitium Verdict Cloud', 'category': 'harmless', 'result': 'clean'}, 'CRDF': {'method': 'blacklist', 'engine_name': 'CRDF', 'category': 'harmless', 'result': 'clean'}, 'CSIS Security Group': {'method': 'blacklist', 'engine_name': 'CSIS Security Group', 'category': 'undetected', 'result': 'unrated'}, 'Cyan': {'method': 'blacklist', 'engine_name': 'Cyan', 'category': 'undetected', 'result': 'unrated'}, 'Cyble': {'method': 'blacklist', 'engine_name': 'Cyble', 'category': 'harmless', 'result': 'clean'}, 'CyRadar': {'method': 'blacklist', 'engine_name': 'CyRadar', 'category': 'harmless', 'result': 'clean'}, 'desenmascara.me': {'method': 'blacklist', 'engine_name': 'desenmascara.me', 'category': 'harmless', 'result': 'clean'}, 'DNS8': {'method': 'blacklist', 'engine_name': 'DNS8', 'category': 'harmless', 'result': 'clean'}, 'Dr.Web': {'method': 'blacklist', 'engine_name': 'Dr.Web', 'category': 'harmless', 'result': 'clean'}, 'Emsisoft': {'method': 'blacklist', 'engine_name': 'Emsisoft', 'category': 'harmless', 'result': 'clean'}, 'Ermes': {'method': 'blacklist', 'engine_name': 'Ermes', 'category': 'undetected', 'result': 'unrated'}, 'ESET': {'method': 'blacklist', 'engine_name': 'ESET', 'category': 'harmless', 'result': 'clean'}, 'ESTsecurity': {'method': 'blacklist', 'engine_name': 'ESTsecurity', 'category': 'harmless', 'result': 'clean'}, 'EmergingThreats': {'method': 'blacklist', 'engine_name': 'EmergingThreats', 'category': 'harmless', 'result': 'clean'}, 'Feodo Tracker': {'method': 'blacklist', 'engine_name': 'Feodo Tracker', 'category': 'harmless', 'result': 'clean'}, 'Fortinet': {'method': 'blacklist', 'engine_name': 'Fortinet', 'category': 'harmless', 'result': 'clean'}, 'G-Data': {'method': 'blacklist', 'engine_name': 'G-Data', 'category': 'harmless', 'result': 'clean'}, 'Google Safebrowsing': {'method': 'blacklist', 'engine_name': 'Google Safebrowsing', 'category': 'harmless', 'result': 'clean'}, 'GCP Abuse Intelligence': {'method': 'blacklist', 'engine_name': 'GCP Abuse Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'GreenSnow': {'method': 'blacklist', 'engine_name': 'GreenSnow', 'category': 'harmless', 'result': 'clean'}, 'Gridinsoft': {'method': 'blacklist', 'engine_name': 'Gridinsoft', 'category': 'undetected', 'result': 'unrated'}, 'Heimdal Security': {'method': 'blacklist', 'engine_name': 'Heimdal Security', 'category': 'harmless', 'result': 'clean'}, 'Hunt.io Intelligence': {'method': 'blacklist', 'engine_name': 'Hunt.io Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'IPsum': {'method': 'blacklist', 'engine_name': 'IPsum', 'category': 'harmless', 'result': 'clean'}, 'Juniper Networks': {'method': 'blacklist', 'engine_name': 'Juniper Networks', 'category': 'harmless', 'result': 'clean'}, 'Kaspersky': {'method': 'blacklist', 'engine_name': 'Kaspersky', 'category': 'harmless', 'result': 'clean'}, 'Lumu': {'method': 'blacklist', 'engine_name': 'Lumu', 'category': 'undetected', 'result': 'unrated'}, 'Malwared': {'method': 'blacklist', 'engine_name': 'Malwared', 'category': 'harmless', 'result': 'clean'}, 'MalwareURL': {'method': 'blacklist', 'engine_name': 'MalwareURL', 'category': 'undetected', 'result': 'unrated'}, 'MalwarePatrol': {'method': 'blacklist', 'engine_name': 'MalwarePatrol', 'category': 'harmless', 'result': 'clean'}, 'malwares.com URL checker': {'method': 'blacklist', 'engine_name': 'malwares.com URL checker', 'category': 'harmless', 'result': 'clean'}, 'Mimecast': {'method': 'blacklist', 'engine_name': 'Mimecast', 'category': 'undetected', 'result': 'unrated'}, 'Netcraft': {'method': 'blacklist', 'engine_name': 'Netcraft', 'category': 'undetected', 'result': 'unrated'}, 'OpenPhish': {'method': 'blacklist', 'engine_name': 'OpenPhish', 'category': 'harmless', 'result': 'clean'}, '0xSI_f33d': {'method': 'blacklist', 'engine_name': '0xSI_f33d', 'category': 'undetected', 'result': 'unrated'}, 'Phishing Database': {'method': 'blacklist', 'engine_name': 'Phishing Database', 'category': 'harmless', 'result': 'clean'}, 'PhishFort': {'method': 'blacklist', 'engine_name': 'PhishFort', 'category': 'undetected', 'result': 'unrated'}, 'PhishLabs': {'method': 'blacklist', 'engine_name': 'PhishLabs', 'category': 'undetected', 'result': 'unrated'}, 'Phishtank': {'method': 'blacklist', 'engine_name': 'Phishtank', 'category': 'harmless', 'result': 'clean'}, 'PREBYTES': {'method': 'blacklist', 'engine_name': 'PREBYTES', 'category': 'harmless', 'result': 'clean'}, 'PrecisionSec': {'method': 'blacklist', 'engine_name': 'PrecisionSec', 'category': 'undetected', 'result': 'unrated'}, 'Quick Heal': {'method': 'blacklist', 'engine_name': 'Quick Heal', 'category': 'harmless', 'result': 'clean'}, 'Quttera': {'method': 'blacklist', 'engine_name': 'Quttera', 'category': 'harmless', 'result': 'clean'}, 'Rising': {'method': 'blacklist', 'engine_name': 'Rising', 'category': 'harmless', 'result': 'clean'}, 'SafeToOpen': {'method': 'blacklist', 'engine_name': 'SafeToOpen', 'category': 'undetected', 'result': 'unrated'}, 'Sangfor': {'method': 'blacklist', 'engine_name': 'Sangfor', 'category': 'harmless', 'result': 'clean'}, 'Sansec eComscan': {'method': 'blacklist', 'engine_name': 'Sansec eComscan', 'category': 'undetected', 'result': 'unrated'}, 'Scantitan': {'method': 'blacklist', 'engine_name': 'Scantitan', 'category': 'harmless', 'result': 'clean'}, 'SCUMWARE.org': {'method': 'blacklist', 'engine_name': 'SCUMWARE.org', 'category': 'harmless', 'result': 'clean'}, 'Seclookup': {'method': 'blacklist', 'engine_name': 'Seclookup', 'category': 'harmless', 'result': 'clean'}, 'SOCRadar': {'method': 'blacklist', 'engine_name': 'SOCRadar', 'category': 'undetected', 'result': 'unrated'}, 'Sophos': {'method': 'blacklist', 'engine_name': 'Sophos', 'category': 'harmless', 'result': 'clean'}, 'Spam404': {'method': 'blacklist', 'engine_name': 'Spam404', 'category': 'harmless', 'result': 'clean'}, 'StopForumSpam': {'method': 'blacklist', 'engine_name': 'StopForumSpam', 'category': 'harmless', 'result': 'clean'}, 'Sucuri SiteCheck': {'method': 'blacklist', 'engine_name': 'Sucuri SiteCheck', 'category': 'harmless', 'result': 'clean'}, 'securolytics': {'method': 'blacklist', 'engine_name': 'securolytics', 'category': 'harmless', 'result': 'clean'}, 'Threatsourcing': {'method': 'blacklist', 'engine_name': 'Threatsourcing', 'category': 'harmless', 'result': 'clean'}, 'ThreatHive': {'method': 'blacklist', 'engine_name': 'ThreatHive', 'category': 'harmless', 'result': 'clean'}, 'Trustwave': {'method': 'blacklist', 'engine_name': 'Trustwave', 'category': 'harmless', 'result': 'clean'}, 'Underworld': {'method': 'blacklist', 'engine_name': 'Underworld', 'category': 'undetected', 'result': 'unrated'}, 'URLhaus': {'method': 'blacklist', 'engine_name': 'URLhaus', 'category': 'harmless', 'result': 'clean'}, 'URLQuery': {'method': 'blacklist', 'engine_name': 'URLQuery', 'category': 'undetected', 'result': 'unrated'}, 'Viettel Threat Intelligence': {'method': 'blacklist', 'engine_name': 'Viettel Threat Intelligence', 'category': 'harmless', 'result': 'clean'}, 'VIPRE': {'method': 'blacklist', 'engine_name': 'VIPRE', 'category': 'undetected', 'result': 'unrated'}, 'ViriBack': {'method': 'blacklist', 'engine_name': 'ViriBack', 'category': 'harmless', 'result': 'clean'}, 'VX Vault': {'method': 'blacklist', 'engine_name': 'VX Vault', 'category': 'harmless', 'result': 'clean'}, 'Webroot': {'method': 'blacklist', 'engine_name': 'Webroot', 'category': 'harmless', 'result': 'clean'}, 'Forcepoint ThreatSeeker': {'method': 'blacklist', 'engine_name': 'Forcepoint ThreatSeeker', 'category': 'harmless', 'result': 'clean'}, 'Yandex Safebrowsing': {'method': 'blacklist', 'engine_name': 'Yandex Safebrowsing', 'category': 'harmless', 'result': 'clean'}, 'ZeroCERT': {'method': 'blacklist', 'engine_name': 'ZeroCERT', 'category': 'harmless', 'result': 'clean'}, 'ZeroFox': {'method': 'blacklist', 'engine_name': 'ZeroFox', 'category': 'undetected', 'result': 'unrated'}}", + "url": "https://www.sekoia.io/en/homepage/" + }, + "error": null + }, + { + "name": "SCAN_FILE", + "method": "POST", + "endpoint": "/api/v3/files", + "status": "SUCCESS", + "response": { + "analysis_stats": "{'malicious': 0, 'suspicious': 0, 'undetected': 61, 'harmless': 0, 'timeout': 1, 'confirmed-timeout': 1, 'failure': 1, 'type-unsupported': 12}", + "analysis_results": "{'Bkav': {'method': 'blacklist', 'engine_name': 'Bkav', 'engine_version': '2.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Lionic': {'method': 'blacklist', 'engine_name': 'Lionic', 'engine_version': '8.16', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ClamAV': {'method': 'blacklist', 'engine_name': 'ClamAV', 'engine_version': '1.5.1.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'CMC': {'method': 'blacklist', 'engine_name': 'CMC', 'engine_version': '2.4.2022.1', 'engine_update': '20251115', 'category': 'undetected', 'result': None}, 'CAT-QuickHeal': {'method': 'blacklist', 'engine_name': 'CAT-QuickHeal', 'engine_version': '22.00', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Skyhigh': {'method': 'blacklist', 'engine_name': 'Skyhigh', 'engine_version': 'v2021.2.0+4045', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ALYac': {'method': 'blacklist', 'engine_name': 'ALYac', 'engine_version': '2.0.0.10', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Malwarebytes': {'method': 'blacklist', 'engine_name': 'Malwarebytes', 'engine_version': '3.1.0.176', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'VIPRE': {'method': 'blacklist', 'engine_name': 'VIPRE', 'engine_version': '6.0.0.35', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Sangfor': {'method': 'blacklist', 'engine_name': 'Sangfor', 'engine_version': '2.22.3.0', 'engine_update': '20251117', 'category': 'undetected', 'result': None}, 'K7AntiVirus': {'method': 'blacklist', 'engine_name': 'K7AntiVirus', 'engine_version': '14.19.57742', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'K7GW': {'method': 'blacklist', 'engine_name': 'K7GW', 'engine_version': '14.19.57743', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'CrowdStrike': {'method': 'blacklist', 'engine_name': 'CrowdStrike', 'engine_version': '1.0', 'engine_update': '20230417', 'category': 'undetected', 'result': None}, 'Arcabit': {'method': 'blacklist', 'engine_name': 'Arcabit', 'engine_version': '2025.0.0.23', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Baidu': {'method': 'blacklist', 'engine_name': 'Baidu', 'engine_version': '1.0.0.2', 'engine_update': '20190318', 'category': 'undetected', 'result': None}, 'VirIT': {'method': 'blacklist', 'engine_name': 'VirIT', 'engine_version': '9.5.1089', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Paloalto': {'method': 'blacklist', 'engine_name': 'Paloalto', 'engine_version': '0.9.0.1003', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Symantec': {'method': 'blacklist', 'engine_name': 'Symantec', 'engine_version': '1.22.0.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ESET-NOD32': {'method': 'blacklist', 'engine_name': 'ESET-NOD32', 'engine_version': '18.2.18.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'APEX': {'method': 'blacklist', 'engine_name': 'APEX', 'engine_version': '6.717', 'engine_update': '20251117', 'category': 'undetected', 'result': None}, 'Avast': {'method': 'blacklist', 'engine_name': 'Avast', 'engine_version': '23.9.8494.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Cynet': {'method': 'blacklist', 'engine_name': 'Cynet', 'engine_version': '4.0.3.4', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Kaspersky': {'method': 'blacklist', 'engine_name': 'Kaspersky', 'engine_version': '22.0.1.28', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'BitDefender': {'method': 'blacklist', 'engine_name': 'BitDefender', 'engine_version': '7.2', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'NANO-Antivirus': {'method': 'blacklist', 'engine_name': 'NANO-Antivirus', 'engine_version': '1.0.170.26895', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'SUPERAntiSpyware': {'method': 'blacklist', 'engine_name': 'SUPERAntiSpyware', 'engine_version': '5.6.0.1032', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'MicroWorld-eScan': {'method': 'blacklist', 'engine_name': 'MicroWorld-eScan', 'engine_version': '14.0.409.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Rising': {'method': 'blacklist', 'engine_name': 'Rising', 'engine_version': '25.0.0.28', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Sophos': {'method': 'blacklist', 'engine_name': 'Sophos', 'engine_version': '3.3.1.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'F-Secure': {'method': 'blacklist', 'engine_name': 'F-Secure', 'engine_version': '18.10.1547.307', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'DrWeb': {'method': 'blacklist', 'engine_name': 'DrWeb', 'engine_version': '7.0.72.9030', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Zillya': {'method': 'blacklist', 'engine_name': 'Zillya', 'engine_version': '2.0.0.5488', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'TrendMicro': {'method': 'blacklist', 'engine_name': 'TrendMicro', 'engine_version': '24.550.0.1002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'McAfeeD': {'method': 'blacklist', 'engine_name': 'McAfeeD', 'engine_version': '1.2.0.10275', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Emsisoft': {'method': 'blacklist', 'engine_name': 'Emsisoft', 'engine_version': '2024.8.0.61147', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Ikarus': {'method': 'blacklist', 'engine_name': 'Ikarus', 'engine_version': '6.4.16.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Jiangmin': {'method': 'blacklist', 'engine_name': 'Jiangmin', 'engine_version': '16.0.100', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Google': {'method': 'blacklist', 'engine_name': 'Google', 'engine_version': '1763712050', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Avira': {'method': 'blacklist', 'engine_name': 'Avira', 'engine_version': '8.3.3.24', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Antiy-AVL': {'method': 'blacklist', 'engine_name': 'Antiy-AVL', 'engine_version': '3.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Kingsoft': {'method': 'blacklist', 'engine_name': 'Kingsoft', 'engine_version': 'None', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Xcitium': {'method': 'blacklist', 'engine_name': 'Xcitium', 'engine_version': '38208', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Microsoft': {'method': 'blacklist', 'engine_name': 'Microsoft', 'engine_version': '1.1.25100.9002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ViRobot': {'method': 'blacklist', 'engine_name': 'ViRobot', 'engine_version': '2014.3.20.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ZoneAlarm': {'method': 'blacklist', 'engine_name': 'ZoneAlarm', 'engine_version': '6.21-110946643', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'GData': {'method': 'blacklist', 'engine_name': 'GData', 'engine_version': 'GD:27.42591AVA:64.30179', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Varist': {'method': 'blacklist', 'engine_name': 'Varist', 'engine_version': '6.6.1.3', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'AhnLab-V3': {'method': 'blacklist', 'engine_name': 'AhnLab-V3', 'engine_version': '3.29.0.10596', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'VBA32': {'method': 'blacklist', 'engine_name': 'VBA32', 'engine_version': '5.4.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TACHYON': {'method': 'blacklist', 'engine_name': 'TACHYON', 'engine_version': '2025-11-21.02', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Zoner': {'method': 'blacklist', 'engine_name': 'Zoner', 'engine_version': '2.2.2.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TrendMicro-HouseCall': {'method': 'blacklist', 'engine_name': 'TrendMicro-HouseCall', 'engine_version': '24.550.0.1002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Tencent': {'method': 'blacklist', 'engine_name': 'Tencent', 'engine_version': '1.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Yandex': {'method': 'blacklist', 'engine_name': 'Yandex', 'engine_version': '5.5.2.24', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TrellixENS': {'method': 'blacklist', 'engine_name': 'TrellixENS', 'engine_version': '6.0.6.653', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'huorong': {'method': 'blacklist', 'engine_name': 'huorong', 'engine_version': 'e496057:e496057:f6223dc:f6223dc', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'MaxSecure': {'method': 'blacklist', 'engine_name': 'MaxSecure', 'engine_version': '1.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Fortinet': {'method': 'blacklist', 'engine_name': 'Fortinet', 'engine_version': '7.0.30.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'AVG': {'method': 'blacklist', 'engine_name': 'AVG', 'engine_version': '23.9.8494.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Panda': {'method': 'blacklist', 'engine_name': 'Panda', 'engine_version': '4.6.4.2', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'alibabacloud': {'method': 'blacklist', 'engine_name': 'alibabacloud', 'engine_version': '2.2.0', 'engine_update': '20250321', 'category': 'undetected', 'result': None}, 'Trustlook': {'method': 'blacklist', 'engine_name': 'Trustlook', 'engine_version': '1.0', 'engine_update': '20251121', 'category': 'timeout', 'result': None}, 'CTX': {'method': 'blacklist', 'engine_name': 'CTX', 'engine_version': '2024.8.29.1', 'engine_update': '20251121', 'category': 'confirmed-timeout', 'result': None}, 'Gridinsoft': {'method': 'blacklist', 'engine_name': 'Gridinsoft', 'engine_version': '1.0.229.174', 'engine_update': '20251121', 'category': 'failure', 'result': None}, 'Avast-Mobile': {'method': 'blacklist', 'engine_name': 'Avast-Mobile', 'engine_version': '251120-00', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}, 'SymantecMobileInsight': {'method': 'blacklist', 'engine_name': 'SymantecMobileInsight', 'engine_version': '2.0', 'engine_update': '20250124', 'category': 'type-unsupported', 'result': None}, 'BitDefenderFalx': {'method': 'blacklist', 'engine_name': 'BitDefenderFalx', 'engine_version': '2.0.936', 'engine_update': '20250416', 'category': 'type-unsupported', 'result': None}, 'DeepInstinct': {'method': 'blacklist', 'engine_name': 'DeepInstinct', 'engine_version': '5.0.0.8', 'engine_update': '20251121', 'category': 'type-unsupported', 'result': None}, 'Acronis': {'method': 'blacklist', 'engine_name': 'Acronis', 'engine_version': '1.2.0.121', 'engine_update': '20240328', 'category': 'type-unsupported', 'result': None}, 'Trapmine': {'method': 'blacklist', 'engine_name': 'Trapmine', 'engine_version': '4.0.6.0', 'engine_update': '20251023', 'category': 'type-unsupported', 'result': None}, 'Alibaba': {'method': 'blacklist', 'engine_name': 'Alibaba', 'engine_version': '0.3.0.5', 'engine_update': '20190527', 'category': 'type-unsupported', 'result': None}, 'Webroot': {'method': 'blacklist', 'engine_name': 'Webroot', 'engine_version': '1.9.0.8', 'engine_update': '20250227', 'category': 'type-unsupported', 'result': None}, 'Cylance': {'method': 'blacklist', 'engine_name': 'Cylance', 'engine_version': '3.0.0.0', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}, 'SentinelOne': {'method': 'blacklist', 'engine_name': 'SentinelOne', 'engine_version': '7.4.1.3', 'engine_update': '20251013', 'category': 'type-unsupported', 'result': None}, 'tehtris': {'method': 'blacklist', 'engine_name': 'tehtris', 'engine_version': None, 'engine_update': '20251121', 'category': 'type-unsupported', 'result': None}, 'Elastic': {'method': 'blacklist', 'engine_name': 'Elastic', 'engine_version': '4.0.238', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}}", + "file": "sample.exe" + }, + "error": null + }, + { + "name": "GET_COMMENTS", + "method": "GET", + "endpoint": "/api/v3/domains/google.com/comments", + "status": "SUCCESS", + "response": { + "comments_count": 10, + "entity": "google.com", + "comments": [ + { + "text": "[`GOOGLEEDGEAi.ONMiCROSOFT.COM`](https://cloud.google.com/docs)", + "date": "2025-10-10 21:46:07+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "Not so sure about this one", + "date": "2025-09-23 15:48:32+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "Test comment", + "date": "2025-09-05 14:57:11+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "This site impersonates the official Bibit website, disseminating false customer service numbers to deceive customers. Fraudulent agents then solicit personal data, passwords, and OTPs, leading to account takeovers and financial losses. Authentic Bibit customer support is exclusively available at https://bibit.id/. We request the immediate takedown of this fraudulent site to prevent further harm.", + "date": "2025-09-02 04:01:21+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "Search engine.", + "date": "2025-08-25 22:19:40+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "🔍 Collection: LIVE VT Upload Demo | 📝 Description: 🎯 LIVE VT UPLOAD TEST via caz-vt-feeder - Domain Comment Added | 🏷️ Tags: live-upload-test, caz-vt-feeder | 📡 Source: caz-vt-feeder-system", + "date": "2025-07-06 23:59:44+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "🔍 Collection: Live Test Upload | 📝 Description: Test domain for VT comment upload | 🏷️ Tags: test, domain | 📡 Source: caz-vt-feeder-live-test", + "date": "2025-07-06 23:53:53+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "🔍 Collection: Test Official Upload | 📝 Description: Test domain upload | 🏷️ Tags: test, domain | 📡 Source: caz-vt-feeder-test", + "date": "2025-07-06 23:49:59+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "VIRUSSSSSSSVIRUSSSS GOOGLE BAD", + "date": "2025-06-12 23:53:06+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + }, + { + "text": "it's google!!!", + "date": "2025-06-10 20:11:20+00:00", + "votes": { + "positive": 0, + "negative": 0 + }, + "author": null + } + ] + }, + "error": null + } +] \ No newline at end of file diff --git a/GoogleThreatIntelligence/logo.svg b/GoogleThreatIntelligence/logo.svg new file mode 100644 index 000000000..8de59504e --- /dev/null +++ b/GoogleThreatIntelligence/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/GoogleThreatIntelligence/main.py b/GoogleThreatIntelligence/main.py new file mode 100644 index 000000000..410e5abec --- /dev/null +++ b/GoogleThreatIntelligence/main.py @@ -0,0 +1,24 @@ +from sekoia_automation.module import Module + +from googlethreatintelligence.get_ioc_report import GTIIoCReport +from googlethreatintelligence.scan_file import GTIScanFile +from googlethreatintelligence.get_comments import GTIGetComments +from googlethreatintelligence.get_file_behaviour import GTIGetFileBehaviour +from googlethreatintelligence.get_passive_dns import GTIGetPassiveDNS +from googlethreatintelligence.get_vulnerability_associations import GTIGetVulnerabilityAssociations +from googlethreatintelligence.get_vulnerability_report import GTIGetVulnerabilityReport +from googlethreatintelligence.scan_url import GTIScanURL + +if __name__ == "__main__": + module = Module() + + module.register(GTIIoCReport, "get_ioc_report") + module.register(GTIScanFile, "scan_file") + module.register(GTIGetComments, "get_comments") + module.register(GTIGetVulnerabilityAssociations, "get_vulnerability_assocations") + module.register(GTIGetFileBehaviour, "get_file_behaviour") + module.register(GTIScanURL, "scan_url") + module.register(GTIGetPassiveDNS, "get_passive_dns") + module.register(GTIGetVulnerabilityReport, "get_vulnerability_report") + + module.run() diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json new file mode 100644 index 000000000..4654f0c5c --- /dev/null +++ b/GoogleThreatIntelligence/manifest.json @@ -0,0 +1,29 @@ +{ + "configuration": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GoogleThreatIntelligence", + "type": "object", + "properties": { + "api_username": { + "description": "Client username", + "type": "string" + }, + "api_key": { + "description": "API key", + "type": "string" + } + }, + "required": [ + "api_key" + ], + "secrets": [ + "api_key" + ] + }, + "description": "Google Threat Intelligence provides unmatched visibility into threats enabling us to deliver detailed and timely threat intelligence to security teams", + "name": "GoogleThreatIntelligence", + "slug": "googlethreatintelligence", + "categories": [], + "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", + "version": "0.4.2" +} diff --git a/GoogleThreatIntelligence/poetry.lock b/GoogleThreatIntelligence/poetry.lock new file mode 100644 index 000000000..8dc8149be --- /dev/null +++ b/GoogleThreatIntelligence/poetry.lock @@ -0,0 +1,3876 @@ +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. + +[[package]] +name = "aiofiles" +version = "25.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695"}, + {file = "aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2"}, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, +] + +[[package]] +name = "aiohttp" +version = "3.13.2" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155"}, + {file = "aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c"}, + {file = "aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802"}, + {file = "aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f"}, + {file = "aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6"}, + {file = "aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251"}, + {file = "aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514"}, + {file = "aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0"}, + {file = "aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb"}, + {file = "aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592"}, + {file = "aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782"}, + {file = "aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8"}, + {file = "aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec"}, + {file = "aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c"}, + {file = "aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b"}, + {file = "aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc"}, + {file = "aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e"}, + {file = "aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169"}, + {file = "aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248"}, + {file = "aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e"}, + {file = "aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45"}, + {file = "aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be"}, + {file = "aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742"}, + {file = "aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e"}, + {file = "aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476"}, + {file = "aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23"}, + {file = "aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254"}, + {file = "aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a"}, + {file = "aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b"}, + {file = "aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61"}, + {file = "aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011"}, + {file = "aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4"}, + {file = "aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a"}, + {file = "aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940"}, + {file = "aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4"}, + {file = "aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673"}, + {file = "aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd"}, + {file = "aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e"}, + {file = "aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be"}, + {file = "aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c"}, + {file = "aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734"}, + {file = "aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f"}, + {file = "aiohttp-3.13.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7fbdf5ad6084f1940ce88933de34b62358d0f4a0b6ec097362dcd3e5a65a4989"}, + {file = "aiohttp-3.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c3a50345635a02db61792c85bb86daffac05330f6473d524f1a4e3ef9d0046d"}, + {file = "aiohttp-3.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e87dff73f46e969af38ab3f7cb75316a7c944e2e574ff7c933bc01b10def7f5"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2adebd4577724dcae085665f294cc57c8701ddd4d26140504db622b8d566d7aa"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e036a3a645fe92309ec34b918394bb377950cbb43039a97edae6c08db64b23e2"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:23ad365e30108c422d0b4428cf271156dd56790f6dd50d770b8e360e6c5ab2e6"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1f9b2c2d4b9d958b1f9ae0c984ec1dd6b6689e15c75045be8ccb4011426268ca"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a92cf4b9bea33e15ecbaa5c59921be0f23222608143d025c989924f7e3e0c07"}, + {file = "aiohttp-3.13.2-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:070599407f4954021509193404c4ac53153525a19531051661440644728ba9a7"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:29562998ec66f988d49fb83c9b01694fa927186b781463f376c5845c121e4e0b"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4dd3db9d0f4ebca1d887d76f7cdbcd1116ac0d05a9221b9dad82c64a62578c4d"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d7bc4b7f9c4921eba72677cd9fedd2308f4a4ca3e12fab58935295ad9ea98700"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:dacd50501cd017f8cccb328da0c90823511d70d24a323196826d923aad865901"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8b2f1414f6a1e0683f212ec80e813f4abef94c739fd090b66c9adf9d2a05feac"}, + {file = "aiohttp-3.13.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04c3971421576ed24c191f610052bcb2f059e395bc2489dd99e397f9bc466329"}, + {file = "aiohttp-3.13.2-cp39-cp39-win32.whl", hash = "sha256:9f377d0a924e5cc94dc620bc6366fc3e889586a7f18b748901cf016c916e2084"}, + {file = "aiohttp-3.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:9c705601e16c03466cb72011bd1af55d68fa65b045356d8f96c216e5f6db0fa5"}, + {file = "aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "brotlicffi ; platform_python_implementation != \"CPython\""] + +[[package]] +name = "aiosignal" +version = "1.4.0" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0)"] + +[[package]] +name = "arrow" +version = "1.4.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205"}, + {file = "arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +tzdata = {version = "*", markers = "python_version >= \"3.9\""} + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2025.2)", "simplejson (==3.*)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +description = "Backport of CPython tarfile module" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.11\"" +files = [ + {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, + {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] + +[[package]] +name = "binaryornot" +version = "0.4.4" +description = "Ultra-lightweight pure Python package to check if a file is binary or text." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, + {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, +] + +[package.dependencies] +chardet = ">=3.0.2" + +[[package]] +name = "black" +version = "25.9.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "black-25.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce41ed2614b706fd55fd0b4a6909d06b5bab344ffbfadc6ef34ae50adba3d4f7"}, + {file = "black-25.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ab0ce111ef026790e9b13bd216fa7bc48edd934ffc4cbf78808b235793cbc92"}, + {file = "black-25.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f96b6726d690c96c60ba682955199f8c39abc1ae0c3a494a9c62c0184049a713"}, + {file = "black-25.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d119957b37cc641596063cd7db2656c5be3752ac17877017b2ffcdb9dfc4d2b1"}, + {file = "black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa"}, + {file = "black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d"}, + {file = "black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608"}, + {file = "black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f"}, + {file = "black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0"}, + {file = "black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4"}, + {file = "black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e"}, + {file = "black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a"}, + {file = "black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175"}, + {file = "black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f"}, + {file = "black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831"}, + {file = "black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357"}, + {file = "black-25.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef69351df3c84485a8beb6f7b8f9721e2009e20ef80a8d619e2d1788b7816d47"}, + {file = "black-25.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3c1f4cd5e93842774d9ee4ef6cd8d17790e65f44f7cdbaab5f2cf8ccf22a823"}, + {file = "black-25.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:154b06d618233fe468236ba1f0e40823d4eb08b26f5e9261526fde34916b9140"}, + {file = "black-25.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e593466de7b998374ea2585a471ba90553283fb9beefcfa430d84a2651ed5933"}, + {file = "black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae"}, + {file = "black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +pytokens = ">=0.1.10" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + +[[package]] +name = "boto3" +version = "1.40.65" +description = "The AWS SDK for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "boto3-1.40.65-py3-none-any.whl", hash = "sha256:ab91d8d8ef0477997d35abebf67829e52e50bf807b02333affa384c70b33c86b"}, + {file = "boto3-1.40.65.tar.gz", hash = "sha256:52e2715838d65e6b000e0077a942ce2d3e1a38f9764414ad01a602912eccf924"}, +] + +[package.dependencies] +botocore = ">=1.40.65,<1.41.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.14.0,<0.15.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.40.65" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "botocore-1.40.65-py3-none-any.whl", hash = "sha256:152f595321f5a2b712601286650e912c2e5ca3b109892ab4c0175ac58d8de10d"}, + {file = "botocore-1.40.65.tar.gz", hash = "sha256:cdbbf9d90a9e9c4a6000055013d98b92efc4ceb1bce0d9bcd70e14461dc22ab3"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.27.6)"] + +[[package]] +name = "build" +version = "1.3.0" +description = "A simple, correct Python build frontend" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4"}, + {file = "build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +packaging = ">=19.1" +pyproject_hooks = "*" + +[package.extras] +uv = ["uv (>=0.1.18)"] +virtualenv = ["virtualenv (>=20.11) ; python_version < \"3.10\"", "virtualenv (>=20.17) ; python_version >= \"3.10\" and python_version < \"3.14\"", "virtualenv (>=20.31) ; python_version >= \"3.14\""] + +[[package]] +name = "cachecontrol" +version = "0.14.3" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "cachecontrol-0.14.3-py3-none-any.whl", hash = "sha256:b35e44a3113f17d2a31c1e6b27b9de6d4405f84ae51baa8c1d3cc5b633010cae"}, + {file = "cachecontrol-0.14.3.tar.gz", hash = "sha256:73e7efec4b06b20d9267b441c1f733664f989fb8688391b670ca812d70795d11"}, +] + +[package.dependencies] +filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["CacheControl[filecache,redis]", "build", "cherrypy", "codespell[tomli]", "furo", "mypy", "pytest", "pytest-cov", "ruff", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\" and sys_platform == \"linux\" or sys_platform == \"darwin\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, +] + +[[package]] +name = "cleo" +version = "2.1.0" +description = "Cleo allows you to create beautiful and testable command-line interfaces." +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"}, + {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"}, +] + +[package.dependencies] +crashtest = ">=0.4.1,<0.5.0" +rapidfuzz = ">=3.0.0,<4.0.0" + +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "platform_system == \"Windows\" or os_name == \"nt\"", dev = "sys_platform == \"win32\""} + +[[package]] +name = "cookiecutter" +version = "2.6.0" +description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "cookiecutter-2.6.0-py3-none-any.whl", hash = "sha256:a54a8e37995e4ed963b3e82831072d1ad4b005af736bb17b99c2cbd9d41b6e2d"}, + {file = "cookiecutter-2.6.0.tar.gz", hash = "sha256:db21f8169ea4f4fdc2408d48ca44859349de2647fbe494a9d6c3edfc0542c21c"}, +] + +[package.dependencies] +arrow = "*" +binaryornot = ">=0.4.4" +click = ">=7.0,<9.0.0" +Jinja2 = ">=2.7,<4.0.0" +python-slugify = ">=4.0.0" +pyyaml = ">=5.3.1" +requests = ">=2.23.0" +rich = "*" + +[[package]] +name = "coverage" +version = "7.11.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "coverage-7.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb53f1e8adeeb2e78962bade0c08bfdc461853c7969706ed901821e009b35e31"}, + {file = "coverage-7.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9a03ec6cb9f40a5c360f138b88266fd8f58408d71e89f536b4f91d85721d075"}, + {file = "coverage-7.11.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d7f0616c557cbc3d1c2090334eddcbb70e1ae3a40b07222d62b3aa47f608fab"}, + {file = "coverage-7.11.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e44a86a47bbdf83b0a3ea4d7df5410d6b1a0de984fbd805fa5101f3624b9abe0"}, + {file = "coverage-7.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:596763d2f9a0ee7eec6e643e29660def2eef297e1de0d334c78c08706f1cb785"}, + {file = "coverage-7.11.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef55537ff511b5e0a43edb4c50a7bf7ba1c3eea20b4f49b1490f1e8e0e42c591"}, + {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cbabd8f4d0d3dc571d77ae5bdbfa6afe5061e679a9d74b6797c48d143307088"}, + {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e24045453384e0ae2a587d562df2a04d852672eb63051d16096d3f08aa4c7c2f"}, + {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7161edd3426c8d19bdccde7d49e6f27f748f3c31cc350c5de7c633fea445d866"}, + {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d4ed4de17e692ba6415b0587bc7f12bc80915031fc9db46a23ce70fc88c9841"}, + {file = "coverage-7.11.0-cp310-cp310-win32.whl", hash = "sha256:765c0bc8fe46f48e341ef737c91c715bd2a53a12792592296a095f0c237e09cf"}, + {file = "coverage-7.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:24d6f3128f1b2d20d84b24f4074475457faedc3d4613a7e66b5e769939c7d969"}, + {file = "coverage-7.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d58ecaa865c5b9fa56e35efc51d1014d4c0d22838815b9fce57a27dd9576847"}, + {file = "coverage-7.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b679e171f1c104a5668550ada700e3c4937110dbdd153b7ef9055c4f1a1ee3cc"}, + {file = "coverage-7.11.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca61691ba8c5b6797deb221a0d09d7470364733ea9c69425a640f1f01b7c5bf0"}, + {file = "coverage-7.11.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aef1747ede4bd8ca9cfc04cc3011516500c6891f1b33a94add3253f6f876b7b7"}, + {file = "coverage-7.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1839d08406e4cba2953dcc0ffb312252f14d7c4c96919f70167611f4dee2623"}, + {file = "coverage-7.11.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0eb0a2dcc62478eb5b4cbb80b97bdee852d7e280b90e81f11b407d0b81c4287"}, + {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fbea96343b53f65d5351d8fd3b34fd415a2670d7c300b06d3e14a5af4f552"}, + {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:214b622259dd0cf435f10241f1333d32caa64dbc27f8790ab693428a141723de"}, + {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:258d9967520cca899695d4eb7ea38be03f06951d6ca2f21fb48b1235f791e601"}, + {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf9e6ff4ca908ca15c157c409d608da77a56a09877b97c889b98fb2c32b6465e"}, + {file = "coverage-7.11.0-cp311-cp311-win32.whl", hash = "sha256:fcc15fc462707b0680cff6242c48625da7f9a16a28a41bb8fd7a4280920e676c"}, + {file = "coverage-7.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:865965bf955d92790f1facd64fe7ff73551bd2c1e7e6b26443934e9701ba30b9"}, + {file = "coverage-7.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:5693e57a065760dcbeb292d60cc4d0231a6d4b6b6f6a3191561e1d5e8820b745"}, + {file = "coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1"}, + {file = "coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007"}, + {file = "coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46"}, + {file = "coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893"}, + {file = "coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115"}, + {file = "coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415"}, + {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186"}, + {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d"}, + {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d"}, + {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2"}, + {file = "coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5"}, + {file = "coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0"}, + {file = "coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad"}, + {file = "coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1"}, + {file = "coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be"}, + {file = "coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d"}, + {file = "coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82"}, + {file = "coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52"}, + {file = "coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b"}, + {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4"}, + {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd"}, + {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc"}, + {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48"}, + {file = "coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040"}, + {file = "coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05"}, + {file = "coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a"}, + {file = "coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b"}, + {file = "coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37"}, + {file = "coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de"}, + {file = "coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f"}, + {file = "coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c"}, + {file = "coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa"}, + {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740"}, + {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef"}, + {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0"}, + {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca"}, + {file = "coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2"}, + {file = "coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268"}, + {file = "coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836"}, + {file = "coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497"}, + {file = "coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e"}, + {file = "coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1"}, + {file = "coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca"}, + {file = "coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd"}, + {file = "coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43"}, + {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777"}, + {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2"}, + {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d"}, + {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4"}, + {file = "coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721"}, + {file = "coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad"}, + {file = "coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479"}, + {file = "coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f"}, + {file = "coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e"}, + {file = "coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44"}, + {file = "coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3"}, + {file = "coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b"}, + {file = "coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d"}, + {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2"}, + {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e"}, + {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996"}, + {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11"}, + {file = "coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73"}, + {file = "coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547"}, + {file = "coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3"}, + {file = "coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68"}, + {file = "coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050"}, +] + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "crashtest" +version = "0.4.1" +description = "Manage Python errors with ease" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"}, + {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"}, +] + +[[package]] +name = "cryptography" +version = "46.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["main"] +markers = "sys_platform == \"linux\"" +files = [ + {file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"}, + {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"}, + {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"}, + {file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"}, + {file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"}, + {file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"}, + {file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"}, + {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"}, + {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"}, + {file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"}, + {file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"}, + {file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"}, + {file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"}, + {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"}, + {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"}, + {file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"}, + {file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"}, + {file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"}, + {file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"}, + {file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"}, + {file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"}, +] + +[package.dependencies] +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "distlib" +version = "0.4.0" +description = "Distribution utilities" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, +] + +[[package]] +name = "dulwich" +version = "0.24.8" +description = "Python Git Library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dulwich-0.24.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:661af1fa3852d970fef68b0ab431f0bd488c3f94306e89244c173c4e6abb978e"}, + {file = "dulwich-0.24.8-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1bac020051cf228b33c787294e17ac80a284e028c3749437ee72577ee04e1cd9"}, + {file = "dulwich-0.24.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:97f7c64b02fbd366c36557aa2e8642fc686aaec7d6569bc1460d0b38a63169f9"}, + {file = "dulwich-0.24.8-cp310-cp310-win32.whl", hash = "sha256:f7519f3b8c66ba2e4ea66f47c2156a66cefedce2a121ac3227e030abe95698f3"}, + {file = "dulwich-0.24.8-cp310-cp310-win_amd64.whl", hash = "sha256:a60f8a5d718c7cc1f60bb931cc915311fd5198d85d68dde0ef3c569311c14a70"}, + {file = "dulwich-0.24.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07aa6e7d41358fcba2a8ac53731e1b8ab201cac7a192ec678ef0da34c7643cf1"}, + {file = "dulwich-0.24.8-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0e9aacbbb0b0cf4b3fecac2c29ddd4d4e36f03ced30851889c193986e8bb327e"}, + {file = "dulwich-0.24.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8eca5242f8aed324394c95ecd13a0a66d2a0c31c2556f0a52e7bb8dd67edef20"}, + {file = "dulwich-0.24.8-cp311-cp311-win32.whl", hash = "sha256:108c74b329599931bfe66c4a34fb9312cd8136053cbfc04e7007e7c34081c6b7"}, + {file = "dulwich-0.24.8-cp311-cp311-win_amd64.whl", hash = "sha256:efbf0f29d8d3d56a098e2b4a9260bdfa5f313142180a882c7b28e648d9b5ca9e"}, + {file = "dulwich-0.24.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ac85e3ea42878fa91b6a9282652327df54f5abea4aaf674036e1000608a15f0"}, + {file = "dulwich-0.24.8-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b31e08bcd0a4eb29915987fa5273a827cccca7ee83eb27ef17bd5899f5668055"}, + {file = "dulwich-0.24.8-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:267e79367cd6f438091248c1e826c6cf7abd84d5b641b9fe46fbc4d9119e11ed"}, + {file = "dulwich-0.24.8-cp312-cp312-win32.whl", hash = "sha256:6a51a41e858e0427b14bb19df7ac1207275dd6b5cc1976f54710bf15cb4c5614"}, + {file = "dulwich-0.24.8-cp312-cp312-win_amd64.whl", hash = "sha256:6016e3f7a0f1dd5e19df14b772cb8f42bfde0cd55c504642c05e1e8328de21e3"}, + {file = "dulwich-0.24.8-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:19be46710a9d810a66d4a925754abf3818ba22d61134e7d7e1d7b1585c9445b6"}, + {file = "dulwich-0.24.8-cp313-cp313-android_21_x86_64.whl", hash = "sha256:17d8223cc69cf79ddd7a2f0893e223708f7efc2ea372f919ddcc0d852b3f5d06"}, + {file = "dulwich-0.24.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a0a780b512d4144336eac2f1f6b982eb78c313442e88ba0db853a224e2b918ef"}, + {file = "dulwich-0.24.8-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e3a9a713fda94f3216da4743db3cc8d670330f44c4d98580ac4600242dba2c4"}, + {file = "dulwich-0.24.8-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b03474c16bcfa3524241b4ae89007931d79ac37c0b19e059133a6e63b7039845"}, + {file = "dulwich-0.24.8-cp313-cp313-win32.whl", hash = "sha256:ec0f62538b6fb26cdd1b2fb70788ccfdb17df26a4ba1ca70e623e196c4004f5c"}, + {file = "dulwich-0.24.8-cp313-cp313-win_amd64.whl", hash = "sha256:16e335bce0d5192d476db0ca81de1f90fb56863ad7d0b985b0333a8194c73c64"}, + {file = "dulwich-0.24.8-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:19855e8a0ce299cdcdafdc8bc4f6653bea9e02124a5022e13cda8103fb36912d"}, + {file = "dulwich-0.24.8-cp314-cp314-android_24_x86_64.whl", hash = "sha256:da03c7a6629b7ed37e7139739a175f2c9678080a45444418c54ab28d2ec6524b"}, + {file = "dulwich-0.24.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7a0d2cef91cf92a44071daa92639f648ab756d3db63a99e37d3a08ebacf69f3"}, + {file = "dulwich-0.24.8-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:5593a7216b27412333b99b2e1851bcc2485485d5c4694430aa86d34a36f08d63"}, + {file = "dulwich-0.24.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ea6c63d3e40fc321ec7c5673b92be036b57aba7802c7e94a18f61451af382de0"}, + {file = "dulwich-0.24.8-cp39-cp39-win32.whl", hash = "sha256:a6055b12cf2b90a0b4c21d38594f2681667d26bb0c135dfc36c2ea6de7458a37"}, + {file = "dulwich-0.24.8-cp39-cp39-win_amd64.whl", hash = "sha256:138fd2480c1db43f372d52e4e6ed050c15f92ffbeeab9a0877dceb9801c65d2a"}, + {file = "dulwich-0.24.8-py3-none-any.whl", hash = "sha256:6ffdd616135bcb31eb2edcccf82d4408720f1db69f596f687ffa2d26c2f5e6f4"}, + {file = "dulwich-0.24.8.tar.gz", hash = "sha256:c9f4748bbcca56fb57458c71c0d30e2351ac15e0583d428c739c09228be68f05"}, +] + +[package.dependencies] +typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.12\""} +urllib3 = ">=2.2.2" + +[package.extras] +colordiff = ["rich"] +dev = ["codespell (==2.4.1)", "dissolve (>=0.1.1)", "mypy (==1.18.2)", "ruff (==0.13.2)"] +fastimport = ["fastimport"] +fuzzing = ["atheris"] +https = ["urllib3 (>=2.2.2)"] +merge = ["merge3"] +paramiko = ["paramiko"] +patiencediff = ["patiencediff"] +pgp = ["gpg"] + +[[package]] +name = "fastjsonschema" +version = "2.21.2" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463"}, + {file = "fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.20.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2"}, + {file = "filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4"}, +] + +[[package]] +name = "findpython" +version = "0.7.0" +description = "A utility to find python versions on your system" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "findpython-0.7.0-py3-none-any.whl", hash = "sha256:f53cfcc29536f5b83c962cf922bba8ff6d6a3c2a05fda6a45aa58a47d005d8fc"}, + {file = "findpython-0.7.0.tar.gz", hash = "sha256:8b31647c76352779a3c1a0806699b68e6a7bdc0b5c2ddd9af2a07a0d40c673dc"}, +] + +[package.dependencies] +packaging = ">=20" +platformdirs = ">=4.3.6" + +[[package]] +name = "flask" +version = "3.1.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"}, + {file = "flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87"}, +] + +[package.dependencies] +blinker = ">=1.9.0" +click = ">=8.1.3" +itsdangerous = ">=2.2.0" +jinja2 = ">=3.1.2" +markupsafe = ">=2.1.1" +werkzeug = ">=3.1.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "frozenlist" +version = "1.8.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, +] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version == \"3.11\"" +files = [ + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "installer" +version = "0.7.0" +description = "A library for installing Python wheels." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"}, + {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, + {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jaraco-context" +version = "6.0.1" +description = "Useful decorators and context managers" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, + {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, +] + +[package.dependencies] +"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] + +[[package]] +name = "jaraco-functools" +version = "4.3.0" +description = "Functools like those found in stdlib" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8"}, + {file = "jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294"}, +] + +[package.dependencies] +more_itertools = "*" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] +type = ["pytest-mypy"] + +[[package]] +name = "jeepney" +version = "0.9.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "sys_platform == \"linux\"" +files = [ + {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, + {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, +] + +[package.extras] +test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["trio"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "jsonschema" +version = "4.25.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, + {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, + {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "keyring" +version = "25.6.0" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, + {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, +] + +[package.dependencies] +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +"jaraco.context" = "*" +"jaraco.functools" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +completion = ["shtab (>=1.1.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] +type = ["pygobject-stubs", "pytest-mypy", "shtab", "types-pywin32"] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"}, + {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"}, +] + +[[package]] +name = "msgpack" +version = "1.1.2" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2"}, + {file = "msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87"}, + {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251"}, + {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a"}, + {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f"}, + {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f"}, + {file = "msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9"}, + {file = "msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa"}, + {file = "msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c"}, + {file = "msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0"}, + {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296"}, + {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef"}, + {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c"}, + {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e"}, + {file = "msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e"}, + {file = "msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68"}, + {file = "msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406"}, + {file = "msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa"}, + {file = "msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb"}, + {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f"}, + {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42"}, + {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9"}, + {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620"}, + {file = "msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029"}, + {file = "msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b"}, + {file = "msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69"}, + {file = "msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf"}, + {file = "msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7"}, + {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999"}, + {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e"}, + {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162"}, + {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794"}, + {file = "msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c"}, + {file = "msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9"}, + {file = "msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84"}, + {file = "msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00"}, + {file = "msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939"}, + {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e"}, + {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931"}, + {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014"}, + {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2"}, + {file = "msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717"}, + {file = "msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b"}, + {file = "msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af"}, + {file = "msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a"}, + {file = "msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b"}, + {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245"}, + {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90"}, + {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20"}, + {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27"}, + {file = "msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b"}, + {file = "msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff"}, + {file = "msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46"}, + {file = "msgpack-1.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ea5405c46e690122a76531ab97a079e184c0daf491e588592d6a23d3e32af99e"}, + {file = "msgpack-1.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fba231af7a933400238cb357ecccf8ab5d51535ea95d94fc35b7806218ff844"}, + {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a8f6e7d30253714751aa0b0c84ae28948e852ee7fb0524082e6716769124bc23"}, + {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94fd7dc7d8cb0a54432f296f2246bc39474e017204ca6f4ff345941d4ed285a7"}, + {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:350ad5353a467d9e3b126d8d1b90fe05ad081e2e1cef5753f8c345217c37e7b8"}, + {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6bde749afe671dc44893f8d08e83bf475a1a14570d67c4bb5cec5573463c8833"}, + {file = "msgpack-1.1.2-cp39-cp39-win32.whl", hash = "sha256:ad09b984828d6b7bb52d1d1d0c9be68ad781fa004ca39216c8a1e63c0f34ba3c"}, + {file = "msgpack-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:67016ae8c8965124fdede9d3769528ad8284f14d635337ffa6a713a580f6c030"}, + {file = "msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e"}, +] + +[[package]] +name = "multidict" +version = "6.7.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "orjson" +version = "3.11.4" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba"}, + {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff"}, + {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac"}, + {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79"}, + {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827"}, + {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b"}, + {file = "orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3"}, + {file = "orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc"}, + {file = "orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39"}, + {file = "orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a"}, + {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be"}, + {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7"}, + {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549"}, + {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905"}, + {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907"}, + {file = "orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c"}, + {file = "orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a"}, + {file = "orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045"}, + {file = "orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50"}, + {file = "orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708"}, + {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210"}, + {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241"}, + {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b"}, + {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c"}, + {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9"}, + {file = "orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa"}, + {file = "orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140"}, + {file = "orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e"}, + {file = "orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534"}, + {file = "orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9"}, + {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73"}, + {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0"}, + {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196"}, + {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a"}, + {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6"}, + {file = "orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839"}, + {file = "orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a"}, + {file = "orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de"}, + {file = "orjson-3.11.4-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:42d43a1f552be1a112af0b21c10a5f553983c2a0938d2bbb8ecd8bc9fb572803"}, + {file = "orjson-3.11.4-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:26a20f3fbc6c7ff2cb8e89c4c5897762c9d88cf37330c6a117312365d6781d54"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e3f20be9048941c7ffa8fc523ccbd17f82e24df1549d1d1fe9317712d19938e"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aac364c758dc87a52e68e349924d7e4ded348dedff553889e4d9f22f74785316"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c54a6d76e3d741dcc3f2707f8eeb9ba2a791d3adbf18f900219b62942803b1"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28485bdca8617b79d44627f5fb04336897041dfd9fa66d383a49d09d86798bc"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2a484cad3585e4ba61985a6062a4c2ed5c7925db6d39f1fa267c9d166487f"}, + {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34dbd508cb91c54f9c9788923daca129fe5b55c5b4eebe713bf5ed3791280cf"}, + {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b13c478fa413d4b4ee606ec8e11c3b2e52683a640b006bb586b3041c2ca5f606"}, + {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:724ca721ecc8a831b319dcd72cfa370cc380db0bf94537f08f7edd0a7d4e1780"}, + {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:977c393f2e44845ce1b540e19a786e9643221b3323dae190668a98672d43fb23"}, + {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e539e382cf46edec157ad66b0b0872a90d829a6b71f17cb633d6c160a223155"}, + {file = "orjson-3.11.4-cp314-cp314-win32.whl", hash = "sha256:d63076d625babab9db5e7836118bdfa086e60f37d8a174194ae720161eb12394"}, + {file = "orjson-3.11.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a54d6635fa3aaa438ae32e8570b9f0de36f3f6562c308d2a2a452e8b0592db1"}, + {file = "orjson-3.11.4-cp314-cp314-win_arm64.whl", hash = "sha256:78b999999039db3cf58f6d230f524f04f75f129ba3d1ca2ed121f8657e575d3d"}, + {file = "orjson-3.11.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:405261b0a8c62bcbd8e2931c26fdc08714faf7025f45531541e2b29e544b545b"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af02ff34059ee9199a3546f123a6ab4c86caf1708c79042caf0820dc290a6d4f"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b2eba969ea4203c177c7b38b36c69519e6067ee68c34dc37081fac74c796e10"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0baa0ea43cfa5b008a28d3c07705cf3ada40e5d347f0f44994a64b1b7b4b5350"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80fd082f5dcc0e94657c144f1b2a3a6479c44ad50be216cf0c244e567f5eae19"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3704d35e47d5bee811fb1cbd8599f0b4009b14d451c4c57be5a7e25eb89a13"}, + {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa447f2b5356779d914658519c874cf3b7629e99e63391ed519c28c8aea4919"}, + {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bba5118143373a86f91dadb8df41d9457498226698ebdf8e11cbb54d5b0e802d"}, + {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:622463ab81d19ef3e06868b576551587de8e4d518892d1afab71e0fbc1f9cffc"}, + {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3e0a700c4b82144b72946b6629968df9762552ee1344bfdb767fecdd634fbd5a"}, + {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e18a5c15e764e5f3fc569b47872450b4bcea24f2a6354c0a0e95ad21045d5a9"}, + {file = "orjson-3.11.4-cp39-cp39-win32.whl", hash = "sha256:fb1c37c71cad991ef4d89c7a634b5ffb4447dbd7ae3ae13e8f5ee7f1775e7ab1"}, + {file = "orjson-3.11.4-cp39-cp39-win_amd64.whl", hash = "sha256:e2985ce8b8c42d00492d0ed79f2bd2b6460d00f2fa671dfde4bf2e02f49bf5c6"}, + {file = "orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pbs-installer" +version = "2025.10.31" +description = "Installer for Python Build Standalone" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pbs_installer-2025.10.31-py3-none-any.whl", hash = "sha256:f24e8f01f13fee9a203feb62e90c8d204d64d71ad0a3f3abfd69673b02bbc68b"}, + {file = "pbs_installer-2025.10.31.tar.gz", hash = "sha256:8529dbac1054408ccce5fb218a85a2d4a02f3657ddbde56eff79464105bba659"}, +] + +[package.dependencies] +httpx = {version = ">=0.27.0,<1", optional = true, markers = "extra == \"download\""} +zstandard = {version = ">=0.21.0", optional = true, markers = "extra == \"install\""} + +[package.extras] +all = ["pbs-installer[download,install]"] +download = ["httpx (>=0.27.0,<1)"] +install = ["zstandard (>=0.21.0)"] + +[[package]] +name = "pkginfo" +version = "1.12.1.2" +description = "Query metadata from sdists / bdists / installed packages." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pkginfo-1.12.1.2-py3-none-any.whl", hash = "sha256:c783ac885519cab2c34927ccfa6bf64b5a704d7c69afaea583dd9b7afe969343"}, + {file = "pkginfo-1.12.1.2.tar.gz", hash = "sha256:5cd957824ac36f140260964eba3c6be6442a8359b8c48f4adf90210f33a04b7b"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov", "wheel"] + +[[package]] +name = "platformdirs" +version = "4.5.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"}, + {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"}, +] + +[package.extras] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "poetry" +version = "2.2.1" +description = "Python dependency management and packaging made easy." +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "poetry-2.2.1-py3-none-any.whl", hash = "sha256:f5958b908b96c5824e2acbb8b19cdef8a3351c62142d7ecff2d705396c8ca34c"}, + {file = "poetry-2.2.1.tar.gz", hash = "sha256:bef9aa4bb00ce4c10b28b25e7bac724094802d6958190762c45df6c12749b37c"}, +] + +[package.dependencies] +build = ">=1.2.1,<2.0.0" +cachecontrol = {version = ">=0.14.0,<0.15.0", extras = ["filecache"]} +cleo = ">=2.1.0,<3.0.0" +dulwich = ">=0.24.0,<0.25.0" +fastjsonschema = ">=2.18.0,<3.0.0" +findpython = ">=0.6.2,<0.8.0" +installer = ">=0.7.0,<0.8.0" +keyring = ">=25.1.0,<26.0.0" +packaging = ">=24.2" +pbs-installer = {version = ">=2025.1.6,<2026.0.0", extras = ["download", "install"]} +pkginfo = ">=1.12,<2.0" +platformdirs = ">=3.0.0,<5" +poetry-core = "2.2.1" +pyproject-hooks = ">=1.0.0,<2.0.0" +requests = ">=2.26,<3.0" +requests-toolbelt = ">=1.0.0,<2.0.0" +shellingham = ">=1.5,<2.0" +tomlkit = ">=0.11.4,<1.0.0" +trove-classifiers = ">=2022.5.19" +virtualenv = ">=20.26.6" +xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""} + +[[package]] +name = "poetry-core" +version = "2.2.1" +description = "Poetry PEP 517 Build Backend" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "poetry_core-2.2.1-py3-none-any.whl", hash = "sha256:bdfce710edc10bfcf9ab35041605c480829be4ab23f5bc01202cfe5db8f125ab"}, + {file = "poetry_core-2.2.1.tar.gz", hash = "sha256:97e50d8593c8729d3f49364b428583e044087ee3def1e010c6496db76bd65ac5"}, +] + +[[package]] +name = "prometheus-client" +version = "0.21.1" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, +] + +[package.extras] +twisted = ["twisted"] + +[[package]] +name = "propcache" +version = "0.4.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, +] + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "(platform_python_implementation != \"PyPy\" and sys_platform == \"linux\" or sys_platform == \"darwin\") and implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, +] + +[[package]] +name = "pydantic" +version = "2.12.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf"}, + {file = "pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.4" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.41.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"}, + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"}, + {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, + {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, +] + +[[package]] +name = "pyrate-limiter" +version = "2.10.0" +description = "Python Rate-Limiter using Leaky-Bucket Algorithm" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "pyrate_limiter-2.10.0-py3-none-any.whl", hash = "sha256:a99e52159f5ed5eb58118bed8c645e30818e7c0e0d127a0585c8277c776b0f7f"}, + {file = "pyrate_limiter-2.10.0.tar.gz", hash = "sha256:98cc52cdbe058458e945ae87d4fd5a73186497ffa545ee6e98372f8599a5bd34"}, +] + +[package.extras] +all = ["filelock (>=3.0)", "redis (>=3.3,<4.0)", "redis-py-cluster (>=2.1.3,<3.0.0)"] +docs = ["furo (>=2022.3.4,<2023.0.0)", "myst-parser (>=0.17)", "sphinx (>=4.3.0,<5.0.0)", "sphinx-autodoc-typehints (>=1.17,<2.0)", "sphinx-copybutton (>=0.5)", "sphinxcontrib-apidoc (>=0.3,<0.4)"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, + {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, +] + +[package.dependencies] +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-slugify" +version = "5.0.2" +description = "A Python Slugify application that handles Unicode" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"}, + {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + +[[package]] +name = "pytokens" +version = "0.2.0" +description = "A Fast, spec compliant Python 3.13+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8"}, + {file = "pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "rapidfuzz" +version = "3.14.3" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea2d113e260a5da0c4003e0a5e9fdf24a9dc2bb9eaa43abd030a1e46ce7837d"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6c31a4aa68cfa75d7eede8b0ed24b9e458447db604c2db53f358be9843d81d3"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02821366d928e68ddcb567fed8723dad7ea3a979fada6283e6914d5858674850"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe8df315ab4e6db4e1be72c5170f8e66021acde22cd2f9d04d2058a9fd8162e"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:769f31c60cd79420188fcdb3c823227fc4a6deb35cafec9d14045c7f6743acae"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54fa03062124e73086dae66a3451c553c1e20a39c077fd704dc7154092c34c63"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:834d1e818005ed0d4ae38f6b87b86fad9b0a74085467ece0727d20e15077c094"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:948b00e8476a91f510dd1ec07272efc7d78c275d83b630455559671d4e33b678"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win32.whl", hash = "sha256:43d0305c36f504232f18ea04e55f2059bb89f169d3119c4ea96a0e15b59e2a91"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:ef6bf930b947bd0735c550683939a032090f1d688dfd8861d6b45307b96fd5c5"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:f3eb0ff3b75d6fdccd40b55e7414bb859a1cda77c52762c9c82b85569f5088e7"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:685c93ea961d135893b5984a5a9851637d23767feabe414ec974f43babbd8226"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa7c8f26f009f8c673fbfb443792f0cf8cf50c4e18121ff1e285b5e08a94fbdb"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57f878330c8d361b2ce76cebb8e3e1dc827293b6abf404e67d53260d27b5d941"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c5f545f454871e6af05753a0172849c82feaf0f521c5ca62ba09e1b382d6382"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:07aa0b5d8863e3151e05026a28e0d924accf0a7a3b605da978f0359bb804df43"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73b07566bc7e010e7b5bd490fb04bb312e820970180df6b5655e9e6224c137db"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6de00eb84c71476af7d3110cf25d8fe7c792d7f5fa86764ef0b4ca97e78ca3ed"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7843a1abf0091773a530636fdd2a49a41bcae22f9910b86b4f903e76ddc82dc"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win32.whl", hash = "sha256:dea97ac3ca18cd3ba8f3d04b5c1fe4aa60e58e8d9b7793d3bd595fdb04128d7a"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:b5100fd6bcee4d27f28f4e0a1c6b5127bc8ba7c2a9959cad9eab0bf4a7ab3329"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:4e49c9e992bc5fc873bd0fff7ef16a4405130ec42f2ce3d2b735ba5d3d4eb70f"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbcb726064b12f356bf10fffdb6db4b6dce5390b23627c08652b3f6e49aa56ae"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1704fc70d214294e554a2421b473779bcdeef715881c5e927dc0f11e1692a0ff"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc65e72790ddfd310c2c8912b45106e3800fefe160b0c2ef4d6b6fec4e826457"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e38c1305cffae8472572a0584d4ffc2f130865586a81038ca3965301f7c97c"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:e195a77d06c03c98b3fc06b8a28576ba824392ce40de8c708f96ce04849a052e"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b7ef2f4b8583a744338a18f12c69693c194fb6777c0e9ada98cd4d9e8f09d10"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a2135b138bcdcb4c3742d417f215ac2d8c2b87bde15b0feede231ae95f09ec41"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33a325ed0e8e1aa20c3e75f8ab057a7b248fdea7843c2a19ade0008906c14af0"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win32.whl", hash = "sha256:8383b6d0d92f6cd008f3c9216535be215a064b2cc890398a678b56e6d280cb63"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:e6b5e3036976f0fde888687d91be86d81f9ac5f7b02e218913c38285b756be6c"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:7ba009977601d8b0828bfac9a110b195b3e4e79b350dcfa48c11269a9f1918a0"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0a28add871425c2fe94358c6300bbeb0bc2ed828ca003420ac6825408f5a424"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010e12e2411a4854b0434f920e72b717c43f8ec48d57e7affe5c42ecfa05dd0e"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfc3d57abd83c734d1714ec39c88a34dd69c85474918ebc21296f1e61eb5ca8"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89acb8cbb52904f763e5ac238083b9fc193bed8d1f03c80568b20e4cef43a519"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:7d9af908c2f371bfb9c985bd134e295038e3031e666e4b2ade1e7cb7f5af2f1a"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1f1925619627f8798f8c3a391d81071336942e5fe8467bc3c567f982e7ce2897"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:152555187360978119e98ce3e8263d70dd0c40c7541193fc302e9b7125cf8f58"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52619d25a09546b8db078981ca88939d72caa6b8701edd8b22e16482a38e799f"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win32.whl", hash = "sha256:489ce98a895c98cad284f0a47960c3e264c724cb4cfd47a1430fa091c0c25204"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win_amd64.whl", hash = "sha256:656e52b054d5b5c2524169240e50cfa080b04b1c613c5f90a2465e84888d6f15"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c7e40c0a0af02ad6e57e89f62bef8604f55a04ecae90b0ceeda591bbf5923317"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:442125473b247227d3f2de807a11da6c08ccf536572d1be943f8e262bae7e4ea"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ec0c8c0c3d4f97ced46b2e191e883f8c82dbbf6d5ebc1842366d7eff13cd5a6"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dc37bc20272f388b8c3a4eba4febc6e77e50a8f450c472def4751e7678f55e4"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee362e7e79bae940a5e2b3f6d09c6554db6a4e301cc68343886c08be99844f1"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:4b39921df948388a863f0e267edf2c36302983459b021ab928d4b801cbe6a421"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:beda6aa9bc44d1d81242e7b291b446be352d3451f8217fcb068fc2933927d53b"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6a014ba09657abfcfeed64b7d09407acb29af436d7fc075b23a298a7e4a6b41c"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32eeafa3abce138bb725550c0e228fc7eaeec7059aa8093d9cbbec2b58c2371a"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win32.whl", hash = "sha256:adb44d996fc610c7da8c5048775b21db60dd63b1548f078e95858c05c86876a3"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:f3d15d8527e2b293e38ce6e437631af0708df29eafd7c9fc48210854c94472f9"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:576e4b9012a67e0bf54fccb69a7b6c94d4e86a9540a62f1a5144977359133583"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cec3c0da88562727dd5a5a364bd9efeb535400ff0bfb1443156dd139a1dd7b50"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d1fa009f8b1100e4880868137e7bf0501422898f7674f2adcd85d5a67f041296"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b86daa7419b5e8b180690efd1fdbac43ff19230803282521c5b5a9c83977655"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7bd1816db05d6c5ffb3a4df0a2b7b56fb8c81ef584d08e37058afa217da91b1"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:33da4bbaf44e9755b0ce192597f3bde7372fe2e381ab305f41b707a95ac57aa7"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3fecce764cf5a991ee2195a844196da840aba72029b2612f95ac68a8b74946bf"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ecd7453e02cf072258c3a6b8e930230d789d5d46cc849503729f9ce475d0e785"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea188aa00e9bcae8c8411f006a5f2f06c4607a02f24eab0d8dc58566aa911f35"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:7ccbf68100c170e9a0581accbe9291850936711548c6688ce3bfb897b8c589ad"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9ec02e62ae765a318d6de38df609c57fc6dacc65c0ed1fd489036834fd8a620c"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e805e52322ae29aa945baf7168b6c898120fbc16d2b8f940b658a5e9e3999253"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7cf174b52cb3ef5d49e45d0a1133b7e7d0ecf770ed01f97ae9962c5c91d97d23"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:442cba39957a008dfc5bdef21a9c3f4379e30ffb4e41b8555dbaf4887eca9300"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1faa0f8f76ba75fd7b142c984947c280ef6558b5067af2ae9b8729b0a0f99ede"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e6eefec45625c634926a9fd46c9e4f31118ac8f3156fff9494422cee45207e6"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56fefb4382bb12250f164250240b9dd7772e41c5c8ae976fd598a32292449cc5"}, + {file = "rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f"}, +] + +[package.extras] +all = ["numpy"] + +[[package]] +name = "referencing" +version = "0.37.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, + {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} + +[[package]] +name = "requests" +version = "2.32.5" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-mock" +version = "1.12.1" +description = "Mock out responses from the requests package" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"}, + {file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"}, +] + +[package.dependencies] +requests = ">=2.22,<3" + +[package.extras] +fixture = ["fixtures"] + +[[package]] +name = "requests-ratelimiter" +version = "0.7.0" +description = "Rate-limiting for the requests library" +optional = false +python-versions = "<4.0,>=3.7" +groups = ["main"] +files = [ + {file = "requests_ratelimiter-0.7.0-py3-none-any.whl", hash = "sha256:1a7ef2faaa790272722db8539728690046237766fcc479f85b9591e5356a8185"}, + {file = "requests_ratelimiter-0.7.0.tar.gz", hash = "sha256:a070c8a359a6f3a001b0ccb08f17228b7ae0a6e21d8df5b6f6bd58389cddde45"}, +] + +[package.dependencies] +pyrate-limiter = "<3.0" +requests = ">=2.20" + +[package.extras] +docs = ["furo (>=2023.3,<2024.0)", "myst-parser (>=1.0)", "sphinx (>=5.2,<6.0)", "sphinx-autodoc-typehints (>=1.22,<2.0)", "sphinx-copybutton (>=0.5)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rich" +version = "14.2.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rpds-py" +version = "0.28.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "rpds_py-0.28.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7b6013db815417eeb56b2d9d7324e64fcd4fa289caeee6e7a78b2e11fc9b438a"}, + {file = "rpds_py-0.28.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a4c6b05c685c0c03f80dabaeb73e74218c49deea965ca63f76a752807397207"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4794c6c3fbe8f9ac87699b131a1f26e7b4abcf6d828da46a3a52648c7930eba"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e8456b6ee5527112ff2354dd9087b030e3429e43a74f480d4a5ca79d269fd85"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:beb880a9ca0a117415f241f66d56025c02037f7c4efc6fe59b5b8454f1eaa50d"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6897bebb118c44b38c9cb62a178e09f1593c949391b9a1a6fe777ccab5934ee7"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b553dd06e875249fd43efd727785efb57a53180e0fde321468222eabbeaafa"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:f0b2044fdddeea5b05df832e50d2a06fe61023acb44d76978e1b060206a8a476"}, + {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05cf1e74900e8da73fa08cc76c74a03345e5a3e37691d07cfe2092d7d8e27b04"}, + {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:efd489fec7c311dae25e94fe7eeda4b3d06be71c68f2cf2e8ef990ffcd2cd7e8"}, + {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada7754a10faacd4f26067e62de52d6af93b6d9542f0df73c57b9771eb3ba9c4"}, + {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c2a34fd26588949e1e7977cfcbb17a9a42c948c100cab890c6d8d823f0586457"}, + {file = "rpds_py-0.28.0-cp310-cp310-win32.whl", hash = "sha256:f9174471d6920cbc5e82a7822de8dfd4dcea86eb828b04fc8c6519a77b0ee51e"}, + {file = "rpds_py-0.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:6e32dd207e2c4f8475257a3540ab8a93eff997abfa0a3fdb287cae0d6cd874b8"}, + {file = "rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296"}, + {file = "rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c03002f54cc855860bfdc3442928ffdca9081e73b5b382ed0b9e8efe6e5e205"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9699fa7990368b22032baf2b2dce1f634388e4ffc03dfefaaac79f4695edc95"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9b06fe1a75e05e0713f06ea0c89ecb6452210fd60e2f1b6ddc1067b990e08d9"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:0d3259ea9ad8743a75a43eb7819324cdab393263c91be86e2d1901ee65c314e0"}, + {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a7548b345f66f6695943b4ef6afe33ccd3f1b638bd9afd0f730dd255c249c9e"}, + {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67"}, + {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f60c7ea34e78c199acd0d3cda37a99be2c861dd2b8cf67399784f70c9f8e57d"}, + {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6"}, + {file = "rpds_py-0.28.0-cp311-cp311-win32.whl", hash = "sha256:5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c"}, + {file = "rpds_py-0.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa"}, + {file = "rpds_py-0.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120"}, + {file = "rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f"}, + {file = "rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ee514e0f0523db5d3fb171f397c54875dbbd69760a414dccf9d4d7ad628b5bd"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3fa06d27fdcee47f07a39e02862da0100cb4982508f5ead53ec533cd5fe55e"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46959ef2e64f9e4a41fc89aa20dbca2b85531f9a72c21099a3360f35d10b0d5a"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ad50614a02c8c2962feebe6012b52f9802deec4263946cddea37aaf28dd25a66"}, + {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5deca01b271492553fdb6c7fd974659dce736a15bae5dad7ab8b93555bceb28"}, + {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a"}, + {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:961ca621ff10d198bbe6ba4957decca61aa2a0c56695384c1d6b79bf61436df5"}, + {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c"}, + {file = "rpds_py-0.28.0-cp312-cp312-win32.whl", hash = "sha256:d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08"}, + {file = "rpds_py-0.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c"}, + {file = "rpds_py-0.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd"}, + {file = "rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e9e184408a0297086f880556b6168fa927d677716f83d3472ea333b42171ee3b"}, + {file = "rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:edd267266a9b0448f33dc465a97cfc5d467594b600fe28e7fa2f36450e03053a"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85beb8b3f45e4e32f6802fb6cd6b17f615ef6c6a52f265371fb916fae02814aa"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d2412be8d00a1b895f8ad827cc2116455196e20ed994bb704bf138fe91a42724"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf128350d384b777da0e68796afdcebc2e9f63f0e9f242217754e647f6d32491"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2036d09b363aa36695d1cc1a97b36865597f4478470b0697b5ee9403f4fe399"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8e1e9be4fa6305a16be628959188e4fd5cd6f1b0e724d63c6d8b2a8adf74ea6"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0a403460c9dd91a7f23fc3188de6d8977f1d9603a351d5db6cf20aaea95b538d"}, + {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7366b6553cdc805abcc512b849a519167db8f5e5c3472010cd1228b224265cb"}, + {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b43c6a3726efd50f18d8120ec0551241c38785b68952d240c45ea553912ac41"}, + {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0cb7203c7bc69d7c1585ebb33a2e6074492d2fc21ad28a7b9d40457ac2a51ab7"}, + {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a52a5169c664dfb495882adc75c304ae1d50df552fbd68e100fdc719dee4ff9"}, + {file = "rpds_py-0.28.0-cp313-cp313-win32.whl", hash = "sha256:2e42456917b6687215b3e606ab46aa6bca040c77af7df9a08a6dcfe8a4d10ca5"}, + {file = "rpds_py-0.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:e0a0311caedc8069d68fc2bf4c9019b58a2d5ce3cd7cb656c845f1615b577e1e"}, + {file = "rpds_py-0.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:04c1b207ab8b581108801528d59ad80aa83bb170b35b0ddffb29c20e411acdc1"}, + {file = "rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f296ea3054e11fc58ad42e850e8b75c62d9a93a9f981ad04b2e5ae7d2186ff9c"}, + {file = "rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5a7306c19b19005ad98468fcefeb7100b19c79fc23a5f24a12e06d91181193fa"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5d9b86aa501fed9862a443c5c3116f6ead8bc9296185f369277c42542bd646b"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e5bbc701eff140ba0e872691d573b3d5d30059ea26e5785acba9132d10c8c31d"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5690671cd672a45aa8616d7374fdf334a1b9c04a0cac3c854b1136e92374fe"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f1d92ecea4fa12f978a367c32a5375a1982834649cdb96539dcdc12e609ab1a"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d252db6b1a78d0a3928b6190156042d54c93660ce4d98290d7b16b5296fb7cc"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d61b355c3275acb825f8777d6c4505f42b5007e357af500939d4a35b19177259"}, + {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:acbe5e8b1026c0c580d0321c8aae4b0a1e1676861d48d6e8c6586625055b606a"}, + {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8aa23b6f0fc59b85b4c7d89ba2965af274346f738e8d9fc2455763602e62fd5f"}, + {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7b14b0c680286958817c22d76fcbca4800ddacef6f678f3a7c79a1fe7067fe37"}, + {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bcf1d210dfee61a6c86551d67ee1031899c0fdbae88b2d44a569995d43797712"}, + {file = "rpds_py-0.28.0-cp313-cp313t-win32.whl", hash = "sha256:3aa4dc0fdab4a7029ac63959a3ccf4ed605fee048ba67ce89ca3168da34a1342"}, + {file = "rpds_py-0.28.0-cp313-cp313t-win_amd64.whl", hash = "sha256:7b7d9d83c942855e4fdcfa75d4f96f6b9e272d42fffcb72cd4bb2577db2e2907"}, + {file = "rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:dcdcb890b3ada98a03f9f2bb108489cdc7580176cb73b4f2d789e9a1dac1d472"}, + {file = "rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f274f56a926ba2dc02976ca5b11c32855cbd5925534e57cfe1fda64e04d1add2"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fe0438ac4a29a520ea94c8c7f1754cdd8feb1bc490dfda1bfd990072363d527"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a358a32dd3ae50e933347889b6af9a1bdf207ba5d1a3f34e1a38cd3540e6733"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e80848a71c78aa328fefaba9c244d588a342c8e03bda518447b624ea64d1ff56"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f586db2e209d54fe177e58e0bc4946bea5fb0102f150b1b2f13de03e1f0976f8"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ae8ee156d6b586e4292491e885d41483136ab994e719a13458055bec14cf370"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a805e9b3973f7e27f7cab63a6b4f61d90f2e5557cff73b6e97cd5b8540276d3d"}, + {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5d3fd16b6dc89c73a4da0b4ac8b12a7ecc75b2864b95c9e5afed8003cb50a728"}, + {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6796079e5d24fdaba6d49bda28e2c47347e89834678f2bc2c1b4fc1489c0fb01"}, + {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:76500820c2af232435cbe215e3324c75b950a027134e044423f59f5b9a1ba515"}, + {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bbdc5640900a7dbf9dd707fe6388972f5bbd883633eb68b76591044cfe346f7e"}, + {file = "rpds_py-0.28.0-cp314-cp314-win32.whl", hash = "sha256:adc8aa88486857d2b35d75f0640b949759f79dc105f50aa2c27816b2e0dd749f"}, + {file = "rpds_py-0.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:66e6fa8e075b58946e76a78e69e1a124a21d9a48a5b4766d15ba5b06869d1fa1"}, + {file = "rpds_py-0.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6fe887c2c5c59413353b7c0caff25d0e566623501ccfff88957fa438a69377d"}, + {file = "rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7a69df082db13c7070f7b8b1f155fa9e687f1d6aefb7b0e3f7231653b79a067b"}, + {file = "rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b1cde22f2c30ebb049a9e74c5374994157b9b70a16147d332f89c99c5960737a"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5338742f6ba7a51012ea470bd4dc600a8c713c0c72adaa0977a1b1f4327d6592"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1460ebde1bcf6d496d80b191d854adedcc619f84ff17dc1c6d550f58c9efbba"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3eb248f2feba84c692579257a043a7699e28a77d86c77b032c1d9fbb3f0219c"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3bbba5def70b16cd1c1d7255666aad3b290fbf8d0fe7f9f91abafb73611a91"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3114f4db69ac5a1f32e7e4d1cbbe7c8f9cf8217f78e6e002cedf2d54c2a548ed"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4b0cb8a906b1a0196b863d460c0222fb8ad0f34041568da5620f9799b83ccf0b"}, + {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf681ac76a60b667106141e11a92a3330890257e6f559ca995fbb5265160b56e"}, + {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1e8ee6413cfc677ce8898d9cde18cc3a60fc2ba756b0dec5b71eb6eb21c49fa1"}, + {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3072b16904d0b5572a15eb9d31c1954e0d3227a585fc1351aa9878729099d6c"}, + {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b670c30fd87a6aec281c3c9896d3bae4b205fd75d79d06dc87c2503717e46092"}, + {file = "rpds_py-0.28.0-cp314-cp314t-win32.whl", hash = "sha256:8014045a15b4d2b3476f0a287fcc93d4f823472d7d1308d47884ecac9e612be3"}, + {file = "rpds_py-0.28.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7a4e59c90d9c27c561eb3160323634a9ff50b04e4f7820600a2beb0ac90db578"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f5e7101145427087e493b9c9b959da68d357c28c562792300dd21a095118ed16"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:31eb671150b9c62409a888850aaa8e6533635704fe2b78335f9aaf7ff81eec4d"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b55c1f64482f7d8bd39942f376bfdf2f6aec637ee8c805b5041e14eeb771db"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24743a7b372e9a76171f6b69c01aedf927e8ac3e16c474d9fe20d552a8cb45c7"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:389c29045ee8bbb1627ea190b4976a310a295559eaf9f1464a1a6f2bf84dde78"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23690b5827e643150cf7b49569679ec13fe9a610a15949ed48b85eb7f98f34ec"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f0c9266c26580e7243ad0d72fc3e01d6b33866cfab5084a6da7576bcf1c4f72"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4c6c4db5d73d179746951486df97fd25e92396be07fc29ee8ff9a8f5afbdfb27"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3b695a8fa799dd2cfdb4804b37096c5f6dba1ac7f48a7fbf6d0485bcd060316"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6aa1bfce3f83baf00d9c5fcdbba93a3ab79958b4c7d7d1f55e7fe68c20e63912"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b0f9dceb221792b3ee6acb5438eb1f02b0cb2c247796a72b016dcc92c6de829"}, + {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5d0145edba8abd3db0ab22b5300c99dc152f5c9021fab861be0f0544dc3cbc5f"}, + {file = "rpds_py-0.28.0.tar.gz", hash = "sha256:abd4df20485a0983e2ca334a216249b6186d6e3c1627e106651943dbdb791aea"}, +] + +[[package]] +name = "s3path" +version = "0.6.4" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "s3path-0.6.4-py3-none-any.whl", hash = "sha256:689232a9df906a7c90e2a00c347c34c4c3dad25cde89b050dd6cf5b6f00c723e"}, + {file = "s3path-0.6.4.tar.gz", hash = "sha256:1006cf38e17c5af031ed8808f8aa9bb7ced06fc8ce3ea92a7e3639290711e5d2"}, +] + +[package.dependencies] +boto3 = ">=1.16.35" +smart-open = ">=5.1.0" + +[[package]] +name = "s3transfer" +version = "0.14.0" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456"}, + {file = "s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125"}, +] + +[package.dependencies] +botocore = ">=1.37.4,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] + +[[package]] +name = "secretstorage" +version = "3.4.0" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "sys_platform == \"linux\"" +files = [ + {file = "secretstorage-3.4.0-py3-none-any.whl", hash = "sha256:0e3b6265c2c63509fb7415717607e4b2c9ab767b7f344a57473b779ca13bd02e"}, + {file = "secretstorage-3.4.0.tar.gz", hash = "sha256:c46e216d6815aff8a8a18706a2fbfd8d53fcbb0dce99301881687a1b0289ef7c"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "sekoia-automation-sdk" +version = "1.21.1" +description = "SDK to create Sekoia.io playbook modules" +optional = false +python-versions = "<4.0,>=3.11" +groups = ["main"] +files = [ + {file = "sekoia_automation_sdk-1.21.1-py3-none-any.whl", hash = "sha256:163f459b33b37771112d7c84de4d4ef43862c7697a8d8f574c19869e10700b3b"}, + {file = "sekoia_automation_sdk-1.21.1.tar.gz", hash = "sha256:043cbd265b4677eb721d17de34ad466f2b5dc67667f6c890ec2feeb47e7931bb"}, +] + +[package.dependencies] +aiohttp = ">=3.8.4,<4.0.0" +black = "*" +cookiecutter = ">=2.1,<3.0" +flask = ">=3.1.2,<4.0.0" +Jinja2 = ">=3.0.3,<4.0.0" +jsonschema = ">=4.22.0,<5.0.0" +orjson = ">=3.8,<4.0" +poetry = "*" +prometheus-client = ">=0.21.0,<0.22.0" +pydantic = ">=2.10,<3.0" +python-slugify = ">=5.0.2,<6.0.0" +PyYAML = ">=6.0,<7.0" +requests = ">=2.25,<3.0" +requests-ratelimiter = ">=0.7.0,<0.8.0" +s3path = ">=0.6.4,<0.7.0" +sentry-sdk = "*" +tenacity = "*" +typer = {version = ">=0.17.3,<0.18.0", extras = ["all"]} + +[package.extras] +all = ["aiobotocore (>=2.20.0,<3.0.0)", "aiocsv (>=1.2.4,<2.0.0)", "aiofiles (>=23.1.0,<24.0.0)", "aiolimiter (>=1.1.0,<2.0.0)", "boto3 (>=1.36.23,<2.0.0)", "loguru (>=0.7.0,<0.8.0)"] +async-aws = ["aiobotocore (>=2.20.0,<3.0.0)", "boto3 (>=1.36.23,<2.0.0)"] +async-files = ["aiocsv (>=1.2.4,<2.0.0)", "aiofiles (>=23.1.0,<24.0.0)"] +async-http = ["aiofiles (>=23.1.0,<24.0.0)", "aiolimiter (>=1.1.0,<2.0.0)"] +logging = ["loguru (>=0.7.0,<0.8.0)"] + +[[package]] +name = "sentry-sdk" +version = "2.43.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "sentry_sdk-2.43.0-py2.py3-none-any.whl", hash = "sha256:4aacafcf1756ef066d359ae35030881917160ba7f6fc3ae11e0e58b09edc2d5d"}, + {file = "sentry_sdk-2.43.0.tar.gz", hash = "sha256:52ed6e251c5d2c084224d73efee56b007ef5c2d408a4a071270e82131d336e20"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = ">=1.26.11" + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +anthropic = ["anthropic (>=0.16)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +celery-redbeat = ["celery-redbeat (>=2)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +google-genai = ["google-genai (>=1.29.0)"] +grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"] +http2 = ["httpcore[http2] (==1.*)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +huggingface-hub = ["huggingface_hub (>=0.22)"] +langchain = ["langchain (>=0.0.210)"] +langgraph = ["langgraph (>=0.6.6)"] +launchdarkly = ["launchdarkly-server-sdk (>=9.8.0)"] +litellm = ["litellm (>=1.77.5)"] +litestar = ["litestar (>=2.0.0)"] +loguru = ["loguru (>=0.5)"] +mcp = ["mcp (>=1.15.0)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +openfeature = ["openfeature-sdk (>=0.7.1)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro"] +pure-eval = ["asttokens", "executing", "pure_eval"] +pydantic-ai = ["pydantic-ai (>=1.0.0)"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +statsig = ["statsig (>=0.55.3)"] +tornado = ["tornado (>=6)"] +unleash = ["UnleashClient (>=6.0.1)"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "smart-open" +version = "7.4.4" +description = "Utils for streaming large files (S3, HDFS, GCS, SFTP, Azure Blob Storage, gzip, bz2, zst...)" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "smart_open-7.4.4-py3-none-any.whl", hash = "sha256:47077ed486a7e66d0bb928c284a8e5775c705092c6ea3e3bc6979d5b561c7bbf"}, + {file = "smart_open-7.4.4.tar.gz", hash = "sha256:2c264f43c55c2fcdea37b1752dcd06bb152afd514490a0aee5d21db0424b0669"}, +] + +[package.dependencies] +wrapt = "*" + +[package.extras] +all = ["smart_open[azure,gcs,http,s3,ssh,webhdfs,zst]"] +azure = ["azure-common", "azure-core", "azure-storage-blob"] +gcs = ["google-api-core (<2.28) ; python_version < \"3.10\"", "google-cloud-storage (>=2.6.0)"] +http = ["requests"] +s3 = ["boto3 (>=1.9.17)"] +ssh = ["paramiko"] +test = ["awscli", "flake8", "moto[server]", "numpy", "pyopenssl", "pytest", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist[psutil]", "pytest_benchmark", "responses", "smart_open[all]"] +webhdfs = ["requests"] +zst = ["backports.zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, + {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, +] + +[[package]] +name = "trove-classifiers" +version = "2025.9.11.17" +description = "Canonical source for classifiers on PyPI (pypi.org)." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "trove_classifiers-2025.9.11.17-py3-none-any.whl", hash = "sha256:5d392f2d244deb1866556457d6f3516792124a23d1c3a463a2e8668a5d1c15dd"}, + {file = "trove_classifiers-2025.9.11.17.tar.gz", hash = "sha256:931ca9841a5e9c9408bc2ae67b50d28acf85bef56219b56860876dd1f2d024dd"}, +] + +[[package]] +name = "typer" +version = "0.17.5" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "typer-0.17.5-py3-none-any.whl", hash = "sha256:45f2709d44be2f0d0f8bc70c9f845f60ef69d6cc80c6e6ae2cc2f29bae650cf6"}, + {file = "typer-0.17.5.tar.gz", hash = "sha256:a6fe2d187feb1b4a10322b910267b6339e4cc98257fae34d22299a47eac704f1"}, +] + +[package.dependencies] +click = ">=8.0.0,<8.3.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.35.4" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, + {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] + +[[package]] +name = "vt-py" +version = "0.22.0" +description = "The official Python client library for VirusTotal" +optional = false +python-versions = ">=3.7.0" +groups = ["main"] +files = [ + {file = "vt_py-0.22.0-py3-none-any.whl", hash = "sha256:cc81107500ef9b4d68e835643a0c4da8bb2c19c265daa7fc23e03d2569246a16"}, + {file = "vt_py-0.22.0.tar.gz", hash = "sha256:241baf95af552376eea7ebd129cb03736d49049190c995193952d2cc82a2ad37"}, +] + +[package.dependencies] +aiofiles = "*" +aiohttp = "*" + +[package.extras] +test = ["flask", "pytest", "pytest_asyncio", "pytest_httpserver"] + +[[package]] +name = "werkzeug" +version = "3.1.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "wrapt" +version = "2.0.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "wrapt-2.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7cebcee61f21b1e46aa32db8d9d93826d0fbf1ad85defc2ccfb93b4adef1435"}, + {file = "wrapt-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:827e6e3a3a560f6ec1f5ee92d4319c21a0549384f896ec692f3201eda31ebd11"}, + {file = "wrapt-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a91075a5383a7cbfe46aed1845ef7c3f027e8e20e7d9a8a75e36ebc9b0dd15e"}, + {file = "wrapt-2.0.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b6a18c813196e18146b8d041e20875bdb0cb09b94ac1d1e1146e0fa87b2deb0d"}, + {file = "wrapt-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec5028d26011a53c76bd91bb6198b30b438c6e0f7adb45f2ad84fe2655b6a104"}, + {file = "wrapt-2.0.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bed9b04900204721a24bcefc652ca267b01c1e8ad8bc8c0cff81558a45a3aadc"}, + {file = "wrapt-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:03442f2b45fa3f2b98a94a1917f52fb34670de8f96c0a009c02dbd512d855a3d"}, + {file = "wrapt-2.0.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:17d0b5c42495ba142a1cee52b76414f9210591c84aae94dffda70240753bfb3c"}, + {file = "wrapt-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ee44215e7d13e112a8fc74e12ed1a1f41cab2bc07b11cc703f2398cd114b261c"}, + {file = "wrapt-2.0.0-cp310-cp310-win32.whl", hash = "sha256:fe6eafac3bc3c957ab6597a0c0654a0a308868458d00d218743e5b5fae51951c"}, + {file = "wrapt-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e070c3491397fba0445b8977900271eca9656570cca7c900d9b9352186703a0"}, + {file = "wrapt-2.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:806e2e73186eb5e3546f39fb5d0405040e0088db0fc8b2f667fd1863de2b3c99"}, + {file = "wrapt-2.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b7e221abb6c5387819db9323dac3c875b459695057449634f1111955d753c621"}, + {file = "wrapt-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1147a84c8fc852426580af8b6e33138461ddbc65aa459a25ea539374d32069fa"}, + {file = "wrapt-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d6691d4a711504a0bc10de789842ad6ac627bed22937b10f37a1211a8ab7bb3"}, + {file = "wrapt-2.0.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f460e1eb8e75a17c3918c8e35ba57625721eef2439ef0bcf05304ac278a65e1d"}, + {file = "wrapt-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12c37784b77bf043bf65cc96c7195a5db474b8e54173208af076bdbb61df7b3e"}, + {file = "wrapt-2.0.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75e5c049eb583835f7a0e0e311d9dde9bfbaac723a6dd89d052540f9b2809977"}, + {file = "wrapt-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e50bcbd5b65dac21b82319fcf18486e6ac439947e9305034b00704eb7405f553"}, + {file = "wrapt-2.0.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:06b78cb6b9320f57737a52fede882640d93cface98332d1a3df0c5696ec9ae9f"}, + {file = "wrapt-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c8349ebfc3cd98bc9105e0112dd8c8ac1f3c7cb5601f9d02248cae83a63f748"}, + {file = "wrapt-2.0.0-cp311-cp311-win32.whl", hash = "sha256:028f19ec29e204fe725139d4a8b09f77ecfb64f8f02b7ab5ee822c85e330b68b"}, + {file = "wrapt-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:c6961f05e58d919153ba311b397b7b904b907132b7b8344dde47865d4bb5ec89"}, + {file = "wrapt-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:be7e316c2accd5a31dbcc230de19e2a846a325f8967fdea72704d00e38e6af06"}, + {file = "wrapt-2.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73c6f734aecb1a030d9a265c13a425897e1ea821b73249bb14471445467ca71c"}, + {file = "wrapt-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b4a7f8023b8ce8a36370154733c747f8d65c8697cb977d8b6efeb89291fff23e"}, + {file = "wrapt-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1cb62f686c50e9dab5983c68f6c8e9cbf14a6007935e683662898a7d892fa69"}, + {file = "wrapt-2.0.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:43dc0550ae15e33e6bb45a82a5e1b5495be2587fbaa996244b509921810ee49f"}, + {file = "wrapt-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39c5b45b056d630545e40674d1f5e1b51864b3546f25ab6a4a331943de96262e"}, + {file = "wrapt-2.0.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:804e88f824b76240a1b670330637ccfd2d18b9efa3bb4f02eb20b2f64880b324"}, + {file = "wrapt-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c2c476aa3fc2b9899c3f7b20963fac4f952e7edb74a31fc92f7745389a2e3618"}, + {file = "wrapt-2.0.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8d851e526891216f89fcb7a1820dad9bd503ba3468fb9635ee28e93c781aa98e"}, + {file = "wrapt-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b95733c2360c4a8656ee93c7af78e84c0bd617da04a236d7a456c8faa34e7a2d"}, + {file = "wrapt-2.0.0-cp312-cp312-win32.whl", hash = "sha256:ea56817176834edf143df1109ae8fdaa087be82fdad3492648de0baa8ae82bf2"}, + {file = "wrapt-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c7d3bee7be7a2665286103f4d1f15405c8074e6e1f89dac5774f9357c9a3809"}, + {file = "wrapt-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:680f707e1d26acbc60926659799b15659f077df5897a6791c7c598a5d4a211c4"}, + {file = "wrapt-2.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e2ea096db28d5eb64d381af0e93464621ace38a7003a364b6b5ffb7dd713aabe"}, + {file = "wrapt-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c92b5a82d28491e3f14f037e1aae99a27a5e6e0bb161e65f52c0445a3fa7c940"}, + {file = "wrapt-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d234718aabe632d179fac52c7f69f0f99fbaac4d4bcd670e62462bbcbfcad7"}, + {file = "wrapt-2.0.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db2eea83c43f84e4e41dbbb4c1de371a53166e55f900a6b130c3ef51c6345c1a"}, + {file = "wrapt-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65f50e356c425c061e1e17fe687ff30e294fed9bf3441dc1f13ef73859c2a817"}, + {file = "wrapt-2.0.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:887f2a667e3cbfb19e204032d42ad7dedaa43972e4861dc7a3d51ae951d9b578"}, + {file = "wrapt-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9054829da4be461e3ad3192e4b6bbf1fc18af64c9975ce613aec191924e004dc"}, + {file = "wrapt-2.0.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b952ffd77133a5a2798ee3feb18e51b0a299d2f440961e5bb7737dbb02e57289"}, + {file = "wrapt-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e25fde03c480061b8234d8ee4863eb5f40a9be4fb258ce105b364de38fc6bcf9"}, + {file = "wrapt-2.0.0-cp313-cp313-win32.whl", hash = "sha256:49e982b7860d325094978292a49e0418833fc7fc42c0dc7cd0b7524d7d06ee74"}, + {file = "wrapt-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:6e5c86389d9964050ce50babe247d172a5e3911d59a64023b90db2b4fa00ae7c"}, + {file = "wrapt-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:b96fdaa4611e05c7231937930567d3c16782be9dbcf03eb9f60d83e57dd2f129"}, + {file = "wrapt-2.0.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f2c7b7fead096dbf1dcc455b7f59facb05de3f5bfb04f60a69f98cdfe6049e5f"}, + {file = "wrapt-2.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:04c7c8393f25b11c0faa5d907dd9eb462e87e4e7ba55e308a046d7ed37f4bbe2"}, + {file = "wrapt-2.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a93e0f8b376c0735b2f4daf58018b4823614d2b896cb72b6641c4d3dbdca1d75"}, + {file = "wrapt-2.0.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b42d13603da4416c43c430dbc6313c8d7ff745c40942f146ed4f6dd02c7d2547"}, + {file = "wrapt-2.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8bbd2472abf8c33480ad2314b1f8fac45d592aba6cc093e8839a7b2045660e6"}, + {file = "wrapt-2.0.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e64a3a1fd9a308ab9b815a2ad7a65b679730629dbf85f8fc3f7f970d634ee5df"}, + {file = "wrapt-2.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d61214525eaf88e0d0edf3d1ad5b5889863c6f88e588c6cdc6aa4ee5d1f10a4a"}, + {file = "wrapt-2.0.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:04f7a5f92c5f7324a1735043cc467b1295a1c5b4e0c1395472b7c44706e3dc61"}, + {file = "wrapt-2.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2356f76cb99b3de5b4e5b8210367fbbb81c7309fe39b622f5d199dd88eb7f765"}, + {file = "wrapt-2.0.0-cp313-cp313t-win32.whl", hash = "sha256:0a921b657a224e40e4bc161b5d33934583b34f0c9c5bdda4e6ac66f9d2fcb849"}, + {file = "wrapt-2.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c16f6d4eea98080f6659a8a7fc559d4a0a337ee66960659265cad2c8a40f7c0f"}, + {file = "wrapt-2.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:52878edc13dc151c58a9966621d67163a80654bc6cff4b2e1c79fa62d0352b26"}, + {file = "wrapt-2.0.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:79a53d86c2aff7b32cc77267e3a308365d1fcb881e74bc9cbe26f63ee90e37f0"}, + {file = "wrapt-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d731a4f22ed6ffa4cb551b4d2b0c24ff940c27a88edaf8e3490a5ee3a05aef71"}, + {file = "wrapt-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3e02ab8c0ac766a5a6e81cd3b6cc39200c69051826243182175555872522bd5a"}, + {file = "wrapt-2.0.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:895870602d65d7338edb3b6a717d856632ad9f14f7ff566214e4fb11f0816649"}, + {file = "wrapt-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b9ad4fab76a0086dc364c4f17f39ad289600e73ef5c6e9ab529aff22cac1ac3"}, + {file = "wrapt-2.0.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e7ca0562606d7bad2736b2c18f61295d61f50cd3f4bfc51753df13614dbcce1b"}, + {file = "wrapt-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe089d9f5a4a3dea0108a8ae34bced114d0c4cca417bada1c5e8f42d98af9050"}, + {file = "wrapt-2.0.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e761f2d2f8dbc80384af3d547b522a80e67db3e319c7b02e7fd97aded0a8a678"}, + {file = "wrapt-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:17ba1bdc52d0c783481850996aa26cea5237720769197335abea2ae6b4c23bc0"}, + {file = "wrapt-2.0.0-cp314-cp314-win32.whl", hash = "sha256:f73318741b141223a4674ba96992aa2291b1b3f7a5e85cb3c2c964f86171eb45"}, + {file = "wrapt-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8e08d4edb13cafe7b3260f31d4de033f73d3205774540cf583bffaa4bec97db9"}, + {file = "wrapt-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:af01695c2b7bbd8d67b869d8e3de2b123a7bfbee0185bdd138c2775f75373b83"}, + {file = "wrapt-2.0.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:057f02c13cce7b26c79624c06a3e1c2353e6dc9708525232232f6768118042ca"}, + {file = "wrapt-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:79bdd84570267f3f43d609c892ae2d30b91ee4b8614c2cbfd311a2965f1c9bdb"}, + {file = "wrapt-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:93c8b4f4d54fd401a817abbfc9bf482aa72fd447f8adf19ce81d035b3f5c762c"}, + {file = "wrapt-2.0.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5e09ffd31001dce71c2c2a4fc201bdba9a2f9f62b23700cf24af42266e784741"}, + {file = "wrapt-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d87c285ff04e26083c4b03546e7b74df7ba4f1f32f1dcb92e9ac13c2dbb4c379"}, + {file = "wrapt-2.0.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e52e50ea0a72ea48d1291cf8b8aaedcc99072d9dc5baba6b820486dcf4c67da8"}, + {file = "wrapt-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fd4c95536975895f32571073446e614d5e2810b666b64955586dcddfd438fd3"}, + {file = "wrapt-2.0.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d6ebfe9283209220ed9de80a3e9442aab8fc2be5a9bbf8491b99e02ca9349a89"}, + {file = "wrapt-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5d3ebd784804f146b7ea55359beb138e23cc18e5a5cc2cf26ad438723c00ce3a"}, + {file = "wrapt-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:9b15940ae9debc8b40b15dc57e1ce4433f7fb9d3f8761c7fab1ddd94cb999d99"}, + {file = "wrapt-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7a0efbbc06d3e2077476a04f55859819d23206600b4c33f791359a8e6fa3c362"}, + {file = "wrapt-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7fec8a9455c029c8cf4ff143a53b6e7c463268d42be6c17efa847ebd2f809965"}, + {file = "wrapt-2.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ac3d8beac68e4863c703b844fcc82693f83f933b37d2a54e9d513b2aab9c76aa"}, + {file = "wrapt-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4b8f8644602803add6848c81b7d214cfd397b1ebab2130dc8530570d888155c"}, + {file = "wrapt-2.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93cb5bff1fcd89b75f869e4f69566a91ab2c9f13e8edf0241fd5777b2fa6d48e"}, + {file = "wrapt-2.0.0-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0eb6d155d02c7525b7ec09856cda5e611fc6eb9ab40d140e1f35f27ac7d5eae"}, + {file = "wrapt-2.0.0-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:309dd467a94ee38a7aa5752bda64e660aeab5723b26200d0b65a375dad9add09"}, + {file = "wrapt-2.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a55e8edd08e2eece131d90d82cd1521962d9152829b22c56e68539526d605825"}, + {file = "wrapt-2.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:1724dd7b84d419c80ba839da81ad78b02ac30df626e5aefcb18e94632a965f13"}, + {file = "wrapt-2.0.0-cp38-cp38-win32.whl", hash = "sha256:f8255c380a79f6752d0b920e69a5d656d863675d9c433eeb5548518ee2c8d9da"}, + {file = "wrapt-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:829c8d46465dbae49dba91516f11200a2b5ea91eae8afaccbc035f0b651eb9c4"}, + {file = "wrapt-2.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:094d348ce7e6ce37bf6ed9a6ecc11886c96f447b3ffebc7539ca197daa9a997e"}, + {file = "wrapt-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98223acaa25b1449d993a3f4ffc8b5a03535e4041b37bf6a25459a0c74ee4cfc"}, + {file = "wrapt-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b79bf04c722035b1c474980dc1a64369feab7b703d6fe67da2d8664ed0bc980"}, + {file = "wrapt-2.0.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:623242959cb0c53f76baeb929be79f5f6a9a1673ef51628072b91bf299af2212"}, + {file = "wrapt-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59dc94afc4542c7d9b9447fb2ae1168b5a29064eca4061dbbf3b3c26df268334"}, + {file = "wrapt-2.0.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7c532cc9f0a9e6017f8d3c37f478a3e3a5dffa955ebba556274e5e916c058f7"}, + {file = "wrapt-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9d72c725cefbcc8ebab85c8352e5062ae87b6e323858e934e16b54ced580435a"}, + {file = "wrapt-2.0.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:2ca35b83497276c2ca0b072d2c00da2edde4c2a6c8c650eafcd1a006c17ab231"}, + {file = "wrapt-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2fc55d0da29318a5da33c2827aef8946bba046ac609a4784a90faff73c511174"}, + {file = "wrapt-2.0.0-cp39-cp39-win32.whl", hash = "sha256:9c100b0598f3763274f2033bcc0454de7486409f85bc6da58b49e5971747eb36"}, + {file = "wrapt-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:1316972a72c67936a07dbb48e2464356d91dd9674335aaec087b60094d87750b"}, + {file = "wrapt-2.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:5aad54ff45da9784573099696fd84841c7e559ce312f02afa6aa7e89b58e2c2f"}, + {file = "wrapt-2.0.0-py3-none-any.whl", hash = "sha256:02482fb0df89857e35427dfb844319417e14fae05878f295ee43fa3bf3b15502"}, + {file = "wrapt-2.0.0.tar.gz", hash = "sha256:35a542cc7a962331d0279735c30995b024e852cf40481e384fd63caaa391cbb9"}, +] + +[package.extras] +dev = ["pytest", "setuptools"] + +[[package]] +name = "xattr" +version = "1.3.0" +description = "Python wrapper for extended filesystem attributes" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "sys_platform == \"darwin\"" +files = [ + {file = "xattr-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a80c4617e08670cdc3ba71f1dbb275c1627744c5c3641280879cb3bc95a07237"}, + {file = "xattr-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51cdaa359f5cd2861178ae01ea3647b56dbdfd98e724a8aa3c04f77123b78217"}, + {file = "xattr-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2fea070768d7d2d25797817bea93bf0a6fda6449e88cfee8bb3d75de9ed11c7b"}, + {file = "xattr-1.3.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69bca34be2d7a928389aff4e32f27857e1c62d04c91ec7c1519b1636870bd58f"}, + {file = "xattr-1.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05f8e068409742d246babba60cff8310b2c577745491f498b08bf068e0c867a3"}, + {file = "xattr-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bbd06987102bc11f5cbd08b15d1029832b862cf5bc61780573fc0828812f01ca"}, + {file = "xattr-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b8589744116d2c37928b771c50383cb281675cd6dcfd740abfab6883e3d4af85"}, + {file = "xattr-1.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:331a51bf8f20c27822f44054b0d760588462d3ed472d5e52ba135cf0bea510e8"}, + {file = "xattr-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:196360f068b74fa0132a8c6001ce1333f095364b8f43b6fd8cdaf2f18741ef89"}, + {file = "xattr-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:405d2e4911d37f2b9400fa501acd920fe0c97fe2b2ec252cb23df4b59c000811"}, + {file = "xattr-1.3.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ae3a66ae1effd40994f64defeeaa97da369406485e60bfb421f2d781be3b75d"}, + {file = "xattr-1.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:69cd3bfe779f7ba87abe6473fdfa428460cf9e78aeb7e390cfd737b784edf1b5"}, + {file = "xattr-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c5742ca61761a99ae0c522f90a39d5fb8139280f27b254e3128482296d1df2db"}, + {file = "xattr-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a04ada131e9bdfd32db3ab1efa9f852646f4f7c9d6fde0596c3825c67161be3"}, + {file = "xattr-1.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd4e63614722d183e81842cb237fd1cc978d43384166f9fe22368bfcb187ebe5"}, + {file = "xattr-1.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:995843ef374af73e3370b0c107319611f3cdcdb6d151d629449efecad36be4c4"}, + {file = "xattr-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa23a25220e29d956cedf75746e3df6cc824cc1553326d6516479967c540e386"}, + {file = "xattr-1.3.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b4345387087fffcd28f709eb45aae113d911e1a1f4f0f70d46b43ba81e69ccdd"}, + {file = "xattr-1.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe92bb05eb849ab468fe13e942be0f8d7123f15d074f3aba5223fad0c4b484de"}, + {file = "xattr-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c42ef5bdac3febbe28d3db14d3a8a159d84ba5daca2b13deae6f9f1fc0d4092"}, + {file = "xattr-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2aaa5d66af6523332189108f34e966ca120ff816dfa077ca34b31e6263f8a236"}, + {file = "xattr-1.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:937d8c91f6f372788aff8cc0984c4be3f0928584839aaa15ff1c95d64562071c"}, + {file = "xattr-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e470b3f15e9c3e263662506ff26e73b3027e1c9beac2cbe9ab89cad9c70c0495"}, + {file = "xattr-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f2238b2a973fcbf5fefa1137db97c296d27f4721f7b7243a1fac51514565e9ec"}, + {file = "xattr-1.3.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f32bb00395371f4a3bed87080ae315b19171ba114e8a5aa403a2c8508998ce78"}, + {file = "xattr-1.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:78df56bfe3dd4912548561ed880225437d6d49ef082fe6ccd45670810fa53cfe"}, + {file = "xattr-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:864c34c14728f21c3ef89a9f276d75ae5e31dd34f48064e0d37e4bf0f671fc6e"}, + {file = "xattr-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1fd185b3f01121bd172c98b943f9341ca3b9ea6c6d3eb7fe7074723614d959ff"}, + {file = "xattr-1.3.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:630c85020282bd0bcb72c3d031491c4e91d7f29bb4c094ebdfb9db51375c5b07"}, + {file = "xattr-1.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:95f1e14a4d9ca160b4b78c527bf2bac6addbeb0fd9882c405fc0b5e3073a8752"}, + {file = "xattr-1.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:88557c0769f64b1d014aada916c9630cfefa38b0be6c247eae20740d2d8f7b47"}, + {file = "xattr-1.3.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6992eb5da32c0a1375a9eeacfab15c66eebc8bd34be63ebd1eae80cc2f8bf03"}, + {file = "xattr-1.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da5954424099ca9d402933eaf6112c29ddde26e6da59b32f0bf5a4e35eec0b28"}, + {file = "xattr-1.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:726b4d0b66724759132cacdcd84a5b19e00b0cdf704f4c2cf96d0c08dc5eaeb5"}, + {file = "xattr-1.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:928c49ceb0c70fc04732e46fa236d7c8281bfc3db1b40875e5f548bb14d2668c"}, + {file = "xattr-1.3.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:f3bef26fd2d5d7b17488f4cc4424a69894c5a8ed71dd5f657fbbf69f77f68a51"}, + {file = "xattr-1.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:64f1fb511f8463851e0d97294eb0e0fde54b059150da90582327fb43baa1bb92"}, + {file = "xattr-1.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1e6c216927b16fd4b72df655d5124b69b2a406cb3132b5231179021182f0f0d1"}, + {file = "xattr-1.3.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c0d9ab346cdd20539afddf2f9e123efee0fe8d54254d9fc580b4e2b4e6d77351"}, + {file = "xattr-1.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2c5e7ba0e893042deef4e8638db7a497680f587ac7bd6d68925f29af633dfa6b"}, + {file = "xattr-1.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1e0dabb39596d8d7b83d6f9f7fa30be68cf15bfb135cb633e2aad9887d308a32"}, + {file = "xattr-1.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5eeaa944516b7507ec51456751334b4880e421de169bbd067c4f32242670d606"}, + {file = "xattr-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:03712f84e056dcd23c36db03a1f45417a26eef2c73d47c2c7d425bf932601587"}, + {file = "xattr-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45f85233a51c71659969ce364abe6bd0c9048a302b7fcdbea675dc63071e47ff"}, + {file = "xattr-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fefcf20d040e79ec3bf6e7dc0fdcfd972f70f740d5a69ed67b20c699bb9cea"}, + {file = "xattr-1.3.0-cp39-cp39-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9e68a02adde8a5f8675be5e8edc837eb6fdbe214a6ee089956fae11d633c0e51"}, + {file = "xattr-1.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:50c12d92f5214b0416cf4b4fafcd02dca5434166657553b74b8ba6abc66cb4b4"}, + {file = "xattr-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c69999ed70411ac2859f1f8c918eb48a6fd2a71ef41dc03ee846f69e2200bb2"}, + {file = "xattr-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3cf29da6840eb94b881eab692ae83b1421c9c15a0cd92ffb97a0696ceac8cac"}, + {file = "xattr-1.3.0.tar.gz", hash = "sha256:30439fabd7de0787b27e9a6e1d569c5959854cb322f64ce7380fedbfa5035036"}, +] + +[package.dependencies] +cffi = ">=1.16.0" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "yarl" +version = "1.22.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.1" + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version == \"3.11\"" +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[[package]] +name = "zstandard" +version = "0.25.0" +description = "Zstandard bindings for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd"}, + {file = "zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74"}, + {file = "zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa"}, + {file = "zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e"}, + {file = "zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c"}, + {file = "zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7"}, + {file = "zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4"}, + {file = "zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2"}, + {file = "zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137"}, + {file = "zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b"}, + {file = "zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa"}, + {file = "zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd"}, + {file = "zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01"}, + {file = "zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9"}, + {file = "zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94"}, + {file = "zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf"}, + {file = "zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09"}, + {file = "zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5"}, + {file = "zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049"}, + {file = "zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3"}, + {file = "zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088"}, + {file = "zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12"}, + {file = "zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2"}, + {file = "zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d"}, + {file = "zstandard-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0"}, + {file = "zstandard-0.25.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27"}, + {file = "zstandard-0.25.0-cp39-cp39-win32.whl", hash = "sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649"}, + {file = "zstandard-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860"}, + {file = "zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b"}, +] + +[package.extras] +cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] + +[metadata] +lock-version = "2.1" +python-versions = "^3.11" +content-hash = "66359712317f75b822b009d3d60f948ef8fdd6036c6bcfc5b1af4ff053c42476" diff --git a/GoogleThreatIntelligence/pyproject.toml b/GoogleThreatIntelligence/pyproject.toml new file mode 100644 index 000000000..df27e6072 --- /dev/null +++ b/GoogleThreatIntelligence/pyproject.toml @@ -0,0 +1,53 @@ +[tool.poetry] +name = "Automation module for Googlethreatintelligence" +version = "0.1" +description = "" +authors = ["AKONIS"] +package-mode = false + +[tool.poetry.dependencies] +python = "^3.11" +sekoia-automation-sdk = "^1.21.1" +vt-py = "^0.22.0" + +[tool.poetry.group.dev.dependencies] +pytest = "*" +pytest-cov = "*" +requests = "*" +requests-mock = "*" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 119 +target-version = ['py38'] +include = '\.pyi?$' + +[tool.isort] +profile = "black" +line_length = 119 + +[tool.coverage.run] +omit = [ + "tests/*", + "main.py", +] + +[tool.pytest.ini_options] +minversion = "6.0" +testpaths = [ + "tests", +] +addopts = ''' + --strict-markers + --tb=short + --cov=googlethreatintelligence + --cov-branch + --cache-clear + --cov-report=html + --cov-report=term-missing:skip-covered + --cov-fail-under=5 + --capture=sys +''' diff --git a/GoogleThreatIntelligence/tests/__init__.py b/GoogleThreatIntelligence/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/GoogleThreatIntelligence/tests/conftest.py b/GoogleThreatIntelligence/tests/conftest.py new file mode 100644 index 000000000..e23646004 --- /dev/null +++ b/GoogleThreatIntelligence/tests/conftest.py @@ -0,0 +1,16 @@ +from shutil import rmtree +from tempfile import mkdtemp + +import pytest +from sekoia_automation import constants + + +@pytest.fixture +def data_storage(): + original_storage = constants.DATA_STORAGE + constants.DATA_STORAGE = mkdtemp() + + yield constants.DATA_STORAGE + + rmtree(constants.DATA_STORAGE) + constants.DATA_STORAGE = original_storage diff --git a/GoogleThreatIntelligence/tests/test_get_comments.py b/GoogleThreatIntelligence/tests/test_get_comments.py new file mode 100644 index 000000000..35cc53043 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_comments.py @@ -0,0 +1,118 @@ +import pytest +from unittest.mock import patch, MagicMock, PropertyMock +import vt + +from googlethreatintelligence.get_comments import GTIGetComments + + +API_KEY = "FAKE_API_KEY" + + +# ============================================================================= +# Helpers +# ============================================================================= + + +def mock_comment(): + c = MagicMock() + c.text = "Test comment" + c.date = "2021-09-05 10:30:31" + c.votes = {"positive": 5, "negative": 1} + c.author = "test_user" + return c + + +def assert_success(response): + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + assert response["data"]["comments_count"] == 1 + assert isinstance(response["data"]["entity"], str) + assert response["data"]["comments"][0]["text"] == "Test comment" + + +def assert_iterator_called(mock_client): + mock_client.iterator.assert_called_once() + args, kwargs = mock_client.iterator.call_args + assert args[0].endswith("/comments") + assert kwargs.get("limit") == 10 + + +# ============================================================================= +# Routing tests based on REAL action behavior +# ============================================================================= + + +@pytest.mark.parametrize( + "ioc_field,ioc_value,expected_prefix", + [ + ("domain", "google.com", "/domains/"), # resolved to IP internally + ("ip", "8.8.8.8", "/ip_addresses/"), + ("url", "http://example.com", "/urls/"), + ("file_hash", "44d88612fea8a8f36de82e1278abb02f", "/files/"), + ], +) +@patch("googlethreatintelligence.get_comments.vt.Client") +def test_get_comments_routing(mock_vt_client, ioc_field, ioc_value, expected_prefix): + + mock_client = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client + mock_client.iterator.return_value = iter([mock_comment()]) + + action = GTIGetComments() + action.module.configuration = {"api_key": API_KEY} + + payload = {ioc_field: ioc_value} + + response = action.run(payload) + assert_success(response) + + mock_client.iterator.assert_called_once() + args, _ = mock_client.iterator.call_args + path = args[0] + + # MATCH THE REAL PREFIX + assert path.startswith(expected_prefix) + assert path.endswith("/comments") + + +# ============================================================================= +# API error scenario +# ============================================================================= + + +@patch("googlethreatintelligence.get_comments.vt.Client") +def test_get_comments_fail(mock_vt_client): + + mock_client = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client + + mock_client.iterator.side_effect = vt.APIError("WrongCredentialsError", "Invalid API key") + + action = GTIGetComments() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"domain": "google.com"}) + + assert isinstance(response, dict) + assert response.get("success") is False + assert response.get("data") is None # REAL behavior + assert "Invalid API key" in response.get("error", "") + + +# ============================================================================= +# Missing API key +# ============================================================================= + + +def test_get_comments_missing_api_key(): + action = GTIGetComments() + + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"domain": "google.com"}) + + assert isinstance(response, dict) + assert response.get("success") is False + assert "API key" in response.get("error", "") diff --git a/GoogleThreatIntelligence/tests/test_get_file_behaviour.py b/GoogleThreatIntelligence/tests/test_get_file_behaviour.py new file mode 100644 index 000000000..eeef8e3e8 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_file_behaviour.py @@ -0,0 +1,98 @@ +from unittest.mock import patch, MagicMock +import vt +from googlethreatintelligence.get_file_behaviour import GTIGetFileBehaviour + +# === Test constants === +API_KEY = "FAKE_API_KEY" +FILE_HASH = "44d88612fea8a8f36de82e1278abb02f" + + +@patch("googlethreatintelligence.get_file_behaviour.vt.Client") +def test_get_file_behaviour_success(mock_vt_client): + """Test successful retrieval of file behaviour via iterator""" + # Create a mock behaviour object that VT iterator would return + mock_behaviour = MagicMock() + mock_behaviour.sandbox_name = "Windows10" + mock_behaviour.processes_created = ["cmd.exe", "calc.exe"] + mock_behaviour.files_written = ["C:\\temp\\file1.tmp"] + mock_behaviour.files_deleted = [] + mock_behaviour.registry_keys_set = ["HKCU\\Software\\Test"] + mock_behaviour.dns_lookups = ["example.com"] + mock_behaviour.ip_traffic = ["8.8.8.8"] + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Make iterator return our fake behaviour + mock_client_instance.iterator.return_value = iter([mock_behaviour]) + + # Setup action + action = GTIGetFileBehaviour() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"entity_type": "files", "entity": FILE_HASH}) + + # Verify response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + + # Verify the data contains behaviour information + assert response["data"]["behaviours_count"] == 1 + assert isinstance(response["data"]["behaviours"], list) + assert response["data"]["behaviours"][0]["sandbox_name"] == "Windows10" + + # Verify vt.Client was called with the correct API key + mock_vt_client.assert_called_once_with(API_KEY) + + # Verify iterator was called with the correct endpoint and limit + mock_client_instance.iterator.assert_called_once_with(f"/files/{FILE_HASH}/behaviours", limit=5) + + +@patch("googlethreatintelligence.get_file_behaviour.vt.Client") +def test_get_file_behaviour_fail_api_error(mock_vt_client): + """Test error handling when VT API raises vt.APIError from iterator""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Simulate an API error when calling iterator + mock_client_instance.iterator.side_effect = vt.APIError("SomeAPIError", "Internal Server Error") + + # Setup action + action = GTIGetFileBehaviour() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"entity_type": "files", "entity": FILE_HASH}) + + # Verify error response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "data" in response or "error" in response + + # Verify vt.Client was called + mock_vt_client.assert_called_once_with(API_KEY) + + # Ensure iterator was attempted + mock_client_instance.iterator.assert_called_once_with(f"/files/{FILE_HASH}/behaviours", limit=5) + + +def test_get_file_behaviour_no_api_key(): + """Test handling of missing API key""" + action = GTIGetFileBehaviour() + + # No API key configured + with patch.object(type(action.module), "configuration", new_callable=MagicMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"entity_type": "files", "entity": FILE_HASH}) + + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "API key" in response.get("error", "") diff --git a/GoogleThreatIntelligence/tests/test_get_ioc_report.py b/GoogleThreatIntelligence/tests/test_get_ioc_report.py new file mode 100644 index 000000000..124ea9be1 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_ioc_report.py @@ -0,0 +1,313 @@ +from unittest.mock import patch, MagicMock, PropertyMock +import vt +from googlethreatintelligence.get_ioc_report import GTIIoCReport + +# === Test constants === +API_KEY = "FAKE_API_KEY" +DOMAIN = "google.com" +IP = "8.8.8.8" +URL = "https://www.sekoia.io" +FILE_HASH = "44d88612fea8a8f36de82e1278abb02f" + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_domain_report_success(mock_connector_class, mock_vt_client): + """Test successful retrieval of domain report""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock result + mock_result = MagicMock() + mock_result.status = "SUCCESS" + mock_result.response = { + "entity_type": "domains", + "entity": DOMAIN, + "id": DOMAIN, + "reputation": 100, + "last_analysis_stats": {"malicious": 0, "suspicious": 0, "clean": 80}, + } + mock_result.error = None + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"domain": DOMAIN}) + + # Verify response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + assert response["data"]["entity"] == DOMAIN + assert response["data"]["entity_type"] == "domains" + + # Verify VTAPIConnector was called correctly + mock_connector_class.assert_called_once_with(API_KEY, domain=DOMAIN, ip="", url="", file_hash="") + + # Verify get_domain_report was called + mock_connector.get_domain_report.assert_called_once_with(mock_client_instance) + + # Verify vt.Client was called with the correct API key + mock_vt_client.assert_called_once_with(API_KEY) + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_ip_report_success(mock_connector_class, mock_vt_client): + """Test successful retrieval of IP report""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock result + mock_result = MagicMock() + mock_result.status = "SUCCESS" + mock_result.response = {"entity_type": "ip_addresses", "entity": IP, "id": IP, "reputation": 95, "country": "US"} + mock_result.error = None + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"ip": IP}) + + # Verify response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + assert response["data"]["entity"] == IP + + # Verify VTAPIConnector was called correctly + mock_connector_class.assert_called_once_with(API_KEY, domain="", ip=IP, url="", file_hash="") + + # Verify get_ip_report was called + mock_connector.get_ip_report.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_url_report_success(mock_connector_class, mock_vt_client): + """Test successful retrieval of URL report""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock result + mock_result = MagicMock() + mock_result.status = "SUCCESS" + mock_result.response = { + "entity_type": "urls", + "entity": URL, + "last_analysis_stats": {"malicious": 0, "suspicious": 0, "clean": 75}, + } + mock_result.error = None + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"url": URL}) + + # Verify response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + + # Verify VTAPIConnector was called correctly + mock_connector_class.assert_called_once_with(API_KEY, domain="", ip="", url=URL, file_hash="") + + # Verify get_url_report was called + mock_connector.get_url_report.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_file_report_success(mock_connector_class, mock_vt_client): + """Test successful retrieval of file report""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock result + mock_result = MagicMock() + mock_result.status = "SUCCESS" + mock_result.response = { + "entity_type": "files", + "entity": FILE_HASH, + "id": FILE_HASH, + "last_analysis_stats": {"malicious": 60, "suspicious": 5, "clean": 0}, + } + mock_result.error = None + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"file_hash": FILE_HASH}) + + # Verify response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + assert response["data"]["entity"] == FILE_HASH + + # Verify VTAPIConnector was called correctly + mock_connector_class.assert_called_once_with(API_KEY, domain="", ip="", url="", file_hash=FILE_HASH) + + # Verify get_file_report was called + mock_connector.get_file_report.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_ioc_report_api_error(mock_connector_class, mock_vt_client): + """Test error handling when API fails""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock error result + mock_result = MagicMock() + mock_result.status = "ERROR" + mock_result.response = None + mock_result.error = "WrongCredentialsError: Invalid API key" + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"domain": DOMAIN}) + + # Verify error response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "error" in response + assert response["error"] == "WrongCredentialsError: Invalid API key" + + # Verify vt.Client was called + mock_vt_client.assert_called_once_with(API_KEY) + + +def test_get_ioc_report_no_api_key(): + """Test handling of missing API key""" + action = GTIIoCReport() + + # Mock the configuration property to return an empty dict + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"domain": DOMAIN}) + + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "API key" in response.get("error", "") + + +def test_get_ioc_report_no_parameters(): + """Test handling when no IoC parameters are provided""" + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run with no parameters + response = action.run({}) + + # Verify error response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "must be provided" in response.get("error", "") + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_ioc_report_exception_handling(mock_connector_class, mock_vt_client): + """Test general exception handling""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Make VTAPIConnector raise an exception + mock_connector_class.side_effect = Exception("Unexpected error occurred") + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"domain": DOMAIN}) + + # Verify error response + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "Unexpected error occurred" in response.get("error", "") + + +@patch("googlethreatintelligence.get_ioc_report.vt.Client") +@patch("googlethreatintelligence.get_ioc_report.VTAPIConnector") +def test_get_ioc_report_multiple_parameters_domain_priority(mock_connector_class, mock_vt_client): + """Test that domain takes priority when multiple parameters are provided""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector_class.return_value = mock_connector + + # Create a mock result + mock_result = MagicMock() + mock_result.status = "SUCCESS" + mock_result.response = {"entity_type": "domains", "entity": DOMAIN} + mock_result.error = None + mock_connector.results = [mock_result] + + # Setup action + action = GTIIoCReport() + action.module.configuration = {"api_key": API_KEY} + + # Run with multiple parameters + response = action.run({"domain": DOMAIN, "ip": IP, "url": URL}) + + # Verify that domain report was called (domain has priority) + mock_connector.get_domain_report.assert_called_once_with(mock_client_instance) + mock_connector.get_ip_report.assert_not_called() + mock_connector.get_url_report.assert_not_called() diff --git a/GoogleThreatIntelligence/tests/test_get_passive_dns.py b/GoogleThreatIntelligence/tests/test_get_passive_dns.py new file mode 100644 index 000000000..fed0f9f1a --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_passive_dns.py @@ -0,0 +1,107 @@ +""" +Unit tests for GTIGetPassiveDNS Action +""" + +from unittest.mock import patch, MagicMock, PropertyMock +import vt +from googlethreatintelligence.get_passive_dns import GTIGetPassiveDNS + +# === Test constants === +API_KEY = "FAKE_API_KEY" +DOMAIN = "google.com" + + +@patch("googlethreatintelligence.get_passive_dns.vt.Client") +def test_get_passive_dns_success(mock_vt_client): + """Test successful retrieval of passive DNS data""" + # Create fake resolution objects + mock_resolution_1 = MagicMock() + mock_resolution_1.ip_address = "8.8.8.8" + mock_resolution_1.host_name = DOMAIN + mock_resolution_1.date = "2024-10-15 12:00:00" + mock_resolution_1.resolver = "resolver1" + + mock_resolution_2 = MagicMock() + mock_resolution_2.ip_address = "8.8.4.4" + mock_resolution_2.host_name = DOMAIN + mock_resolution_2.date = "2024-10-15 12:05:00" + mock_resolution_2.resolver = "resolver2" + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Mock iterator() to return our fake DNS resolutions + mock_client_instance.iterator.return_value = iter([mock_resolution_1, mock_resolution_2]) + + # Setup the action + action = GTIGetPassiveDNS() + action.module.configuration = {"api_key": API_KEY} + + # Execute the action + response = action.run({"domain": DOMAIN}) + + # === Assertions === + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is True + assert "data" in response + data = response["data"] + + assert data["resolutions_count"] == 2 + assert data["unique_ips_count"] == 2 + assert "8.8.8.8" in data["unique_ips"] + assert "8.8.4.4" in data["unique_ips"] + assert len(data["resolutions"]) == 2 + assert data["resolutions"][0]["ip_address"] == "8.8.8.8" + + # Verify vt.Client was instantiated with the correct API key + mock_vt_client.assert_called_once_with(API_KEY) + + # Verify iterator was called with correct endpoint and limit + mock_client_instance.iterator.assert_called_once_with(f"/domains/{DOMAIN}/resolutions", limit=40) + + +@patch("googlethreatintelligence.get_passive_dns.vt.Client") +def test_get_passive_dns_fail(mock_vt_client): + """Test failure when VirusTotal API raises an exception""" + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Simulate API error + mock_client_instance.iterator.side_effect = vt.APIError("QuotaExceededError", "Rate limit exceeded") + + # Setup the action + action = GTIGetPassiveDNS() + action.module.configuration = {"api_key": API_KEY} + + # Execute the action + response = action.run({"domain": DOMAIN}) + + # === Assertions === + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "error" in response + assert "Rate limit exceeded" in response["error"] + + # Ensure vt.Client was called + mock_vt_client.assert_called_once_with(API_KEY) + + +def test_get_passive_dns_no_api_key(): + """Test missing API key handling""" + action = GTIGetPassiveDNS() + + # Patch the configuration property to simulate missing key + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"domain": DOMAIN}) + + # === Assertions === + assert response is not None + assert isinstance(response, dict) + assert response.get("success") is False + assert "API key" in response.get("error", "") diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py new file mode 100644 index 000000000..58a6c7072 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py @@ -0,0 +1,109 @@ +# tests/test_get_vulnerability_associations.py + +from unittest.mock import patch, MagicMock, PropertyMock +from googlethreatintelligence.get_vulnerability_associations import GTIGetVulnerabilityAssociations +import vt + +# === Test constants === +API_KEY = "FAKE_API_KEY" +IP = "8.8.8.8" + + +@patch("googlethreatintelligence.get_vulnerability_associations.vt.Client") +@patch("googlethreatintelligence.get_vulnerability_associations.VTAPIConnector") +def test_get_vulnerability_associations_success(mock_connector_class, mock_vt_client): + """Test successful retrieval of vulnerability associations""" + # Mock VTAPIConnector instance and its behavior + mock_connector = MagicMock() + mock_connector.results = [ + MagicMock( + status="SUCCESS", + response={ + "vulnerabilities_count": 2, + "unique_cves_count": 2, + "high_severity_count": 1, + "cve_ids": ["CVE-2024-0001", "CVE-2024-0002"], + "vulnerabilities": [ + {"id": "1", "cve_id": "CVE-2024-0001", "cvss_severity": "HIGH"}, + {"id": "2", "cve_id": "CVE-2024-0002", "cvss_severity": "MEDIUM"}, + ], + }, + ) + ] + mock_connector_class.return_value = mock_connector + + # Mock vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Setup action + action = GTIGetVulnerabilityAssociations() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"ip": IP}) + + # === Assertions === + assert response is not None + assert isinstance(response, dict) + assert response["success"] is True + assert "data" in response + + # Validate the structure of the returned data + assert response["data"]["vulnerabilities_count"] == 2 + assert response["data"]["unique_cves_count"] == 2 + assert len(response["data"]["vulnerabilities"]) == 2 + + # Ensure the mocks were called properly + mock_vt_client.assert_called_once_with(API_KEY) + mock_connector_class.assert_called_once_with(API_KEY, ip=IP, domain="", url="", file_hash="") + mock_connector.get_vulnerability_associations.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.get_vulnerability_associations.vt.Client") +@patch("googlethreatintelligence.get_vulnerability_associations.VTAPIConnector") +def test_get_vulnerability_associations_fail(mock_connector_class, mock_vt_client): + """Test handling of APIError during vulnerability association retrieval""" + # Mock VTAPIConnector instance + mock_connector = MagicMock() + mock_connector.get_vulnerability_associations.side_effect = vt.APIError("WrongCredentialsError", "Invalid API key") + mock_connector_class.return_value = mock_connector + + # Mock vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Setup action + action = GTIGetVulnerabilityAssociations() + action.module.configuration = {"api_key": API_KEY} + + # Run the action (should trigger exception) + response = action.run({"ip": IP}) + + # === Assertions === + assert response is not None + assert isinstance(response, dict) + assert response["success"] is False + assert "error" in response + assert "Invalid API key" in response["error"] + + # Ensure mocks were called + mock_vt_client.assert_called_once_with(API_KEY) + mock_connector_class.assert_called_once_with(API_KEY, ip=IP, domain="", url="", file_hash="") + mock_connector.get_vulnerability_associations.assert_called_once_with(mock_client_instance) + + +def test_get_vulnerability_associations_no_api_key(): + """Test handling of missing API key""" + action = GTIGetVulnerabilityAssociations() + + # Patch the configuration property to return an empty dict + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"ip": IP}) + + assert response is not None + assert isinstance(response, dict) + assert response["success"] is False + assert "API key not configured" in response.get("error", "") diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py new file mode 100644 index 000000000..85da8bc78 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py @@ -0,0 +1,78 @@ +from unittest.mock import patch, MagicMock, PropertyMock +import vt +from googlethreatintelligence.get_vulnerability_report import GTIGetVulnerabilityReport + +# === Constants === +API_KEY = "FAKE_API_KEY" +CVE_ID = "CVE-2021-34527" + + +@patch("googlethreatintelligence.get_vulnerability_report.vt.Client") +def test_get_vulnerability_report_success(mock_vt_client): + """Test successful retrieval of vulnerability report""" + # Arrange + mock_vuln = MagicMock() + mock_vuln.id = "vuln123" + mock_vuln.title = "PrintNightmare" + mock_vuln.description = "Remote code execution vulnerability in Windows Print Spooler" + mock_vuln.cvss = {"score": 8.8, "severity": "HIGH"} + + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + mock_client_instance.get_object.return_value = mock_vuln + + action = GTIGetVulnerabilityReport() + action.module.configuration = {"api_key": API_KEY} + + # Act + response = action.run({"cve": CVE_ID}) + + # Assert + assert response["success"] is True + assert response["data"]["cve"] == CVE_ID + assert response["data"]["title"] == "PrintNightmare" + assert response["data"]["cvss"]["severity"] == "HIGH" + mock_vt_client.assert_called_once_with(API_KEY) + mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") + + +@patch("googlethreatintelligence.get_vulnerability_report.vt.Client") +def test_get_vulnerability_report_fail_api(mock_vt_client): + """Test behavior when VirusTotal API raises an exception""" + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Simulate APIError + mock_client_instance.get_object.side_effect = vt.APIError("WrongCredentialsError", "Invalid API key") + + action = GTIGetVulnerabilityReport() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"cve": CVE_ID}) + + assert response["success"] is False + assert "Invalid API key" in response["error"] + mock_vt_client.assert_called_once_with(API_KEY) + mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") + + +def test_get_vulnerability_report_no_api_key(): + """Test behavior when API key is missing""" + action = GTIGetVulnerabilityReport() + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + response = action.run({"cve": CVE_ID}) + + assert response["success"] is False + assert "API key" in response["error"] + + +def test_get_vulnerability_report_no_cve(): + """Test behavior when CVE ID is missing""" + action = GTIGetVulnerabilityReport() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({}) # No CVE + + assert response["success"] is False + assert "CVE" in response["error"] diff --git a/GoogleThreatIntelligence/tests/test_scan_file.py b/GoogleThreatIntelligence/tests/test_scan_file.py new file mode 100644 index 000000000..3be0ade19 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_scan_file.py @@ -0,0 +1,156 @@ +from unittest.mock import patch, MagicMock, PropertyMock +from googlethreatintelligence.scan_file import GTIScanFile +import vt +import tempfile +import os + +API_KEY = "FAKE_API_KEY" + + +# === SUCCESS CASE === +@patch("googlethreatintelligence.scan_file.vt.Client") +@patch("googlethreatintelligence.scan_file.VTAPIConnector") +def test_scan_file_success(mock_connector_class, mock_vt_client): + """Test successful file scan""" + + # Create a temporary file to simulate a real file + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_path = tmp_file.name + tmp_file.write(b"dummy content") + + try: + # Mock VTAPIConnector instance + mock_connector_instance = MagicMock() + mock_connector_class.return_value = mock_connector_instance + + # Create a mock Result object that will be in connector.results + mock_result = MagicMock() + mock_analysis = MagicMock() + mock_analysis.stats = {"malicious": 0, "suspicious": 0, "harmless": 50} + mock_analysis.results = {"scanner1": "clean", "scanner2": "clean"} + mock_result.response = mock_analysis + + # Mock the results list - scan_file() appends to this list + mock_connector_instance.results = [mock_result] + mock_connector_instance.scan_file.return_value = None # scan_file returns None + + # Mock vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Initialize action and mock configuration + action = GTIScanFile() + action.module.configuration = {"api_key": API_KEY} + + # Run the action + response = action.run({"file_path": tmp_path}) + + # === Assertions === + assert response is not None + assert response["success"] is True + assert "data" in response + assert "file_path" in response["data"] + assert "analysis_stats" in response["data"] + assert "analysis_results" in response["data"] + + mock_connector_class.assert_called_once_with(API_KEY, url="", domain="", ip="", file_hash="", cve="") + mock_connector_instance.scan_file.assert_called_once_with(mock_client_instance, tmp_path) + mock_vt_client.assert_called_once_with(API_KEY) + finally: + os.unlink(tmp_path) + + +# === MISSING API KEY === +def test_scan_file_no_api_key(): + """Test behavior when API key is missing""" + action = GTIScanFile() + + # Correctly mock the module.configuration PropertyMock + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"file_path": "dummy_path"}) + + assert response is not None + assert response["success"] is False + assert "API key" in response["error"] + + +# === FILE NOT FOUND === +def test_scan_file_file_not_found(): + """Test behavior when the file does not exist""" + action = GTIScanFile() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"file_path": "/nonexistent/file.txt"}) + + assert response is not None + assert response["success"] is False + assert "File not found" in response["error"] + + +# === API ERROR HANDLING === +@patch("googlethreatintelligence.scan_file.vt.Client") +@patch("googlethreatintelligence.scan_file.VTAPIConnector") +def test_scan_file_api_error(mock_connector_class, mock_vt_client): + """Test behavior when the VirusTotal API fails""" + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_path = tmp_file.name + tmp_file.write(b"dummy content") + + try: + # Mock connector that raises an APIError + mock_connector_instance = MagicMock() + mock_connector_instance.scan_file.side_effect = vt.APIError("QuotaExceededError", "API quota exceeded") + mock_connector_class.return_value = mock_connector_instance + + # Mock vt.Client context + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + action = GTIScanFile() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"file_path": tmp_path}) + + assert response is not None + assert response["success"] is False + assert "API quota exceeded" in response["error"] + + mock_connector_instance.scan_file.assert_called_once_with(mock_client_instance, tmp_path) + mock_vt_client.assert_called_once_with(API_KEY) + finally: + os.unlink(tmp_path) + + +# === ADDITIONAL TEST: Empty results list === +@patch("googlethreatintelligence.scan_file.vt.Client") +@patch("googlethreatintelligence.scan_file.VTAPIConnector") +def test_scan_file_empty_results(mock_connector_class, mock_vt_client): + """Test behavior when connector.results is empty (edge case)""" + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_path = tmp_file.name + tmp_file.write(b"dummy content") + + try: + # Mock connector with empty results + mock_connector_instance = MagicMock() + mock_connector_instance.results = [] # Empty results list + mock_connector_instance.scan_file.return_value = None + mock_connector_class.return_value = mock_connector_instance + + # Mock vt.Client context + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + action = GTIScanFile() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"file_path": tmp_path}) + + # This should cause an IndexError which gets caught by the general exception handler + assert response is not None + assert response["success"] is False + assert "error" in response + finally: + os.unlink(tmp_path) diff --git a/GoogleThreatIntelligence/tests/test_scan_url.py b/GoogleThreatIntelligence/tests/test_scan_url.py new file mode 100644 index 000000000..597e9b361 --- /dev/null +++ b/GoogleThreatIntelligence/tests/test_scan_url.py @@ -0,0 +1,178 @@ +from unittest.mock import patch, MagicMock, PropertyMock +import pytest +from googlethreatintelligence.scan_url import GTIScanURL + +# === Test constants === +API_KEY = "FAKE_API_KEY" +TEST_URL = "https://example.com/malware-test" + + +@patch("googlethreatintelligence.scan_url.vt.Client") +@patch("googlethreatintelligence.scan_url.VTAPIConnector") +def test_scan_url_success(mock_connector_class, mock_vt_client): + """Test successful URL scan""" + # Create mock connector instance + mock_connector_instance = MagicMock() + mock_connector_class.return_value = mock_connector_instance + + # Create mock analysis object with stats and results attributes + mock_analysis = MagicMock() + mock_analysis.stats = {"malicious": 5, "suspicious": 2, "harmless": 80} + mock_analysis.results = {"Google Safebrowsing": "clean", "Kaspersky": "malware"} + + # Create mock result that will be in connector.results list + mock_result = MagicMock() + + # /!\ A dict that matches what scan_url.py expects + mock_result.response = { + "analysis_stats": mock_analysis.stats, + "analysis_results": mock_analysis.results, + "url": TEST_URL, + } + + # Set up the results list to contain our mock result + mock_connector_instance.results = [mock_result] + + # scan_url() doesn't return anything, it modifies results internally + mock_connector_instance.scan_url.return_value = None + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Run the action + action = GTIScanURL() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"url": TEST_URL}) + + # Assertions + assert response is not None + assert isinstance(response, dict) + assert response["success"] is True + assert "data" in response + assert response["data"]["analysis_stats"] == mock_analysis.stats + assert response["data"]["analysis_results"] == mock_analysis.results + assert response["data"]["url"] == TEST_URL + + # Verify mock calls + mock_connector_class.assert_called_once_with(API_KEY, url=TEST_URL, domain="", ip="", file_hash="", cve="") + mock_vt_client.assert_called_once_with(API_KEY) + mock_connector_instance.scan_url.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.scan_url.vt.Client") +@patch("googlethreatintelligence.scan_url.VTAPIConnector") +def test_scan_url_failure(mock_connector_class, mock_vt_client): + """Test URL scan failure (no results in connector)""" + # Create mock connector instance with empty results + mock_connector_instance = MagicMock() + mock_connector_class.return_value = mock_connector_instance + + # Empty results list simulates a failed scan + mock_connector_instance.results = [] + mock_connector_instance.scan_url.return_value = None + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Run the action + action = GTIScanURL() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"url": TEST_URL}) + + # Assertions - should fail with IndexError when trying to access results[-1] + assert response["success"] is False + assert "error" in response + # The actual error will be an IndexError: list index out of range + + # Verify mock calls + mock_connector_class.assert_called_once_with(API_KEY, url=TEST_URL, domain="", ip="", file_hash="", cve="") + mock_connector_instance.scan_url.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.scan_url.vt.Client") +def test_scan_url_no_api_key(mock_vt_client): + """Test missing API key""" + action = GTIScanURL() + with patch.object(type(action.module), "configuration", new_callable=PropertyMock) as mock_config: + mock_config.return_value = {} + + response = action.run({"url": TEST_URL}) + + assert response["success"] is False + assert "API key" in response["error"] + + mock_vt_client.assert_not_called() + + +@patch("googlethreatintelligence.scan_url.vt.Client") +def test_scan_url_no_url_provided(mock_vt_client): + """Test missing URL in arguments""" + action = GTIScanURL() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({}) + + assert response["success"] is False + assert "No URL provided" in response["error"] + + mock_vt_client.assert_not_called() + + +@patch("googlethreatintelligence.scan_url.vt.Client") +@patch("googlethreatintelligence.scan_url.VTAPIConnector") +def test_scan_url_exception(mock_connector_class, mock_vt_client): + """Test exception handling""" + # Create mock connector instance that raises exception + mock_connector_instance = MagicMock() + mock_connector_class.return_value = mock_connector_instance + mock_connector_instance.scan_url.side_effect = Exception("Unexpected Error") + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Run the action + action = GTIScanURL() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"url": TEST_URL}) + + # Assertions + assert response["success"] is False + assert "Unexpected Error" in response["error"] + + # Verify mock calls + mock_connector_class.assert_called_once() + mock_connector_instance.scan_url.assert_called_once_with(mock_client_instance) + + +@patch("googlethreatintelligence.scan_url.vt.Client") +@patch("googlethreatintelligence.scan_url.VTAPIConnector") +def test_scan_url_api_error(mock_connector_class, mock_vt_client): + """Test VT API error handling""" + # Create mock connector instance + mock_connector_instance = MagicMock() + mock_connector_class.return_value = mock_connector_instance + + # Simulate API error by having scan_url not add results + # (based on client.py, scan_url catches vt.APIError and returns None) + mock_connector_instance.results = [] + mock_connector_instance.scan_url.return_value = None + + # Mock the vt.Client context manager + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + # Run the action + action = GTIScanURL() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"url": TEST_URL}) + + # Assertions - should fail when accessing empty results list + assert response["success"] is False + assert "error" in response From 01e63e9a39252652f61eb30225c3b0e83c87c485 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Wed, 3 Dec 2025 09:39:19 +0000 Subject: [PATCH 2/8] remove unecessary files --- GoogleThreatIntelligence/README.md | 56 -- GoogleThreatIntelligence/actions.csv | 10 - .../googlethreatintelligence/sample.exe | 0 .../googlethreatintelligence/script_v2.py | 608 ------------------ .../vt_test_results.json | 129 ---- 5 files changed, 803 deletions(-) delete mode 100644 GoogleThreatIntelligence/README.md delete mode 100644 GoogleThreatIntelligence/actions.csv delete mode 100644 GoogleThreatIntelligence/googlethreatintelligence/sample.exe delete mode 100644 GoogleThreatIntelligence/googlethreatintelligence/script_v2.py delete mode 100644 GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json diff --git a/GoogleThreatIntelligence/README.md b/GoogleThreatIntelligence/README.md deleted file mode 100644 index 5003c4219..000000000 --- a/GoogleThreatIntelligence/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# README - -## **Key Features** - -### 1. **Security Enhancements** -- API key loaded from environment variable (`VT_API_KEY`) -- Validation to prevent running with hardcoded keys -- Proper file existence checks before scanning - -### 2. **Error Handling** -- Try-catch blocks for all API calls -- Specific exception handling for `vt.APIError`, `FileNotFoundError`, `IOError` -- Detailed error logging and tracking -- Graceful degradation when tests fail - -### 3. **Official vt-py Library Usage** -- `client.scan_url()` for URL scanning -- `client.scan_file()` for file scanning -- `client.get_object()` for retrieving reports -- `client.get_json()` for raw JSON responses -- `client.iterator()` for paginated results -- Proper context manager usage (`with vt.Client()`) - -### 4. **Structured Results** -- `TestResult` dataclass for type-safe result storage -- JSON output with detailed success/error information -- Summary statistics at the end - -### 5. **Comprehensive Coverage** -All your required actions are covered: -- ✅ Scan File -- ✅ Get IOC Report (IP, URL, domain, file) -- ✅ Get Comments -- ✅ Get Vulnerability Associations -- ✅ Get File Sandbox Report -- ✅ Scan URL -- ✅ Get Passive DNS Data -- ✅ Get Vulnerability Report - -## **Usage** - -```bash -# Install vt-py -pip install vt-py - -# Set your API key -export VT_API_KEY='your_actual_api_key' - -# Run the script -python script.py - -# Optional: test with a file -# Modify main() to include: tester.run_all_tests(test_file_path="your_file.png") -``` - -The script will generate `vt_test_results.json` with detailed results and print a summary to the console. \ No newline at end of file diff --git a/GoogleThreatIntelligence/actions.csv b/GoogleThreatIntelligence/actions.csv deleted file mode 100644 index bfbf897c9..000000000 --- a/GoogleThreatIntelligence/actions.csv +++ /dev/null @@ -1,10 +0,0 @@ -Get IOC Report (API action) - IP address, URL, domain, or file;GET /api/v3/{entity_type}/{entity} -Get Comments (API action) - IP address, URL, domain, or file;GET /api/v3/{entity_type}/{entity}/comments -Get Vulnerability Associations - IP address, URL, domain, or file(API action);"GET /api/v3/{entity_type}/{entity}/vulnerabilities -Example https://www.virustotal.com/api/v3/ip_addresses/45.182.189.102/vulnerabilities" -Get File Sandbox Report (API action);GET /api/v3/files/{file_id}/behaviours -Scan Url (API action);"POST /api/v3/urls -POST /api/v3/private/urls" -Get Curated Associations (API action);GET /api/v3/{entity_type}/{entity} (avec entity_type=collections, cti, … selon vos collections VT) -Get Passive DNS Data (API action);GET /api/v3/{entity_type}/{entity}/resolutions -Get Vulnerability Report (API action);GET /api/v3/collections/vulnerability--cve-2024-6284 diff --git a/GoogleThreatIntelligence/googlethreatintelligence/sample.exe b/GoogleThreatIntelligence/googlethreatintelligence/sample.exe deleted file mode 100644 index e69de29bb..000000000 diff --git a/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py b/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py deleted file mode 100644 index 5dd724bf3..000000000 --- a/GoogleThreatIntelligence/googlethreatintelligence/script_v2.py +++ /dev/null @@ -1,608 +0,0 @@ -""" -Google Threat Intelligence (VirusTotal) API Connector -Production-ready connector using vt-py official library -""" - -import vt -import json -import time -import base64 -from pathlib import Path -from typing import Dict, List, Optional, Any -from dataclasses import dataclass, asdict -import logging - -# Configure logging -logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") -logger = logging.getLogger(__name__) - -# Security: Load API key from environment variable -import os - -API_KEY = os.getenv("VT_API_KEY", "REDACTED") - -if API_KEY == "REDACTED": - logger.warning("API_KEY not set! Set VT_API_KEY environment variable") - - -@dataclass -class Result: - """Structure to hold test results""" - - name: str - method: str - endpoint: str - status: str - response: Any - error: Optional[str] = None - - -class VTAPIConnector: - """VirusTotal API Connector Class using vt-py""" - - def __init__( - self, - api_key: str, - domain: Optional[str] = None, - ip: Optional[str] = None, - url: Optional[str] = None, - file_hash: Optional[str] = None, - cve: Optional[str] = None, - ): - """ - Initialize the VT API Connector - - Args: - api_key: VirusTotal API key - domain: Domain to query (optional, defaults to google.com) - ip: IP address to query (optional, defaults to 8.8.8.8) - url: URL to query (optional, defaults to sekoia.io) - file_hash: File hash to query (optional, defaults to EICAR test file) - cve: CVE ID to query (optional, defaults to CVE-2021-34527) - """ - self.api_key = api_key - self.results: List[Result] = [] - - # Use provided values when available, otherwise fall back to sensible defaults - self.domain = domain or "google.com" - self.ip = ip or "8.8.8.8" - self.url = url or "https://www.sekoia.io/en/homepage/" - self.file_hash = file_hash or "44d88612fea8a8f36de82e1278abb02f" # EICAR test file - self.cve = cve or "CVE-2021-34527" - - # Headers used by the internal _request helper - self.headers = {"x-apikey": self.api_key} - - def _add_result( - self, name: str, method: str, endpoint: str, status: str, response: Any, error: Optional[str] = None - ): - """Add a test result""" - # Convert VT objects to JSON-serializable format - if response is not None: - response = self._make_serializable(response) - - result = Result(name, method, endpoint, status, response, error) - self.results.append(result) - - if error: - logger.error(f"[{status}] {name}: {error}") - else: - logger.info(f"[{status}] {name}: Success") - - def _make_serializable(self, obj: Any) -> Any: - """Convert VT objects to JSON-serializable format""" - if isinstance(obj, dict): - return {k: self._make_serializable(v) for k, v in obj.items()} - elif isinstance(obj, (list, tuple)): - return [self._make_serializable(item) for item in obj] - elif hasattr(obj, "__dict__"): - return str(obj) - else: - return obj - - def test_connectivity(self, client: vt.Client): - """Test API connectivity""" - try: - # Use get_object instead of get_json for /me endpoint - user = client.get_object("/users/me") - self._add_result( - "TEST_CONNECTIVITY", - "GET", - "/api/v3/users/me", - "SUCCESS", - {"user_id": user.id if hasattr(user, "id") else None}, - ) - except vt.APIError as e: - self._add_result("TEST_CONNECTIVITY", "GET", "/api/v3/users/me", "ERROR", None, str(e)) - - def get_ioc_report(self, client: vt.Client, entity_type: str, entity: str): - """ - Generic IOC Report - handles IP, URL, domain, or file - GET /api/v3/{entity_type}/{entity} - """ - try: - # Handle URL encoding for URLs - if entity_type == "urls": - entity = base64.urlsafe_b64encode(entity.encode()).decode().strip("=") - - ioc_obj = client.get_object(f"/{entity_type}/{entity}") - - response_data = { - "entity_type": entity_type, - "entity": entity, - "id": ioc_obj.id if hasattr(ioc_obj, "id") else None, - "reputation": ioc_obj.reputation if hasattr(ioc_obj, "reputation") else None, - } - - # Add type-specific attributes - if hasattr(ioc_obj, "last_analysis_stats"): - response_data["last_analysis_stats"] = dict(ioc_obj.last_analysis_stats) - if hasattr(ioc_obj, "country"): - response_data["country"] = ioc_obj.country - if hasattr(ioc_obj, "categories"): - response_data["categories"] = dict(ioc_obj.categories) if ioc_obj.categories else None - - self._add_result( - f"GET_IOC_REPORT_{entity_type.upper()}", - "GET", - f"/api/v3/{entity_type}/{entity}", - "SUCCESS", - response_data, - ) - except vt.APIError as e: - self._add_result( - f"GET_IOC_REPORT_{entity_type.upper()}", - "GET", - f"/api/v3/{entity_type}/{entity}", - "ERROR", - None, - str(e), - ) - - def get_ip_report(self, client: vt.Client): - """Get IP address report""" - self.get_ioc_report(client, "ip_addresses", self.ip) - - def get_domain_report(self, client: vt.Client): - """Get domain report""" - self.get_ioc_report(client, "domains", self.domain) - - def get_url_report(self, client: vt.Client): - """Get URL report""" - self.get_ioc_report(client, "urls", self.url) - - def get_file_report(self, client: vt.Client): - """Get file report""" - self.get_ioc_report(client, "files", self.file_hash) - - def scan_url(self, client: vt.Client): - """Scan a URL - This returns an Analysis ID. The analysis can be retrieved by using the Analysis endpoint. - -> https://docs.virustotal.com/reference/analysis - - """ - try: - analysis = client.scan_url(self.url, wait_for_completion=True) - print("Analysis completed") - # print(analysis.stats) - # print(analysis.results) - self._add_result( - "SCAN_URL", - "POST", - "/api/v3/urls", - "SUCCESS", - {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "url": self.url}, - ) - except vt.APIError as e: - self._add_result("SCAN_URL", "POST", "/api/v3/urls", "ERROR", None, str(e)) - return None - - def scan_file(self, client: vt.Client, file_path: str): - """Scan a file""" - try: - path = Path(file_path) - if not path.exists(): - raise FileNotFoundError(f"File not found: {file_path}") - - with open(file_path, "rb") as f: - analysis = client.scan_file(f, wait_for_completion=True) - - self._add_result( - "SCAN_FILE", - "POST", - "/api/v3/files", - "SUCCESS", - {"analysis_stats": analysis.stats, "analysis_results": analysis.results, "file": file_path}, - ) - return analysis.id - except (vt.APIError, FileNotFoundError, IOError) as e: - self._add_result("SCAN_FILE", "POST", "/api/v3/files", "ERROR", None, str(e)) - return None - - def get_analysis(self, client: vt.Client, analysis_id: str): - """Get analysis results""" - try: - analysis = client.get_object(f"/analyses/{analysis_id}") - self._add_result( - "GET_ANALYSIS", - "GET", - f"/api/v3/analyses/{analysis_id}", - "SUCCESS", - {"status": analysis.status, "stats": analysis.stats if hasattr(analysis, "stats") else None}, - ) - except vt.APIError as e: - self._add_result("GET_ANALYSIS", "GET", f"/api/v3/analyses/{analysis_id}", "ERROR", None, str(e)) - - def get_file_behaviour(self, client: vt.Client): - """Get file sandbox behavior""" - try: - # Use iterator for behaviours and FULLY consume it - behaviours_it = client.iterator(f"/files/{self.file_hash}/behaviours", limit=5) - - # IMPORTANT: Fully consume the iterator to test it properly - behaviours = [] - for behaviour in behaviours_it: - behaviour_data = { - "sandbox_name": behaviour.sandbox_name if hasattr(behaviour, "sandbox_name") else None, - } - - # Extract detailed behaviour information - if hasattr(behaviour, "processes_created"): - behaviour_data["processes_created"] = len(behaviour.processes_created) - if hasattr(behaviour, "files_written"): - behaviour_data["files_written"] = len(behaviour.files_written) - if hasattr(behaviour, "files_deleted"): - behaviour_data["files_deleted"] = len(behaviour.files_deleted) - if hasattr(behaviour, "registry_keys_set"): - behaviour_data["registry_keys_set"] = len(behaviour.registry_keys_set) - if hasattr(behaviour, "dns_lookups"): - behaviour_data["dns_lookups"] = len(behaviour.dns_lookups) - if hasattr(behaviour, "ip_traffic"): - behaviour_data["ip_traffic"] = len(behaviour.ip_traffic) - - behaviours.append(behaviour_data) - - self._add_result( - "GET_FILE_SANDBOX", - "GET", - f"/api/v3/files/{self.file_hash}/behaviours", - "SUCCESS", - {"behaviours_count": len(behaviours), "behaviours": behaviours}, - ) - except vt.APIError as e: - # This endpoint requires Premium API - log as warning not error - logger.warning(f"File behaviours not available (may require Premium API): {e}") - self._add_result( - "GET_FILE_SANDBOX", - "GET", - f"/api/v3/files/{self.file_hash}/behaviours", - "NOT_AVAILABLE", - None, - f"May require Premium API: {str(e)}", - ) - - def get_comments(self, client: vt.Client, entity_type: str = "domains"): - """Get comments for an entity - FULLY tests the iterator""" - try: - entity = self.domain if entity_type == "domains" else self.ip - - # IMPORTANT: Fully consume the iterator to test it properly - comments_it = client.iterator(f"/{entity_type}/{entity}/comments", limit=10) - - comments = [] - for comment in comments_it: - comment_data = { - "text": comment.text if hasattr(comment, "text") else None, - "date": str(comment.date) if hasattr(comment, "date") else None, - "votes": ( - { - "positive": comment.votes.get("positive", 0) if hasattr(comment, "votes") else 0, - "negative": comment.votes.get("negative", 0) if hasattr(comment, "votes") else 0, - } - if hasattr(comment, "votes") - else {"positive": 0, "negative": 0} - ), - "author": comment.author if hasattr(comment, "author") else None, - } - comments.append(comment_data) - - self._add_result( - "GET_COMMENTS", - "GET", - f"/api/v3/{entity_type}/{entity}/comments", - "SUCCESS", - {"comments_count": len(comments), "entity": entity, "comments": comments}, - ) - - logger.info(f"Retrieved and processed {len(comments)} comments") - - except vt.APIError as e: - self._add_result("GET_COMMENTS", "GET", f"/api/v3/{entity_type}/{entity}/comments", "ERROR", None, str(e)) - - def get_passive_dns(self, client: vt.Client): - """Get passive DNS resolutions - FULLY tests the iterator""" - try: - # IMPORTANT: Fully consume the iterator to test it properly - resolutions_it = client.iterator(f"/domains/{self.domain}/resolutions", limit=40) - - resolutions = [] - unique_ips = set() - - for resolution in resolutions_it: - resolution_data = { - "ip_address": resolution.ip_address if hasattr(resolution, "ip_address") else None, - "host_name": resolution.host_name if hasattr(resolution, "host_name") else None, - "date": str(resolution.date) if hasattr(resolution, "date") else None, - "resolver": resolution.resolver if hasattr(resolution, "resolver") else None, - } - resolutions.append(resolution_data) - - # Track unique IPs - if resolution_data["ip_address"]: - unique_ips.add(resolution_data["ip_address"]) - - self._add_result( - "PASSIVE_DNS", - "GET", - f"/api/v3/domains/{self.domain}/resolutions", - "SUCCESS", - { - "resolutions_count": len(resolutions), - "unique_ips_count": len(unique_ips), - "unique_ips": list(unique_ips), - "resolutions": resolutions, - }, - ) - - logger.info( - f"Retrieved and processed {len(resolutions)} DNS resolutions with {len(unique_ips)} unique IPs" - ) - - except vt.APIError as e: - logger.warning(f"Passive DNS not available (may require Premium API): {e}") - self._add_result( - "PASSIVE_DNS", - "GET", - f"/api/v3/domains/{self.domain}/resolutions", - "NOT_AVAILABLE", - None, - f"May require Premium API: {str(e)}", - ) - - def get_vulnerability_report(self, client: vt.Client): - """Get vulnerability report""" - try: - # Correct path for vulnerability collections - print("Getting vuln", self.cve) - # `https://www.virustotal.com/api/v3/collections/vulnerability--cve-2010-3765` - vuln = client.get_object(f"/collections/vulnerability--{self.cve}") - print("VULN is:", vuln) - - vuln_data = { - "cve": self.cve, - "id": vuln.id if hasattr(vuln, "id") else None, - } - - # Extract additional vulnerability details - if hasattr(vuln, "title"): - vuln_data["title"] = vuln.title - if hasattr(vuln, "description"): - vuln_data["description"] = ( - vuln.description[:200] + "..." if len(vuln.description) > 200 else vuln.description - ) - if hasattr(vuln, "cvss"): - vuln_data["cvss"] = vuln.cvss - - self._add_result( - "VULN_REPORT", - "GET", - f"/api/v3/intelligence/vulnerability_collections/{self.cve}", - "SUCCESS", - vuln_data, - ) - except vt.APIError as e: - logger.warning(f"OUCH! Vulnerability report not available (may require Premium API): {e}") - self._add_result( - "VULN_REPORT", - "GET", - f"/api/v3/intelligence/vulnerability_collections/{self.cve}", - "NOT_AVAILABLE", - None, - f"May require Premium API: {str(e)}", - ) - - def get_vulnerability_associations(self, client: vt.Client): - """Get vulnerability associations for an entity - FULLY tests the iterator""" - try: - # IMPORTANT: Fully consume the iterator to test it properly - vulns_it = client.iterator(f"/ip_addresses/{self.ip}/vulnerabilities", limit=20) - - vulnerabilities = [] - cve_ids = set() - high_severity_count = 0 - - for vuln in vulns_it: - vuln_data = { - "id": vuln.id if hasattr(vuln, "id") else None, - } - - # Extract CVE information - if hasattr(vuln, "cve_id"): - vuln_data["cve_id"] = vuln.cve_id - cve_ids.add(vuln.cve_id) - - if hasattr(vuln, "cvss"): - if isinstance(vuln.cvss, dict): - vuln_data["cvss_score"] = vuln.cvss.get("score") - vuln_data["cvss_severity"] = vuln.cvss.get("severity") - if vuln.cvss.get("severity") in ["HIGH", "CRITICAL"]: - high_severity_count += 1 - - if hasattr(vuln, "description"): - vuln_data["description"] = ( - vuln.description[:150] + "..." if len(vuln.description) > 150 else vuln.description - ) - - if hasattr(vuln, "published_date"): - vuln_data["published_date"] = str(vuln.published_date) - - vulnerabilities.append(vuln_data) - - self._add_result( - "VULN_ASSOCIATIONS", - "GET", - f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", - "SUCCESS", - { - "vulnerabilities_count": len(vulnerabilities), - "unique_cves_count": len(cve_ids), - "high_severity_count": high_severity_count, - "cve_ids": list(cve_ids), - "vulnerabilities": vulnerabilities, - }, - ) - - logger.info( - f"Retrieved and processed {len(vulnerabilities)} vulnerability associations ({len(cve_ids)} unique CVEs)" - ) - - except vt.APIError as e: - logger.warning(f"Vulnerability associations not available (may require Premium API): {e}") - self._add_result( - "VULN_ASSOCIATIONS", - "GET", - f"/api/v3/ip_addresses/{self.ip}/vulnerabilities", - "NOT_AVAILABLE", - None, - f"May require Premium API: {str(e)}", - ) - - def run_all_tests(self, test_file_path: Optional[str] = None): - """Run all API tests""" - logger.info("Starting VirusTotal API tests...") - logger.info( - f"Using: domain={self.domain}, ip={self.ip}, url={self.url}, file_hash={self.file_hash}, cve={self.cve}" - ) - - with vt.Client(self.api_key) as client: - # Additional data - FULLY test iterators - logger.info("Testing iterators (comments, passive DNS, vulnerability associations)...") - - # Get comments - default to domain - self.get_comments(client, "domains") # Use plural "domains" - print("Comment (domain):", self.results[-1].response) - time.sleep(0.5) - - # Get vulnerability report - self.get_vulnerability_report(client) - print("VULN REPORT:", self.results[-1].response) - time.sleep(0.5) - - logger.info("All tests completed!") - - # Alternative version that queries based on what's provided: - def run_all_tests_smart(self, test_file_path: Optional[str] = None): - """Run all API tests - intelligently choose entity type""" - logger.info("Starting VirusTotal API tests...") - logger.info( - f"Using: domain={self.domain}, ip={self.ip}, url={self.url}, file_hash={self.file_hash}, cve={self.cve}" - ) - - with vt.Client(self.api_key) as client: - logger.info("Testing iterators (comments, passive DNS, vulnerability associations)...") - - self.scan_url(client) - print("SCAN URL:", self.results[-1].response) - time.sleep(0.5) - - if test_file_path is not None: - self.scan_file(client, test_file_path) - print("SCAN FILE:", self.results[-1].response) - time.sleep(0.5) - - # Determine which entity to query for comments - # Priority: domain > ip > url > file_hash - entity_type = None - entity_name = None - - if self.domain: - entity_type = "domains" - entity_name = self.domain - elif self.ip: - entity_type = "ip_addresses" - entity_name = self.ip - elif self.url: - entity_type = "urls" - entity_name = self.url - elif self.file_hash: - entity_type = "files" - entity_name = self.file_hash - else: - # Use default domain - entity_type = "domains" - entity_name = self.domain - - print(f"Getting comments for {entity_type}: {entity_name}") - self.get_comments(client, entity_type) - print("Comments:", self.results[-1].response) - time.sleep(0.5) - - # Get vulnerability report - # self.get_vulnerability_report(client) - # print("VULN REPORT:", self.results[-1].response) - # time.sleep(0.5) - # logger.info("All tests completed!") - - def save_results(self, output_file: str = "vt_test_results.json"): - """Save test results to JSON file""" - results_dict = [asdict(r) for r in self.results] - - with open(output_file, "w", encoding="utf-8") as f: - json.dump(results_dict, f, indent=2, ensure_ascii=False) - - logger.info(f"Results saved to {output_file}") - - # Print summary - success_count = sum(1 for r in self.results if r.status == "SUCCESS") - error_count = sum(1 for r in self.results if r.status == "ERROR") - not_available_count = sum(1 for r in self.results if r.status == "NOT_AVAILABLE") - - print(f"\n{'='*60}") - print(f"TEST SUMMARY") - print(f"{'='*60}") - print(f"Total tests: {len(self.results)}") - print(f"Successful: {success_count}") - print(f"Failed: {error_count}") - print(f"Not Available (Premium API): {not_available_count}") - print(f"{'='*60}\n") - - -def main(): - """Main execution function""" - # Security check - if API_KEY == "REDACTED": - print("ERROR: Please set VT_API_KEY environment variable") - print("Example: export VT_API_KEY='your_api_key_here'") - return - - # Initialize connector with custom values (or use defaults) - connector = VTAPIConnector( - api_key=API_KEY, - domain="google.com", # Optional: override default - ip="8.8.8.8", # Optional: override default - url="https://www.sekoia.io/en/homepage/", # Optional: override default - file_hash="44d88612fea8a8f36de82e1278abb02f", # Optional: override default - cve="CVE-2021-34527", # Optional: override default - ) - - # Run tests (optionally provide a test file path) - # connector.run_all_tests(test_file_path="upload.png") - # connector.run_all_tests() - connector.run_all_tests_smart("sample.exe") - - # Save results - connector.save_results() - - -if __name__ == "__main__": - main() diff --git a/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json b/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json deleted file mode 100644 index 956cb76d0..000000000 --- a/GoogleThreatIntelligence/googlethreatintelligence/vt_test_results.json +++ /dev/null @@ -1,129 +0,0 @@ -[ - { - "name": "SCAN_URL", - "method": "POST", - "endpoint": "/api/v3/urls", - "status": "SUCCESS", - "response": { - "analysis_stats": "{'malicious': 0, 'suspicious': 0, 'undetected': 30, 'harmless': 68, 'timeout': 0}", - "analysis_results": "{'Artists Against 419': {'method': 'blacklist', 'engine_name': 'Artists Against 419', 'category': 'harmless', 'result': 'clean'}, 'Acronis': {'method': 'blacklist', 'engine_name': 'Acronis', 'category': 'harmless', 'result': 'clean'}, 'Abusix': {'method': 'blacklist', 'engine_name': 'Abusix', 'category': 'harmless', 'result': 'clean'}, 'ADMINUSLabs': {'method': 'blacklist', 'engine_name': 'ADMINUSLabs', 'category': 'harmless', 'result': 'clean'}, 'Lionic': {'method': 'blacklist', 'engine_name': 'Lionic', 'category': 'harmless', 'result': 'clean'}, 'Criminal IP': {'method': 'blacklist', 'engine_name': 'Criminal IP', 'category': 'harmless', 'result': 'clean'}, 'AILabs (MONITORAPP)': {'method': 'blacklist', 'engine_name': 'AILabs (MONITORAPP)', 'category': 'harmless', 'result': 'clean'}, 'AlienVault': {'method': 'blacklist', 'engine_name': 'AlienVault', 'category': 'harmless', 'result': 'clean'}, 'alphaMountain.ai': {'method': 'blacklist', 'engine_name': 'alphaMountain.ai', 'category': 'undetected', 'result': 'unrated'}, 'AlphaSOC': {'method': 'blacklist', 'engine_name': 'AlphaSOC', 'category': 'undetected', 'result': 'unrated'}, 'Antiy-AVL': {'method': 'blacklist', 'engine_name': 'Antiy-AVL', 'category': 'harmless', 'result': 'clean'}, 'ArcSight Threat Intelligence': {'method': 'blacklist', 'engine_name': 'ArcSight Threat Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'AutoShun': {'method': 'blacklist', 'engine_name': 'AutoShun', 'category': 'undetected', 'result': 'unrated'}, 'Axur': {'method': 'blacklist', 'engine_name': 'Axur', 'category': 'undetected', 'result': 'unrated'}, 'benkow.cc': {'method': 'blacklist', 'engine_name': 'benkow.cc', 'category': 'harmless', 'result': 'clean'}, 'Bfore.Ai PreCrime': {'method': 'blacklist', 'engine_name': 'Bfore.Ai PreCrime', 'category': 'undetected', 'result': 'unrated'}, 'BitDefender': {'method': 'blacklist', 'engine_name': 'BitDefender', 'category': 'harmless', 'result': 'clean'}, 'Bkav': {'method': 'blacklist', 'engine_name': 'Bkav', 'category': 'undetected', 'result': 'unrated'}, 'BlockList': {'method': 'blacklist', 'engine_name': 'BlockList', 'category': 'harmless', 'result': 'clean'}, 'Blueliv': {'method': 'blacklist', 'engine_name': 'Blueliv', 'category': 'harmless', 'result': 'clean'}, 'Certego': {'method': 'blacklist', 'engine_name': 'Certego', 'category': 'harmless', 'result': 'clean'}, 'ChainPatrol': {'method': 'blacklist', 'engine_name': 'ChainPatrol', 'category': 'undetected', 'result': 'unrated'}, 'Chong Lua Dao': {'method': 'blacklist', 'engine_name': 'Chong Lua Dao', 'category': 'harmless', 'result': 'clean'}, 'CINS Army': {'method': 'blacklist', 'engine_name': 'CINS Army', 'category': 'harmless', 'result': 'clean'}, 'Snort IP sample list': {'method': 'blacklist', 'engine_name': 'Snort IP sample list', 'category': 'harmless', 'result': 'clean'}, 'Cluster25': {'method': 'blacklist', 'engine_name': 'Cluster25', 'category': 'undetected', 'result': 'unrated'}, 'CMC Threat Intelligence': {'method': 'blacklist', 'engine_name': 'CMC Threat Intelligence', 'category': 'harmless', 'result': 'clean'}, 'Xcitium Verdict Cloud': {'method': 'blacklist', 'engine_name': 'Xcitium Verdict Cloud', 'category': 'harmless', 'result': 'clean'}, 'CRDF': {'method': 'blacklist', 'engine_name': 'CRDF', 'category': 'harmless', 'result': 'clean'}, 'CSIS Security Group': {'method': 'blacklist', 'engine_name': 'CSIS Security Group', 'category': 'undetected', 'result': 'unrated'}, 'Cyan': {'method': 'blacklist', 'engine_name': 'Cyan', 'category': 'undetected', 'result': 'unrated'}, 'Cyble': {'method': 'blacklist', 'engine_name': 'Cyble', 'category': 'harmless', 'result': 'clean'}, 'CyRadar': {'method': 'blacklist', 'engine_name': 'CyRadar', 'category': 'harmless', 'result': 'clean'}, 'desenmascara.me': {'method': 'blacklist', 'engine_name': 'desenmascara.me', 'category': 'harmless', 'result': 'clean'}, 'DNS8': {'method': 'blacklist', 'engine_name': 'DNS8', 'category': 'harmless', 'result': 'clean'}, 'Dr.Web': {'method': 'blacklist', 'engine_name': 'Dr.Web', 'category': 'harmless', 'result': 'clean'}, 'Emsisoft': {'method': 'blacklist', 'engine_name': 'Emsisoft', 'category': 'harmless', 'result': 'clean'}, 'Ermes': {'method': 'blacklist', 'engine_name': 'Ermes', 'category': 'undetected', 'result': 'unrated'}, 'ESET': {'method': 'blacklist', 'engine_name': 'ESET', 'category': 'harmless', 'result': 'clean'}, 'ESTsecurity': {'method': 'blacklist', 'engine_name': 'ESTsecurity', 'category': 'harmless', 'result': 'clean'}, 'EmergingThreats': {'method': 'blacklist', 'engine_name': 'EmergingThreats', 'category': 'harmless', 'result': 'clean'}, 'Feodo Tracker': {'method': 'blacklist', 'engine_name': 'Feodo Tracker', 'category': 'harmless', 'result': 'clean'}, 'Fortinet': {'method': 'blacklist', 'engine_name': 'Fortinet', 'category': 'harmless', 'result': 'clean'}, 'G-Data': {'method': 'blacklist', 'engine_name': 'G-Data', 'category': 'harmless', 'result': 'clean'}, 'Google Safebrowsing': {'method': 'blacklist', 'engine_name': 'Google Safebrowsing', 'category': 'harmless', 'result': 'clean'}, 'GCP Abuse Intelligence': {'method': 'blacklist', 'engine_name': 'GCP Abuse Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'GreenSnow': {'method': 'blacklist', 'engine_name': 'GreenSnow', 'category': 'harmless', 'result': 'clean'}, 'Gridinsoft': {'method': 'blacklist', 'engine_name': 'Gridinsoft', 'category': 'undetected', 'result': 'unrated'}, 'Heimdal Security': {'method': 'blacklist', 'engine_name': 'Heimdal Security', 'category': 'harmless', 'result': 'clean'}, 'Hunt.io Intelligence': {'method': 'blacklist', 'engine_name': 'Hunt.io Intelligence', 'category': 'undetected', 'result': 'unrated'}, 'IPsum': {'method': 'blacklist', 'engine_name': 'IPsum', 'category': 'harmless', 'result': 'clean'}, 'Juniper Networks': {'method': 'blacklist', 'engine_name': 'Juniper Networks', 'category': 'harmless', 'result': 'clean'}, 'Kaspersky': {'method': 'blacklist', 'engine_name': 'Kaspersky', 'category': 'harmless', 'result': 'clean'}, 'Lumu': {'method': 'blacklist', 'engine_name': 'Lumu', 'category': 'undetected', 'result': 'unrated'}, 'Malwared': {'method': 'blacklist', 'engine_name': 'Malwared', 'category': 'harmless', 'result': 'clean'}, 'MalwareURL': {'method': 'blacklist', 'engine_name': 'MalwareURL', 'category': 'undetected', 'result': 'unrated'}, 'MalwarePatrol': {'method': 'blacklist', 'engine_name': 'MalwarePatrol', 'category': 'harmless', 'result': 'clean'}, 'malwares.com URL checker': {'method': 'blacklist', 'engine_name': 'malwares.com URL checker', 'category': 'harmless', 'result': 'clean'}, 'Mimecast': {'method': 'blacklist', 'engine_name': 'Mimecast', 'category': 'undetected', 'result': 'unrated'}, 'Netcraft': {'method': 'blacklist', 'engine_name': 'Netcraft', 'category': 'undetected', 'result': 'unrated'}, 'OpenPhish': {'method': 'blacklist', 'engine_name': 'OpenPhish', 'category': 'harmless', 'result': 'clean'}, '0xSI_f33d': {'method': 'blacklist', 'engine_name': '0xSI_f33d', 'category': 'undetected', 'result': 'unrated'}, 'Phishing Database': {'method': 'blacklist', 'engine_name': 'Phishing Database', 'category': 'harmless', 'result': 'clean'}, 'PhishFort': {'method': 'blacklist', 'engine_name': 'PhishFort', 'category': 'undetected', 'result': 'unrated'}, 'PhishLabs': {'method': 'blacklist', 'engine_name': 'PhishLabs', 'category': 'undetected', 'result': 'unrated'}, 'Phishtank': {'method': 'blacklist', 'engine_name': 'Phishtank', 'category': 'harmless', 'result': 'clean'}, 'PREBYTES': {'method': 'blacklist', 'engine_name': 'PREBYTES', 'category': 'harmless', 'result': 'clean'}, 'PrecisionSec': {'method': 'blacklist', 'engine_name': 'PrecisionSec', 'category': 'undetected', 'result': 'unrated'}, 'Quick Heal': {'method': 'blacklist', 'engine_name': 'Quick Heal', 'category': 'harmless', 'result': 'clean'}, 'Quttera': {'method': 'blacklist', 'engine_name': 'Quttera', 'category': 'harmless', 'result': 'clean'}, 'Rising': {'method': 'blacklist', 'engine_name': 'Rising', 'category': 'harmless', 'result': 'clean'}, 'SafeToOpen': {'method': 'blacklist', 'engine_name': 'SafeToOpen', 'category': 'undetected', 'result': 'unrated'}, 'Sangfor': {'method': 'blacklist', 'engine_name': 'Sangfor', 'category': 'harmless', 'result': 'clean'}, 'Sansec eComscan': {'method': 'blacklist', 'engine_name': 'Sansec eComscan', 'category': 'undetected', 'result': 'unrated'}, 'Scantitan': {'method': 'blacklist', 'engine_name': 'Scantitan', 'category': 'harmless', 'result': 'clean'}, 'SCUMWARE.org': {'method': 'blacklist', 'engine_name': 'SCUMWARE.org', 'category': 'harmless', 'result': 'clean'}, 'Seclookup': {'method': 'blacklist', 'engine_name': 'Seclookup', 'category': 'harmless', 'result': 'clean'}, 'SOCRadar': {'method': 'blacklist', 'engine_name': 'SOCRadar', 'category': 'undetected', 'result': 'unrated'}, 'Sophos': {'method': 'blacklist', 'engine_name': 'Sophos', 'category': 'harmless', 'result': 'clean'}, 'Spam404': {'method': 'blacklist', 'engine_name': 'Spam404', 'category': 'harmless', 'result': 'clean'}, 'StopForumSpam': {'method': 'blacklist', 'engine_name': 'StopForumSpam', 'category': 'harmless', 'result': 'clean'}, 'Sucuri SiteCheck': {'method': 'blacklist', 'engine_name': 'Sucuri SiteCheck', 'category': 'harmless', 'result': 'clean'}, 'securolytics': {'method': 'blacklist', 'engine_name': 'securolytics', 'category': 'harmless', 'result': 'clean'}, 'Threatsourcing': {'method': 'blacklist', 'engine_name': 'Threatsourcing', 'category': 'harmless', 'result': 'clean'}, 'ThreatHive': {'method': 'blacklist', 'engine_name': 'ThreatHive', 'category': 'harmless', 'result': 'clean'}, 'Trustwave': {'method': 'blacklist', 'engine_name': 'Trustwave', 'category': 'harmless', 'result': 'clean'}, 'Underworld': {'method': 'blacklist', 'engine_name': 'Underworld', 'category': 'undetected', 'result': 'unrated'}, 'URLhaus': {'method': 'blacklist', 'engine_name': 'URLhaus', 'category': 'harmless', 'result': 'clean'}, 'URLQuery': {'method': 'blacklist', 'engine_name': 'URLQuery', 'category': 'undetected', 'result': 'unrated'}, 'Viettel Threat Intelligence': {'method': 'blacklist', 'engine_name': 'Viettel Threat Intelligence', 'category': 'harmless', 'result': 'clean'}, 'VIPRE': {'method': 'blacklist', 'engine_name': 'VIPRE', 'category': 'undetected', 'result': 'unrated'}, 'ViriBack': {'method': 'blacklist', 'engine_name': 'ViriBack', 'category': 'harmless', 'result': 'clean'}, 'VX Vault': {'method': 'blacklist', 'engine_name': 'VX Vault', 'category': 'harmless', 'result': 'clean'}, 'Webroot': {'method': 'blacklist', 'engine_name': 'Webroot', 'category': 'harmless', 'result': 'clean'}, 'Forcepoint ThreatSeeker': {'method': 'blacklist', 'engine_name': 'Forcepoint ThreatSeeker', 'category': 'harmless', 'result': 'clean'}, 'Yandex Safebrowsing': {'method': 'blacklist', 'engine_name': 'Yandex Safebrowsing', 'category': 'harmless', 'result': 'clean'}, 'ZeroCERT': {'method': 'blacklist', 'engine_name': 'ZeroCERT', 'category': 'harmless', 'result': 'clean'}, 'ZeroFox': {'method': 'blacklist', 'engine_name': 'ZeroFox', 'category': 'undetected', 'result': 'unrated'}}", - "url": "https://www.sekoia.io/en/homepage/" - }, - "error": null - }, - { - "name": "SCAN_FILE", - "method": "POST", - "endpoint": "/api/v3/files", - "status": "SUCCESS", - "response": { - "analysis_stats": "{'malicious': 0, 'suspicious': 0, 'undetected': 61, 'harmless': 0, 'timeout': 1, 'confirmed-timeout': 1, 'failure': 1, 'type-unsupported': 12}", - "analysis_results": "{'Bkav': {'method': 'blacklist', 'engine_name': 'Bkav', 'engine_version': '2.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Lionic': {'method': 'blacklist', 'engine_name': 'Lionic', 'engine_version': '8.16', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ClamAV': {'method': 'blacklist', 'engine_name': 'ClamAV', 'engine_version': '1.5.1.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'CMC': {'method': 'blacklist', 'engine_name': 'CMC', 'engine_version': '2.4.2022.1', 'engine_update': '20251115', 'category': 'undetected', 'result': None}, 'CAT-QuickHeal': {'method': 'blacklist', 'engine_name': 'CAT-QuickHeal', 'engine_version': '22.00', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Skyhigh': {'method': 'blacklist', 'engine_name': 'Skyhigh', 'engine_version': 'v2021.2.0+4045', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ALYac': {'method': 'blacklist', 'engine_name': 'ALYac', 'engine_version': '2.0.0.10', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Malwarebytes': {'method': 'blacklist', 'engine_name': 'Malwarebytes', 'engine_version': '3.1.0.176', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'VIPRE': {'method': 'blacklist', 'engine_name': 'VIPRE', 'engine_version': '6.0.0.35', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Sangfor': {'method': 'blacklist', 'engine_name': 'Sangfor', 'engine_version': '2.22.3.0', 'engine_update': '20251117', 'category': 'undetected', 'result': None}, 'K7AntiVirus': {'method': 'blacklist', 'engine_name': 'K7AntiVirus', 'engine_version': '14.19.57742', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'K7GW': {'method': 'blacklist', 'engine_name': 'K7GW', 'engine_version': '14.19.57743', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'CrowdStrike': {'method': 'blacklist', 'engine_name': 'CrowdStrike', 'engine_version': '1.0', 'engine_update': '20230417', 'category': 'undetected', 'result': None}, 'Arcabit': {'method': 'blacklist', 'engine_name': 'Arcabit', 'engine_version': '2025.0.0.23', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Baidu': {'method': 'blacklist', 'engine_name': 'Baidu', 'engine_version': '1.0.0.2', 'engine_update': '20190318', 'category': 'undetected', 'result': None}, 'VirIT': {'method': 'blacklist', 'engine_name': 'VirIT', 'engine_version': '9.5.1089', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Paloalto': {'method': 'blacklist', 'engine_name': 'Paloalto', 'engine_version': '0.9.0.1003', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Symantec': {'method': 'blacklist', 'engine_name': 'Symantec', 'engine_version': '1.22.0.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ESET-NOD32': {'method': 'blacklist', 'engine_name': 'ESET-NOD32', 'engine_version': '18.2.18.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'APEX': {'method': 'blacklist', 'engine_name': 'APEX', 'engine_version': '6.717', 'engine_update': '20251117', 'category': 'undetected', 'result': None}, 'Avast': {'method': 'blacklist', 'engine_name': 'Avast', 'engine_version': '23.9.8494.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Cynet': {'method': 'blacklist', 'engine_name': 'Cynet', 'engine_version': '4.0.3.4', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Kaspersky': {'method': 'blacklist', 'engine_name': 'Kaspersky', 'engine_version': '22.0.1.28', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'BitDefender': {'method': 'blacklist', 'engine_name': 'BitDefender', 'engine_version': '7.2', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'NANO-Antivirus': {'method': 'blacklist', 'engine_name': 'NANO-Antivirus', 'engine_version': '1.0.170.26895', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'SUPERAntiSpyware': {'method': 'blacklist', 'engine_name': 'SUPERAntiSpyware', 'engine_version': '5.6.0.1032', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'MicroWorld-eScan': {'method': 'blacklist', 'engine_name': 'MicroWorld-eScan', 'engine_version': '14.0.409.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Rising': {'method': 'blacklist', 'engine_name': 'Rising', 'engine_version': '25.0.0.28', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Sophos': {'method': 'blacklist', 'engine_name': 'Sophos', 'engine_version': '3.3.1.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'F-Secure': {'method': 'blacklist', 'engine_name': 'F-Secure', 'engine_version': '18.10.1547.307', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'DrWeb': {'method': 'blacklist', 'engine_name': 'DrWeb', 'engine_version': '7.0.72.9030', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Zillya': {'method': 'blacklist', 'engine_name': 'Zillya', 'engine_version': '2.0.0.5488', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'TrendMicro': {'method': 'blacklist', 'engine_name': 'TrendMicro', 'engine_version': '24.550.0.1002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'McAfeeD': {'method': 'blacklist', 'engine_name': 'McAfeeD', 'engine_version': '1.2.0.10275', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Emsisoft': {'method': 'blacklist', 'engine_name': 'Emsisoft', 'engine_version': '2024.8.0.61147', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Ikarus': {'method': 'blacklist', 'engine_name': 'Ikarus', 'engine_version': '6.4.16.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Jiangmin': {'method': 'blacklist', 'engine_name': 'Jiangmin', 'engine_version': '16.0.100', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Google': {'method': 'blacklist', 'engine_name': 'Google', 'engine_version': '1763712050', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Avira': {'method': 'blacklist', 'engine_name': 'Avira', 'engine_version': '8.3.3.24', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Antiy-AVL': {'method': 'blacklist', 'engine_name': 'Antiy-AVL', 'engine_version': '3.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Kingsoft': {'method': 'blacklist', 'engine_name': 'Kingsoft', 'engine_version': 'None', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Xcitium': {'method': 'blacklist', 'engine_name': 'Xcitium', 'engine_version': '38208', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Microsoft': {'method': 'blacklist', 'engine_name': 'Microsoft', 'engine_version': '1.1.25100.9002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ViRobot': {'method': 'blacklist', 'engine_name': 'ViRobot', 'engine_version': '2014.3.20.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'ZoneAlarm': {'method': 'blacklist', 'engine_name': 'ZoneAlarm', 'engine_version': '6.21-110946643', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'GData': {'method': 'blacklist', 'engine_name': 'GData', 'engine_version': 'GD:27.42591AVA:64.30179', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Varist': {'method': 'blacklist', 'engine_name': 'Varist', 'engine_version': '6.6.1.3', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'AhnLab-V3': {'method': 'blacklist', 'engine_name': 'AhnLab-V3', 'engine_version': '3.29.0.10596', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'VBA32': {'method': 'blacklist', 'engine_name': 'VBA32', 'engine_version': '5.4.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TACHYON': {'method': 'blacklist', 'engine_name': 'TACHYON', 'engine_version': '2025-11-21.02', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Zoner': {'method': 'blacklist', 'engine_name': 'Zoner', 'engine_version': '2.2.2.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TrendMicro-HouseCall': {'method': 'blacklist', 'engine_name': 'TrendMicro-HouseCall', 'engine_version': '24.550.0.1002', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Tencent': {'method': 'blacklist', 'engine_name': 'Tencent', 'engine_version': '1.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Yandex': {'method': 'blacklist', 'engine_name': 'Yandex', 'engine_version': '5.5.2.24', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'TrellixENS': {'method': 'blacklist', 'engine_name': 'TrellixENS', 'engine_version': '6.0.6.653', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'huorong': {'method': 'blacklist', 'engine_name': 'huorong', 'engine_version': 'e496057:e496057:f6223dc:f6223dc', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'MaxSecure': {'method': 'blacklist', 'engine_name': 'MaxSecure', 'engine_version': '1.0.0.1', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'Fortinet': {'method': 'blacklist', 'engine_name': 'Fortinet', 'engine_version': '7.0.30.0', 'engine_update': '20251121', 'category': 'undetected', 'result': None}, 'AVG': {'method': 'blacklist', 'engine_name': 'AVG', 'engine_version': '23.9.8494.0', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'Panda': {'method': 'blacklist', 'engine_name': 'Panda', 'engine_version': '4.6.4.2', 'engine_update': '20251120', 'category': 'undetected', 'result': None}, 'alibabacloud': {'method': 'blacklist', 'engine_name': 'alibabacloud', 'engine_version': '2.2.0', 'engine_update': '20250321', 'category': 'undetected', 'result': None}, 'Trustlook': {'method': 'blacklist', 'engine_name': 'Trustlook', 'engine_version': '1.0', 'engine_update': '20251121', 'category': 'timeout', 'result': None}, 'CTX': {'method': 'blacklist', 'engine_name': 'CTX', 'engine_version': '2024.8.29.1', 'engine_update': '20251121', 'category': 'confirmed-timeout', 'result': None}, 'Gridinsoft': {'method': 'blacklist', 'engine_name': 'Gridinsoft', 'engine_version': '1.0.229.174', 'engine_update': '20251121', 'category': 'failure', 'result': None}, 'Avast-Mobile': {'method': 'blacklist', 'engine_name': 'Avast-Mobile', 'engine_version': '251120-00', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}, 'SymantecMobileInsight': {'method': 'blacklist', 'engine_name': 'SymantecMobileInsight', 'engine_version': '2.0', 'engine_update': '20250124', 'category': 'type-unsupported', 'result': None}, 'BitDefenderFalx': {'method': 'blacklist', 'engine_name': 'BitDefenderFalx', 'engine_version': '2.0.936', 'engine_update': '20250416', 'category': 'type-unsupported', 'result': None}, 'DeepInstinct': {'method': 'blacklist', 'engine_name': 'DeepInstinct', 'engine_version': '5.0.0.8', 'engine_update': '20251121', 'category': 'type-unsupported', 'result': None}, 'Acronis': {'method': 'blacklist', 'engine_name': 'Acronis', 'engine_version': '1.2.0.121', 'engine_update': '20240328', 'category': 'type-unsupported', 'result': None}, 'Trapmine': {'method': 'blacklist', 'engine_name': 'Trapmine', 'engine_version': '4.0.6.0', 'engine_update': '20251023', 'category': 'type-unsupported', 'result': None}, 'Alibaba': {'method': 'blacklist', 'engine_name': 'Alibaba', 'engine_version': '0.3.0.5', 'engine_update': '20190527', 'category': 'type-unsupported', 'result': None}, 'Webroot': {'method': 'blacklist', 'engine_name': 'Webroot', 'engine_version': '1.9.0.8', 'engine_update': '20250227', 'category': 'type-unsupported', 'result': None}, 'Cylance': {'method': 'blacklist', 'engine_name': 'Cylance', 'engine_version': '3.0.0.0', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}, 'SentinelOne': {'method': 'blacklist', 'engine_name': 'SentinelOne', 'engine_version': '7.4.1.3', 'engine_update': '20251013', 'category': 'type-unsupported', 'result': None}, 'tehtris': {'method': 'blacklist', 'engine_name': 'tehtris', 'engine_version': None, 'engine_update': '20251121', 'category': 'type-unsupported', 'result': None}, 'Elastic': {'method': 'blacklist', 'engine_name': 'Elastic', 'engine_version': '4.0.238', 'engine_update': '20251120', 'category': 'type-unsupported', 'result': None}}", - "file": "sample.exe" - }, - "error": null - }, - { - "name": "GET_COMMENTS", - "method": "GET", - "endpoint": "/api/v3/domains/google.com/comments", - "status": "SUCCESS", - "response": { - "comments_count": 10, - "entity": "google.com", - "comments": [ - { - "text": "[`GOOGLEEDGEAi.ONMiCROSOFT.COM`](https://cloud.google.com/docs)", - "date": "2025-10-10 21:46:07+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "Not so sure about this one", - "date": "2025-09-23 15:48:32+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "Test comment", - "date": "2025-09-05 14:57:11+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "This site impersonates the official Bibit website, disseminating false customer service numbers to deceive customers. Fraudulent agents then solicit personal data, passwords, and OTPs, leading to account takeovers and financial losses. Authentic Bibit customer support is exclusively available at https://bibit.id/. We request the immediate takedown of this fraudulent site to prevent further harm.", - "date": "2025-09-02 04:01:21+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "Search engine.", - "date": "2025-08-25 22:19:40+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "🔍 Collection: LIVE VT Upload Demo | 📝 Description: 🎯 LIVE VT UPLOAD TEST via caz-vt-feeder - Domain Comment Added | 🏷️ Tags: live-upload-test, caz-vt-feeder | 📡 Source: caz-vt-feeder-system", - "date": "2025-07-06 23:59:44+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "🔍 Collection: Live Test Upload | 📝 Description: Test domain for VT comment upload | 🏷️ Tags: test, domain | 📡 Source: caz-vt-feeder-live-test", - "date": "2025-07-06 23:53:53+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "🔍 Collection: Test Official Upload | 📝 Description: Test domain upload | 🏷️ Tags: test, domain | 📡 Source: caz-vt-feeder-test", - "date": "2025-07-06 23:49:59+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "VIRUSSSSSSSVIRUSSSS GOOGLE BAD", - "date": "2025-06-12 23:53:06+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - }, - { - "text": "it's google!!!", - "date": "2025-06-10 20:11:20+00:00", - "votes": { - "positive": 0, - "negative": 0 - }, - "author": null - } - ] - }, - "error": null - } -] \ No newline at end of file From b97c29ce8d22ed50c552a42cc92f3846056b3b50 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Wed, 3 Dec 2025 18:09:15 +0000 Subject: [PATCH 3/8] fix issues --- .../action_get_comments.json | 62 ++++++-- .../action_get_file_behaviour.json | 33 ++++- .../action_get_ioc_report.json | 56 +++++-- .../action_get_passive_dns.json | 37 ++++- ...action_get_vulnerability_associations.json | 83 +++++++++-- .../action_get_vulnerability_report.json | 36 ++++- .../action_scan_file.json | 85 ++++++++--- GoogleThreatIntelligence/action_scan_url.json | 85 ++++++++--- .../googlethreatintelligence/client.py | 71 +++------ .../googlethreatintelligence/get_comments.py | 3 - .../get_vulnerability_associations.py | 3 +- .../googlethreatintelligence/scan_file.py | 1 - .../googlethreatintelligence/scan_url.py | 1 - GoogleThreatIntelligence/logo.png | Bin 0 -> 3276 bytes GoogleThreatIntelligence/logo.svg | 1 - GoogleThreatIntelligence/manifest.json | 6 +- .../tests/client/conftest.py | 21 +++ .../tests/client/test_analysis.py | 29 ++++ .../tests/client/test_api_key_import.py | 23 +++ .../tests/client/test_connectivity.py | 35 +++++ .../tests/client/test_file_behaviour.py | 138 ++++++++++++++++++ .../tests/client/test_ioc_attributes.py | 25 ++++ .../tests/client/test_ioc_reports.py | 51 +++++++ .../tests/client/test_scan.py | 65 +++++++++ 24 files changed, 791 insertions(+), 159 deletions(-) create mode 100644 GoogleThreatIntelligence/logo.png delete mode 100644 GoogleThreatIntelligence/logo.svg create mode 100644 GoogleThreatIntelligence/tests/client/conftest.py create mode 100644 GoogleThreatIntelligence/tests/client/test_analysis.py create mode 100644 GoogleThreatIntelligence/tests/client/test_api_key_import.py create mode 100644 GoogleThreatIntelligence/tests/client/test_connectivity.py create mode 100644 GoogleThreatIntelligence/tests/client/test_file_behaviour.py create mode 100644 GoogleThreatIntelligence/tests/client/test_ioc_attributes.py create mode 100644 GoogleThreatIntelligence/tests/client/test_ioc_reports.py create mode 100644 GoogleThreatIntelligence/tests/client/test_scan.py diff --git a/GoogleThreatIntelligence/action_get_comments.json b/GoogleThreatIntelligence/action_get_comments.json index 5c048c034..c600d6a8d 100644 --- a/GoogleThreatIntelligence/action_get_comments.json +++ b/GoogleThreatIntelligence/action_get_comments.json @@ -29,10 +29,26 @@ } }, "oneOf": [ - {"required": ["ip"]}, - {"required": ["domain"]}, - {"required": ["url"]}, - {"required": ["file_hash"]} + { + "required": [ + "ip" + ] + }, + { + "required": [ + "domain" + ] + }, + { + "required": [ + "url" + ] + }, + { + "required": [ + "file_hash" + ] + } ] }, "results": { @@ -40,7 +56,9 @@ "type": "object", "title": "Get Comments Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "Container for the comments data", @@ -62,7 +80,9 @@ "type": { "type": "string", "description": "Type of the object (always 'comment')", - "enum": ["comment"] + "enum": [ + "comment" + ] }, "id": { "type": "string", @@ -73,7 +93,10 @@ "description": "The comment text content" }, "date": { - "type": ["integer", "string"], + "type": [ + "integer", + "string" + ], "description": "Comment timestamp (Unix timestamp or ISO 8601 string)" }, "votes": { @@ -89,21 +112,34 @@ "description": "Number of negative votes" } }, - "required": ["positive", "negative"] + "required": [ + "positive", + "negative" + ] }, "author": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "Username of the comment author (may be null for anonymous)" } }, - "required": ["text", "date", "votes"] + "required": [ + "text", + "date", + "votes" + ] } } }, - "required": ["comments_count", "entity", "comments"] + "required": [ + "comments_count", + "entity", + "comments" + ] } - } }, "slug": "get_comments_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_file_behaviour.json b/GoogleThreatIntelligence/action_get_file_behaviour.json index acfa048c5..e270fdc8e 100644 --- a/GoogleThreatIntelligence/action_get_file_behaviour.json +++ b/GoogleThreatIntelligence/action_get_file_behaviour.json @@ -13,14 +13,18 @@ "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" } }, - "required": ["file_hash"] + "required": [ + "file_hash" + ] }, "results": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Get File Behaviour Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "Container for the file behaviour data", @@ -42,7 +46,9 @@ "type": { "type": "string", "description": "Type of the object (always 'file_behaviour')", - "enum": ["file_behaviour"] + "enum": [ + "file_behaviour" + ] }, "id": { "type": "string", @@ -176,7 +182,11 @@ "transport_layer_protocol": { "type": "string", "description": "Protocol used (TCP/UDP)", - "enum": ["TCP", "UDP", "ICMP"] + "enum": [ + "TCP", + "UDP", + "ICMP" + ] } } } @@ -238,13 +248,22 @@ } } }, - "required": ["type", "id", "sandbox_name", "analysis_date"] + "required": [ + "type", + "id", + "sandbox_name", + "analysis_date" + ] } } }, - "required": ["behaviours_count", "file_hash", "behaviours"] + "required": [ + "behaviours_count", + "file_hash", + "behaviours" + ] } } }, "slug": "get_file_behaviour_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_ioc_report.json b/GoogleThreatIntelligence/action_get_ioc_report.json index ac5097ce2..509997786 100644 --- a/GoogleThreatIntelligence/action_get_ioc_report.json +++ b/GoogleThreatIntelligence/action_get_ioc_report.json @@ -30,10 +30,26 @@ } }, "oneOf": [ - {"required": ["ip"]}, - {"required": ["domain"]}, - {"required": ["url"]}, - {"required": ["file_hash"]} + { + "required": [ + "ip" + ] + }, + { + "required": [ + "domain" + ] + }, + { + "required": [ + "url" + ] + }, + { + "required": [ + "file_hash" + ] + } ] }, "results": { @@ -69,20 +85,36 @@ "type": "object", "description": "Statistics from last analysis", "properties": { - "harmless": {"type": "integer"}, - "malicious": {"type": "integer"}, - "suspicious": {"type": "integer"}, - "undetected": {"type": "integer"} + "harmless": { + "type": "integer" + }, + "malicious": { + "type": "integer" + }, + "suspicious": { + "type": "integer" + }, + "undetected": { + "type": "integer" + } } }, "detections": { "type": "object", "description": "Detection summary", "properties": { - "harmless": {"type": "integer"}, - "malicious": {"type": "integer"}, - "suspicious": {"type": "integer"}, - "undetected": {"type": "integer"} + "harmless": { + "type": "integer" + }, + "malicious": { + "type": "integer" + }, + "suspicious": { + "type": "integer" + }, + "undetected": { + "type": "integer" + } } }, "country": { diff --git a/GoogleThreatIntelligence/action_get_passive_dns.json b/GoogleThreatIntelligence/action_get_passive_dns.json index ada0bec1a..dec8e33c8 100644 --- a/GoogleThreatIntelligence/action_get_passive_dns.json +++ b/GoogleThreatIntelligence/action_get_passive_dns.json @@ -13,14 +13,18 @@ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" } }, - "required": ["domain"] + "required": [ + "domain" + ] }, "results": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Passive DNS Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "Container for passive DNS resolution data", @@ -44,7 +48,10 @@ "entity_type": { "type": "string", "description": "Type of entity queried", - "enum": ["domains", "ip_addresses"] + "enum": [ + "domains", + "ip_addresses" + ] }, "unique_ips": { "type": "array", @@ -69,7 +76,9 @@ "type": { "type": "string", "description": "Type of the object (always 'resolution')", - "enum": ["resolution"] + "enum": [ + "resolution" + ] }, "id": { "type": "string", @@ -85,7 +94,10 @@ "description": "The domain name associated with the IP" }, "date": { - "type": ["integer", "string"], + "type": [ + "integer", + "string" + ], "description": "Timestamp when the resolution was observed (Unix timestamp or ISO 8601 string)" }, "resolver": { @@ -145,7 +157,11 @@ } } }, - "required": ["type", "id", "date"] + "required": [ + "type", + "id", + "date" + ] } }, "links": { @@ -163,9 +179,14 @@ } } }, - "required": ["resolutions_count", "entity", "entity_type", "resolutions"] + "required": [ + "resolutions_count", + "entity", + "entity_type", + "resolutions" + ] } } }, "slug": "get_passive_dns_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_vulnerability_associations.json b/GoogleThreatIntelligence/action_get_vulnerability_associations.json index 73f2a49e6..1ca27d389 100644 --- a/GoogleThreatIntelligence/action_get_vulnerability_associations.json +++ b/GoogleThreatIntelligence/action_get_vulnerability_associations.json @@ -29,10 +29,26 @@ } }, "oneOf": [ - {"required": ["ip"]}, - {"required": ["domain"]}, - {"required": ["url"]}, - {"required": ["file_hash"]} + { + "required": [ + "ip" + ] + }, + { + "required": [ + "domain" + ] + }, + { + "required": [ + "url" + ] + }, + { + "required": [ + "file_hash" + ] + } ] }, "results": { @@ -40,7 +56,9 @@ "type": "object", "title": "Vulnerability Associations Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "Container for vulnerability association data", @@ -64,7 +82,11 @@ "entity_type": { "type": "string", "description": "Type of entity queried", - "enum": ["ip_addresses", "domains", "files"] + "enum": [ + "ip_addresses", + "domains", + "files" + ] }, "cve_ids": { "type": "array", @@ -83,7 +105,9 @@ "type": { "type": "string", "description": "Type of the object (always 'vulnerability')", - "enum": ["vulnerability"] + "enum": [ + "vulnerability" + ] }, "id": { "type": "string", @@ -103,11 +127,17 @@ "description": "Detailed description of the vulnerability" }, "published_date": { - "type": ["integer", "string"], + "type": [ + "integer", + "string" + ], "description": "Date when the vulnerability was published (Unix timestamp or ISO 8601 string)" }, "last_modification_date": { - "type": ["integer", "string"], + "type": [ + "integer", + "string" + ], "description": "Date of the last modification to the vulnerability record" }, "cvss": { @@ -123,7 +153,13 @@ "severity": { "type": "string", "description": "Severity rating based on CVSS score", - "enum": ["NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL"] + "enum": [ + "NONE", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ] }, "vector_string": { "type": "string", @@ -132,7 +168,12 @@ "version": { "type": "string", "description": "CVSS version used", - "enum": ["2.0", "3.0", "3.1", "4.0"] + "enum": [ + "2.0", + "3.0", + "3.1", + "4.0" + ] }, "base_metrics": { "type": "object", @@ -173,7 +214,10 @@ } } }, - "required": ["score", "severity"] + "required": [ + "score", + "severity" + ] }, "cvss_v2": { "type": "object", @@ -279,7 +323,11 @@ } } }, - "required": ["type", "id", "cve_id"] + "required": [ + "type", + "id", + "cve_id" + ] } }, "links": { @@ -297,9 +345,14 @@ } } }, - "required": ["vulnerabilities_count", "entity", "entity_type", "vulnerabilities"] + "required": [ + "vulnerabilities_count", + "entity", + "entity_type", + "vulnerabilities" + ] } } }, "slug": "get_vulnerability_associations_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_vulnerability_report.json b/GoogleThreatIntelligence/action_get_vulnerability_report.json index 876c06f16..beed4ab9d 100644 --- a/GoogleThreatIntelligence/action_get_vulnerability_report.json +++ b/GoogleThreatIntelligence/action_get_vulnerability_report.json @@ -12,14 +12,18 @@ "description": "CVE query (e.g. CVE-2021-34527)" } }, - "required": ["cve"] + "required": [ + "cve" + ] }, "results": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Vulnerability Report Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "properties": { @@ -53,12 +57,23 @@ }, "risk_rating": { "type": "string", - "enum": ["Critical", "High", "Medium", "Low"], + "enum": [ + "Critical", + "High", + "Medium", + "Low" + ], "description": "Google TI's assessment of vulnerability severity" }, "exploitation_state": { "type": "string", - "enum": ["Wide", "Confirmed", "Reported", "Suspected", "No Known"], + "enum": [ + "Wide", + "Confirmed", + "Reported", + "Suspected", + "No Known" + ], "description": "Current exploitation status based on GTI intelligence" }, "exploit_availability": { @@ -133,7 +148,9 @@ }, "tags": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Tags associated with the vulnerability" }, "first_published_date": { @@ -145,9 +162,14 @@ "description": "Organization that curated the intelligence (e.g., 'Google Threat Intelligence')" } }, - "required": ["id", "type", "name", "description"] + "required": [ + "id", + "type", + "name", + "description" + ] } } }, "slug": "get_vulnerability_report_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_scan_file.json b/GoogleThreatIntelligence/action_scan_file.json index ea7137227..707744c1e 100644 --- a/GoogleThreatIntelligence/action_scan_file.json +++ b/GoogleThreatIntelligence/action_scan_file.json @@ -12,14 +12,18 @@ "description": "Local path to the file to scan" } }, - "required": ["file_path"] + "required": [ + "file_path" + ] }, "results": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Scan File Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "VirusTotal FILE analysis result", @@ -32,14 +36,30 @@ "type": "object", "description": "Summary statistics of the FILE scan", "properties": { - "harmless": { "type": "integer" }, - "malicious": { "type": "integer" }, - "suspicious": { "type": "integer" }, - "undetected": { "type": "integer" }, - "timeout": { "type": "integer" }, - "failure": { "type": "integer" }, - "confirmed-timeout": { "type": "integer" }, - "type-unsupported": { "type": "integer" } + "harmless": { + "type": "integer" + }, + "malicious": { + "type": "integer" + }, + "suspicious": { + "type": "integer" + }, + "undetected": { + "type": "integer" + }, + "timeout": { + "type": "integer" + }, + "failure": { + "type": "integer" + }, + "confirmed-timeout": { + "type": "integer" + }, + "type-unsupported": { + "type": "integer" + } }, "required": [ "harmless", @@ -71,19 +91,48 @@ "type-unsupported" ] }, - "engine_name": { "type": "string" }, - "engine_version": { "type": ["string", "null"] }, - "engine_update": { "type": ["string", "null"] }, - "method": { "type": ["string", "null"] }, - "result": { "type": ["string", "null"] } + "engine_name": { + "type": "string" + }, + "engine_version": { + "type": [ + "string", + "null" + ] + }, + "engine_update": { + "type": [ + "string", + "null" + ] + }, + "method": { + "type": [ + "string", + "null" + ] + }, + "result": { + "type": [ + "string", + "null" + ] + } }, - "required": ["category", "engine_name"] + "required": [ + "category", + "engine_name" + ] } } }, - "required": ["file_path", "analysis_stats", "analysis_results"] + "required": [ + "file_path", + "analysis_stats", + "analysis_results" + ] } } }, "slug": "scan_file_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_scan_url.json b/GoogleThreatIntelligence/action_scan_url.json index 8ca814c2f..109fa099c 100644 --- a/GoogleThreatIntelligence/action_scan_url.json +++ b/GoogleThreatIntelligence/action_scan_url.json @@ -12,14 +12,18 @@ "description": "URL to scan" } }, - "required": ["url"] + "required": [ + "url" + ] }, "results": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Scan URL Results", "properties": { - "success": { "type": "boolean" }, + "success": { + "type": "boolean" + }, "data": { "type": "object", "description": "VirusTotal URL analysis result", @@ -32,14 +36,30 @@ "type": "object", "description": "Summary statistics of the URL scan", "properties": { - "harmless": { "type": "integer" }, - "malicious": { "type": "integer" }, - "suspicious": { "type": "integer" }, - "undetected": { "type": "integer" }, - "timeout": { "type": "integer" }, - "failure": { "type": "integer" }, - "confirmed-timeout": { "type": "integer" }, - "type-unsupported": { "type": "integer" } + "harmless": { + "type": "integer" + }, + "malicious": { + "type": "integer" + }, + "suspicious": { + "type": "integer" + }, + "undetected": { + "type": "integer" + }, + "timeout": { + "type": "integer" + }, + "failure": { + "type": "integer" + }, + "confirmed-timeout": { + "type": "integer" + }, + "type-unsupported": { + "type": "integer" + } }, "required": [ "harmless", @@ -71,19 +91,48 @@ "type-unsupported" ] }, - "engine_name": { "type": "string" }, - "engine_version": { "type": ["string", "null"] }, - "engine_update": { "type": ["string", "null"] }, - "method": { "type": ["string", "null"] }, - "result": { "type": ["string", "null"] } + "engine_name": { + "type": "string" + }, + "engine_version": { + "type": [ + "string", + "null" + ] + }, + "engine_update": { + "type": [ + "string", + "null" + ] + }, + "method": { + "type": [ + "string", + "null" + ] + }, + "result": { + "type": [ + "string", + "null" + ] + } }, - "required": ["category", "engine_name"] + "required": [ + "category", + "engine_name" + ] } } }, - "required": ["url", "analysis_stats", "analysis_results"] + "required": [ + "url", + "analysis_stats", + "analysis_results" + ] } } }, "slug": "scan_url_in_gti" -} +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/googlethreatintelligence/client.py b/GoogleThreatIntelligence/googlethreatintelligence/client.py index cedecc8a5..f6d61d03b 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/client.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/client.py @@ -1,29 +1,3 @@ -""" -Google Threat Intelligence (VirusTotal) API Testing Script -Uses vt-py official library with comprehensive error handling - -ACTIONS MAPPING (from requirements): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Action | API Endpoint | Method(s) -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Scan File | POST /api/v3/files | scan_file() - | POST /api/v3/private/files | -Get IOC Report | GET /api/v3/{entity_type}/{entity} | get_ioc_report() - - IP address | GET /api/v3/ip_addresses/{ip} | get_ip_report() - - URL | GET /api/v3/urls/{url_id} | get_url_report() - - Domain | GET /api/v3/domains/{domain} | get_domain_report() - - File | GET /api/v3/files/{hash} | get_file_report() -Get Comments | GET /api/v3/{entity_type}/{entity}/comments | get_comments() -Get Vulnerability Associations | GET /api/v3/{entity_type}/{entity}/vulnerabilities | get_vulnerability_associations() -Get File Sandbox Report | GET /api/v3/files/{hash}/behaviours | get_file_behaviour() -Scan URL | POST /api/v3/urls | scan_url() - | POST /api/v3/private/urls | -Get Curated Associations | GET /api/v3/{entity_type}/{entity} | get_ioc_report() -Get Passive DNS Data | GET /api/v3/{entity_type}/{entity}/resolutions | get_passive_dns() -Get Vulnerability Report | GET /api/v3/collections/{vuln_id} | get_vulnerability_report() -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -""" - import vt import base64 from pathlib import Path @@ -210,9 +184,6 @@ def scan_url(self, client: vt.Client): """ try: analysis = client.scan_url(self.url, wait_for_completion=True) - print("Analysis completed") - # print(analysis.stats) - # print(analysis.results) self._add_result( "SCAN_URL", "POST", @@ -233,7 +204,6 @@ def scan_file(self, client: vt.Client, file_path: str): with open(file_path, "rb") as f: analysis = client.scan_file(f, wait_for_completion=True) - print("Analysis completed") self._add_result( "SCAN_FILE", @@ -263,29 +233,30 @@ def get_analysis(self, client: vt.Client, analysis_id: str): def get_file_behaviour(self, client: vt.Client): """Get file sandbox behavior""" try: - # Use iterator for behaviours and FULLY consume it behaviours_it = client.iterator(f"/files/{self.file_hash}/behaviours", limit=5) - # IMPORTANT: Fully consume the iterator to test it properly behaviours = [] for behaviour in behaviours_it: - behaviour_data = { - "sandbox_name": behaviour.sandbox_name if hasattr(behaviour, "sandbox_name") else None, - } - # Extract detailed behaviour information - if hasattr(behaviour, "processes_created"): - behaviour_data["processes_created"] = len(behaviour.processes_created) - if hasattr(behaviour, "files_written"): - behaviour_data["files_written"] = len(behaviour.files_written) - if hasattr(behaviour, "files_deleted"): - behaviour_data["files_deleted"] = len(behaviour.files_deleted) - if hasattr(behaviour, "registry_keys_set"): - behaviour_data["registry_keys_set"] = len(behaviour.registry_keys_set) - if hasattr(behaviour, "dns_lookups"): - behaviour_data["dns_lookups"] = len(behaviour.dns_lookups) - if hasattr(behaviour, "ip_traffic"): - behaviour_data["ip_traffic"] = len(behaviour.ip_traffic) + behaviour_data = {} + + # sandbox_name always included when present (tests expect it) + if hasattr(behaviour, "sandbox_name"): + behaviour_data["sandbox_name"] = behaviour.sandbox_name + + # For behaviour list attributes: include key if attribute exists + # even if attribute is an empty list. + for attr in [ + "processes_created", + "files_written", + "files_deleted", + "registry_keys_set", + "dns_lookups", + "ip_traffic", + ]: + value = getattr(behaviour, attr, None) + if value is not None: + behaviour_data[attr] = len(value) behaviours.append(behaviour_data) @@ -296,8 +267,8 @@ def get_file_behaviour(self, client: vt.Client): "SUCCESS", {"behaviours_count": len(behaviours), "behaviours": behaviours}, ) + except vt.APIError as e: - # This endpoint requires Premium API - log as warning not error logger.warning(f"File behaviours not available (may require Premium API): {e}") self._add_result( "GET_FILE_SANDBOX", @@ -434,7 +405,7 @@ def get_vulnerability_report(self, client: vt.Client): vuln_data, ) except vt.APIError as e: - logger.warning(f"OUCH! Vulnerability report not available (may require Premium API): {e}") + logger.warning(f"Vulnerability report not available (may require Premium API): {e}") self._add_result( "VULN_REPORT", "GET", diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py index 1520d00b9..49bace934 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py @@ -47,12 +47,9 @@ def run(self, arguments: dict): entity_type = "domains" entity_name = domain - print(f"Getting comments for {entity_type}: {entity_name}") - # connector.get_comments(client, entity_type) connector.get_comments(client, entity_type, entity_name) result = connector.results[-1] - print("Comments:", result.response) return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py index f19ce65b1..da3742db2 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py @@ -16,8 +16,7 @@ def run(self, arguments: dict): api_key = self.module.configuration.get("api_key") if not api_key: return {"success": False, "error": "API key not configured"} - # TODO: Check README about parameters -> needs to be domain or ip, fix client.py accordingly - # entity_type = arguments.get("entity_type", "domains") + ip = arguments.get("ip", "") try: diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py index f15ea17ab..8b16dd785 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py @@ -27,7 +27,6 @@ def run(self, arguments: dict): connector.scan_file(client, file_path) analysis = connector.results[-1].response - print(f"API call response: {analysis}") # Debugging line return { "success": True, diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py index 6d0d81c0d..59cbfc0b9 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py @@ -26,7 +26,6 @@ def run(self, arguments: dict): connector.scan_url(client) analysis = connector.results[-1].response - print(f"API call response: {analysis}") # Debugging line return { "success": True, diff --git a/GoogleThreatIntelligence/logo.png b/GoogleThreatIntelligence/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6193c298cc85c9658333d014e726ac63163a938e GIT binary patch literal 3276 zcmbW4_dgVl1IEuN$CY!oPR1FXE$fomopIKgg*f|Tl=;EQUWa5PD=zBnEo5`n$q3n< zl9L^k%ZP;f_W2jSKfIs!>v{fzC)o_6f0^Y93jhGPY=G9W_-p_F0ABo?L!S6vN{}x7%TPC*vfX2t5Gv^Bcz$Iq`om*B9=)M(# zf;i{-hW{v@j*CMxGw|K=j3Ve#7FY|!Oz5sx%|m3}I|0s?RlCRjrMk6TH9snEOY5sz z)Nxhbjo>U|Qx+2FGt^~Ng$rFo3K;=IwT@KPW@)}U$@B_=9=MRzvm2_+Uz@I}^`7jm zo$ivP;EdA7DL{~=E3=Ynu6EY{CETGxwi(b+9>93k_@Zd!Q1&XDh5BI*eSaac&4yy|V0xm6F+zwYRRt^_xQ$h)c0<%U+a!-M{uo_X*!*X)&Gw@;zoqu*A^KeEH z+rj=e^JcbWo{xD}v!5KyPDS{z7cBJc16>)3%ic`s!#-%`EI_qjV~c$K=OtiOl{~1N ztrE?S{PIrd`wk2pjw7Q7YkU0D1elHo9S1atjH4I}klSipj$gm&>N<)EVqLi$WNjR? zMfl<@1=nfHY^E1kY$dw>c715EoQq}@|96$M6hnd_2|S~EGt8;Kj>vyz4uJJlIttEX zaxoq|LG5w{+SVr8O&M1741T1<`qz^vFP~dYs{&edRfr|J>uOA++7>{o0q38NQIOa& zqt9DxnOC{fA_WS;cFxhF1U$&a8FE} z{vSS{<7L;d&`Y>et#cfZMnfBmit9KH&pw8ApNQn8`HiW?8MD8rVsUjWNqxW(68qcQ z0qMV?ke7D*yn^473L&U*jYlft$=~*Jc&%es>I6&fPb2fv3^R}Ss}Pa2gL`oL7j=&Y zVG0g|bzA=&bk$!tfa`O|nhzT4z7}iX0pl0sz{s$hX$s!j@Q3i6{NmH2pr-{pV2gbD zFpHNRlHz_hMfcWa=R2|NxzR4d48hECu;^v8p^m;$8_+`F&7%kpqqc_-{sH-VI+09MVOy82 zV&>&^#UH4nNxSatL#YXQSxhEVo;SJ!H0W!n#WZM``l0W%sI)AJ3o=b7V=S=USI493 ztP}uQ`29T+SXUS4&_3jt+T7)S%h)8|0sro$?y#b%wwHQp%Z1*)z=}ymFpi~%r=o%= zG`j#-vUh4OzcPWqa&#?*LijNcJSNL3puexk-a=S8S3?? zm-~yzsmPSMBa{+9zml@)BV7mxb?3 zlbl#mBMD01)hjZ(<%_V)&r*jl|X|Wy&=V zcG1^cyDb}`flu^0LwyswJ!F;XekoHJnsf`W z13U>mclcg8US1_l9@as3u9+Q)bOy6$x2B=tEE_kEHR(^rY^SoW$Dc3>U%hp~wD!`9 zmfc}i<>S5)=SSTsXetw$m2>2D3swJ|fy8@9dT7SA8$YsSnPb&Y@shdx=cTA+mHU>D z*$0I1`;7YnC5H>_pXtWDHa&LQh{uy-sGr5pdrjB5;j|J)e-yj*g54jGhEsfACVx-V zcTbz#&3;525Z`~{sdibQov~9BjN3T=#%H;ylw#?~r*o7xi3d%Ye!_}L+B+UNs(~U- zD-&{^CiyUaYn$LZcC^!bEiTQFLYtuq-s6TNx-s$Gk%Vg`=MIa#RNc-k!$O>^N{e(Q zi%9KskxHz0Q_q$uT5m9lGZdAUpL8WhS#z%5X$j+VQJL9_AW27>z2lwyN=h*9*!$NN zOk23@+>DGLBz3?@_25=0Y{v5N@nBn<0pU$Kp&Bx6DoJdvWkSRizCm)arT8Xfm}BNg zQw6|MD=4Lb$gGQorskT}n|L`cXv3vY*1Z^PxVwG!Y5`m>_Z-Q^QYOZieW ztZ@|&@6Sw00C{2l0#AAmn~`8_o>oiv_HuFtdCpA>4HMMbB)bShecr}D`O&}D-z0TF zW&NUXy*Hwip{>RqSk}pWn?PNAs-IxH`2CH|T*OQDMD*zTOp-9`8978y5?!F4NRjyz zReg;8!QoRwN#9)Cffq|YGoPp55Pw_eWEIhpL()F~C|{{7ozD=^hfsZ+*WI6-6{19W zP8(EZIN_l7a0g$UXU-PMR#;oi9uNiEe&7*L7JF6I%fF;C=^E+4KH_>qf}-)of`?-N zM0eRESdZlrWtg2p2yjDvjaF+Ha#msIdA23SXIn)t3+<<5@A}A_5!oxZHN%ZXT&OuQ z6AZrU6~eZBYJ;qZVf-?s*w?s9lRk9hh9If1Ta%vU6*0dYgF5X4JYeJJr;Up?#Iu1E zTEOmS!XhFWAw813UE}Ju{sEk{d7PPK#d*XAO6)_dAbp|pw~$Vg4GFWI)bFyyvN;>S zq;GHQ?!VKVGP?a9UBwP>Kf#Dw5UfkuECX$ZV#U8f@)q^~F|G=0t%E&SHf@0n29IUT&OZ$}JLkOp(F*+%n8_c;FqKZEsHg0@AFMcbn18hH zeI<6N@h4fY)LXxHGMM}M!npVew&C+;0XuZJx8Q1Vh2K5XUb~gbr_z!=tRSh);^s+- zpG9j8%z1_)CfuQ6W3Iem3s^L(tL=|_-TSW%=YqxEHH_~q@f()Cju!Tp{1IG&d9^|* z;s3l$iMes|sGYwp}+n7W_uI`)+p6N&AlGuhZfFC;mRK1xm0 zR>ZZvBes&0;(zNYUA$XqOg<9WIhG2&dyOpcj#lc}RhUDH;U|!}+*shFMUr8HbOIhO xn)90<{{i()2yg%Z literal 0 HcmV?d00001 diff --git a/GoogleThreatIntelligence/logo.svg b/GoogleThreatIntelligence/logo.svg deleted file mode 100644 index 8de59504e..000000000 --- a/GoogleThreatIntelligence/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 4654f0c5c..0f3b26fa9 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -23,7 +23,7 @@ "description": "Google Threat Intelligence provides unmatched visibility into threats enabling us to deliver detailed and timely threat intelligence to security teams", "name": "GoogleThreatIntelligence", "slug": "googlethreatintelligence", - "categories": [], + "category": "threat-intelligence", "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", - "version": "0.4.2" -} + "version": "0.1.0" +} \ No newline at end of file diff --git a/GoogleThreatIntelligence/tests/client/conftest.py b/GoogleThreatIntelligence/tests/client/conftest.py new file mode 100644 index 000000000..2fdeecf67 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/conftest.py @@ -0,0 +1,21 @@ +import pytest +from unittest.mock import MagicMock +import vt + + +@pytest.fixture +def mock_vt_client(monkeypatch): + """Mock vt.Client context manager""" + mock_client_instance = MagicMock() + mock_client = MagicMock() + mock_client.return_value.__enter__.return_value = mock_client_instance + monkeypatch.setattr(vt, "Client", mock_client) + return mock_client_instance + + +@pytest.fixture +def fake_file(tmp_path): + """Create a temporary fake file""" + file_path = tmp_path / "testfile.txt" + file_path.write_text("dummy content") + return str(file_path) diff --git a/GoogleThreatIntelligence/tests/client/test_analysis.py b/GoogleThreatIntelligence/tests/client/test_analysis.py new file mode 100644 index 000000000..8457c07b7 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_analysis.py @@ -0,0 +1,29 @@ +import pytest +from unittest.mock import MagicMock +from googlethreatintelligence.client import VTAPIConnector + +API_KEY = "FAKE_API_KEY" + + +def test_get_analysis_success(mock_vt_client): + connector = VTAPIConnector(api_key=API_KEY) + mock_analysis = MagicMock() + mock_analysis.status = "completed" + mock_analysis.stats = {"malicious": 1} + mock_vt_client.get_object.return_value = mock_analysis + + connector.get_analysis(mock_vt_client, "analysis_id") + result = connector.results[0] + assert result.status == "SUCCESS" + assert result.response["status"] == "completed" + + +def test_get_analysis_api_error(mock_vt_client): + from vt import APIError + + connector = VTAPIConnector(api_key=API_KEY) + mock_vt_client.get_object.side_effect = APIError("FailedError", "Failed") + connector.get_analysis(mock_vt_client, "analysis_id") + result = connector.results[0] + assert result.status == "ERROR" + assert "Failed" in result.error diff --git a/GoogleThreatIntelligence/tests/client/test_api_key_import.py b/GoogleThreatIntelligence/tests/client/test_api_key_import.py new file mode 100644 index 000000000..9c8f96d59 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_api_key_import.py @@ -0,0 +1,23 @@ +import os +from unittest.mock import patch +import importlib +import googlethreatintelligence.client as client_module + + +def test_api_key_warning_when_missing(caplog): + """Verify that API_KEY == REDACTED triggers a warning log.""" + + with patch.dict(os.environ, {"VT_API_KEY": "REDACTED"}, clear=True): + with caplog.at_level("WARNING"): + importlib.reload(client_module) + + assert "API_KEY not set! Set VT_API_KEY environment variable" in caplog.text + assert client_module.API_KEY == "REDACTED" + + +def test_api_key_loaded_from_env(): + """Verify that API_KEY imports correctly when set.""" + with patch.dict(os.environ, {"VT_API_KEY": "MY_REAL_KEY"}, clear=True): + importlib.reload(client_module) + + assert client_module.API_KEY == "MY_REAL_KEY" diff --git a/GoogleThreatIntelligence/tests/client/test_connectivity.py b/GoogleThreatIntelligence/tests/client/test_connectivity.py new file mode 100644 index 000000000..9868ed085 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_connectivity.py @@ -0,0 +1,35 @@ +import vt +from unittest.mock import MagicMock +from googlethreatintelligence.client import VTAPIConnector + + +def test_test_connectivity_success(): + connector = VTAPIConnector(api_key="key") + mock_client = MagicMock() + + mock_user = MagicMock() + mock_user.id = "u123" + mock_client.get_object.return_value = mock_user + + connector.test_connectivity(mock_client) + result = connector.results[-1] + + assert result.method == "GET" + assert result.endpoint == "/api/v3/users/me" + assert result.status == "SUCCESS" + assert result.response["user_id"] == "u123" + mock_client.get_object.assert_called_once_with("/users/me") + + +def test_test_connectivity_api_error(): + connector = VTAPIConnector(api_key="key") + mock_client = MagicMock() + + mock_client.get_object.side_effect = vt.APIError("ConnError", "Connection failed") + + connector.test_connectivity(mock_client) + result = connector.results[-1] + + assert result.status == "ERROR" + assert "Connection failed" in result.error + mock_client.get_object.assert_called_once_with("/users/me") diff --git a/GoogleThreatIntelligence/tests/client/test_file_behaviour.py b/GoogleThreatIntelligence/tests/client/test_file_behaviour.py new file mode 100644 index 000000000..b3630408d --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_file_behaviour.py @@ -0,0 +1,138 @@ +import pytest +from unittest.mock import MagicMock +from googlethreatintelligence.client import VTAPIConnector +import vt + + +API_KEY = "FAKE_API_KEY" + + +def test_get_file_behaviour_success(mock_vt_client): + connector = VTAPIConnector(api_key=API_KEY) + mock_behaviour = MagicMock() + mock_behaviour.sandbox_name = "sandbox1" + mock_behaviour.processes_created = ["proc1", "proc2"] + mock_behaviour.files_written = [] + mock_vt_client.iterator.return_value = iter([mock_behaviour]) + + connector.get_file_behaviour(mock_vt_client) + result = connector.results[0] + assert result.status == "SUCCESS" + assert result.response["behaviours_count"] == 1 + assert result.response["behaviours"][0]["sandbox_name"] == "sandbox1" + + +@pytest.fixture +def connector(): + return VTAPIConnector(api_key="dummy") + + +def make_behaviour( + sandbox_name="DefaultSandbox", + processes=None, + files_written=None, + files_deleted=None, + registry_keys=None, + dns_lookups=None, + ip_traffic=None, +): + """Helper to create a fake behaviour object.""" + b = MagicMock() + b.sandbox_name = sandbox_name + + if processes is not None: + b.processes_created = processes + if files_written is not None: + b.files_written = files_written + if files_deleted is not None: + b.files_deleted = files_deleted + if registry_keys is not None: + b.registry_keys_set = registry_keys + if dns_lookups is not None: + b.dns_lookups = dns_lookups + if ip_traffic is not None: + b.ip_traffic = ip_traffic + + return b + + +# ------------------------------------------------------- +# 1) SUCCESS: multiple behaviours, all attributes present +# ------------------------------------------------------- + + +def test_get_file_behaviour_success_full_attributes(connector): + mock_client = MagicMock() + + behaviour1 = make_behaviour( + sandbox_name="SandboxA", + processes=[1, 2], + files_written=["a", "b", "c"], + files_deleted=["d1"], + registry_keys=["k1", "k2"], + dns_lookups=["dns1"], + ip_traffic=["tcp1", "tcp2"], + ) + + behaviour2 = make_behaviour( + sandbox_name="SandboxB", + processes=[], + files_written=[], + files_deleted=None, + registry_keys=None, + dns_lookups=[], + ip_traffic=["tcp"], + ) + + mock_client.iterator.return_value = iter([behaviour1, behaviour2]) + + connector.file_hash = "abc123" + connector.get_file_behaviour(mock_client) + + result = connector.results[-1] + + assert result.status == "SUCCESS" + assert result.response["behaviours_count"] == 2 + assert result.method == "GET" + assert result.endpoint.endswith("/api/v3/files/abc123/behaviours") + + # Validate behaviour1 extraction + b1 = result.response["behaviours"][0] + assert b1["sandbox_name"] == "SandboxA" + assert b1["processes_created"] == 2 + assert b1["files_written"] == 3 + assert b1["files_deleted"] == 1 + assert b1["registry_keys_set"] == 2 + assert b1["dns_lookups"] == 1 + assert b1["ip_traffic"] == 2 + + # Validate behaviour2 minimal fields + b2 = result.response["behaviours"][1] + assert b2["sandbox_name"] == "SandboxB" + assert b2["processes_created"] == 0 + assert b2["files_written"] == 0 + assert b2["dns_lookups"] == 0 + assert b2["ip_traffic"] == 1 + + +# ------------------------------------------------------- +# 2) APIError: Premium API missing → NOT_AVAILABLE +# ------------------------------------------------------- + + +def test_get_file_behaviour_api_error(connector, caplog): + mock_client = MagicMock() + + mock_client.iterator.side_effect = vt.APIError("PremiumError", "Requires Premium") + + connector.file_hash = "123xyz" + connector.get_file_behaviour(mock_client) + + result = connector.results[-1] + + assert result.status == "NOT_AVAILABLE" + assert "Premium API" in result.error + assert result.method == "GET" + + # Warning should have been logged + assert "File behaviours not available" in caplog.text diff --git a/GoogleThreatIntelligence/tests/client/test_ioc_attributes.py b/GoogleThreatIntelligence/tests/client/test_ioc_attributes.py new file mode 100644 index 000000000..e58addf85 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_ioc_attributes.py @@ -0,0 +1,25 @@ +import vt +from unittest.mock import MagicMock +from googlethreatintelligence.client import VTAPIConnector + + +def test_ioc_report_with_specific_attributes(): + connector = VTAPIConnector(api_key="k") + mock_client = MagicMock() + + mock_obj = MagicMock() + mock_obj.id = "hash123" + mock_obj.reputation = 10 + mock_obj.last_analysis_stats = {"malicious": 1, "undetected": 50} + mock_obj.country = "FR" + mock_obj.categories = {"malware": True} + + mock_client.get_object.return_value = mock_obj + + connector.get_ioc_report(mock_client, "files", "hash123") + result = connector.results[-1] + + assert result.status == "SUCCESS" + assert result.response["last_analysis_stats"] == {"malicious": 1, "undetected": 50} + assert result.response["country"] == "FR" + assert result.response["categories"] == {"malware": True} diff --git a/GoogleThreatIntelligence/tests/client/test_ioc_reports.py b/GoogleThreatIntelligence/tests/client/test_ioc_reports.py new file mode 100644 index 000000000..2cc83206c --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_ioc_reports.py @@ -0,0 +1,51 @@ +import pytest +from unittest.mock import MagicMock, patch +from googlethreatintelligence.client import VTAPIConnector + +API_KEY = "FAKE_API_KEY" + + +@pytest.mark.parametrize( + "entity_type, entity, report_method", + [ + ("ip_addresses", "8.8.8.8", "get_ip_report"), + ("domains", "google.com", "get_domain_report"), + ("urls", "https://example.com", "get_url_report"), + ("files", "44d88612fea8a8f36de82e1278abb02f", "get_file_report"), + ], +) +def test_ioc_reports_success(mock_vt_client, entity_type, entity, report_method): + """Test generic IOC report methods success""" + + connector = VTAPIConnector(api_key=API_KEY) + + # Mock get_object to return a custom object + mock_obj = MagicMock() + mock_obj.id = entity + mock_obj.reputation = 100 + mock_obj.last_analysis_stats = {"malicious": 0, "suspicious": 0, "clean": 80} + + mock_vt_client.get_object.return_value = mock_obj + + # Call specific report method dynamically + getattr(connector, report_method)(mock_vt_client) + + # Validate results + assert len(connector.results) == 1 + result = connector.results[0] + assert result.status == "SUCCESS" + assert result.response["entity_type"] == entity_type + + +def test_ioc_report_api_error(mock_vt_client): + """Test get_ioc_report handles APIError""" + connector = VTAPIConnector(api_key=API_KEY) + from vt import APIError + + mock_vt_client.get_object.side_effect = APIError("FailedError", "FakeError") + + connector.get_ioc_report(mock_vt_client, "domains", "google.com") + + result = connector.results[0] + assert result.status == "ERROR" + assert "FakeError" in result.error diff --git a/GoogleThreatIntelligence/tests/client/test_scan.py b/GoogleThreatIntelligence/tests/client/test_scan.py new file mode 100644 index 000000000..82b543170 --- /dev/null +++ b/GoogleThreatIntelligence/tests/client/test_scan.py @@ -0,0 +1,65 @@ +import pytest +from unittest.mock import MagicMock +from googlethreatintelligence.client import VTAPIConnector + +API_KEY = "FAKE_API_KEY" + + +def test_scan_url_success(mock_vt_client): + connector = VTAPIConnector(api_key=API_KEY) + mock_analysis = MagicMock() + mock_analysis.stats = {"malicious": 5} + mock_analysis.results = {"scanner": "clean"} + + mock_vt_client.scan_url.return_value = mock_analysis + + connector.scan_url(mock_vt_client) + + result = connector.results[0] + assert result.status == "SUCCESS" + assert result.response["analysis_stats"] == {"malicious": 5} + + +def test_scan_url_api_error(mock_vt_client): + from vt import APIError + + connector = VTAPIConnector(api_key=API_KEY) + mock_vt_client.scan_url.side_effect = APIError("FailedError", "API failed") + + connector.scan_url(mock_vt_client) + result = connector.results[0] + assert result.status == "ERROR" + assert "API failed" in result.error + + +def test_scan_file_success(mock_vt_client, fake_file): + connector = VTAPIConnector(api_key=API_KEY) + mock_analysis = MagicMock() + mock_analysis.stats = {"malicious": 0} + mock_analysis.results = {"scanner": "clean"} + + mock_vt_client.scan_file.return_value = mock_analysis + + connector.scan_file(mock_vt_client, fake_file) + result = connector.results[0] + assert result.status == "SUCCESS" + assert result.response["file_path"] == fake_file + + +def test_scan_file_not_found(mock_vt_client): + connector = VTAPIConnector(api_key=API_KEY) + connector.scan_file(mock_vt_client, "/nonexistent/path.txt") + result = connector.results[0] + assert result.status == "ERROR" + assert "File not found" in result.error + + +def test_scan_file_api_error(mock_vt_client, fake_file): + from vt import APIError + + connector = VTAPIConnector(api_key=API_KEY) + mock_vt_client.scan_file.side_effect = APIError("FailedError", "Scan failed") + connector.scan_file(mock_vt_client, fake_file) + result = connector.results[0] + assert result.status == "ERROR" + assert "Scan failed" in result.error From b18c50a2c938a65db8b615dec2d19c7fc057cfea Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Wed, 3 Dec 2025 18:12:22 +0000 Subject: [PATCH 4/8] fix manifest categories --- GoogleThreatIntelligence/manifest.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 0f3b26fa9..6744f85c7 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -23,7 +23,9 @@ "description": "Google Threat Intelligence provides unmatched visibility into threats enabling us to deliver detailed and timely threat intelligence to security teams", "name": "GoogleThreatIntelligence", "slug": "googlethreatintelligence", - "category": "threat-intelligence", + "category": [ + "Threat Intelligence" + ], "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", "version": "0.1.0" } \ No newline at end of file From d7f35c86f9015e6fd3f7f7ac399e013375d76264 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Thu, 18 Dec 2025 15:27:30 +0000 Subject: [PATCH 5/8] remove validation pattern that enters in conflict with jinja patterning --- GoogleThreatIntelligence/CHANGELOG.md | 6 ++++++ GoogleThreatIntelligence/action_get_comments.json | 12 ++++-------- .../action_get_file_behaviour.json | 3 +-- GoogleThreatIntelligence/action_get_ioc_report.json | 12 ++++-------- GoogleThreatIntelligence/action_get_passive_dns.json | 3 +-- .../action_get_vulnerability_associations.json | 12 ++++-------- GoogleThreatIntelligence/manifest.json | 2 +- 7 files changed, 21 insertions(+), 29 deletions(-) diff --git a/GoogleThreatIntelligence/CHANGELOG.md b/GoogleThreatIntelligence/CHANGELOG.md index a4af7e839..dc4a8e4f1 100644 --- a/GoogleThreatIntelligence/CHANGELOG.md +++ b/GoogleThreatIntelligence/CHANGELOG.md @@ -6,3 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## 2025-12-18 - 1.0.1 + +### Fixed + +- Remove validation patterns as not working with jinja templates inputs \ No newline at end of file diff --git a/GoogleThreatIntelligence/action_get_comments.json b/GoogleThreatIntelligence/action_get_comments.json index c600d6a8d..89011b0b2 100644 --- a/GoogleThreatIntelligence/action_get_comments.json +++ b/GoogleThreatIntelligence/action_get_comments.json @@ -9,23 +9,19 @@ "properties": { "ip": { "type": "string", - "description": "IP address to query (e.g., 8.8.8.8)", - "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + "description": "IP address to query (e.g., 8.8.8.8)" }, "domain": { "type": "string", - "description": "Domain name to query (e.g., google.com)", - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + "description": "Domain name to query (e.g., google.com)" }, "url": { "type": "string", - "description": "URL to query (e.g., https://example.com/path)", - "format": "uri" + "description": "URL to query (e.g., https://example.com/path)" }, "file_hash": { "type": "string", - "description": "File hash to query (MD5, SHA1, or SHA256)", - "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + "description": "File hash to query (MD5, SHA1, or SHA256)" } }, "oneOf": [ diff --git a/GoogleThreatIntelligence/action_get_file_behaviour.json b/GoogleThreatIntelligence/action_get_file_behaviour.json index e270fdc8e..7140bd0ed 100644 --- a/GoogleThreatIntelligence/action_get_file_behaviour.json +++ b/GoogleThreatIntelligence/action_get_file_behaviour.json @@ -9,8 +9,7 @@ "properties": { "file_hash": { "type": "string", - "description": "File hash to query (MD5, SHA1, or SHA256)", - "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + "description": "File hash to query (MD5, SHA1, or SHA256)" } }, "required": [ diff --git a/GoogleThreatIntelligence/action_get_ioc_report.json b/GoogleThreatIntelligence/action_get_ioc_report.json index 509997786..9be98e3e3 100644 --- a/GoogleThreatIntelligence/action_get_ioc_report.json +++ b/GoogleThreatIntelligence/action_get_ioc_report.json @@ -10,23 +10,19 @@ "properties": { "ip": { "type": "string", - "description": "IP address to query (e.g., 8.8.8.8)", - "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + "description": "IP address to query (e.g., 8.8.8.8)" }, "domain": { "type": "string", - "description": "Domain name to query (e.g., google.com)", - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + "description": "Domain name to query (e.g., google.com)" }, "url": { "type": "string", - "description": "URL to query (e.g., https://example.com/path)", - "format": "uri" + "description": "URL to query (e.g., https://example.com/path)" }, "file_hash": { "type": "string", - "description": "File hash to query (MD5, SHA1, or SHA256)", - "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + "description": "File hash to query (MD5, SHA1, or SHA256)" } }, "oneOf": [ diff --git a/GoogleThreatIntelligence/action_get_passive_dns.json b/GoogleThreatIntelligence/action_get_passive_dns.json index dec8e33c8..35d5bc491 100644 --- a/GoogleThreatIntelligence/action_get_passive_dns.json +++ b/GoogleThreatIntelligence/action_get_passive_dns.json @@ -9,8 +9,7 @@ "properties": { "domain": { "type": "string", - "description": "Domain name to query (e.g., google.com)", - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + "description": "Domain name to query (e.g., google.com)" } }, "required": [ diff --git a/GoogleThreatIntelligence/action_get_vulnerability_associations.json b/GoogleThreatIntelligence/action_get_vulnerability_associations.json index 1ca27d389..680107c4d 100644 --- a/GoogleThreatIntelligence/action_get_vulnerability_associations.json +++ b/GoogleThreatIntelligence/action_get_vulnerability_associations.json @@ -9,23 +9,19 @@ "properties": { "ip": { "type": "string", - "description": "IP address to query (e.g., 8.8.8.8)", - "pattern": "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + "description": "IP address to query (e.g., 8.8.8.8)" }, "domain": { "type": "string", - "description": "Domain name to query (e.g., google.com)", - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$" + "description": "Domain name to query (e.g., google.com)" }, "url": { "type": "string", - "description": "URL to query (e.g., https://example.com/path)", - "format": "uri" + "description": "URL to query (e.g., https://example.com/path)" }, "file_hash": { "type": "string", - "description": "File hash to query (MD5, SHA1, or SHA256)", - "pattern": "^[a-fA-F0-9]{32}$|^[a-fA-F0-9]{40}$|^[a-fA-F0-9]{64}$" + "description": "File hash to query (MD5, SHA1, or SHA256)" } }, "oneOf": [ diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 6744f85c7..33ca6c659 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -27,5 +27,5 @@ "Threat Intelligence" ], "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", - "version": "0.1.0" + "version": "0.1.1" } \ No newline at end of file From a5af46bdfe8f6abffe661d04e25b02ab9528aeb5 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Fri, 19 Dec 2025 09:48:41 +0000 Subject: [PATCH 6/8] push gti vulnerability fix before deployment --- GoogleThreatIntelligence/CHANGELOG.md | 8 +++- .../googlethreatintelligence/client.py | 40 ++++++++++++++----- GoogleThreatIntelligence/manifest.json | 2 +- .../tests/test_get_vulnerability_report.py | 28 ++++++++++++- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/GoogleThreatIntelligence/CHANGELOG.md b/GoogleThreatIntelligence/CHANGELOG.md index dc4a8e4f1..0c16ee29f 100644 --- a/GoogleThreatIntelligence/CHANGELOG.md +++ b/GoogleThreatIntelligence/CHANGELOG.md @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## 2025-12-18 - 1.0.1 +## 2025-12-19 - 0.1.2 + +### Fixed + +- Get Vulnerability Report: Extract all available fields from VT API response including counters, risk_rating, exploitation_state, exploit_availability, and other critical fields that were previously missing + +## 2025-12-18 - 0.1.1 ### Fixed diff --git a/GoogleThreatIntelligence/googlethreatintelligence/client.py b/GoogleThreatIntelligence/googlethreatintelligence/client.py index f6d61d03b..afbf3da9b 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/client.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/client.py @@ -387,15 +387,37 @@ def get_vulnerability_report(self, client: vt.Client): "id": vuln.id if hasattr(vuln, "id") else None, } - # Extract additional vulnerability details - if hasattr(vuln, "title"): - vuln_data["title"] = vuln.title - if hasattr(vuln, "description"): - vuln_data["description"] = ( - vuln.description[:200] + "..." if len(vuln.description) > 200 else vuln.description - ) - if hasattr(vuln, "cvss"): - vuln_data["cvss"] = vuln.cvss + # Extract all available vulnerability attributes + attributes_to_extract = [ + "type", + "collection_type", + "name", + "title", + "description", + "creation_date", + "last_modification_date", + "risk_rating", + "exploitation_state", + "exploit_availability", + "exploited_as_zero_day", + "exploited_in_the_wild", + "cvss", + "counters", + "origin", + "tags", + "first_published_date", + "intelligence_source", + ] + + for attr in attributes_to_extract: + if hasattr(vuln, attr): + value = getattr(vuln, attr) + # Convert nested objects to dict if needed + if hasattr(value, "__dict__") and not isinstance(value, (str, int, float, bool, list)): + # For objects like cvss or counters, convert to dict + vuln_data[attr] = {k: v for k, v in value.__dict__.items() if not k.startswith("_")} + else: + vuln_data[attr] = value self._add_result( "VULN_REPORT", diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 33ca6c659..23b2388d6 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -27,5 +27,5 @@ "Threat Intelligence" ], "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", - "version": "0.1.1" + "version": "0.1.2" } \ No newline at end of file diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py index 85da8bc78..b9717bcd6 100644 --- a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py @@ -10,12 +10,33 @@ @patch("googlethreatintelligence.get_vulnerability_report.vt.Client") def test_get_vulnerability_report_success(mock_vt_client): """Test successful retrieval of vulnerability report""" + # Arrange + # Create a simple object that mimics the VT vulnerability object + class MockCVSS: + def __init__(self): + self.score = 8.8 + self.severity = "HIGH" + self.version = "3.1" + + class MockCounters: + def __init__(self): + self.iocs = 5 + self.files = 10 + self.domains = 2 + self.ip_addresses = 3 + mock_vuln = MagicMock() mock_vuln.id = "vuln123" mock_vuln.title = "PrintNightmare" mock_vuln.description = "Remote code execution vulnerability in Windows Print Spooler" - mock_vuln.cvss = {"score": 8.8, "severity": "HIGH"} + + # Use the custom classes for nested objects + type(mock_vuln).cvss = PropertyMock(return_value=MockCVSS()) + type(mock_vuln).counters = PropertyMock(return_value=MockCounters()) + + # Configure hasattr to return True for our attributes + mock_vuln.configure_mock(**{"cvss": MockCVSS(), "counters": MockCounters()}) mock_client_instance = MagicMock() mock_vt_client.return_value.__enter__.return_value = mock_client_instance @@ -32,6 +53,11 @@ def test_get_vulnerability_report_success(mock_vt_client): assert response["data"]["cve"] == CVE_ID assert response["data"]["title"] == "PrintNightmare" assert response["data"]["cvss"]["severity"] == "HIGH" + assert response["data"]["cvss"]["score"] == 8.8 + # Verify counters are present + assert "counters" in response["data"] + assert response["data"]["counters"]["iocs"] == 5 + assert response["data"]["counters"]["files"] == 10 mock_vt_client.assert_called_once_with(API_KEY) mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") From c957903345fac4dde62e0eda93b31c8e16a34f46 Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Fri, 19 Dec 2025 21:28:24 +0000 Subject: [PATCH 7/8] add proxy support --- GoogleThreatIntelligence/CHANGELOG.md | 6 ++++++ .../googlethreatintelligence/get_comments.py | 2 +- .../googlethreatintelligence/get_file_behaviour.py | 2 +- .../googlethreatintelligence/get_ioc_report.py | 2 +- .../googlethreatintelligence/get_passive_dns.py | 2 +- .../get_vulnerability_associations.py | 2 +- .../googlethreatintelligence/get_vulnerability_report.py | 2 +- .../googlethreatintelligence/scan_file.py | 2 +- .../googlethreatintelligence/scan_url.py | 2 +- GoogleThreatIntelligence/manifest.json | 2 +- GoogleThreatIntelligence/tests/test_get_file_behaviour.py | 4 ++-- GoogleThreatIntelligence/tests/test_get_ioc_report.py | 4 ++-- GoogleThreatIntelligence/tests/test_get_passive_dns.py | 4 ++-- .../tests/test_get_vulnerability_associations.py | 4 ++-- .../tests/test_get_vulnerability_report.py | 4 ++-- GoogleThreatIntelligence/tests/test_scan_file.py | 4 ++-- GoogleThreatIntelligence/tests/test_scan_url.py | 2 +- 17 files changed, 28 insertions(+), 22 deletions(-) diff --git a/GoogleThreatIntelligence/CHANGELOG.md b/GoogleThreatIntelligence/CHANGELOG.md index 0c16ee29f..3d2173f0e 100644 --- a/GoogleThreatIntelligence/CHANGELOG.md +++ b/GoogleThreatIntelligence/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 2025-12-19 - 0.1.3 + +### Fixed + +- Add proxy support + ## 2025-12-19 - 0.1.2 ### Fixed diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py index 49bace934..a5043d605 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_comments.py @@ -23,7 +23,7 @@ def run(self, arguments: dict): file_hash = arguments.get("file_hash", "") connector = VTAPIConnector(api_key, domain=domain, ip=ip, url=url, file_hash=file_hash) - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: # Determine which entity to query for comments # Priority: domain > ip > url > file_hash # Only one can be provided at a time because of input constraints diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py b/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py index 1fb78d675..8c5ee915b 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_file_behaviour.py @@ -24,7 +24,7 @@ def run(self, arguments: dict): try: connector = VTAPIConnector(api_key, domain="", ip="", url="", file_hash=file_hash) - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.get_file_behaviour(client) result = connector.results[-1] diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py b/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py index 25afc41d4..566c35e9e 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_ioc_report.py @@ -37,7 +37,7 @@ def run(self, arguments: dict[str, Any]) -> dict[str, Any]: file_hash = arguments.get("file_hash", "") connector = VTAPIConnector(api_key, domain=domain, ip=ip, url=url, file_hash=file_hash) - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: if domain != "": connector.get_domain_report(client) elif ip != "": diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py b/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py index 127596cf4..3b971dffb 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_passive_dns.py @@ -20,7 +20,7 @@ def run(self, arguments: dict): domain = arguments.get("domain", "") connector = VTAPIConnector(api_key, domain=domain, ip="", url="", file_hash="") - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.get_passive_dns(client) result = connector.results[-1] diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py index da3742db2..8cbb2baa5 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_associations.py @@ -21,7 +21,7 @@ def run(self, arguments: dict): try: connector = VTAPIConnector(api_key, ip=ip, domain="", url="", file_hash="") - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.get_vulnerability_associations(client) result = connector.results[-1] diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py index 6f4ed3d58..894dccf8a 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py @@ -22,7 +22,7 @@ def run(self, arguments: dict): return {"success": False, "error": "No CVE provided"} connector = VTAPIConnector(api_key, cve=cve, domain="", ip="", url="", file_hash="") - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.get_vulnerability_report(client) result = connector.results[-1] diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py index 8b16dd785..20e663687 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_file.py @@ -23,7 +23,7 @@ def run(self, arguments: dict): return {"success": False, "error": f"File not found: {file_path}"} connector = VTAPIConnector(api_key, url="", domain="", ip="", file_hash="", cve="") - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.scan_file(client, file_path) analysis = connector.results[-1].response diff --git a/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py index 59cbfc0b9..0eae45e5f 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/scan_url.py @@ -22,7 +22,7 @@ def run(self, arguments: dict): return {"success": False, "error": "No URL provided"} connector = VTAPIConnector(api_key, url=url, domain="", ip="", file_hash="", cve="") - with vt.Client(api_key) as client: + with vt.Client(api_key, trust_env=True) as client: connector.scan_url(client) analysis = connector.results[-1].response diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 23b2388d6..78d477dda 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -27,5 +27,5 @@ "Threat Intelligence" ], "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", - "version": "0.1.2" + "version": "0.1.3" } \ No newline at end of file diff --git a/GoogleThreatIntelligence/tests/test_get_file_behaviour.py b/GoogleThreatIntelligence/tests/test_get_file_behaviour.py index eeef8e3e8..f79bd8400 100644 --- a/GoogleThreatIntelligence/tests/test_get_file_behaviour.py +++ b/GoogleThreatIntelligence/tests/test_get_file_behaviour.py @@ -46,7 +46,7 @@ def test_get_file_behaviour_success(mock_vt_client): assert response["data"]["behaviours"][0]["sandbox_name"] == "Windows10" # Verify vt.Client was called with the correct API key - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) # Verify iterator was called with the correct endpoint and limit mock_client_instance.iterator.assert_called_once_with(f"/files/{FILE_HASH}/behaviours", limit=5) @@ -76,7 +76,7 @@ def test_get_file_behaviour_fail_api_error(mock_vt_client): assert "data" in response or "error" in response # Verify vt.Client was called - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) # Ensure iterator was attempted mock_client_instance.iterator.assert_called_once_with(f"/files/{FILE_HASH}/behaviours", limit=5) diff --git a/GoogleThreatIntelligence/tests/test_get_ioc_report.py b/GoogleThreatIntelligence/tests/test_get_ioc_report.py index 124ea9be1..0ff7c9947 100644 --- a/GoogleThreatIntelligence/tests/test_get_ioc_report.py +++ b/GoogleThreatIntelligence/tests/test_get_ioc_report.py @@ -57,7 +57,7 @@ def test_get_domain_report_success(mock_connector_class, mock_vt_client): mock_connector.get_domain_report.assert_called_once_with(mock_client_instance) # Verify vt.Client was called with the correct API key - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) @patch("googlethreatintelligence.get_ioc_report.vt.Client") @@ -222,7 +222,7 @@ def test_get_ioc_report_api_error(mock_connector_class, mock_vt_client): assert response["error"] == "WrongCredentialsError: Invalid API key" # Verify vt.Client was called - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) def test_get_ioc_report_no_api_key(): diff --git a/GoogleThreatIntelligence/tests/test_get_passive_dns.py b/GoogleThreatIntelligence/tests/test_get_passive_dns.py index fed0f9f1a..16b4cb8da 100644 --- a/GoogleThreatIntelligence/tests/test_get_passive_dns.py +++ b/GoogleThreatIntelligence/tests/test_get_passive_dns.py @@ -56,7 +56,7 @@ def test_get_passive_dns_success(mock_vt_client): assert data["resolutions"][0]["ip_address"] == "8.8.8.8" # Verify vt.Client was instantiated with the correct API key - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) # Verify iterator was called with correct endpoint and limit mock_client_instance.iterator.assert_called_once_with(f"/domains/{DOMAIN}/resolutions", limit=40) @@ -87,7 +87,7 @@ def test_get_passive_dns_fail(mock_vt_client): assert "Rate limit exceeded" in response["error"] # Ensure vt.Client was called - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) def test_get_passive_dns_no_api_key(): diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py index 58a6c7072..a99fdfa65 100644 --- a/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_associations.py @@ -55,7 +55,7 @@ def test_get_vulnerability_associations_success(mock_connector_class, mock_vt_cl assert len(response["data"]["vulnerabilities"]) == 2 # Ensure the mocks were called properly - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) mock_connector_class.assert_called_once_with(API_KEY, ip=IP, domain="", url="", file_hash="") mock_connector.get_vulnerability_associations.assert_called_once_with(mock_client_instance) @@ -88,7 +88,7 @@ def test_get_vulnerability_associations_fail(mock_connector_class, mock_vt_clien assert "Invalid API key" in response["error"] # Ensure mocks were called - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) mock_connector_class.assert_called_once_with(API_KEY, ip=IP, domain="", url="", file_hash="") mock_connector.get_vulnerability_associations.assert_called_once_with(mock_client_instance) diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py index b9717bcd6..28f1977a1 100644 --- a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py @@ -58,7 +58,7 @@ def __init__(self): assert "counters" in response["data"] assert response["data"]["counters"]["iocs"] == 5 assert response["data"]["counters"]["files"] == 10 - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") @@ -78,7 +78,7 @@ def test_get_vulnerability_report_fail_api(mock_vt_client): assert response["success"] is False assert "Invalid API key" in response["error"] - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") diff --git a/GoogleThreatIntelligence/tests/test_scan_file.py b/GoogleThreatIntelligence/tests/test_scan_file.py index 3be0ade19..13280b769 100644 --- a/GoogleThreatIntelligence/tests/test_scan_file.py +++ b/GoogleThreatIntelligence/tests/test_scan_file.py @@ -55,7 +55,7 @@ def test_scan_file_success(mock_connector_class, mock_vt_client): mock_connector_class.assert_called_once_with(API_KEY, url="", domain="", ip="", file_hash="", cve="") mock_connector_instance.scan_file.assert_called_once_with(mock_client_instance, tmp_path) - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) finally: os.unlink(tmp_path) @@ -118,7 +118,7 @@ def test_scan_file_api_error(mock_connector_class, mock_vt_client): assert "API quota exceeded" in response["error"] mock_connector_instance.scan_file.assert_called_once_with(mock_client_instance, tmp_path) - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) finally: os.unlink(tmp_path) diff --git a/GoogleThreatIntelligence/tests/test_scan_url.py b/GoogleThreatIntelligence/tests/test_scan_url.py index 597e9b361..a3a62473d 100644 --- a/GoogleThreatIntelligence/tests/test_scan_url.py +++ b/GoogleThreatIntelligence/tests/test_scan_url.py @@ -57,7 +57,7 @@ def test_scan_url_success(mock_connector_class, mock_vt_client): # Verify mock calls mock_connector_class.assert_called_once_with(API_KEY, url=TEST_URL, domain="", ip="", file_hash="", cve="") - mock_vt_client.assert_called_once_with(API_KEY) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) mock_connector_instance.scan_url.assert_called_once_with(mock_client_instance) From ea47573cfbd195812c8eff81cc94b6f7db82774a Mon Sep 17 00:00:00 2001 From: CharlesLR-sekoia Date: Sun, 21 Dec 2025 20:55:00 +0000 Subject: [PATCH 8/8] fix infinit loop on vulnerability report module --- GoogleThreatIntelligence/CHANGELOG.md | 6 ++ .../googlethreatintelligence/client.py | 77 +++++++++---------- .../get_vulnerability_report.py | 14 +++- GoogleThreatIntelligence/manifest.json | 2 +- .../tests/test_get_vulnerability_report.py | 41 +++++++--- 5 files changed, 85 insertions(+), 55 deletions(-) diff --git a/GoogleThreatIntelligence/CHANGELOG.md b/GoogleThreatIntelligence/CHANGELOG.md index 3d2173f0e..d212a1e75 100644 --- a/GoogleThreatIntelligence/CHANGELOG.md +++ b/GoogleThreatIntelligence/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 2025-12-21 - 0.1.4 + +### Fixed + +- Get Vulnerability Report: infinite loop with edge cases. + ## 2025-12-19 - 0.1.3 ### Fixed diff --git a/GoogleThreatIntelligence/googlethreatintelligence/client.py b/GoogleThreatIntelligence/googlethreatintelligence/client.py index afbf3da9b..7df1cf0cf 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/client.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/client.py @@ -90,14 +90,23 @@ def _add_result( else: logger.info(f"[{status}] {name}: Success") - def _make_serializable(self, obj: Any) -> Any: - """Convert VT objects to JSON-serializable format""" + def _make_serializable(self, obj: Any, depth: int = 0, max_depth: int = 10) -> Any: + """Convert VT objects to JSON-serializable format with depth limit to prevent infinite recursion""" + # Prevent infinite recursion by limiting depth + if depth > max_depth: + return str(obj) + if isinstance(obj, dict): - return {k: self._make_serializable(v) for k, v in obj.items()} + return {k: self._make_serializable(v, depth + 1, max_depth) for k, v in obj.items()} elif isinstance(obj, (list, tuple)): - return [self._make_serializable(item) for item in obj] + return [self._make_serializable(item, depth + 1, max_depth) for item in obj] elif hasattr(obj, "__dict__"): - return str(obj) + # Convert object to dict but limit recursion + try: + obj_dict = {k: v for k, v in obj.__dict__.items() if not k.startswith("_")} + return self._make_serializable(obj_dict, depth + 1, max_depth) + except Exception: + return str(obj) else: return obj @@ -380,44 +389,28 @@ def get_vulnerability_report(self, client: vt.Client): try: # Correct path for vulnerability collections # `https://www.virustotal.com/api/v3/collections/vulnerability--cve-2010-3765` + logger.info(f"Fetching vulnerability report for {self.cve}") vuln = client.get_object(f"/collections/vulnerability--{self.cve}") - - vuln_data = { - "cve": self.cve, - "id": vuln.id if hasattr(vuln, "id") else None, - } - - # Extract all available vulnerability attributes - attributes_to_extract = [ - "type", - "collection_type", - "name", - "title", - "description", - "creation_date", - "last_modification_date", - "risk_rating", - "exploitation_state", - "exploit_availability", - "exploited_as_zero_day", - "exploited_in_the_wild", - "cvss", - "counters", - "origin", - "tags", - "first_published_date", - "intelligence_source", - ] - - for attr in attributes_to_extract: - if hasattr(vuln, attr): - value = getattr(vuln, attr) - # Convert nested objects to dict if needed - if hasattr(value, "__dict__") and not isinstance(value, (str, int, float, bool, list)): - # For objects like cvss or counters, convert to dict - vuln_data[attr] = {k: v for k, v in value.__dict__.items() if not k.startswith("_")} - else: - vuln_data[attr] = value + logger.info(f"Successfully retrieved vulnerability report for {self.cve}") + + # Use the VT object's to_dict() method if available, otherwise convert manually + # This is much faster and safer than manual attribute extraction + vuln_data = {"cve": self.cve} + + # Get all non-private attributes from the vuln object + if hasattr(vuln, "to_dict"): + # Use VT's built-in serialization if available + vuln_dict = vuln.to_dict() + vuln_data.update(vuln_dict) + elif hasattr(vuln, "__dict__"): + # Manually extract attributes, but with depth limit + for key, value in vuln.__dict__.items(): + if not key.startswith("_"): + # Use _make_serializable with depth limit to prevent infinite loops + vuln_data[key] = self._make_serializable(value, depth=0, max_depth=5) + else: + # Fallback: just store basic info + vuln_data["id"] = str(vuln.id) if hasattr(vuln, "id") else None self._add_result( "VULN_REPORT", diff --git a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py index 894dccf8a..6cacfaf19 100644 --- a/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py +++ b/GoogleThreatIntelligence/googlethreatintelligence/get_vulnerability_report.py @@ -22,11 +22,23 @@ def run(self, arguments: dict): return {"success": False, "error": "No CVE provided"} connector = VTAPIConnector(api_key, cve=cve, domain="", ip="", url="", file_hash="") - with vt.Client(api_key, trust_env=True) as client: + # Add explicit timeout to prevent infinite loops (30 seconds) + with vt.Client(api_key, trust_env=True, timeout=30.0) as client: connector.get_vulnerability_report(client) + + # Check if we have results + if not connector.results: + return {"success": False, "error": "No results returned from API"} + result = connector.results[-1] return {"success": result.status == "SUCCESS", "data": result.response, "error": result.error} + except vt.APIError as e: + # Handle VT API specific errors + return {"success": False, "error": f"VirusTotal API Error: {str(e)}"} + except TimeoutError as e: + # Handle timeout errors explicitly + return {"success": False, "error": f"Request timeout: {str(e)}"} except Exception as e: return {"success": False, "error": str(e)} diff --git a/GoogleThreatIntelligence/manifest.json b/GoogleThreatIntelligence/manifest.json index 78d477dda..f5ebd9a58 100644 --- a/GoogleThreatIntelligence/manifest.json +++ b/GoogleThreatIntelligence/manifest.json @@ -27,5 +27,5 @@ "Threat Intelligence" ], "uuid": "2002b12b-00a9-4dde-a1a5-1c0b27dcc4db", - "version": "0.1.3" + "version": "0.1.4" } \ No newline at end of file diff --git a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py index 28f1977a1..4a269c136 100644 --- a/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py +++ b/GoogleThreatIntelligence/tests/test_get_vulnerability_report.py @@ -1,6 +1,7 @@ from unittest.mock import patch, MagicMock, PropertyMock import vt from googlethreatintelligence.get_vulnerability_report import GTIGetVulnerabilityReport +import socket # Simulate timeout # === Constants === API_KEY = "FAKE_API_KEY" @@ -26,17 +27,17 @@ def __init__(self): self.domains = 2 self.ip_addresses = 3 - mock_vuln = MagicMock() - mock_vuln.id = "vuln123" - mock_vuln.title = "PrintNightmare" - mock_vuln.description = "Remote code execution vulnerability in Windows Print Spooler" + class MockVulnerability: + """Mock VT vulnerability object with real __dict__""" - # Use the custom classes for nested objects - type(mock_vuln).cvss = PropertyMock(return_value=MockCVSS()) - type(mock_vuln).counters = PropertyMock(return_value=MockCounters()) + def __init__(self): + self.id = "vuln123" + self.title = "PrintNightmare" + self.description = "Remote code execution vulnerability in Windows Print Spooler" + self.cvss = MockCVSS() + self.counters = MockCounters() - # Configure hasattr to return True for our attributes - mock_vuln.configure_mock(**{"cvss": MockCVSS(), "counters": MockCounters()}) + mock_vuln = MockVulnerability() mock_client_instance = MagicMock() mock_vt_client.return_value.__enter__.return_value = mock_client_instance @@ -58,7 +59,7 @@ def __init__(self): assert "counters" in response["data"] assert response["data"]["counters"]["iocs"] == 5 assert response["data"]["counters"]["files"] == 10 - mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True, timeout=30.0) mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") @@ -78,7 +79,7 @@ def test_get_vulnerability_report_fail_api(mock_vt_client): assert response["success"] is False assert "Invalid API key" in response["error"] - mock_vt_client.assert_called_once_with(API_KEY, trust_env=True) + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True, timeout=30.0) mock_client_instance.get_object.assert_called_once_with(f"/collections/vulnerability--{CVE_ID}") @@ -102,3 +103,21 @@ def test_get_vulnerability_report_no_cve(): assert response["success"] is False assert "CVE" in response["error"] + + +@patch("googlethreatintelligence.get_vulnerability_report.vt.Client") +def test_get_vulnerability_report_timeout(mock_vt_client): + """Test behavior when request times out""" + mock_client_instance = MagicMock() + mock_vt_client.return_value.__enter__.return_value = mock_client_instance + + mock_client_instance.get_object.side_effect = socket.timeout("Request timed out") + + action = GTIGetVulnerabilityReport() + action.module.configuration = {"api_key": API_KEY} + + response = action.run({"cve": CVE_ID}) + + assert response["success"] is False + assert "timeout" in response["error"].lower() + mock_vt_client.assert_called_once_with(API_KEY, trust_env=True, timeout=30.0)