diff --git a/content/response_integrations/third_party/thinkst_canary/.python-version b/content/response_integrations/third_party/thinkst_canary/.python-version new file mode 100644 index 000000000..902b2c90c --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/.python-version @@ -0,0 +1 @@ +3.11 \ No newline at end of file diff --git a/content/response_integrations/third_party/thinkst_canary/__init__.py b/content/response_integrations/third_party/thinkst_canary/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/actions/__init__.py b/content/response_integrations/third_party/thinkst_canary/actions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.py b/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.py new file mode 100644 index 000000000..b11ef91a6 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.py @@ -0,0 +1,54 @@ +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from soar_sdk.SiemplifyAction import SiemplifyAction +from soar_sdk.SiemplifyUtils import output_handler + +from ..core.constants import ( + THINKST_DEFAULT_API_KEY, + THINKST_DEFAULT_CONSOLE, + THINKST_INTEGRATION_NAME, +) +from ..core.thinkst_manager import ThinkstActionManager, str_to_bool + + +@output_handler +def main(): + status = EXECUTION_STATE_COMPLETED + result_value = False + output_message = "" + + siemplify = SiemplifyAction() + + console_api_key = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, + param_name="API Key", + default_value=THINKST_DEFAULT_API_KEY, + ) + if console_api_key == THINKST_DEFAULT_API_KEY: + status = EXECUTION_STATE_FAILED + output_message = "Please provide a valid API Key" + siemplify.end(output_message, result_value, status) + return + + console_hash = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, + param_name="Console Hash", + default_value=THINKST_DEFAULT_CONSOLE, + ) + if console_hash == THINKST_DEFAULT_CONSOLE: + status = EXECUTION_STATE_FAILED + output_message = "Please provide a valid Console Hash" + siemplify.end(output_message, result_value, status) + return + + ssl_verify = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, param_name="Verify SSL" + ) + + ssl = str_to_bool(ssl_verify) + manager = ThinkstActionManager(console_api_key, console_hash, siemplify, ssl) + status, output_message = manager.ack_alert() + siemplify.end(output_message, True, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.yaml b/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.yaml new file mode 100644 index 000000000..4a355ebe7 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/actions/ack_alert.yaml @@ -0,0 +1,11 @@ +creator: Thinkst +default_result_value: '' +description: Set an alert as acknowledged on the Canary console +dynamic_results_metadata: +- result_example_path: null + result_name: JsonResult + show_result: true +integration_identifier: THINKST +name: Acknowledge Console Alert +parameters: [] +script_result_name: ScriptResult diff --git a/content/response_integrations/third_party/thinkst_canary/actions/ping.py b/content/response_integrations/third_party/thinkst_canary/actions/ping.py new file mode 100644 index 000000000..41fcbd982 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/actions/ping.py @@ -0,0 +1,68 @@ +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from soar_sdk.SiemplifyAction import SiemplifyAction +from soar_sdk.SiemplifyUtils import output_handler + +from ..core.constants import ( + THINKST_DEFAULT_API_KEY, + THINKST_DEFAULT_CONSOLE, + THINKST_INTEGRATION_NAME, +) +from ..core.thinkst_manager import ThinkstActionManager, str_to_bool + + +@output_handler +def main(): + status = EXECUTION_STATE_COMPLETED + result_value = False + output_message = "" + + siemplify = SiemplifyAction() + + console_api_key = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, + param_name="API Key", + default_value=THINKST_DEFAULT_API_KEY, + ) + if console_api_key == THINKST_DEFAULT_API_KEY: + status = EXECUTION_STATE_FAILED + output_message = "Please provide a valid API Key" + siemplify.end(output_message, result_value, status) + return + + console_hash = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, + param_name="Console Hash", + default_value=THINKST_DEFAULT_CONSOLE, + ) + if console_hash == THINKST_DEFAULT_CONSOLE: + status = EXECUTION_STATE_FAILED + output_message = "Please provide a valid Console Hash" + siemplify.end(output_message, result_value, status) + return + + ssl_verify = siemplify.extract_configuration_param( + provider_name=THINKST_INTEGRATION_NAME, param_name="Verify SSL" + ) + + try: + ssl = str_to_bool(ssl_verify) + manager = ThinkstActionManager(console_api_key, console_hash, siemplify, ssl) + ping_res = manager.ping() + + if ping_res: + output_message = f"Successfully connected to Canary Console '{console_hash}'." + result_value = True + else: + output_message = f"Failed to connect to Canary Console '{console_hash}'." + + except Exception as e: + status = EXECUTION_STATE_FAILED + output_message = f"Ping failed: {str(e)}" + siemplify.LOGGER.exception(e) + + siemplify.LOGGER.info(f"Action finished: {output_message}") + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/third_party/thinkst_canary/actions/ping.yaml b/content/response_integrations/third_party/thinkst_canary/actions/ping.yaml new file mode 100644 index 000000000..931380f07 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/actions/ping.yaml @@ -0,0 +1,11 @@ +creator: Thinkst +default_result_value: '' +description: Uses the /ping endpoint to verify that API key and console hash is correct +dynamic_results_metadata: +- result_example_path: null + result_name: JsonResult + show_result: true +integration_identifier: THINKST +name: Ping +parameters: [] +script_result_name: ScriptResult diff --git a/content/response_integrations/third_party/thinkst_canary/connectors/__init__.py b/content/response_integrations/third_party/thinkst_canary/connectors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.py b/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.py new file mode 100644 index 000000000..a7e167a05 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.py @@ -0,0 +1,52 @@ +# ============================================================================== +# This connector retrieves Incidents from a Thinkst Canary Console and creates +# alerts/cases in Google SecOps SOAR. Each Canary Incident generates one alert. +# ============================================================================== +import sys + +from soar_sdk.SiemplifyConnectors import SiemplifyConnectorExecution +from soar_sdk.SiemplifyUtils import output_handler + +from ..core.constants import ( + THINKST_CONNECTOR_NAME, + THINKST_DEFAULT_API_KEY, + THINKST_DEFAULT_CONSOLE, +) +from ..core.thinkst_manager import ThinkstConnectorManager, str_to_bool + + +@output_handler +def main(is_test_run): + alerts = [] + siemplify = SiemplifyConnectorExecution() + siemplify.script_name = THINKST_CONNECTOR_NAME + + if is_test_run: + siemplify.LOGGER.info( + '***** This is an "IDE Play Button"\\"Run Connector once" test run ******' + ) + + siemplify.LOGGER.info("==================== Main - Param Init ====================") + + console_api_key = siemplify.extract_connector_param("API Key", THINKST_DEFAULT_API_KEY) + if console_api_key == THINKST_DEFAULT_API_KEY: + siemplify.LOGGER.error("Please provide a valid API Key") + return + + console_hash = siemplify.extract_connector_param("Console Hash", THINKST_DEFAULT_CONSOLE) + if console_hash == THINKST_DEFAULT_CONSOLE: + siemplify.LOGGER.error("Please provide a valid Console Hash") + return + + ssl_verify = siemplify.extract_connector_param("Verify SSL") + ssl = str_to_bool(ssl_verify) + + manager = ThinkstConnectorManager(console_api_key, console_hash, siemplify, ssl) + alerts = manager.fetch_alerts() + siemplify.return_package(alerts) + + +if __name__ == "__main__": + # Connectors are run in iterations. The interval is configurable from the ConnectorsScreen UI. + is_test_run = not (len(sys.argv) < 2 or sys.argv[1] == "True") + main(is_test_run) diff --git a/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.yaml b/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.yaml new file mode 100644 index 000000000..370aeb11b --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/connectors/thinkst_alert_connector.yaml @@ -0,0 +1,70 @@ +name: Thinkst - Alert Connector +parameters: + - name: API Key + default_value: + type: password + description: The API key for your console. If not specified will try and + get it from integration configuration. + is_mandatory: true + is_advanced: false + mode: script + integration_identifier: THINKST + + - name: Console Hash + default_value: + type: string + description: The hash for your console (the part before .canary.tools). If + not specified will try and get it from integration configuration. + is_mandatory: true + integration_identifier: THINKST + is_advanced: false + mode: script + + - name: Verify SSL + type: boolean + description: Whether to verify SSL certificates when connecting to the console + is_mandatory: false + default_value: true + integration_identifier: THINKST + is_advanced: false + mode: script + + - name: Ignore Informative + type: boolean + description: Do not create cases for informative incidents + is_mandatory: false + default_value: false + integration_identifier: THINKST + is_advanced: false + mode: script + + - name: PythonProcessTimeout + default_value: '60' + type: string + description: The timeout limit (in seconds) for the python process running current + script + is_mandatory: true + is_advanced: false + mode: regular + + - name: DeviceProductField + default_value: device_product + type: string + description: The field name used to determine the device product + is_mandatory: true + is_advanced: false + mode: regular + + - name: EventClassId + default_value: SourceType + type: string + description: The field name used to determine the event name (sub-type) + is_mandatory: true + is_advanced: false + mode: regular + +description: 'Gets security incidents from the Canary Console' +integration: THINKST +rules: [] +is_connector_rules_supported: true +creator: Thinkst diff --git a/content/response_integrations/third_party/thinkst_canary/core/__init__.py b/content/response_integrations/third_party/thinkst_canary/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/core/constants.py b/content/response_integrations/third_party/thinkst_canary/core/constants.py new file mode 100644 index 000000000..9e029fa04 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/core/constants.py @@ -0,0 +1,12 @@ +CONNECTOR_CONTEXT_TYPE = 4 # From soar_sdk/SiemplifyConnectors/SiemplifyConnecterExecution() +THINKST_CONNECTOR_NAME = "ThinkstConsoleConnector" +THINKST_INTEGRATION_NAME = "Thinkst" +THINKST_PRODUCT = "Thinkst Canary" +THINKST_CONTEXT_IDENTIFIER = "ThinkstState" +THINKST_FALLBACK_TIME_MINS = 60 * 24 +THINKST_LAST_TIMESTAMP = "LastTimestamp" +THINKST_UPDATE_KEY = "LastUpdateID" +THINKST_VENDOR = "Thinkst" +THINKST_DEFAULT_CONSOLE = "" +THINKST_DEFAULT_API_KEY = "" +THINKST_OPERATIONAL_LOGTYPES = ["1004", "23001", "23002", "23003"] diff --git a/content/response_integrations/third_party/thinkst_canary/core/thinkst_manager.py b/content/response_integrations/third_party/thinkst_canary/core/thinkst_manager.py new file mode 100644 index 000000000..34a350a5e --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/core/thinkst_manager.py @@ -0,0 +1,735 @@ +from __future__ import annotations + +import copy +import json +from datetime import datetime, timedelta, timezone +from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence + +import requests +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from soar_sdk.SiemplifyConnectorsDataModel import AlertInfo + +if TYPE_CHECKING: + from soar_sdk.SiemplifyAction import SiemplifyAction + from soar_sdk.SiemplifyConnectors import SiemplifyConnectorExecution + +from ..core.constants import ( + THINKST_CONTEXT_IDENTIFIER, + THINKST_FALLBACK_TIME_MINS, + THINKST_LAST_TIMESTAMP, + THINKST_OPERATIONAL_LOGTYPES, + THINKST_PRODUCT, + THINKST_UPDATE_KEY, + THINKST_VENDOR, +) + +PRIORITY_INFO = -1 +PRIORITY_LOW = 40 +PRIORITY_MED = 60 +PRIORITY_HIGH = 80 +PRIORITY_CRIT = 100 + +TOKEN_NAME_MAP = { + "active-directory-login": "Active Directory Login", + "autoreg-google-docs": "Google Doc", + "autoreg-google-sheets": "Google Sheet", + "aws-id": "AWS API Key", + "aws-s3": "AWS S3 Bucket", + "azure-entra-login": "Azure Entra Login", + "azure-id": "Azure Login Certificate and Config", + "cloned-css": "CSS cloned site", + "cloned-web": "Cloned Website", + "credit-card": "Credit Card", + "dns": "DNS", + "doc-msexcel": "MS Excel Document", + "doc-msword": "MS Word Document", + "fast-redirect": "Fast Redirect", + "gmail": "Gmail", + "google-docs": "Google Doc", + "google-sheets": "Google Sheet", + "http": "Web Bug", + "idp-app": "IdP App", + "msexcel-macro": "MS Excel Macro Document", + "msword-macro": "MS Word Macro Document", + "mysql-dump": "MySQL Dump", + "office365mail": "Office 365 Mail Bug", + "pdf-acrobat-reader": "Acrobat PDF", + "pwa": "Fake App", + "qr-code": "QR Code", + "sensitive-cmd": "Sensitive Command", + "signed-exe": "Custom Exe/Binary", + "slack-api": "Slack API Key", + "slow-redirect": "Slow Redirect", + "web-image": "Custom Web Image", + "windows-dir": "Windows Folder", + "wireguard": "WireGuard VPN", +} + + +class ThinkstManager: + """ + This is provides the base requests session and api_get/api_post + functionality to talk to the Canry Console. It is expected to be subclassed + for the Actions and Connector specific implementation. + """ + def __init__( + self, + api_key: str, + console_hash: str, + siemplify: SiemplifyAction | SiemplifyConnectorExecution, + verify_ssl: bool = True, + ) -> None: + """ + Initialise the ThinkstManager + + Args: + api_key: api_key of the Canary Console + console_hash: The domain name of Canary Console (the part before canary.tools) + siemplify: The specific siemplify object this uses + verify_ssl: Whether to ignore ssl errors or not + """ + self.api_key = api_key + self.console_hash = console_hash + self.siemplify = siemplify + + # Just so we have bit of a shorter path to call the logger + self.logger = siemplify.LOGGER + + self.session = requests.Session() + self.session.verify = verify_ssl + self.session.params = {"auth_token": api_key} + + self.console_url = f"https://{self.console_hash}.canary.tools" + self.api_base_url = f"{self.console_url}/api/v1" + + def api_get_request(self, endpoint: str, params: dict = {}) -> dict[str, Any]: + """ + Get the json data from the API endpoint + + Args: + endpoint: The api endpoint URL + params: Any extra parameters for the request + + Returns: + Json object + + Raises: + Generic Exception if status_code != 200 + """ + self.logger.debug(f"api_get_request: {endpoint=}, {params=}") + response = self.session.get(endpoint, params=params) + if response.status_code != 200: + raise Exception(f"Unexpected HTTP {response.status_code}: {response.text}") + + return response.json() + + def api_post_request(self, endpoint: str, params: dict = {}) -> dict[str, Any]: + """ + Post data to the API endpoint + + Args: + endpoint: The api endpoint URL + params: Any extra parameters for the request + + Returns: + Json object + + Raises: + Generic Exception if status_code != 200 + """ + self.logger.debug(f"api_post_request: {endpoint=}, {params=}") + response = self.session.post(endpoint, params=params) + if response.status_code != 200: + raise Exception(f"Unexpected HTTP {response.status_code}: {response.text}") + + return response.json() + + +class ThinkstActionManager(ThinkstManager): + """ + The Action specific implementation of the ThinkstManager + """ + def __init__( + self, + api_key: str, + console_hash: str, + siemplify: SiemplifyAction, + verify_ssl: bool = True, + ) -> None: + super().__init__( + api_key=api_key, + console_hash=console_hash, + siemplify=siemplify, + verify_ssl=verify_ssl) + + def ping(self) -> bool: + """ + Call the 'ping' endpoint on the Canary Console + + Returns: + True if the request succeeded + """ + api_endpoint = f"{self.api_base_url}/ping" + + ping_res = self.api_get_request(api_endpoint) + self.logger.debug(f"{ping_res}") + + return ping_res.get("result", "error") == "success" + + def ack_alert(self) -> tuple[Any, str]: + """ + Acknowledge an alert on the Canary Console + + This grabs the 'security_events' from the current_alert and tries to + find the "AlertId" which was stashed there when the connector created + the alert. This is then used to acknowledge the 'incident' on the + Canary Console + + Returns: + Tuple of Execution state and an accompanying message + """ + api_endpoint = f"{self.api_base_url}/incident/acknowledge" + incident_id = None + try: + events = self.siemplify.current_alert.security_events + if len(events) > 0: + properties = events[0].additional_properties + incident_id = properties.get("AlertId") + else: + msg = "No events found for alert" + return (EXECUTION_STATE_FAILED, msg) + except Exception as e: + msg = f"Could not get incident information from alert. {e}" + return (EXECUTION_STATE_FAILED, msg) + + try: + if incident_id: + params = {"incident": incident_id} + resp = self.api_post_request(api_endpoint, params=params) + if resp.get("result", "") == "success": + msg = "Alert acknowledged!" + return (EXECUTION_STATE_COMPLETED, msg) + except Exception as e: + msg = f"Could not acknowledge incident on Canary Console. {e}" + return (EXECUTION_STATE_FAILED, msg) + + msg = "Could not acknowledge incident on Canary Console" + return (EXECUTION_STATE_FAILED, msg) + + +def get_past_time(minutes: int) -> str: + """ + Calculate a time X minutes in the past from now + + The return value is in the format expected by the Canary Console API + + Args: + minutes: How far back to calculate + + Returns: + A string timestamp, formatted as "%Y-%m-%d-%H:%M:%S" + """ + now = datetime.utcnow() + tmp = now - timedelta(minutes=minutes) + past_time = tmp.strftime("%Y-%m-%d-%H:%M:%S") + return past_time + + +def to_unix_milliseconds(timestamp: int | str) -> int: + """ + Converts a UNIX timestamp (in seconds) to milliseconds. + + Args: + timestamp: Input UNIX timestamp in seconds as int or string + + Returns: + UNIX timestamp in milliseconds + """ + timestamp = int(timestamp) + return timestamp * 1000 + + +def value_in_ranged_list(in_list: Sequence[int | tuple[int, int]], check_value: int) -> bool: + """ + Check if value is present in a list (or in range in the list) + + This is used to check if 'check_value' is in a specific set of values + specified by 'in_list'. 'in_list' can contain normal ints, which can just + be directly compared, but it can also specify a range of values to compare + by using a tuple with the start/end values. + + NOTE: The tuples are passed to the 'range' function, so the first value of + the tuple is INCLUDED, and the last one is EXCLUDED. e.g, (0, 3) will only + check if 'check_value' is '0', '1', or '2'. + + Args: + in_list: List containing a mix of ints, or tuples of (int, int) + check_value: the value to search for in the list + + Returns: + True if value was found in list, else False + """ + for entry in in_list: + if isinstance(entry, tuple) and check_value in range(*entry): + return True + elif check_value == entry: + return True + return False + + +def str_to_bool(str_bool: str) -> bool: + """Simple helper to turn a string of true/false into Bool + + Args: + str_bool: A boolean value as string + + Returns: + Boolean True/False + + Raises: + Raises ValueError if it cannot be converted bo boolean + """ + if str_bool.lower() == "true": + return True + elif str_bool.lower() == "false": + return False + + raise ValueError(f"{str_bool} could not be converted to bool") + + +class ThinkstConnectorManager(ThinkstManager): + """ + The Connector specific implementation of the ThinkstManager + """ + def __init__( + self, + api_key: str, + console_hash: str, + siemplify: SiemplifyConnectorExecution, + verify_ssl: bool = True, + ) -> None: + super().__init__( + api_key=api_key, + console_hash=console_hash, + siemplify=siemplify, + verify_ssl=verify_ssl) + + # The 'inflight_x' variants are used to keep track track of latest ID + # and timestamp while alerts from the console has been processed. Once + # that has been successfull if will be saved to the siemplify_context, + # and kept track of with the 'completed_x' variants. + self.inflight_id = 0 + self.inflight_timestamp = 0 + + self.skip_info_prio = siemplify.extract_connector_param( + param_name="Ignore Informative", + default_value="false" + ) + self.skip_info_prio = str_to_bool(self.skip_info_prio) + + # Make the context unique per-console + self.context_id = f"{THINKST_CONTEXT_IDENTIFIER}.{console_hash}" + + self.completed_id = siemplify.get_connector_context_property( + identifier=self.context_id, + property_key=THINKST_UPDATE_KEY, + ) + self.completed_timestamp = siemplify.get_connector_context_property( + identifier=self.context_id, + property_key=THINKST_LAST_TIMESTAMP, + ) + + def _fetch_console_alerts(self, page_limit: int = 20) -> list[dict]: + """ + Fetch new alerts from the Canary Console + + This calls the api, handle pagination, and returns all the incidents + since the last request. The 'last request' is handled by a few layers + of fallback methods: + - Try to use the last stored 'update_id' field + - If that does not exist try to use the stored timestamp + - And if that does not exist use a fixed time + (THINKST_FALLBACK_TIME_MINS) in the past + + Args: + page_limit: Paramter to tell the API to paginate the response + + Returns: + All the incidents as json data + """ + api_endpoint = f"{self.api_base_url}/incidents/unacknowledged" + params = {"limit": str(page_limit)} + + # Prepare to grab incidents from a certain point in time. First try + # from previous id, then try previous timestamp, and if that is not set + # use THINKST_FALLBACK_TIME_MINS + if self.completed_id: + params["incidents_since"] = self.completed_id + elif self.completed_timestamp: + # The timestamp is stored as UNIX time in int, but the API expects + # a string, convert it + timestamp = datetime.fromtimestamp( + self.completed_timestamp, + timezone.utc + ).strftime("%Y-%m-%d-%H:%M:%S") + params["newer_than"] = timestamp + else: + params["newer_than"] = get_past_time(THINKST_FALLBACK_TIME_MINS) + + # Collect incidents accross different pages + new_alerts = [] + last_page = False + while not last_page: + last_page = True + incidents = self.api_get_request(api_endpoint, params=params) + if cursor := incidents.get("cursor"): + if cursor_next := cursor.get("next"): + params["cursor"] = cursor_next + # "limit" parameter is only valid for the first request + params.pop("limit", None) + last_page = False + + # Keep track of the maximum id and timestamp as we + # iterate through the pages + self.inflight_id = max( + self.inflight_id, + incidents.get("max_updated_id", 0) + ) + self.inflight_timestamp = max( + self.inflight_timestamp, + incidents.get("updated_timestamp", 0) + ) + + for incident in incidents.get("incidents", []): + new_alerts.append(incident) + + return new_alerts + + def _gen_portal_url(self, hash_id: str) -> str: + return f"{self.console_url}/nest/incidents/{hash_id}" + + def _gen_hostname(self, hostname: str) -> Optional[str]: + if hostname == "N/A": + return None + return str(hostname) + + def _gen_annotation(self, annotation: dict) -> Optional[str]: + if annotation == {}: + return None + return json.dumps(annotation) + + def _get_priority(self, logtype: str) -> str: + """ + Get priority for specific incident types + + This uses the 'value_in_ranged_list' helper to check if the 'logtype' + is in a specific range of values. This can be used to configure what + priority level should be assigned to every type of event. If the + 'logtype' is not in any of the list it will return the default + priority of 'CRITICAL'. + + Some of the event lists might be empty, they are just placeholders + to allow for easy addition of more priority mappings should it be + needed. + + Args: + logtype: The logtype which corresponds with the type of incident + + Returns: + A string value of the relevant priority + """ + # Informative = -1,Low = 40,Medium = 60,High = 80,Critical = 100. + log_int = int(logtype) + informative_events: Sequence[int | tuple[int, int]] = [ + 1004, # Canary Disconnected + 23002, # Canary Settings Changed + 23001, # Console Settings Changed + 23003, # Flock Settings Changed + ] + low_events: Sequence[int | tuple[int, int]] = [] + medium_events: Sequence[int | tuple[int, int]] = [ + (5001, 5010), # Port scan incident range 1 + (5011, 5013), # Port scan incident range 2 + ] + high_events: Sequence[int | tuple[int, int]] = [] + + if value_in_ranged_list(informative_events, log_int): + return str(PRIORITY_INFO) + if value_in_ranged_list(low_events, log_int): + return str(PRIORITY_LOW) + if value_in_ranged_list(medium_events, log_int): + return str(PRIORITY_MED) + if value_in_ranged_list(high_events, log_int): + return str(PRIORITY_HIGH) + + return str(PRIORITY_CRIT) + + def _gen_new_val(self, in_val: Any) -> Optional[str]: + """ + Get the string representation of in_val for event mapping + + If the value is None, "", or -1, return None. Or if the str() + conversion fails, just return None. This will cause this key/value + combination to not be included in the event data. + + Args: + in_val: The input value to convert + + Returns: + str(in_val) or None + """ + + if in_val in [None, "", -1, "-1"]: + return None + + try: + return str(in_val) + except Exception: + self.logger.info(f"Converting {in_val} to string failed, skipping.") + return None + + def _get_new_key_val(self, in_dict: dict) -> dict: + """ + Converts existing dictionary key/values to new ones + + This function is used to convert an existing key/value pairs in the input + dictionary into a new key, value pair. The 'common_mapping' dictionary + is used for this. The original key is used to lookup a tuple in the + mapping dictionary to find a new key, as well as a transformation function + that needs to be applied to the value. The argument to the transformation + function is always just the original value. + + Note: This returns a new dictionary, and if the key is not present in + the mapping dictionary it will not be included in the result. The transformer + function can also return 'None' as indicator that this key should be + skipped. + + Args: + in_dict: Original values to convert to new dict + + Returns: + Dictionary containing transformed key/value pairs based on the + 'common_mapping' dictionary. + """ + common_mapping: dict[str, tuple[str, Callable]] = { + "summary": ("Name", self._gen_new_val), + "logtype": ("Priority", self._get_priority), + "memo": ("Reminder", self._gen_new_val), + "name": ("DestinationHostName", self._gen_hostname), + "flock_name": ("FlockName", self._gen_new_val), + "flock_id": ("FlockId", self._gen_new_val), + "id": ("AlertId", self._gen_new_val), + "hash_id": ("PortalURL", self._gen_portal_url), + "created": ("StartTime", to_unix_milliseconds), + "updated_time": ("EndTime", to_unix_milliseconds), + "src_port": ("SourcePort", self._gen_new_val), + "src_host": ("SourceAddress", self._gen_new_val), + "src_host_reverse": ("SourceHostName", self._gen_new_val), + "dst_port": ("DestinationPort", self._gen_new_val), + "dst_host": ("DestinationAddress", self._gen_new_val), + "USERNAME": ("IncidentUserName", self._gen_new_val), + "PASSWORD": ("IncidentPassword", self._gen_new_val), + "KEY": ("IncidentSSHPubKey", self._gen_new_val), + "matched_annotations": ("Annotations", self._gen_annotation), + "FILENAME": ("FileName", self._gen_new_val), + "USER": ("IncidentUserName", self._gen_new_val), + "REMOTENAME": ("smb_RemoteName", self._gen_new_val), + "DOMAIN": ("smb_Domain", self._gen_new_val), + "SMBARCH": ("smb_Architecture", self._gen_new_val), + "SHARENAME": ("smb_ShareName", self._gen_new_val), + "MODE": ("smb_Mode", self._gen_new_val), + } + + new_dict = {} + for old_key, old_val in in_dict.items(): + new_key, func = common_mapping.get(old_key, (None, None)) + # If key is not present in mapping it is not included in the new dictionary + if not new_key: + continue + if callable(func): + # get the transformed new value calling the function from the tuple + new_val = func(old_val) + # The transformer function might return "None" as indication that + # the key should not be included + if new_val is None: + continue + else: + new_val = old_val + new_dict[new_key] = new_val + + return new_dict + + def _gen_event_name(self, event: Optional[dict], incident: dict, alert_name: bool = False): + """ + Generate an incident name + + The level of detail depens on whether or not this is being generated for an + alert or an event inside the alert. The alert has limited display space, + so this returns a slightly shorter name. + + Args: + event: A single event dictionary. Might be 'None' in some cases + incident: The incident dictionary + alert_name: Set if it should generate an alert name instead of event name + + Returns: + String of the new name + """ + incident_desc = incident.get("description", {}) + desc = incident_desc.get("description", "") + + if "canarytoken" not in incident.get("id", "") or event is None: + return f"{desc}" + else: + # Do token things + token_type = event.get("type", "") + token_name = TOKEN_NAME_MAP.get(token_type, token_type) + if alert_name: + return f"{token_name} {desc}" + memo = incident_desc.get("memo", "") + return f"{token_name} {desc}: {memo}" + + def _parse_events(self, events: list[dict], incident: dict) -> list[dict]: + """ + Generate SOAR event entries from Canary Console Incidents + + The '_get_new_key_val' helper is used to create a list of new event + dictionaries with keys and values understandable by the SOAR + integration. + + Note: The 'events' list might be empty, in which case just the single + 'event_common' dictionary is returned as a list entry. + + Args: + events: The list of events associated with the incident + incident: The original incident + + Returns: + A list of flat event dictionaries which is valid to add to + AlertInfo().events + """ + incident_desc = incident.get("description", {}) + event_common = self._get_new_key_val(incident) + event_common.update(self._get_new_key_val(incident_desc)) + event_common["DeviceProduct"] = THINKST_PRODUCT + event_common["device_product"] = THINKST_PRODUCT + event_common["DeviceVendor"] = THINKST_VENDOR + event_common["device_vendor"] = THINKST_VENDOR + if "canarytoken" in incident.get("id", ""): + event_common["SourceType"] = "Canarytoken" + elif incident_desc.get("logtype", "") in THINKST_OPERATIONAL_LOGTYPES: + event_common["SourceType"] = "Operational" + else: + event_common["SourceType"] = "Honeypot" + + # Some incidents do not generate an 'events' list, just return + # the information common to all events. + if events == []: + event_common["Name"] = self._gen_event_name(None, incident) + return [event_common] + + alert_events = [] + for event in events: + # Every event contains the common event data + event specific data + new_event = copy.deepcopy(event_common) + new_event.update(self._get_new_key_val(event)) + # Event naming is more complicated than just a simple + # transformation function, so populate it here instead + new_event["Name"] = self._gen_event_name(event, incident) + new_event["Event_RawJSON"] = json.dumps(event) + new_event["Alert_RawJSON"] = json.dumps(incident) + alert_events.append(new_event) + return alert_events + + def _fill_alert_info(self, event: Optional[dict], incident: dict) -> AlertInfo: + """ + Create and populate the AlertInfo object + + Note: This excludes the event information + + Args: + event: Just a single event - used to generate the alert name + incident: The Canary Console incident data + + Returns: + The populated AlertInfo object + """ + incident_desc = incident.get("description", {}) + + alert_info = AlertInfo() + alert_info.id = incident.get("id") + alert_info.display_id = incident.get("id") + alert_info.ticket_id = incident.get("id") + alert_info.identifier = incident.get("id") + + name = self._gen_event_name(event, incident, alert_name=True) + alert_info.name = name + alert_info.rule_generator = name + + start_time = to_unix_milliseconds(incident_desc.get("created", 0)) + end_time = to_unix_milliseconds(incident.get("updated_time", 0)) + + # Adjust for bug where this will show as 1970 if they are the same + if end_time == start_time: + end_time += 1 + alert_info.start_time = start_time + alert_info.end_time = end_time + + log_type = incident_desc.get("logtype", "") + alert_info.priority = int(self._get_priority(log_type)) + + alert_info.device_vendor = THINKST_VENDOR + alert_info.device_product = THINKST_PRODUCT + alert_info.environment = self.siemplify.context.connector_info.environment + + return alert_info + + def fetch_alerts(self) -> list[AlertInfo]: + """ + Fetches and converts Canary Console incidents into SOAR AlertInfo objects + + This fetches the latest incidents from the Canary console, use the helper + functions to exctract and transform the data, and then returns it as a + list of AlertInfo objects. + + Returns: + A list of AlertInfo() objects which can be used by the SiemplifyConnector + """ + alerts = [] + self.logger.debug("Fetching Canary Console alerts") + incidents = self._fetch_console_alerts(page_limit=20) + self.logger.debug("Canary Console Alerts fetched, converting to AlertInfo") + for incident in incidents: + description = incident.get("description", {}) + + # If "Ignore Informative" is set by the user then do not create + # an alert for this incident + if self.skip_info_prio: + logtype = description.get("logtype") + if logtype and self._get_priority(logtype) == "-1": + continue + + # Fetch and parse the events from the Canary Console + console_events = description.pop("events", []) + alert_events = self._parse_events(console_events, incident) + + # Generate and fill in the basic AlertInfo object + event0 = console_events[0] if len(console_events) > 0 else None + alert_info = self._fill_alert_info(event0, incident) + + # Add the parsed events and entities to it, and add it to the list + alert_info.events = alert_events + alerts.append(alert_info) + + # Allerts have been collected succesfully, store the ID and timestamp + self.siemplify.set_connector_context_property( + identifier=self.context_id, + property_key=THINKST_UPDATE_KEY, + property_value=self.inflight_id, + ) + self.siemplify.set_connector_context_property( + identifier=self.context_id, + property_key=THINKST_LAST_TIMESTAMP, + property_value=self.inflight_timestamp, + ) + return alerts diff --git a/content/response_integrations/third_party/thinkst_canary/definition.yaml b/content/response_integrations/third_party/thinkst_canary/definition.yaml new file mode 100644 index 000000000..8db1c1c41 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/definition.yaml @@ -0,0 +1,31 @@ +identifier: THINKST +name: Thinkst +parameters: + - name: API Key + default_value: + type: password + description: The API key for your console + is_mandatory: true + integration_identifier: THINKST + + - name: Console Hash + default_value: + type: string + description: The hash for your console (the part before .canary.tools) + is_mandatory: true + integration_identifier: THINKST + + # The Canary Consoles all have valid certificates, but this toggle is + # required by the SecOps infrastructure. + - name: Verify SSL + type: boolean + description: If you need to toggle this to connect something is wrong + is_mandatory: false + default_value: true + integration_identifier: THINKST + +categories: + - Security + - Threat Intelligence +svg_logo_path: resources/logo.svg +image_path: resources/image.png diff --git a/content/response_integrations/third_party/thinkst_canary/ontology_mapping.yaml b/content/response_integrations/third_party/thinkst_canary/ontology_mapping.yaml new file mode 100644 index 000000000..62f52c662 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/ontology_mapping.yaml @@ -0,0 +1,182 @@ +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: StartTime + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: StartTime + transformation_function: from_unix_time_string_or_long + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: EndTime + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: EndTime + transformation_function: from_unix_time_string_or_long + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: Name + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: Name + transformation_function: to_string + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: DestinationPort + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: DestinationPort + transformation_function: to_string + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: '' + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: SourceAddress + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: SourceAddress + transformation_function: to_ip_address + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: '' + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: DestinationAddress + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: DestinationAddress + transformation_function: to_ip_address + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: '' + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: DestinationHostName + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: DestinationHostName + transformation_function: to_string + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: '' + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: SourceAddress + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: SourceAddress + transformation_function: to_ip_address + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: '' + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: DestinationAddress + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: DestinationAddress + transformation_function: to_ip_address + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: StartTime + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: StartTime + transformation_function: from_unix_time_string_or_long + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: EndTime + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: EndTime + transformation_function: from_unix_time_string_or_long + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: Name + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: Name + transformation_function: to_string + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: DestinationPort + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: DestinationPort + transformation_function: to_string + transformation_function_param: null +- source: THINKST + extraction_function: none + extraction_function_param: null + is_artifact: false + raw_data_primary_field_comparison_type: equal + raw_data_primary_field_match_term: Reminder + raw_data_secondary_field_comparison_type: equal + raw_data_secondary_field_match_term: null + raw_data_third_field_comparison_type: equal + raw_data_third_field_match_term: null + security_event_file_name: Message + transformation_function: to_string + transformation_function_param: null diff --git a/content/response_integrations/third_party/thinkst_canary/pyproject.toml b/content/response_integrations/third_party/thinkst_canary/pyproject.toml new file mode 100644 index 000000000..82d38eae8 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/pyproject.toml @@ -0,0 +1,25 @@ +[project] +name = "Thinkst-Canary" +version = "1.0" +description = "Listening to events from your Thinkst canary console. This integration is owned by the Thinkst Canary. Support Contact: support@thinkst.com. " +requires-python = ">=3.11,<3.12" +dependencies = [ + "requests", +] + +[dependency-groups] +dev = [ + "environmentcommon", + "integration-testing", + "pytest-json-report>=1.5.0", + "pytest>=8.3.5", + "soar-sdk", + "tipcommon", +] + +[tool.uv.sources] +soar-sdk = { git = "https://github.com/chronicle/soar-sdk.git" } +integration-testing = { path = "../../../../packages/integration_testing_whls/integration_testing-2.2.7-py3-none-any.whl" } +tipcommon = { path = "../../../../packages/tipcommon/TIPCommon-2.2.7/TIPCommon-2.2.7-py2.py3-none-any.whl" } +environmentcommon = { path = "../../../../packages/envcommon/EnvironmentCommon-1.0.2/EnvironmentCommon-1.0.2-py2.py3-none-any.whl" } + diff --git a/content/response_integrations/third_party/thinkst_canary/release_notes.yaml b/content/response_integrations/third_party/thinkst_canary/release_notes.yaml new file mode 100644 index 000000000..7d7e73fe4 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/release_notes.yaml @@ -0,0 +1,7 @@ +- description: NEW! Thinkst Canary integration. + integration_version: 1.0 + item_name: THINKST + item_type: Integration + publish_time: '2025-11-14' + ticket_number: xxx + new: true diff --git a/content/response_integrations/third_party/thinkst_canary/resources/image.png b/content/response_integrations/third_party/thinkst_canary/resources/image.png new file mode 100644 index 000000000..59ae89294 Binary files /dev/null and b/content/response_integrations/third_party/thinkst_canary/resources/image.png differ diff --git a/content/response_integrations/third_party/thinkst_canary/resources/logo.svg b/content/response_integrations/third_party/thinkst_canary/resources/logo.svg new file mode 100644 index 000000000..16056b091 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/resources/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/content/response_integrations/third_party/thinkst_canary/tests/__init__.py b/content/response_integrations/third_party/thinkst_canary/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/tests/common.py b/content/response_integrations/third_party/thinkst_canary/tests/common.py new file mode 100644 index 000000000..85a6c8fd6 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/tests/common.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +import pathlib + +INTEGRATION_PATH: pathlib.Path = pathlib.Path(__file__).parent.parent +MOCKS_PATH = pathlib.Path.joinpath(INTEGRATION_PATH, "tests", "mocks") diff --git a/content/response_integrations/third_party/thinkst_canary/tests/conftest.py b/content/response_integrations/third_party/thinkst_canary/tests/conftest.py new file mode 100644 index 000000000..3d4d6f23e --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/tests/conftest.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +pytest_plugins = ("integration_testing.conftest",) diff --git a/content/response_integrations/third_party/thinkst_canary/tests/mocks/incidents_1.json b/content/response_integrations/third_party/thinkst_canary/tests/mocks/incidents_1.json new file mode 100644 index 000000000..6e486fb36 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/tests/mocks/incidents_1.json @@ -0,0 +1,410 @@ + [ + { + "description": { + "acknowledged": "False", + "created": "1762757102", + "created_std": "2025-11-10 06:45:02 UTC+0000", + "description": "SSH Login Attempt", + "dst_host": "192.168.50.220", + "dst_port": "22", + "events": [ + { + "INSTANCE_NAME": "Default SSH server", + "KEY": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJbH1IIYhkoe9R6c3DEkou53nDWK5rIblsOLMCBG8Utd", + "LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", + "PORT": 22, + "REMOTEVERSION": "SSH-2.0-OpenSSH_9.9", + "USERNAME": "root", + "timestamp": 1762757102, + "timestamp_std": "2025-11-10 06:45:02 UTC+0000" + }, + { + "INSTANCE_NAME": "Default SSH server", + "KEY": "ssh-ed25519 AAAAIHNzaC1lZDI1NTE5LWNlnQtdjAxQG9wZW5zc2guY29tAAAAIGNLR18lDmJH/qpleBDMXe5sqeVIy/zEZZ1Yt26e/moRAAAAINpUce0LFRnpcIYACCx/7CQ6n+DedDdKTOy1zeW4Kb0AAAAAAAAAAAAAAABAAAAJDZlMzE3MTc0LTU1MTAtNDczNi1iOGVmLTk5ZDY4NjllZmNjMAAAAAcAAAADZ2l0AAAAAGkUFrIAAAAAaRSHqgAAACMAAAAOc291cmNlLWFkZHJlc3MAAAANAAAACTAuMC4wLjAvMAAAADEAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAAAAAAAhwAAAAMcnNhLXNoYTItNTEyAAAAAwEAAQAAAgEAb1ABAJfWZMJAC5XzigLVg/i4pFGbSudSbGgBPdRAz5ccnFt4ord1/ov8q5tpSWJRPvOO1/DQL3MOt5fqgE9ulK/u+S4x7y+iS18Z1aMuABha79Z9AUwktGW8MYqJJPh198esZkvv0U6bGFjTl7Qem4HVu9AirFH4RrSrHESOuTuHpXrfK8um90UhZ6KJgLjbT+McKLnIUzC52njH1G44kJl9DaliRBTEhYK5KMWNZZQeuUc8mAeuBEyF08NE9h17AL2cE5D2dgOGMdOo/pkxbkApWrzF37cbimhwUzY5UU9D3TT2ZONtc//igvOwz1zzTXdjKjzFk897HegS7lUnkG9pFt5Clr/JGtbYdaDF4ohdmVU02UOOHnDyaTR3vGhZMMBnt9w3dm61agRnr8AXRh0r9veVMwGMzNjxfx96nbHdoe5dpGug6vxmCvL01pFFyfcltyGYj1wCFcnGAzlxF0qnXUUeCIv37HMGTgcNhT+2LNHSuL2P4FWCL4//qOCmlSOQZzXNLC/N7pa4AJ5m7O4QqUO/2SiwmqY/ByBhxK0AN3cy6WyqRdFo8B2GI/zjPJdFnX2poRXYUmIBYzkUWZsT5VyOFu99JRYHTqRezMFUYMOiQJBn3aKSCwkmIs/x+rWAJV0bBdEY083NRND7/lYlVJ2W1mkMjmUwiqr8AAAIUAAAADHJzYS1zaGEyLTUxMgAAAgBXqOrCXD9gqZWSAUEPlY5aG7AFV1GLB/SE7ny5e4YlyIRKIBtfzoKbXnxfw95KwGgPL4sTH09NtpwVR682Pw/5gMVbCM5QoomR4TQhQ3XGXM2PY2DuLkk0SbA3NKle4fdBmdD32riIqRge/XjzwVNqg8TH/3bc2yL1duUI/CD6ZTULJ5u5DjFfLxURXGDSrzqLyr3JzJII6JX2vZMw/Qxlma3ZVfEsZxnUH6KpHRcIaG7duVFmcgZSE5V9ZDDU1nua5lKELEMTV7SgUwVVkiGQS1FIdhp4BLeYzITB5B4438nIzVjvh0zA6DLDoJgvYSPRvxKGPf8QmrfjaZfOGyk6kR5toe0rZ8J6jMmqcxmln+0Lg5co5huqriQ72LYr2SFne4fcL9qva8YBa158WGFo4rN2OecWaDa17imB6M7Bb0SVa9SXP1ECH/Qp7N50PRJg4cXhuS29OLVvS/4zfntmEDxkR6pESLI1FlFA77rC+CD01Df/K+JvJi1XzD9zQvnaVUuL0pOUrcWOroCqEQObSo/dy1QwG9xWN0y1J3mI5GqbTNfUVPcs31AK6lyhAfdVhdOWf9XcJ59GepkB/M/zKZ7d4MujMWgWsVnp82ZfkaJ+ra6k9S7n6puA4/mxFYh8xdZfjw==", + "LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", + "PORT": 22, + "REMOTEVERSION": "SSH-2.0-OpenSSH_9.9", + "USERNAME": "root", + "timestamp": 1762757103, + "timestamp_std": "2025-11-10 06:45:03 UTC+0000" + }, + { + "INSTANCE_NAME": "Default SSH server", + "LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", + "PASSWORD": "qwerwer", + "PORT": 22, + "REMOTEVERSION": "SSH-2.0-OpenSSH_9.9", + "USERNAME": "root", + "timestamp": 1762757106, + "timestamp_std": "2025-11-10 06:45:06 UTC+0000" + } + ], + "events_count": "3", + "events_list": "1762757102,1762757103,1762757106", + "flock_id": "flock:855404d8f81efb6fe8447f2001d32531", + "flock_name": "Testing_latency", + "ippers": "", + "local_time": "2025-11-10 06:44:54", + "logtype": "4002", + "mac_address": "", + "matched_annotations": {}, + "name": "test-host", + "node_id": "000000004e46095c", + "notified": "False", + "src_host": "192.168.50.32", + "src_host_reverse": "", + "src_port": "55202" + }, + "hash_id": "fe817692b611ed086a6d26b0f2e93298", + "id": "incident:sshlogin:832426ae8c3a0e1450ac6dba:192.168.50.32:1762757102", + "summary": "SSH Login Attempt", + "updated": "Mon, 10 Nov 2025 06:45:06 GMT", + "updated_id": 5429, + "updated_std": "2025-11-10 06:45:06 UTC+0000", + "updated_time": "1762757106" + }, + { + "description": { + "acknowledged": "False", + "created": "1762769080", + "created_std": "2025-11-10 10:04:40 UTC+0000", + "description": "Local Tampering Detected", + "dst_host": "192.168.50.220", + "dst_port": "-1", + "events": [ + { + "MESSAGE": "An interactive login shell was started on bird by uid 0 for shell `bash`.", + "OLD_LOGTYPE": 1031, + "sectors": [ + "2e134c53", + "2e13482c", + "2e13302c", + "2e132d62", + "2e136173", + "2e13682c", + "2e133936", + "2e133338", + "2e132c73", + "2e137368", + "2e1364", + "", + "" + ], + "timestamp": 1762769080, + "timestamp_std": "2025-11-10 10:04:40 UTC+0000" + } + ], + "events_count": "1", + "events_list": "1762769080", + "flock_id": "flock:855404d8f81efb6fe8447f2001d32531", + "flock_name": "Testing_latency", + "ippers": "", + "local_time": "2025-11-10 10:04:32", + "logtype": "30001", + "mac_address": "", + "matched_annotations": { + "tripwire_trigger": [ + "One of your birds has suffered a loss of integrity and requires special attention.", + "https://help.canary.tools/hc/en-gb/articles/4495566572317-Local-Tampering-Detected-Alert" + ] + }, + "name": "test-host", + "node_id": "000000004e46095c", + "notified": "False", + "src_host": "", + "src_host_reverse": "", + "src_port": "-1" + }, + "hash_id": "bda958254d8aefbabdf7dad1a665351b", + "id": "incident:canaryviolation:832426ae8c3a0e1450ac6dba::1762769080", + "summary": "Local Tampering Detected", + "updated": "Mon, 10 Nov 2025 10:04:40 GMT", + "updated_id": 5430, + "updated_std": "2025-11-10 10:04:40 UTC+0000", + "updated_time": "1762769080" + }, + { + "description": { + "acknowledged": "False", + "created": "1762769151", + "created_std": "2025-11-10 10:05:51 UTC+0000", + "description": "Canarytoken triggered", + "dst_host": "1.1.1.1", + "dst_port": "80", + "events": [ + { + "canarytoken": "f86k6sqfb7rzdobwwzpzyueow", + "dst_port": 80, + "geoip": { + "city": "Santas Workshop", + "continent_code": "N", + "country": "North Pole", + "country_code": "N/A", + "country_code3": "N/A", + "currency_code": "N/A", + "host_domain": "", + "hostname": "", + "ip": "9.8.7.6", + "is_bogon": false, + "is_v4_mapped": false, + "is_v6": false, + "language_code": "e", + "latitude": -0.0, + "longitude": 0.0, + "region": "Somewhere", + "region_code": "N", + "timezone": { + "abbr": "GMT", + "date": "2025-11-10", + "id": "Pole/North", + "name": "Christmas Time", + "offset": "+00:00", + "time": "12:05:54.238262775" + }, + "valid": true + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", + "Connection": "keep-alive", + "Host": "a7608dbdf80e.test.dom", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" + }, + "ip_blocklist": { + "is_proxy": false, + "is_tor": false, + "is_vpn": false + }, + "request_args": {}, + "scanner": { + "Browser": { + "browser": "Chrome", + "enabled": "1", + "installed": "1", + "language": "en-GB", + "mimetypes": "Portable Document Format;pdf;application/pdf|||Portable Document Format;pdf;text/pdf|||", + "name": "Browser", + "os": "Macintosh", + "platform": "MacIntel", + "vendor": "Google Inc.", + "version": "142.0.0.0" + }, + "Javascript": { + "enabled": "1", + "installed": "1", + "name": "Javascript", + "version": "" + } + }, + "src_host": "9.8.7.6", + "src_port": 57538, + "timestamp": 1762769151, + "timestamp_std": "2025-11-10 10:05:51 UTC+0000", + "type": "http" + } + ], + "events_count": "1", + "events_list": "1762769151", + "flock_id": "flock:855404d8f81efb6fe8447f2001d32531", + "flock_name": "Testing_latency", + "local_time": "2025-11-10 10:05:51 (UTC)", + "logtype": "17000", + "matched_annotations": {}, + "memo": "dns type check", + "name": "N/A", + "node_id": "f86k6sqfb7rzdobwwzpzyueow", + "notified": "False", + "src_host": "9.8.7.6", + "src_port": "57538" + }, + "hash_id": "ce0c46d3f1cbae9f4c4ef221456613a3", + "id": "incident:canarytoken:b73fbf21b2f237b7129013dc:169.1.17.138:1762769151", + "summary": "Canarytoken triggered", + "updated": "Mon, 10 Nov 2025 10:05:55 GMT", + "updated_id": 5434, + "updated_std": "2025-11-10 10:05:55 UTC+0000", + "updated_time": "1762769155" + }, + { + "description": { + "acknowledged": "False", + "created": "1762769174", + "created_std": "2025-11-10 10:06:14 UTC+0000", + "description": "Canarytoken triggered", + "dst_host": "9.8.7.6", + "dst_port": "53", + "events": [ + { + "canarytoken": "ojoh77dp1rxvwzvmvldohusm1", + "dst_port": 53, + "hostname": "ojoh77dp1rxvwzvmvldohusm1.a7608dbdf80e.test.dom", + "src_host": "1.2.3.4", + "src_port": 12277, + "timestamp": 1762769174, + "timestamp_std": "2025-11-10 10:06:14 UTC+0000", + "type": "dns" + }, + { + "canarytoken": "ojoh77dp1rxvwzvmvldohusm1", + "dst_port": 53, + "hostname": "oJoH77dp1RXvwZVmVlDoHUsM1.a7608DBdf80E.test.dom", + "src_host": "5.6.7.8", + "src_port": 41944, + "timestamp": 1762769174, + "timestamp_std": "2025-11-10 10:06:14 UTC+0000", + "type": "dns" + }, + { + "canarytoken": "ojoh77dp1rxvwzvmvldohusm1", + "dst_port": 53, + "hostname": "ojoh77dp1rxvwzvmvldohusm1.a7608dbdf80e.test.dom", + "src_host": "9.10.11.12", + "src_port": 51773, + "timestamp": 1762769174, + "timestamp_std": "2025-11-10 10:06:14 UTC+0000", + "type": "dns" + }, + { + "canarytoken": "ojoh77dp1rxvwzvmvldohusm1", + "dst_port": 53, + "hostname": "ojoh77dp1rxvwzvmvldohusm1.a7608dbdf80e.test.dom", + "src_host": "13.14.15.16", + "src_port": 62087, + "timestamp": 1762769174, + "timestamp_std": "2025-11-10 10:06:14 UTC+0000", + "type": "dns" + }, + { + "canarytoken": "ojoh77dp1rxvwzvmvldohusm1", + "dst_port": 53, + "geoip": { + "city": "Santas Workshop", + "continent_code": "N", + "country": "North Pole", + "country_code": "N/A", + "country_code3": "N/A", + "currency_code": "N/A", + "host_domain": "", + "hostname": "", + "ip": "9.8.7.6", + "is_bogon": false, + "is_v4_mapped": false, + "is_v6": false, + "language_code": "e", + "latitude": -0.0, + "longitude": 0.0, + "region": "Somewhere", + "region_code": "N", + "timezone": { + "abbr": "GMT", + "date": "2025-11-10", + "id": "Pole/North", + "name": "Christmas Time", + "offset": "+00:00", + "time": "12:05:54.238262775" + }, + "valid": true + }, + "hostname": "ojoh77dp1rxvwzvmvldohusm1.a7608dbdf80e.test.dom", + "ip_blocklist": { + "is_proxy": false, + "is_tor": false, + "is_vpn": false + }, + "src_host": "5.5.5.5", + "src_port": 12277, + "timestamp": 1762769174, + "timestamp_std": "2025-11-10 10:06:14 UTC+0000", + "type": "dns" + } + ], + "events_count": "5", + "events_list": "1762769174,1762769174,1762769174,1762769174,1762769174", + "flock_id": "flock:855404d8f81efb6fe8447f2001d32531", + "flock_name": "Testing_latency", + "local_time": "2025-11-10 10:06:14 (UTC)", + "logtype": "16000", + "matched_annotations": {}, + "memo": "temp token", + "name": "N/A", + "node_id": "ojoh77dp1rxvwzvmvldohusm1", + "notified": "False", + "src_host": "5.5.5.5", + "src_port": "12277" + }, + "hash_id": "d7d73a2f77fb1e46ac89a4c5f6dd414a", + "id": "incident:canarytoken:093f843c12eac61b1822fc8c:1762769174", + "summary": "Canarytoken triggered", + "updated": "Mon, 10 Nov 2025 10:06:14 GMT", + "updated_id": 5439, + "updated_std": "2025-11-10 10:06:14 UTC+0000", + "updated_time": "1762769174" + }, + { + "description": { + "acknowledged": "False", + "created": "1762775926", + "created_std": "2025-11-10 11:58:46 UTC+0000", + "description": "Shared File Opened", + "dst_host": "4.3.2.1", + "dst_host_public_ip": "1.2.3.4", + "dst_port": "445", + "events": [ + { + "AUDITACTION": "pread", + "DOMAIN": "AWS-NEW-I-XXXX", + "FILENAME": "IT/Default Cisco Router Config.docx", + "LOCALNAME": "aws-new-i-xxxxx", + "MODE": "workgroup", + "REMOTENAME": "mac-xxxxx", + "SHARENAME": "Documents", + "SMBARCH": "OSX", + "SMBVER": "SMB3_00", + "STATUS": "ok", + "USER": "guest", + "timestamp": 1762775926, + "timestamp_std": "2025-11-10 11:58:46 UTC+0000" + }, + { + "AUDITACTION": "pread", + "DOMAIN": "AWS-NEW-I-0B8A3", + "FILENAME": "IT/Default Cisco Router Config.docx", + "LOCALNAME": "aws-new-i-xxxxx", + "MODE": "workgroup", + "REMOTENAME": "mac-xxxxx", + "SHARENAME": "Documents", + "SMBARCH": "OSX", + "SMBVER": "SMB3_00", + "STATUS": "ok", + "USER": "guest", + "timestamp": 1762775928, + "timestamp_std": "2025-11-10 11:58:48 UTC+0000" + } + ], + "events_count": "2", + "events_list": "1762775926,1762775928", + "flock_id": "flock:855404d8f81efb6fe8447f2001d32531", + "flock_name": "Testing_latency", + "ippers": "", + "local_time": "2025-11-10 11:58:37", + "logtype": "5000", + "mac_address": "", + "matched_annotations": {}, + "name": "aws-af-south-new", + "node_id": "00010b8a3c0cbcf3", + "notified": "False", + "src_host": "1.2.3.4", + "src_host_reverse": "ec2-1-2-3-4.af-south-1.compute.amazonaws.com", + "src_port": "-1" + }, + "hash_id": "42fbf671571ba4ae6c82050fba8a23cc", + "id": "incident:smbfileopen:d12f737f35c9dce750a03df1:13.245.103.199:1762775926", + "summary": "Shared File Opened", + "updated": "Mon, 10 Nov 2025 11:58:48 GMT", + "updated_id": 5441, + "updated_std": "2025-11-10 11:58:48 UTC+0000", + "updated_time": "1762775928" + } + ] diff --git a/content/response_integrations/third_party/thinkst_canary/tests/test_connectors/test_thinkst_connector.py b/content/response_integrations/third_party/thinkst_canary/tests/test_connectors/test_thinkst_connector.py new file mode 100644 index 000000000..ca1e6baa2 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/tests/test_connectors/test_thinkst_connector.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +import json +import logging +import pathlib + +import pytest +from thinkst_canary.core.constants import ( + THINKST_PRODUCT, + THINKST_VENDOR, +) +from thinkst_canary.core.thinkst_manager import ThinkstConnectorManager +from thinkst_canary.tests.common import MOCKS_PATH + +FAKE_API_KEY: str = "424d71d40253b9d9e555563b1e8f7b1025ca95a7d261642427f0a2d9906560d3" +FAKE_CONSOLE_HASH: str = "b1946ac9" +INCIDENTS_FILE: str = "incidents_1.json" + + +def mock_fetch_console_alerts(self, page_limit: int = 20) -> list[dict]: + """ + Mocks the "fetch_console_alerts() function + + Instead of calling the Canary Console Api it just reads in + incident data from file. The 'page_limit' argument is ignored + """ + incident_json = pathlib.Path.joinpath(MOCKS_PATH, INCIDENTS_FILE) + with open(incident_json, "r") as alert_file: + incident_data = json.load(alert_file) + + return incident_data + + +class MockSiemplifyConnector: + """ + Mock the mininmum functions used by the ThinkstConnectorManager + """ + LOGGER = logging.getLogger(__name__) + + def set_connector_context_property(self, identifier, property_key, property_value): + return + + def get_connector_context_property(self, identifier, property_key): + return None + + def extract_connector_param(self, param_name, default_value): + return default_value + + class context: + class connector_info: + environment = "ENV" + + +@pytest.fixture +def connector(monkeypatch: pytest.MonkeyPatch): + """ + Creates a ThinkstConnectorManager instance for testing + + This includes mocking any functionality which might be needed + """ + monkeypatch.setattr(ThinkstConnectorManager, '_fetch_console_alerts', mock_fetch_console_alerts) + siemplify = MockSiemplifyConnector() + connector = ThinkstConnectorManager( + FAKE_API_KEY, + FAKE_CONSOLE_HASH, + siemplify, + True, + ) + yield connector + + +def test_parse_events(connector: ThinkstConnectorManager): + """ + Check that parsing events from incidents works correctly + + It checks that the correct number of events are created for every + incident, and that certain key fields and values are present. + """ + incidents = connector._fetch_console_alerts() + for index, incident in enumerate(incidents): + description = incident.get("description", {}) + console_events = description.pop("events", []) + alert_events = connector._parse_events(console_events, incident) + + # Check the number of events + if console_events == []: + assert len(alert_events) == 1 + else: + assert len(alert_events) == len(console_events) + + # Spot check a few important fields + for event in alert_events: + assert event.get("DeviceProduct") == THINKST_PRODUCT + assert event.get("DeviceVendor") == THINKST_VENDOR + + important_keys = ["AlertId", "EndTime", "StartTime", "Name", + "SourceType", "Event_RawJSON", "Alert_RawJSON"] + for check_key in important_keys: + assert check_key in event.keys(), f"'{check_key}' not in {event.keys()=}" + + +def test_fetch_alerts(connector: ThinkstConnectorManager): + """ + Check that creating AlertInfo() objects works correctly + + It checks the correct number of alerts are created, and that important + fields are set + """ + incidents = connector._fetch_console_alerts() + alerts = connector.fetch_alerts() + assert len(incidents) == len(alerts) + + for alert in alerts: + assert alert.device_product == THINKST_PRODUCT + assert alert.device_vendor == THINKST_VENDOR + assert alert.ticket_id is not None + assert alert.display_id is not None + assert alert.name is not None diff --git a/content/response_integrations/third_party/thinkst_canary/tests/test_defaults/__init__.py b/content/response_integrations/third_party/thinkst_canary/tests/test_defaults/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/content/response_integrations/third_party/thinkst_canary/tests/test_defaults/test_imports.py b/content/response_integrations/third_party/thinkst_canary/tests/test_defaults/test_imports.py new file mode 100644 index 000000000..09334b755 --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/tests/test_defaults/test_imports.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from integration_testing.default_tests.import_test import import_all_integration_modules + +from .. import common + + +def test_imports() -> None: + """Import all integration modules to validate there are no import errors.""" + import_all_integration_modules(common.INTEGRATION_PATH) diff --git a/content/response_integrations/third_party/thinkst_canary/uv.lock b/content/response_integrations/third_party/thinkst_canary/uv.lock new file mode 100644 index 000000000..5b74d63fd --- /dev/null +++ b/content/response_integrations/third_party/thinkst_canary/uv.lock @@ -0,0 +1,878 @@ +version = 1 +revision = 3 +requires-python = "==3.11.*" + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/fa/3ae643cd525cf6844d3dc810481e5748107368eb49563c15a5fb9f680750/aiohttp-3.13.1.tar.gz", hash = "sha256:4b7ee9c355015813a6aa085170b96ec22315dabc3d866fd77d147927000e9464", size = 7835344, upload-time = "2025-10-17T14:03:29.337Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/2c/739d03730ffce57d2093e2e611e1541ac9a4b3bb88288c33275058b9ffc2/aiohttp-3.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eefa0a891e85dca56e2d00760945a6325bd76341ec386d3ad4ff72eb97b7e64", size = 742004, upload-time = "2025-10-17T13:59:29.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f8/7f5b7f7184d7c80e421dbaecbd13e0b2a0bb8663fd0406864f9a167a438c/aiohttp-3.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c20eb646371a5a57a97de67e52aac6c47badb1564e719b3601bbb557a2e8fd0", size = 495601, upload-time = "2025-10-17T13:59:31.312Z" }, + { url = "https://files.pythonhosted.org/packages/3e/af/fb78d028b9642dd33ff127d9a6a151586f33daff631b05250fecd0ab23f8/aiohttp-3.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfc28038cd86fb1deed5cc75c8fda45c6b0f5c51dfd76f8c63d3d22dc1ab3d1b", size = 491790, upload-time = "2025-10-17T13:59:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ae/e40e422ee995e4f91f7f087b86304e3dd622d3a5b9ca902a1e94ebf9a117/aiohttp-3.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b22eeffca2e522451990c31a36fe0e71079e6112159f39a4391f1c1e259a795", size = 1746350, upload-time = "2025-10-17T13:59:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/28/a5/fe6022bb869bf2d2633b155ed8348d76358c22d5ff9692a15016b2d1019f/aiohttp-3.13.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:65782b2977c05ebd78787e3c834abe499313bf69d6b8be4ff9c340901ee7541f", size = 1703046, upload-time = "2025-10-17T13:59:37.077Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a5/c4ef3617d7cdc49f2d5af077f19794946f0f2d94b93c631ace79047361a2/aiohttp-3.13.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dacba54f9be3702eb866b0b9966754b475e1e39996e29e442c3cd7f1117b43a9", size = 1806161, upload-time = "2025-10-17T13:59:38.837Z" }, + { url = "https://files.pythonhosted.org/packages/ad/45/b87d2430aee7e7d00b24e3dff2c5bd69f21017f6edb19cfd91e514664fc8/aiohttp-3.13.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aa878da718e8235302c365e376b768035add36b55177706d784a122cb822a6a4", size = 1894546, upload-time = "2025-10-17T13:59:40.741Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a2/79eb466786a7f11a0292c353a8a9b95e88268c48c389239d7531d66dbb48/aiohttp-3.13.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e4b4e607fbd4964d65945a7b9d1e7f98b0d5545736ea613f77d5a2a37ff1e46", size = 1745683, upload-time = "2025-10-17T13:59:42.59Z" }, + { url = "https://files.pythonhosted.org/packages/93/1a/153b0ad694f377e94eacc85338efe03ed4776a396c8bb47bd9227135792a/aiohttp-3.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c3db2d0e5477ad561bf7ba978c3ae5f8f78afda70daa05020179f759578754f", size = 1605418, upload-time = "2025-10-17T13:59:45.229Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4e/18605b1bfeb4b00d3396d833647cdb213118e2a96862e5aebee62ad065b4/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9739d34506fdf59bf2c092560d502aa728b8cdb33f34ba15fb5e2852c35dd829", size = 1722379, upload-time = "2025-10-17T13:59:46.969Z" }, + { url = "https://files.pythonhosted.org/packages/72/13/0a38ad385d547fb283e0e1fe1ff1dff8899bd4ed0aaceeb13ec14abbf136/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b902e30a268a85d50197b4997edc6e78842c14c0703450f632c2d82f17577845", size = 1716693, upload-time = "2025-10-17T13:59:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/55/65/7029d7573ab9009adde380052c6130d02c8db52195fda112db35e914fe7b/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbfc04c8de7def6504cce0a97f9885a5c805fd2395a0634bc10f9d6ecb42524", size = 1784174, upload-time = "2025-10-17T13:59:51.439Z" }, + { url = "https://files.pythonhosted.org/packages/2d/36/fd46e39cb85418e45b0e4a8bfc39651ee0b8f08ea006adf217a221cdb269/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6941853405a38a5eeb7d9776db77698df373ff7fa8c765cb81ea14a344fccbeb", size = 1593716, upload-time = "2025-10-17T13:59:53.367Z" }, + { url = "https://files.pythonhosted.org/packages/85/b8/188e0cb1be37b4408373171070fda17c3bf9c67c0d3d4fd5ee5b1fa108e1/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7764adcd2dc8bd21c8228a53dda2005428498dc4d165f41b6086f0ac1c65b1c9", size = 1799254, upload-time = "2025-10-17T13:59:55.352Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/fdf768764eb427b0cc9ebb2cebddf990f94d98b430679f8383c35aa114be/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c09e08d38586fa59e5a2f9626505a0326fadb8e9c45550f029feeb92097a0afc", size = 1738122, upload-time = "2025-10-17T13:59:57.263Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/fce7a4d575943394d7c0e632273838eb6f39de8edf25386017bf5f0de23b/aiohttp-3.13.1-cp311-cp311-win32.whl", hash = "sha256:ce1371675e74f6cf271d0b5530defb44cce713fd0ab733713562b3a2b870815c", size = 430491, upload-time = "2025-10-17T13:59:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d2/d21b8ab6315a5d588c550ab285b4f02ae363edf012920e597904c5a56608/aiohttp-3.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:77a2f5cc28cf4704cc157be135c6a6cfb38c9dea478004f1c0fd7449cf445c28", size = 454808, upload-time = "2025-10-17T14:00:01.247Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "anyio" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "cachetools" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325, upload-time = "2025-10-12T14:55:30.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280, upload-time = "2025-10-12T14:55:28.382Z" }, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, +] + +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" }, + { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" }, + { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" }, + { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" }, + { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" }, +] + +[[package]] +name = "environmentcommon" +version = "1.0.2" +source = { path = "../../../../packages/envcommon/EnvironmentCommon-1.0.2/EnvironmentCommon-1.0.2-py2.py3-none-any.whl" } +wheels = [ + { filename = "environmentcommon-1.0.2-py2.py3-none-any.whl", hash = "sha256:a31d611ddf539fb081cfe9423a8d506d198fb5aefdb225c5534904ef9d80aaaa" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "google-api-core" +version = "2.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/6c8b44ecc28026fd9441d7fcc5434ee1b3976c491f2f810b464c4702c975/google_api_core-2.27.0.tar.gz", hash = "sha256:d32e2f5dd0517e91037169e75bf0a9783b255aff1d11730517c0b2b29e9db06a", size = 168851, upload-time = "2025-10-22T23:54:14.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/93/ecf9f7caa99c71e969091e9a78789f11b2dea5c684917eab7c54a8d13560/google_api_core-2.27.0-py3-none-any.whl", hash = "sha256:779a380db4e21a4ee3d717cf8efbf324e53900bf37e1ffb273e5348a9916dd42", size = 167110, upload-time = "2025-10-22T23:54:12.805Z" }, +] + +[[package]] +name = "google-api-python-client" +version = "2.185.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httplib2" }, + { name = "uritemplate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/5a/6f9b49d67ea91376305fdb8bbf2877c746d756e45fd8fb7d2e32d6dad19b/google_api_python_client-2.185.0.tar.gz", hash = "sha256:aa1b338e4bb0f141c2df26743f6b46b11f38705aacd775b61971cbc51da089c3", size = 13885609, upload-time = "2025-10-17T15:00:35.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/28/be3b17bd6a190c8c2ec9e4fb65d43e6ecd7b7a1bb19ccc1d9ab4f687a58c/google_api_python_client-2.185.0-py3-none-any.whl", hash = "sha256:00fe173a4b346d2397fbe0d37ac15368170dfbed91a0395a66ef2558e22b93fc", size = 14453595, upload-time = "2025-10-17T15:00:33.176Z" }, +] + +[[package]] +name = "google-auth" +version = "2.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/af/5129ce5b2f9688d2fa49b463e544972a7c82b0fdb50980dafee92e121d9f/google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2", size = 292284, upload-time = "2025-09-30T22:51:26.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/a4/7319a2a8add4cc352be9e3efeff5e2aacee917c85ca2fa1647e29089983c/google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d", size = 221302, upload-time = "2025-09-30T22:51:24.212Z" }, +] + +[[package]] +name = "google-auth-httplib2" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "httplib2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842, upload-time = "2023-12-12T17:40:30.722Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253, upload-time = "2023-12-12T17:40:13.055Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/43/b25abe02db2911397819003029bef768f68a974f2ece483e6084d1a5f754/googleapis_common_protos-1.71.0.tar.gz", hash = "sha256:1aec01e574e29da63c80ba9f7bbf1ccfaacf1da877f23609fe236ca7c72a2e2e", size = 146454, upload-time = "2025-10-20T14:58:08.732Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/e8/eba9fece11d57a71e3e22ea672742c8f3cf23b35730c9e96db768b295216/googleapis_common_protos-1.71.0-py3-none-any.whl", hash = "sha256:59034a1d849dc4d18971997a72ac56246570afdd17f9369a0ff68218d50ab78c", size = 294576, upload-time = "2025-10-20T14:56:21.295Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httplib2" +version = "0.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759, upload-time = "2025-09-11T12:16:03.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "integration-testing" +version = "2.2.7" +source = { path = "../../../../packages/integration_testing_whls/integration_testing-2.2.7-py3-none-any.whl" } +dependencies = [ + { name = "aiohttp" }, + { name = "environmentcommon" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "soar-sdk" }, + { name = "tipcommon" }, + { name = "yarl" }, +] +wheels = [ + { filename = "integration_testing-2.2.7-py3-none-any.whl", hash = "sha256:50d8f71e80162f4b312c1048d775fc4247cae15933b2d6954b5b941ed5555b53" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.12.13" }, + { name = "environmentcommon" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "soar-sdk" }, + { name = "tipcommon" }, + { name = "yarl", specifier = ">=1.20.1" }, +] + +[[package]] +name = "multidict" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" }, + { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" }, + { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" }, + { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" }, + { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" }, + { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" }, + { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" }, + { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" }, + { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" }, + { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" }, + { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521, upload-time = "2025-10-15T20:39:43.803Z" }, + { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, + { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, + { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pycparser" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyopenssl" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329", size = 184073, upload-time = "2025-09-17T00:32:21.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6", size = 57268, upload-time = "2025-09-17T00:32:19.474Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-json-report" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "pytest-metadata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/d3/765dae9712fcd68d820338908c1337e077d5fdadccd5cacf95b9b0bea278/pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de", size = 21241, upload-time = "2022-03-15T21:03:10.2Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/35/d07400c715bf8a88aa0c1ee9c9eb6050ca7fe5b39981f0eea773feeb0681/pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325", size = 13222, upload-time = "2022-03-15T21:03:08.65Z" }, +] + +[[package]] +name = "pytest-metadata" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/85/8c969f8bec4e559f8f2b958a15229a35495f5b4ce499f6b865eac54b878d/pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8", size = 9952, upload-time = "2024-02-12T19:38:44.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/43/7e7b2ec865caa92f67b8f0e9231a798d102724ca4c0e1f414316be1c1ef2/pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b", size = 11428, upload-time = "2024-02-12T19:38:42.531Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "soar-sdk" +version = "0.2.0" +source = { git = "https://github.com/chronicle/soar-sdk.git#5f99e1e838e203a5abbd38d8f6e1cae69b4e4a66" } +dependencies = [ + { name = "arrow" }, + { name = "chardet" }, + { name = "cryptography" }, + { name = "google-auth" }, + { name = "pyopenssl" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "six" }, +] + +[[package]] +name = "thinkst-canary" +version = "1.0" +source = { virtual = "." } +dependencies = [ + { name = "requests" }, +] + +[package.dev-dependencies] +dev = [ + { name = "environmentcommon" }, + { name = "integration-testing" }, + { name = "pytest" }, + { name = "pytest-json-report" }, + { name = "soar-sdk" }, + { name = "tipcommon" }, +] + +[package.metadata] +requires-dist = [{ name = "requests" }] + +[package.metadata.requires-dev] +dev = [ + { name = "environmentcommon", path = "../../../../packages/envcommon/EnvironmentCommon-1.0.2/EnvironmentCommon-1.0.2-py2.py3-none-any.whl" }, + { name = "integration-testing", path = "../../../../packages/integration_testing_whls/integration_testing-2.2.7-py3-none-any.whl" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-json-report", specifier = ">=1.5.0" }, + { name = "soar-sdk", git = "https://github.com/chronicle/soar-sdk.git" }, + { name = "tipcommon", path = "../../../../packages/tipcommon/TIPCommon-2.2.7/TIPCommon-2.2.7-py2.py3-none-any.whl" }, +] + +[[package]] +name = "tipcommon" +version = "2.2.7" +source = { path = "../../../../packages/tipcommon/TIPCommon-2.2.7/TIPCommon-2.2.7-py2.py3-none-any.whl" } +dependencies = [ + { name = "google-api-python-client" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httpx" }, + { name = "pycryptodome" }, + { name = "requests" }, +] +wheels = [ + { filename = "tipcommon-2.2.7-py2.py3-none-any.whl", hash = "sha256:e814f0ffa0b177aac36b49aee479bbafdce756bed5eedf9955f4d3d0d5e7fa85" }, +] + +[package.metadata] +requires-dist = [ + { name = "google-api-python-client" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httpx" }, + { name = "pycryptodome" }, + { name = "requests" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + +[[package]] +name = "uritemplate" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +]