From 78cbac8278eab08e82c0209d222503c24326f835 Mon Sep 17 00:00:00 2001 From: haggit-eliyahu Date: Mon, 20 Apr 2026 12:14:59 +0300 Subject: [PATCH 1/2] migrate illusive_networks --- .../google/illusive_networks/.python-version | 1 + .../actions/AddDeceptiveServer.py | 114 ++ .../actions/AddDeceptiveServer.yaml | 39 + .../actions/AddDeceptiveUser.py | 115 ++ .../actions/AddDeceptiveUser.yaml | 43 + .../actions/EnrichEntities.py | 156 +++ .../actions/EnrichEntities.yaml | 26 + .../actions/ListDeceptiveItems.py | 148 +++ .../actions/ListDeceptiveItems.yaml | 49 + .../google/illusive_networks/actions/Ping.py | 93 ++ .../illusive_networks/actions/Ping.yaml | 22 + .../actions/RemoveDeceptiveServer.py | 106 ++ .../actions/RemoveDeceptiveServer.yaml | 26 + .../actions/RemoveDeceptiveUser.py | 104 ++ .../actions/RemoveDeceptiveUser.yaml | 26 + .../actions/RunForensicScan.py | 572 ++++++++ .../actions/RunForensicScan.yaml | 68 + .../illusive_networks/actions/__init__.py | 14 + .../connectors/IncidentsConnector.py | 294 +++++ .../connectors/IncidentsConnector.yaml | 154 +++ .../illusive_networks/connectors/__init__.py | 14 + .../core/IllusiveNetworksExceptions.py | 33 + .../core/IllusiveNetworksManager.py | 566 ++++++++ .../core/IllusiveNetworksParser.py | 278 ++++ .../google/illusive_networks/core/Utils.py | 330 +++++ .../google/illusive_networks/core/__init__.py | 14 + .../illusive_networks/core/constants.py | 69 + .../illusive_networks/core/datamodels.py | 395 ++++++ .../google/illusive_networks/definition.yaml | 47 + .../google/illusive_networks/pyproject.toml | 34 + .../illusive_networks/release_notes.yaml | 109 ++ .../resources/ai/actions_ai_description.yaml | 562 ++++++++ .../enrich_entities_JsonResult_example.json | 47 + .../illusive_networks/resources/image.png | Bin 0 -> 2054 bytes ...st_deceptive_items_JsonResult_example.json | 40 + .../illusive_networks/resources/logo.svg | 1 + .../run_forensic_scan_JsonResult_example.json | 153 +++ .../illusive_networks/tests/__init__.py | 14 + .../google/illusive_networks/tests/common.py | 22 + .../illusive_networks/tests/config.json | 6 + .../tests/test_defaults/__init__.py | 14 + .../tests/test_defaults/test_imports.py | 57 + .../google/illusive_networks/uv.lock | 570 ++++++++ .../widgets/EnrichEntities.html | 1155 +++++++++++++++++ .../widgets/EnrichEntities.yaml | 44 + .../widgets/ListDeceptiveItems.html | 1087 ++++++++++++++++ .../widgets/ListDeceptiveItems.yaml | 44 + .../widgets/RunForensicScan.html | 1155 +++++++++++++++++ .../widgets/RunForensicScan.yaml | 44 + .../response_integrations/google/ruff.toml | 1 + 50 files changed, 9075 insertions(+) create mode 100644 content/response_integrations/google/illusive_networks/.python-version create mode 100644 content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.py create mode 100644 content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.yaml create mode 100644 content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.py create mode 100644 content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.yaml create mode 100755 content/response_integrations/google/illusive_networks/actions/EnrichEntities.py create mode 100644 content/response_integrations/google/illusive_networks/actions/EnrichEntities.yaml create mode 100644 content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.py create mode 100644 content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.yaml create mode 100755 content/response_integrations/google/illusive_networks/actions/Ping.py create mode 100644 content/response_integrations/google/illusive_networks/actions/Ping.yaml create mode 100644 content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.py create mode 100644 content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.yaml create mode 100644 content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.py create mode 100644 content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.yaml create mode 100755 content/response_integrations/google/illusive_networks/actions/RunForensicScan.py create mode 100644 content/response_integrations/google/illusive_networks/actions/RunForensicScan.yaml create mode 100644 content/response_integrations/google/illusive_networks/actions/__init__.py create mode 100644 content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.py create mode 100644 content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.yaml create mode 100644 content/response_integrations/google/illusive_networks/connectors/__init__.py create mode 100755 content/response_integrations/google/illusive_networks/core/IllusiveNetworksExceptions.py create mode 100755 content/response_integrations/google/illusive_networks/core/IllusiveNetworksManager.py create mode 100755 content/response_integrations/google/illusive_networks/core/IllusiveNetworksParser.py create mode 100755 content/response_integrations/google/illusive_networks/core/Utils.py create mode 100644 content/response_integrations/google/illusive_networks/core/__init__.py create mode 100755 content/response_integrations/google/illusive_networks/core/constants.py create mode 100755 content/response_integrations/google/illusive_networks/core/datamodels.py create mode 100644 content/response_integrations/google/illusive_networks/definition.yaml create mode 100644 content/response_integrations/google/illusive_networks/pyproject.toml create mode 100644 content/response_integrations/google/illusive_networks/release_notes.yaml create mode 100644 content/response_integrations/google/illusive_networks/resources/ai/actions_ai_description.yaml create mode 100644 content/response_integrations/google/illusive_networks/resources/enrich_entities_JsonResult_example.json create mode 100644 content/response_integrations/google/illusive_networks/resources/image.png create mode 100644 content/response_integrations/google/illusive_networks/resources/list_deceptive_items_JsonResult_example.json create mode 100644 content/response_integrations/google/illusive_networks/resources/logo.svg create mode 100644 content/response_integrations/google/illusive_networks/resources/run_forensic_scan_JsonResult_example.json create mode 100644 content/response_integrations/google/illusive_networks/tests/__init__.py create mode 100644 content/response_integrations/google/illusive_networks/tests/common.py create mode 100644 content/response_integrations/google/illusive_networks/tests/config.json create mode 100644 content/response_integrations/google/illusive_networks/tests/test_defaults/__init__.py create mode 100644 content/response_integrations/google/illusive_networks/tests/test_defaults/test_imports.py create mode 100644 content/response_integrations/google/illusive_networks/uv.lock create mode 100644 content/response_integrations/google/illusive_networks/widgets/EnrichEntities.html create mode 100644 content/response_integrations/google/illusive_networks/widgets/EnrichEntities.yaml create mode 100644 content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.html create mode 100644 content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.yaml create mode 100644 content/response_integrations/google/illusive_networks/widgets/RunForensicScan.html create mode 100644 content/response_integrations/google/illusive_networks/widgets/RunForensicScan.yaml diff --git a/content/response_integrations/google/illusive_networks/.python-version b/content/response_integrations/google/illusive_networks/.python-version new file mode 100644 index 000000000..902b2c90c --- /dev/null +++ b/content/response_integrations/google/illusive_networks/.python-version @@ -0,0 +1 @@ +3.11 \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.py b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.py new file mode 100644 index 000000000..0cc1da568 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.py @@ -0,0 +1,114 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.Utils import string_to_multi_value +from ..core.IllusiveNetworksExceptions import ManagerAlreadyExistException +from ..core.constants import INTEGRATION_NAME, PRODUCT_NAME, ADD_DECEPTIVE_SERVER_SCRIPT_NAME + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = ADD_DECEPTIVE_SERVER_SCRIPT_NAME + + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, provider_name=INTEGRATION_NAME, param_name="CA Certificate File" + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + server_name = extract_action_param( + siemplify, param_name="Server Name", is_mandatory=True, print_value=True + ) + server_types = string_to_multi_value( + extract_action_param( + siemplify, param_name="Service Types", is_mandatory=True, print_value=True + ) + ) + policy_names = string_to_multi_value( + extract_action_param(siemplify, param_name="Policy Names", print_value=True) + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + + status = EXECUTION_STATE_COMPLETED + result_value = True + + try: + manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + if manager.get_deceptive_server(server_name) is not None: + raise ManagerAlreadyExistException( + f'Deceptive server "{server_name}" already exists.' + ) + + manager.add_deceptive_server( + host=server_name, server_types=server_types, policy_names=policy_names + ) + + output_message = f"Successfully added deceptive server in {PRODUCT_NAME}." + except Exception as e: + output_message = ( + f"Error executing action '{ADD_DECEPTIVE_SERVER_SCRIPT_NAME}'. Reason: {e}" + ) + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.yaml b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.yaml new file mode 100644 index 000000000..667a305e0 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveServer.yaml @@ -0,0 +1,39 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Add Deceptive Server +description: Add deceptive servers in Illusive Networks. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#add_deceptive_server +integration_identifier: IllusiveNetworks +parameters: +- name: Server Name + default_value: '' + type: string + description: Specify what kind of deceptive items should be returned. + is_mandatory: true +- name: Service Types + default_value: DB + type: string + description: Specify a comma-separated list of service types for new deceptive + server. + is_mandatory: true +- name: Policy Names + default_value: '' + type: string + description: Specify a comma-separated list of policies that need to be applied + to the new deceptive server. If nothing is provided action will use by default + all policies. + is_mandatory: false +dynamic_results_metadata: [] +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.py b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.py new file mode 100644 index 000000000..892338b9c --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.py @@ -0,0 +1,115 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.Utils import string_to_multi_value +from ..core.IllusiveNetworksExceptions import ManagerAlreadyExistException +from ..core.constants import INTEGRATION_NAME, ADD_DECEPTIVE_USER_SCRIPT_NAME, PRODUCT_NAME + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = ADD_DECEPTIVE_USER_SCRIPT_NAME + + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, provider_name=INTEGRATION_NAME, param_name="CA Certificate File" + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + username = extract_action_param( + siemplify, param_name="Username", is_mandatory=True, print_value=True + ) + password = extract_action_param(siemplify, param_name="Password", is_mandatory=True) + dns_domain = extract_action_param( + siemplify, param_name="DNS Domain", print_value=True + ) + policy_names = string_to_multi_value( + extract_action_param(siemplify, param_name="Policy Names", print_value=True) + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + + status = EXECUTION_STATE_COMPLETED + result_value = True + + try: + manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + if manager.get_deceptive_user(username) is not None: + raise ManagerAlreadyExistException( + f'Deceptive user "{username}" already exists.' + ) + + manager.add_deceptive_user( + dns_domain=dns_domain, + username=username, + password=password, + policy_names=policy_names, + ) + + output_message = f"Successfully added deceptive user in {PRODUCT_NAME}." + except Exception as e: + output_message = ( + f"Error executing action '{ADD_DECEPTIVE_USER_SCRIPT_NAME}'. Reason: {e}" + ) + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.yaml b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.yaml new file mode 100644 index 000000000..3e7e8fad5 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/AddDeceptiveUser.yaml @@ -0,0 +1,43 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Add Deceptive User +description: Add deceptive users in Illusive Networks. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#add_deceptive_user +integration_identifier: IllusiveNetworks +parameters: +- name: Username + default_value: '' + type: string + description: Specify the username for the new deceptive user. + is_mandatory: true +- name: Password + default_value: '' + type: password + description: Specify the password for the new deceptive user. + is_mandatory: true +- name: DNS Domain + default_value: '' + type: string + description: Specify the domain name for the new deceptive user. + is_mandatory: false +- name: Policy Names + default_value: '' + type: string + description: Specify a comma-separated list of policies that need to be applied + to the new deceptive user. If nothing is provided action will use by default + all policies. + is_mandatory: false +dynamic_results_metadata: [] +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/EnrichEntities.py b/content/response_integrations/google/illusive_networks/actions/EnrichEntities.py new file mode 100755 index 000000000..1511d5a7a --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/EnrichEntities.py @@ -0,0 +1,156 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler, convert_dict_to_json_result_dict +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, construct_csv +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from soar_sdk.SiemplifyDataModel import EntityTypes +from ..core.IllusiveNetworksExceptions import RateLimitException +from ..core.constants import INTEGRATION_NAME, ENRICH_ENTITIES_ACTION, PRODUCT_NAME + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = ENRICH_ENTITIES_ACTION + siemplify.LOGGER.info("================= Main - Param Init =================") + + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="CA Certificate File", + is_mandatory=False, + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + status = EXECUTION_STATE_COMPLETED + result_value = True + output_message = "" + failed_entities = [] + json_results = {} + entities_to_update = [] + + scope_entities = [ + entity + for entity in siemplify.target_entities + if entity.entity_type == EntityTypes.HOSTNAME + ] + + try: + illusivenetworks_manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + illusivenetworks_manager.test_connectivity() + for entity in scope_entities: + try: + host_object = illusivenetworks_manager.enrich_entity( + host_entity_name=entity.identifier + ) + + if not host_object.raw_data: + failed_entities.append(entity) + continue + + json_results[entity.identifier] = host_object.to_json() + entity.is_enriched = True + entity.additional_properties.update(host_object.as_enrichment_data()) + entities_to_update.append(entity) + + siemplify.result.add_entity_table( + f"{entity.identifier}", + construct_csv(host_object.to_table()), + ) + + except RateLimitException as e: + raise + except Exception as e: + failed_entities.append(entity) + siemplify.LOGGER.error( + f"An error occurred on entity: {entity.identifier}" + ) + siemplify.LOGGER.exception(e) + + if len(scope_entities) == len(failed_entities): + output_message += "No entities were enriched." + result_value = False + + else: + if entities_to_update: + siemplify.update_entities(entities_to_update) + output_message += ( + "Successfully enriched the following entities using {}:\n{}".format( + PRODUCT_NAME, + "\n".join([entity.identifier for entity in entities_to_update]), + ) + ) + + siemplify.result.add_result_json( + convert_dict_to_json_result_dict(json_results) + ) + if failed_entities: + output_message += "\nAction wasn't able to enrich the following entities using {}:\n{}".format( + PRODUCT_NAME, + "\n".join([entity.identifier for entity in failed_entities]), + ) + + except Exception as e: + output_message += (f"Error executing action {ENRICH_ENTITIES_ACTION}. " + f"Reason: {e}.") + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/EnrichEntities.yaml b/content/response_integrations/google/illusive_networks/actions/EnrichEntities.yaml new file mode 100644 index 000000000..5ef783673 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/EnrichEntities.yaml @@ -0,0 +1,26 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Enrich Entities +description: 'Enrich entities using information from Illusive Networks. Supported + entities: Hostname.' +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#enrich_entities +integration_identifier: IllusiveNetworks +parameters: [] +dynamic_results_metadata: +- result_example_path: resources/enrich_entities_JsonResult_example.json + result_name: JsonResult + show_result: true +creator: admin +simulation_data_json: '{"Entities": ["HOSTNAME"]}' diff --git a/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.py b/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.py new file mode 100644 index 000000000..1157e709f --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.py @@ -0,0 +1,148 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param, construct_csv +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.constants import ( + INTEGRATION_NAME, + PRODUCT_NAME, + LIST_DECEPTIVE_ITEMS_ACTION, + ALL, + ONLY_USERS, + ONLY_SERVERS, + DECEPTIVE_STATE_MAPPING, + DECEPTIVE_USERS_TABLE_NAME, + DECEPTIVE_SERVERS_TABLE_NAME, +) + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = LIST_DECEPTIVE_ITEMS_ACTION + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, provider_name=INTEGRATION_NAME, param_name="CA Certificate File" + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + deceptive_type = extract_action_param( + siemplify, param_name="Deceptive Type", print_value=True, default_value=ALL + ) + deceptive_state = extract_action_param( + siemplify, param_name="Deceptive State", print_value=True, default_value=ALL + ) + max_items_to_return = extract_action_param( + siemplify, param_name="Max Items To Return", print_value=True, input_type=int + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + + status = EXECUTION_STATE_COMPLETED + result_value = True + output_message = ( + f"Successfully returned available deceptive items from {PRODUCT_NAME}." + ) + deceptive_users, deceptive_servers = [], [] + + try: + manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + if deceptive_type == ALL or deceptive_type == ONLY_USERS: + deceptive_users = manager.get_deceptive_users( + deceptive_state=DECEPTIVE_STATE_MAPPING[deceptive_state], + limit=max_items_to_return, + ) + if deceptive_type == ALL or deceptive_type == ONLY_SERVERS: + deceptive_servers = manager.get_deceptive_servers( + deceptive_state=DECEPTIVE_STATE_MAPPING[deceptive_state], + limit=max_items_to_return, + ) + + if deceptive_users: + siemplify.result.add_data_table( + DECEPTIVE_USERS_TABLE_NAME, + construct_csv([user.to_table() for user in deceptive_users]), + ) + if deceptive_servers: + siemplify.result.add_data_table( + DECEPTIVE_SERVERS_TABLE_NAME, + construct_csv([server.to_table() for server in deceptive_servers]), + ) + + if deceptive_users or deceptive_servers: + json_results = { + "users": [user.to_json() for user in deceptive_users], + "servers": [server.to_json() for server in deceptive_servers], + } + siemplify.result.add_result_json(json_results) + else: + output_message = ( + f"No data was found regarding deceptive items based on " + f"the provided criteria in {PRODUCT_NAME}." + ) + result_value = False + + except Exception as err: + output_message = ( + f"Error executing action '{LIST_DECEPTIVE_ITEMS_ACTION}'. Reason: {err}" + ) + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(err) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.yaml b/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.yaml new file mode 100644 index 000000000..f53e21146 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/ListDeceptiveItems.yaml @@ -0,0 +1,49 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: List Deceptive Items +description: List available deceptive items in Illusive Networks. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#list_deceptive_items +integration_identifier: IllusiveNetworks +parameters: +- name: Deceptive Type + default_value: All + type: ddl + optional_values: + - All + - Only Users + - Only Servers + description: Specify what kind of deceptive items should be returned. + is_mandatory: true +- name: Deceptive State + default_value: All + type: ddl + optional_values: + - All + - Only Approved + - Only Suggested + description: Specify what kind of deceptive items should be returned based on + state. + is_mandatory: true +- name: Max Items To Return + default_value: 50 + type: string + description: 'Specify how many items to return. Default: 50. If nothing is specified, + action will return all items.' + is_mandatory: false +dynamic_results_metadata: +- result_example_path: resources/list_deceptive_items_JsonResult_example.json + result_name: JsonResult + show_result: true +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/Ping.py b/content/response_integrations/google/illusive_networks/actions/Ping.py new file mode 100755 index 000000000..be8c43389 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/Ping.py @@ -0,0 +1,93 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.constants import INTEGRATION_NAME, PING_ACTION, PRODUCT_NAME + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = PING_ACTION + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="CA Certificate File", + is_mandatory=False, + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + status = EXECUTION_STATE_COMPLETED + result_value = True + output_message = "" + + try: + illusivenetworks_manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + illusivenetworks_manager.test_connectivity() + output_message += (f"Successfully connected to the {INTEGRATION_NAME} " + f"server with the provided connection parameters!") + + except Exception as e: + output_message += f"Failed to connect to the {PRODUCT_NAME}! Error is {e}." + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/Ping.yaml b/content/response_integrations/google/illusive_networks/actions/Ping.yaml new file mode 100644 index 000000000..f6e095911 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/Ping.yaml @@ -0,0 +1,22 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Ping +description: Test connectivity to the Illusive Networks with parameters provided at + the integration configuration page on the Marketplace tab. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#ping +integration_identifier: IllusiveNetworks +parameters: [] +dynamic_results_metadata: [] +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.py b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.py new file mode 100644 index 000000000..eb8550b1e --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.py @@ -0,0 +1,106 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.IllusiveNetworksExceptions import ManagerNotFoundException +from ..core.constants import ( + INTEGRATION_NAME, + PRODUCT_NAME, + REMOVE_DECEPTIVE_SERVER_SCRIPT_NAME, +) + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = REMOVE_DECEPTIVE_SERVER_SCRIPT_NAME + + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, provider_name=INTEGRATION_NAME, param_name="CA Certificate File" + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + server_name = extract_action_param( + siemplify, param_name="Server Name", is_mandatory=True, print_value=True + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + + status = EXECUTION_STATE_COMPLETED + result_value = False + + try: + manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + deceptive_server = manager.get_deceptive_server_or_raise(server_name) + + manager.remove_deceptive_server([deceptive_server.domain]) + result_value = True + output_message = f"Successfully removed deceptive server in {PRODUCT_NAME}." + + except ManagerNotFoundException as e: + output_message = (f'Action wasn\'t able to remove deceptive server ' + f'"{server_name}". Reason: {e}') + except Exception as e: + output_message = (f"Error executing action " + f"'{REMOVE_DECEPTIVE_SERVER_SCRIPT_NAME}'. Reason: {e}") + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.yaml b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.yaml new file mode 100644 index 000000000..df23c058b --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveServer.yaml @@ -0,0 +1,26 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Remove Deceptive Server +description: Remove deceptive server from Illusive Networks. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#remove_deceptive_server +integration_identifier: IllusiveNetworks +parameters: +- name: Server Name + default_value: '' + type: string + description: Specify the name of the deceptive server that needs to be removed. + is_mandatory: true +dynamic_results_metadata: [] +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.py b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.py new file mode 100644 index 000000000..873a46f8a --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.py @@ -0,0 +1,104 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param +from soar_sdk.ScriptResult import EXECUTION_STATE_COMPLETED, EXECUTION_STATE_FAILED +from ..core.IllusiveNetworksExceptions import ManagerNotFoundException +from ..core.constants import INTEGRATION_NAME, PRODUCT_NAME, REMOVE_DECEPTIVE_USER_SCRIPT_NAME + + +@output_handler +def main(): + siemplify = SiemplifyAction() + siemplify.script_name = REMOVE_DECEPTIVE_USER_SCRIPT_NAME + + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, provider_name=INTEGRATION_NAME, param_name="CA Certificate File" + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + username = extract_action_param( + siemplify, param_name="Username", is_mandatory=True, print_value=True + ) + + siemplify.LOGGER.info("----------------- Main - Started -----------------") + + status = EXECUTION_STATE_COMPLETED + result_value = False + + try: + manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + deceptive_user = manager.get_deceptive_user_or_raise(username) + + manager.remove_deceptive_user([deceptive_user.username]) + result_value = True + output_message = f"Successfully removed deceptive user in {PRODUCT_NAME}." + + except ManagerNotFoundException as e: + output_message = ( + f'Action wasn\'t able to remove deceptive user "{username}". Reason: {e}' + ) + except Exception as e: + output_message = ( + f"Error executing action '{REMOVE_DECEPTIVE_USER_SCRIPT_NAME}'. Reason: {e}" + ) + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + + siemplify.LOGGER.info("----------------- Main - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.yaml b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.yaml new file mode 100644 index 000000000..407c69956 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RemoveDeceptiveUser.yaml @@ -0,0 +1,26 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Remove Deceptive User +description: Remove deceptive user from Illusive Networks. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#remove_deceptive_user +integration_identifier: IllusiveNetworks +parameters: +- name: Username + default_value: '' + type: string + description: Specify the username of the deceptive user that needs to be removed. + is_mandatory: true +dynamic_results_metadata: [] +creator: admin diff --git a/content/response_integrations/google/illusive_networks/actions/RunForensicScan.py b/content/response_integrations/google/illusive_networks/actions/RunForensicScan.py new file mode 100755 index 000000000..6446f688b --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RunForensicScan.py @@ -0,0 +1,572 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from soar_sdk.SiemplifyUtils import output_handler, convert_dict_to_json_result_dict +from soar_sdk.SiemplifyAction import SiemplifyAction +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from TIPCommon import extract_configuration_param, extract_action_param, construct_csv +from soar_sdk.ScriptResult import ( + EXECUTION_STATE_COMPLETED, + EXECUTION_STATE_FAILED, + EXECUTION_STATE_INPROGRESS, +) +from ..core.constants import INTEGRATION_NAME, RUN_FORENSIC_SCAN_ACTION, DEFAULT_ITEMS +from ..core.IllusiveNetworksExceptions import IncidentNotReadyException, RateLimitException +from soar_sdk.SiemplifyDataModel import EntityTypes +import json +import sys + + +def start_operation(siemplify, manager): + """ + Function that initiates the forensic scan + :param siemplify: SiemplifyAction object. + :param manager: IllusiveNetworks manager object. + """ + status = EXECUTION_STATE_INPROGRESS + result_value = {} + + scope_entities = [ + entity + for entity in siemplify.target_entities + if entity.entity_type == EntityTypes.HOSTNAME + or entity.entity_type == EntityTypes.ADDRESS + ] + event_ids = [] + successful_entities = [] + failed_entities = [] + + if scope_entities: + for entity in scope_entities: + try: + siemplify.LOGGER.error( + f"Started processing entity: {entity.identifier}" + ) + event_id = manager.create_forensic_scan_request(entity.identifier) + event_ids.append({event_id: entity.identifier}) + successful_entities.append(entity.identifier) + + except RateLimitException: + raise + + except Exception as e: + siemplify.LOGGER.error( + f"Failed to process entity: {entity.identifier}. Reason: {e}" + ) + failed_entities.append(entity.identifier) + + result_value = json.dumps({"event_ids": event_ids, "events_ready": []}) + + output_message = "Started the forensic scan on the following endpoints: {}. Checking if forensic scan is ready...".format( + ", ".join([entity for entity in successful_entities]) + ) + + else: + output_message = "No forensic information was found on the provided endpoints" + result_value = False + status = EXECUTION_STATE_COMPLETED + return output_message, result_value, status + + if len(scope_entities) == len(failed_entities): + output_message = "No forensic information was found on the provided endpoints" + + return output_message, result_value, status + + +def query_operation_status( + siemplify, + manager, + include_sys_info, + include_prefetch_files_info, + include_add_remove, + include_startup_info, + include_running_info, + include_user_assist_info, + include_powershell_info, + max_items_to_return, +): + """ + Function that checks if the forensic scan is ready and fetch the results if ready + :param siemplify: SiemplifyAction object. + :param manager: IllusiveNetworks manager object. + :param include_sys_info: True if System Info should be included in results + :param include_prefetch_files_info: True if Prefetch Files Info should be included + in results + :param include_add_remove: True if Add Remove Processes should be included + in results + :param include_startup_info: True if Startup Processes should be included in results + :param include_running_info: True if Running Processes should be included in results + :param include_user_assist_info: True if User Assist should be included in results + :param include_powershell_info: True if Powershell Info should be included + in results + :param max_items_to_return: Max number of results in the output + """ + + event_ids_all = json.loads(siemplify.extract_action_param("additional_data")) + event_ids = event_ids_all.get("event_ids") + events_ready = event_ids_all.get("events_ready") + + events_to_check_again = [] + events_ready_incidents = [] + successfully_fetched_forensics = [] + failed_fetched_forensics = [] + output_message = "" + + json_results = {} + + if event_ids: + # If we still have some events that are not ready we need to get the status, + # until the last event is ready + for event_data in event_ids: + event_id = list(event_data.keys())[0] + try: + manager.get_incident_id(event_id=event_id) + events_ready_incidents.append(event_data) + siemplify.LOGGER.error( + f"Incident for event with ID {event_id} is ready." + ) + except RateLimitException: + raise + except IncidentNotReadyException: + events_to_check_again.append(event_data) + siemplify.LOGGER.error( + f"Incident for event with ID {event_id} is not ready yet." + ) + + result_value = json.dumps( + {"event_ids": events_to_check_again, "events_ready": events_ready_incidents} + ) + status = EXECUTION_STATE_INPROGRESS + output_message += "Created events {}. Waiting for forensic data to become available...".format( + ", ".join([list(entity.keys())[0] for entity in events_to_check_again]) + ) + + elif events_ready: + + scope_entities = [ + entity + for entity in siemplify.target_entities + if entity.entity_type == EntityTypes.HOSTNAME + or entity.entity_type == EntityTypes.ADDRESS + ] + suitable_entities = {entity.identifier: entity for entity in scope_entities} + + # when all of the events are ready we will fetch results + entities_to_update = [] + + for event_ready in events_ready: + event_ready_key = list(event_ready.keys())[0] + entity = suitable_entities[event_ready.get(event_ready_key)] + + try: + + forensic_data = manager.get_forensic_data( + event_id=event_ready_key, + include_sys_info=include_sys_info, + include_prefetch_files_info=include_prefetch_files_info, + include_add_remove=include_add_remove, + include_startup_info=include_startup_info, + include_running_info=include_running_info, + include_user_assist_info=include_user_assist_info, + include_powershell_info=include_powershell_info, + ) + entity_json = {} + + if forensic_data and include_sys_info: + entity_json["host_info"] = forensic_data.get("host_info").raw_data + siemplify.result.add_entity_table( + f"{event_ready.get(event_ready_key)}", + construct_csv(forensic_data.get("host_info").to_table()), + ) + + entity.is_enriched = True + entity.additional_properties.update( + forensic_data.get("host_info").as_enrichment_data() + ) + entities_to_update.append(entity) + + if ( + forensic_data + and include_prefetch_files_info + and forensic_data.get("prefetch_info") + ): + + prefetch_data = forensic_data.get("prefetch_info") + prefetch_table = [] + prefetch_json = [] + + for prefetch in prefetch_data: + prefetch_table.append(prefetch.to_table()) + prefetch_json.append(prefetch.raw_data) + + if max_items_to_return: + prefetch_table = prefetch_table[:max_items_to_return] + prefetch_json = prefetch_json[:max_items_to_return] + + entity_json["prefetch_info"] = prefetch_json + + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: " + f"Prefetch Files Information", + construct_csv(prefetch_table), + ) + + if ( + forensic_data + and include_add_remove + and forensic_data.get("include_add_remove") + ): + + add_remove_data = forensic_data.get("include_add_remove") + add_remove_table = [] + add_remove_json = [] + + for add_remove in add_remove_data: + add_remove_table.append(add_remove.to_table()) + add_remove_json.append(add_remove.raw_data) + + if max_items_to_return: + add_remove_table = add_remove_table[:max_items_to_return] + add_remove_json = add_remove_json[:max_items_to_return] + + entity_json["installed_programs_info"] = add_remove_json + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: " + f"Add-Remove Programs Information", + construct_csv(add_remove_table), + ) + + if ( + forensic_data + and include_startup_info + and forensic_data.get("include_startup_info") + ): + + startup_data = forensic_data.get("include_startup_info") + startup_table = [] + startup_json = [] + + for startup in startup_data: + startup_table.append(startup.to_table()) + startup_json.append(startup.raw_data) + + if max_items_to_return: + startup_table = startup_table[:max_items_to_return] + startup_json = startup_json[:max_items_to_return] + + entity_json["startup_processes"] = startup_json + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: Startup Processes", + construct_csv(startup_table), + ) + + if ( + forensic_data + and include_running_info + and forensic_data.get("include_running_info") + ): + + running_info_data = forensic_data.get("include_running_info") + running_info_table = [] + running_info_json = [] + + for startup in running_info_data: + running_info_table.append(startup.to_table()) + running_info_json.append(startup.raw_data) + + if max_items_to_return: + running_info_table = running_info_table[:max_items_to_return] + running_info_json = running_info_json[:max_items_to_return] + + entity_json["running_processes"] = running_info_json + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: Running Processes", + construct_csv(running_info_table), + ) + + if ( + forensic_data + and include_user_assist_info + and forensic_data.get("include_user_assist_info") + ): + + user_assist_info_data = forensic_data.get( + "include_user_assist_info" + ) + user_assist_info_table = [] + user_assist_info_json = [] + + for user_assist_info in user_assist_info_data: + user_assist_info_table.append(user_assist_info.to_table()) + user_assist_info_json.append(user_assist_info.raw_data) + + if max_items_to_return: + user_assist_info_table = user_assist_info_table[ + :max_items_to_return + ] + user_assist_info_json = user_assist_info_json[ + :max_items_to_return + ] + + entity_json["user_assist_info"] = user_assist_info_json + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: " + f"User-Assist Programs Information", + construct_csv(user_assist_info_table), + ) + + if ( + forensic_data + and include_powershell_info + and forensic_data.get("include_powershell_info") + ): + + powershell_data = forensic_data.get("include_powershell_info") + powershell_table = [] + powershell_json = [] + + for powershell in powershell_data: + powershell_table.append(powershell.to_table()) + powershell_json.append(powershell.raw_data) + + if max_items_to_return: + powershell_table = powershell_table[:max_items_to_return] + powershell_json = powershell_json[:max_items_to_return] + + entity_json["powershell_history"] = powershell_json + siemplify.result.add_data_table( + f"{event_ready.get(event_ready_key)}: Powershell History", + construct_csv(powershell_table), + ) + + if entity_json: + json_results[event_ready.get(event_ready_key)] = entity_json + successfully_fetched_forensics.append( + event_ready.get(event_ready_key) + ) + else: + failed_fetched_forensics.append(event_ready.get(event_ready_key)) + except RateLimitException: + raise + + except Exception as e: + siemplify.LOGGER.error( + f"An error occurred on entity: {entity.identifier}" + ) + siemplify.LOGGER.exception(e) + failed_fetched_forensics.append(event_ready.get(event_ready_key)) + continue + + if successfully_fetched_forensics: + + if entities_to_update: + siemplify.update_entities(entities_to_update) + siemplify.result.add_result_json( + convert_dict_to_json_result_dict(json_results) + ) + output_message += "Successfully ran forensic scan on the following endpoints in Illusive Networks: {}.".format( + ", ".join([entity for entity in successfully_fetched_forensics]) + ) + status = EXECUTION_STATE_COMPLETED + result_value = True + + if failed_fetched_forensics: + status = EXECUTION_STATE_COMPLETED + result_value = True + output_message += "\nAction wasn't able to get any information from forensic scan on the following endpoints: {}.".format( + ", ".join([entity for entity in failed_fetched_forensics]) + ) + + if failed_fetched_forensics and not successfully_fetched_forensics: + output_message = ( + "No forensic information was found on the provided endpoints" + ) + result_value = False + status = EXECUTION_STATE_COMPLETED + + return output_message, result_value, status + + +@output_handler +def main(is_first_run): + siemplify = SiemplifyAction() + siemplify.script_name = RUN_FORENSIC_SCAN_ACTION + mode = "Main" if is_first_run else "Get Forensic Data" + siemplify.LOGGER.info("----------------- Main - Param Init -----------------") + + api_root = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Root", + is_mandatory=True, + print_value=True, + ) + api_key = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="API Key", + is_mandatory=True, + print_value=False, + ) + ca_certificate = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="CA Certificate File", + is_mandatory=False, + ) + verify_ssl = extract_configuration_param( + siemplify, + provider_name=INTEGRATION_NAME, + param_name="Verify SSL", + default_value=False, + input_type=bool, + is_mandatory=True, + ) + + include_sys_info = extract_action_param( + siemplify, + param_name="Include System Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_prefetch_files_info = extract_action_param( + siemplify, + param_name="Include Prefetch Files Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_add_remove = extract_action_param( + siemplify, + param_name="Include Add-Remove Programs Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_startup_info = extract_action_param( + siemplify, + param_name="Include Startup Processes Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_running_info = extract_action_param( + siemplify, + param_name="Include Running Processes Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_user_assist_info = extract_action_param( + siemplify, + param_name="Include User-Assist Programs Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + include_powershell_info = extract_action_param( + siemplify, + param_name="Include Powershell History Information", + is_mandatory=True, + print_value=True, + input_type=bool, + ) + + siemplify.LOGGER.info(f"----------------- {mode} - Started -----------------") + output_message = "" + try: + max_items_to_return = extract_action_param( + siemplify, + param_name="Max Items To Return", + is_mandatory=False, + print_value=True, + input_type=int, + ) + + if ( + include_sys_info + or include_prefetch_files_info + or include_add_remove + or include_startup_info + or include_running_info + or include_user_assist_info + or include_user_assist_info + or include_powershell_info + ): + + if max_items_to_return and max_items_to_return < 0: + siemplify.LOGGER.error( + f'Given value for "Max Items To Return" has to be non negative. ' + f'Using default value of {DEFAULT_ITEMS}' + ) + max_items_to_return = DEFAULT_ITEMS + + illusivenetworks_manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + if is_first_run: + illusivenetworks_manager.test_connectivity() + output_message, result_value, status = start_operation( + siemplify=siemplify, manager=illusivenetworks_manager + ) + + else: + output_message, result_value, status = query_operation_status( + siemplify=siemplify, + manager=illusivenetworks_manager, + include_sys_info=include_sys_info, + include_prefetch_files_info=include_prefetch_files_info, + include_add_remove=include_add_remove, + include_startup_info=include_startup_info, + include_running_info=include_running_info, + include_user_assist_info=include_user_assist_info, + include_powershell_info=include_powershell_info, + max_items_to_return=max_items_to_return, + ) + else: + output_message += (f'Error executing action {RUN_FORENSIC_SCAN_ACTION}. ' + f'Reason: You need to enable at least one of the ' + f'"Include ..." parameters.') + siemplify.LOGGER.error(output_message) + status = EXECUTION_STATE_FAILED + result_value = False + + except Exception as e: + output_message += ( + f"Error executing action {RUN_FORENSIC_SCAN_ACTION}. Reason {e}." + ) + siemplify.LOGGER.error(output_message) + siemplify.LOGGER.exception(e) + status = EXECUTION_STATE_FAILED + result_value = False + + siemplify.LOGGER.info(f"----------------- {mode} - Finished -----------------") + siemplify.LOGGER.info( + f"\n status: {status}\n " + f"result_value: {result_value}\n " + f"output_message: {output_message}" + ) + siemplify.end(output_message, result_value, status) + + +if __name__ == "__main__": + is_first_run = len(sys.argv) < 3 or sys.argv[2] == "True" + main(is_first_run) diff --git a/content/response_integrations/google/illusive_networks/actions/RunForensicScan.yaml b/content/response_integrations/google/illusive_networks/actions/RunForensicScan.yaml new file mode 100644 index 000000000..747d0e0f1 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/RunForensicScan.yaml @@ -0,0 +1,68 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Run Forensic Scan +description: Run forensic scan on the endpoint in the Illusive Networks. Works with + IP and Hostname entities. +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#run_forensic_scan +integration_identifier: IllusiveNetworks +parameters: +- name: Include System Information + default_value: true + type: boolean + description: If enabled, action will return system information. + is_mandatory: true +- name: Include Prefetch Files Information + default_value: true + type: boolean + description: If enabled, action will return information about prefetch files. + is_mandatory: true +- name: Include Add-Remove Programs Information + default_value: false + type: boolean + description: If enabled, action will return information about add-remove programs. + is_mandatory: true +- name: Include Startup Processes Information + default_value: false + type: boolean + description: If enabled, action will return information about startup processes. + is_mandatory: true +- name: Include Running Processes Information + default_value: false + type: boolean + description: If enabled, action will return information about running processes. + is_mandatory: false +- name: Include User-Assist Programs Information + default_value: false + type: boolean + description: If enabled, action will return information about user-assist programs. + is_mandatory: true +- name: Include Powershell History Information + default_value: false + type: boolean + description: If enabled, action will return information about powershell history. + is_mandatory: true +- name: Max Items To Return + default_value: 50 + type: string + description: Specify how many items to return. If nothing is provided, action + will return everything. + is_mandatory: false +dynamic_results_metadata: +- result_example_path: resources/run_forensic_scan_JsonResult_example.json + result_name: JsonResult + show_result: true +creator: admin +is_async: true +simulation_data_json: '{"Entities": ["HOSTNAME","ADDRESS"]}' diff --git a/content/response_integrations/google/illusive_networks/actions/__init__.py b/content/response_integrations/google/illusive_networks/actions/__init__.py new file mode 100644 index 000000000..9f71a2dc3 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/actions/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.py b/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.py new file mode 100644 index 000000000..f7ab3a8fd --- /dev/null +++ b/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.py @@ -0,0 +1,294 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +import sys +import os +import uuid +from ..core.constants import ( + TIMEOUT_THRESHOLD, + DEFAULT_DEVICE_PRODUCT, + DEFAULT_DEVICE_VENDOR, + CONNECTOR_NAME, + RATE_LIMIT_ERROR_IDENTIFIER, +) +from soar_sdk.SiemplifyConnectors import SiemplifyConnectorExecution +from soar_sdk.SiemplifyConnectorsDataModel import AlertInfo +from soar_sdk.SiemplifyUtils import output_handler, unix_now +from TIPCommon import extract_connector_param, validate_map_file +from ..core.IllusiveNetworksManager import IllusiveNetworksManager +from ..core.Utils import ( + get_environment_common, + get_last_success_time, + is_overflowed, + save_timestamp, + read_ids, + write_ids, + is_approaching_timeout, + filter_old_alerts, + priority_text_to_value, + pass_whitelist_filter, +) + +MAP_FILE = "map.json" +INCIDENT_NAME = "Incident:{}" + + +@output_handler +def main(is_test_run): + processed_incidents = [] + all_incidents = [] + existing_ids = [] + connector_starting_time = unix_now() + siemplify = SiemplifyConnectorExecution() + siemplify.script_name = 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 -----------------") + + environment_field_name = extract_connector_param( + siemplify, + param_name="Environment Field Name", + is_mandatory=False, + print_value=True, + ) + + environment_regex_pattern = extract_connector_param( + siemplify, + param_name="Environment Regex Pattern", + is_mandatory=False, + print_value=True, + ) + + api_root = extract_connector_param( + siemplify, param_name="API Root", is_mandatory=True, print_value=True + ) + + api_key = extract_connector_param( + siemplify, param_name="API Key", is_mandatory=True + ) + + alert_severity = extract_connector_param( + siemplify, param_name="Alert Severity", print_value=True, is_mandatory=True + ) + + max_hours_backwards = extract_connector_param( + siemplify, + param_name="Max Hours Backwards", + input_type=int, + default_value=1, + print_value=True, + ) + + max_incidents_to_fetch = extract_connector_param( + siemplify, + param_name="Max Incidents To Fetch", + input_type=int, + default_value=10, + print_value=True, + ) + + use_whitelist_as_a_blacklist = extract_connector_param( + siemplify, + param_name="Use whitelist as a blacklist", + input_type=bool, + default_value=True, + print_value=True, + ) + + python_process_timeout = extract_connector_param( + siemplify, + param_name="PythonProcessTimeout", + input_type=int, + is_mandatory=True, + print_value=True, + ) + + verify_ssl = extract_connector_param( + siemplify, + param_name="Verify SSL", + input_type=bool, + default_value=False, + is_mandatory=True, + print_value=True, + ) + ca_certificate = extract_connector_param( + siemplify, param_name="CA Certificate File" + ) + + siemplify.LOGGER.info("------------------- Main - Started -------------------") + map_file_path = os.path.join(siemplify.run_folder, MAP_FILE) + validate_map_file(siemplify, map_file_path) + + try: + severity_value = priority_text_to_value(alert_severity) + environment_common = get_environment_common( + siemplify, environment_field_name, environment_regex_pattern + ) + + last_success_time = get_last_success_time( + siemplify=siemplify, + date_time_format="%Y-%m-%dT%H:%M:%S.000Z", + offset_with_metric={"hours": max_hours_backwards}, + ) + + illusive_networks_manager = IllusiveNetworksManager( + api_root=api_root, + api_key=api_key, + ca_certificate=ca_certificate, + verify_ssl=verify_ssl, + siemplify_logger=siemplify.LOGGER, + ) + + existing_ids = read_ids(siemplify) + siemplify.LOGGER.info(f"Successfully loaded {len(existing_ids)} existing ids") + incidents = illusive_networks_manager.get_incidents( + start_date=last_success_time + ) + filtered_incidents = filter_old_alerts( + siemplify.LOGGER, incidents, existing_ids, "incident_id" + ) + + siemplify.LOGGER.info( + f"Alert to process after filtering already processed once " + f"{len(filtered_incidents)} out of {len(incidents)} received from the API" + ) + + for incident in filtered_incidents: + siemplify.LOGGER.info("\n") + try: + if is_approaching_timeout( + python_process_timeout, connector_starting_time, TIMEOUT_THRESHOLD + ): + siemplify.LOGGER.info( + "Timeout is approaching. Connector will gracefully exit" + ) + break + + if is_test_run and processed_incidents: + siemplify.LOGGER.info( + "Maximum incidents count (1) for test run reached!" + ) + break + if len(processed_incidents) >= max_incidents_to_fetch: + siemplify.LOGGER.info( + f"Maximum incidents count ({max_incidents_to_fetch}) " + f"reached! Stopping..." + ) + break + + siemplify.LOGGER.info( + f"Starting incident with id: {incident.incident_id}" + ) + + all_incidents.append(incident) + + if not pass_whitelist_filter( + siemplify, use_whitelist_as_a_blacklist, incident, "incident_types" + ): + continue + + siemplify.LOGGER.info( + f"Processing incident with id: {incident.incident_id}" + ) + incident.set_event( + illusive_networks_manager.get_incident_timeline( + incident_id=incident.incident_id + ) + ) + alert_info = create_alert_info( + siemplify, environment_common, incident, severity_value + ) + if is_overflowed(siemplify, alert_info, is_test_run): + siemplify.LOGGER.info( + f"{alert_info.rule_generator}-{alert_info.ticket_id}-" + f"{alert_info.environment}-{alert_info.device_product} " + f"found as overflow alert. Skipping..." + ) + # If is overflowed we should skip + continue + + processed_incidents.append(alert_info) + siemplify.LOGGER.info( + f"Incident with id {incident.incident_id} was created." + ) + + except Exception as e: + siemplify.LOGGER.error( + f"Failed to process incident with id {incident.incident_id}" + ) + siemplify.LOGGER.exception(e) + if RATE_LIMIT_ERROR_IDENTIFIER in e: + siemplify.LOGGER.info( + f"Stopping connector execution because of api error" + ) + all_incidents.pop() + raise + if is_test_run: + raise + siemplify.LOGGER.info("\n") + except Exception as e: + siemplify.LOGGER.error(f"General error: {e}") + siemplify.LOGGER.exception(e) + + if is_test_run: + raise + + if not is_test_run and all_incidents: + siemplify.LOGGER.info("Saving existing ids.") + write_ids( + siemplify, + existing_ids + [incident.incident_id for incident in all_incidents], + ) + save_timestamp(siemplify=siemplify, alerts=all_incidents) + + siemplify.LOGGER.info( + f"Incidents processed: {len(processed_incidents)} out of {len(all_incidents)}" + ) + siemplify.LOGGER.info(f"Created total of {len(processed_incidents)} alerts") + + siemplify.LOGGER.info("------------------- Main - Finished -------------------") + siemplify.return_package(processed_incidents) + + +def create_alert_info(siemplify, environment_common, incident, severity_value): + siemplify.LOGGER.info(f"Creating alert info for incident {incident.incident_id}") + + alert_info = AlertInfo() + alert_info.ticket_id = incident.incident_id + alert_info.display_id = str(uuid.uuid4()) + alert_info.rule_generator = ", ".join(incident.incident_types) + alert_info.name = INCIDENT_NAME.format(alert_info.rule_generator) + alert_info.device_vendor = DEFAULT_DEVICE_VENDOR + alert_info.device_product = DEFAULT_DEVICE_PRODUCT + alert_info.priority = severity_value + alert_info.end_time = alert_info.start_time = incident.timestamp + alert_info.events = incident.to_events() + environment_dict = incident.to_flat_dict() + if alert_info.events: + environment_dict.update(alert_info.events[0]) + alert_info.environment = environment_common.get_environment(environment_dict) + + siemplify.LOGGER.info(f"Alert info created for incident {incident.incident_id}") + + return alert_info + + +if __name__ == "__main__": + is_test = not (len(sys.argv) < 2 or sys.argv[1] == "True") + main(is_test) diff --git a/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.yaml b/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.yaml new file mode 100644 index 000000000..b751484a5 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/connectors/IncidentsConnector.yaml @@ -0,0 +1,154 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Illusive Networks - Incidents Connector +parameters: +- name: DeviceProductField + default_value: Product Name + type: string + description: Enter the source field name in order to retrieve the Product Field + name. + is_mandatory: true + is_advanced: false + mode: regular +- name: EventClassId + default_value: details_serviceType + type: string + description: Enter the source field name in order to retrieve the Event Field + name. + is_mandatory: true + is_advanced: false + mode: regular +- name: PythonProcessTimeout + default_value: '180' + type: integer + description: Timeout limit for the python process running the current script. + is_mandatory: true + is_advanced: false + mode: regular +- name: Environment Field Name + default_value: '' + type: string + description: Describes the name of the field where the environment name is stored. + If the environment field isn't found, the environment is the default environment. + is_mandatory: false + is_advanced: false + mode: script +- name: Environment Regex Pattern + default_value: .* + type: string + description: A regex pattern to run on the value found in the "Environment Field + Name" field. Default is .* to catch all and return the value unchanged. Used + to allow the user to manipulate the environment field via regex logic. If + the regex pattern is null or empty, or the environment value is null, the + final environment result is the default environment. + is_mandatory: false + is_advanced: false + mode: script +- name: API Root + default_value: http://x.x.x.x + type: string + description: API root of the Illusive Networks instance. + is_mandatory: true + is_advanced: false + mode: script +- name: API Key + default_value: '' + type: password + description: 'API Key of the Illusive Networks. Note: string "Basic" shouldn''t + be a part of the value.' + is_mandatory: true + is_advanced: false + mode: script +- name: CA Certificate File + default_value: '' + type: string + description: Base 64 encoded CA certificate file. + is_mandatory: false + is_advanced: false + mode: script +- name: Verify SSL + default_value: false + type: boolean + description: If enabled, verify the SSL certificate for the connection to the + Illusive Networks server is valid. + is_mandatory: true + is_advanced: false + mode: script +- name: Alert Severity + default_value: Medium + type: string + description: 'Severity of the Siemplify alert that will be created based on the + incidents from Illusive Networks. Possible values: Informational Low Medium + High Critical' + is_mandatory: true + is_advanced: false + mode: script +- name: Max Hours Backwards + default_value: 1 + type: integer + description: Number of hours before the first connector iteration to retrieve + incidents from. This parameter applies to the initial connector iteration + after you enable the connector for the first time, or used as a fallback value + in cases where connector's last run timestamp expires. + is_mandatory: false + is_advanced: false + mode: script +- name: Max Incidents To Fetch + default_value: 10 + type: integer + description: How many incidents to process per one connector iteration. Maximum + is 1000. + is_mandatory: false + is_advanced: false + mode: script +- name: Use whitelist as a blacklist + default_value: true + type: boolean + description: If enabled, whitelist will be used as a blacklist. + is_mandatory: true + is_advanced: false + mode: script +- name: Proxy Server Address + default_value: '' + type: string + description: The address of the proxy server to use. + is_mandatory: false + is_advanced: false + mode: script +- name: Proxy Username + default_value: '' + type: string + description: The proxy username to authenticate with. + is_mandatory: false + is_advanced: false + mode: script +- name: Proxy Password + default_value: '' + type: password + description: The proxy password to authenticate with. + is_mandatory: false + is_advanced: false + mode: script +description: 'Pull incidents with related forensic timeline from Illusive Networks. + Note: This connector requires changes to the rate limiting on the Illusive Networks + server. Default rate limit is too small. All of the steps are available in the + documentation. Whitelisting and Blacklisting is done via type of the incident' +integration: IllusiveNetworks +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks#connectors +rules: +- rule_name: EXTERNAL + rule_type: allow_list +is_connector_rules_supported: false +creator: admin diff --git a/content/response_integrations/google/illusive_networks/connectors/__init__.py b/content/response_integrations/google/illusive_networks/connectors/__init__.py new file mode 100644 index 000000000..9f71a2dc3 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/connectors/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/content/response_integrations/google/illusive_networks/core/IllusiveNetworksExceptions.py b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksExceptions.py new file mode 100755 index 000000000..96d804d85 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksExceptions.py @@ -0,0 +1,33 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +class IllusiveNetworksException(Exception): + pass + + +class IncidentNotReadyException(Exception): + pass + + +class RateLimitException(Exception): + pass + + +class ManagerNotFoundException(IllusiveNetworksException): + pass + + +class ManagerAlreadyExistException(IllusiveNetworksException): + pass diff --git a/content/response_integrations/google/illusive_networks/core/IllusiveNetworksManager.py b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksManager.py new file mode 100755 index 000000000..b53602da7 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksManager.py @@ -0,0 +1,566 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +import requests +import urllib.parse +from .IllusiveNetworksExceptions import ( + IllusiveNetworksException, + IncidentNotReadyException, + RateLimitException, + ManagerNotFoundException, +) +from .IllusiveNetworksParser import IllusiveNetworksParser +import base64 +from .constants import ( + CA_CERTIFICATE_FILE_PATH, + PING_QUERY, + FORENSIC_SCAN_QUERY, + GET_INCIDENT_ID_QUERY, + GET_FORENSIC_DATA_QUERY, + ENRICH_ENTITIES_QUERY, + FORENSIC_DATA_TYPES, + GET_DECEPTIVE_USERS_QUERY, + GET_DECEPTIVE_SERVERS_QUERY, +) + +API_ENDPOINTS = { + "get_incidents": "/api/v1/incidents", + "timeline": "/api/v1/forensics/timeline", + "deceptive_server": "api/v1/deceptive-entities/server", + "deceptive_servers": "api/v1/deceptive-entities/servers", + "deceptive_user": "api/v1/deceptive-entities/user", + "deceptive_users": "api/v1/deceptive-entities/users", +} + + +class IllusiveNetworksManager: + def __init__( + self, + api_root=None, + api_key=None, + ca_certificate=None, + verify_ssl=False, + siemplify_logger=None, + ): + """ + The method is used to init an object of Manager class + :param api_root: API Root of the Illusive Networks + :param api_key: API Key of the Illusive Networks instance. + :param ca_certificate: CA Certificate as base64 + :param verify_ssl: If enabled, verify the SSL certificate for the connection to + the Illusive Networks server is valid. + :param siemplify_logger: Siemplify logger. + """ + ca_certs = False if not verify_ssl else None + + # ca_certificate is base64 string which needs to be decoded and a temp file + # for the cacert is created + if ca_certificate: + try: + file_content = base64.b64decode(ca_certificate) + with open(CA_CERTIFICATE_FILE_PATH, "w+") as f: + f.write(file_content.decode("utf-8")) + + except Exception as e: + raise IllusiveNetworksException(e) + + if verify_ssl and ca_certificate: + verify = CA_CERTIFICATE_FILE_PATH + + elif verify_ssl and not ca_certificate: + verify = True + else: + verify = False + + self.session = requests.session() + self.session.verify = verify + + self.api_root = api_root[:-1] if api_root.endswith("/") else api_root + self.api_key = api_key + self.siemplify_logger = siemplify_logger + self.session.headers.update({"Authorization": f"Basic {self.api_key}"}) + self.parser = IllusiveNetworksParser() + + @staticmethod + def validate_response(response, error_msg="An error occurred"): + try: + if response.status_code == 202: + raise IncidentNotReadyException("Incident Not Ready") + + if response.status_code == 429: + raise RateLimitException( + "Rate limit error. Please refer to the documentation on how to " + "increase the rate limit" + ) + response.raise_for_status() + + except requests.HTTPError as error: + try: + response_json = response.json() + except: + # Not a JSON - return content + raise IllusiveNetworksException( + f"{error_msg}: {error} - {error.response.content}" + ) + raise IllusiveNetworksException( + f"{error_msg}: {response_json.get('message', '')} " + f"{response_json.get('errorMessage', '')}" + ) + + def test_connectivity(self): + """ + Test integration connectivity. + """ + + result = self.session.get(PING_QUERY.format(self.api_root)) + # Verify result. + self.validate_response(result) + + def create_forensic_scan_request(self, entity_name): + """ + Function that initiates the forensic scan + :param entity_name: {str} Entity for which the scan should be initiated + """ + result = self.session.post( + FORENSIC_SCAN_QUERY.format(self.api_root, entity_name) + ) + # Verify result. + self.validate_response(result) + + return result.json().get("eventId") + + def get_incident_id(self, event_id): + """ + Function that checks if the incident for given event_id is ready to be fetched + :param event_id: {str} Event ID that needs to be checked + """ + + result = self.session.get(GET_INCIDENT_ID_QUERY.format(self.api_root, event_id)) + # Verify result. + self.validate_response(result) + + def get_forensic_data( + self, + event_id, + include_sys_info, + include_prefetch_files_info, + include_add_remove, + include_startup_info, + include_running_info, + include_user_assist_info, + include_powershell_info, + ): + """ + Function that fetches the forensic scan information + :param include_sys_info: True if System Info should be included in results + :param include_prefetch_files_info: True if Prefetch Files Info should be + included in results + :param include_add_remove: True if Add Remove Processes should be included + in results + :param include_startup_info: True if Startup Processes should be included + in results + :param include_running_info: True if Running Processes should be included + in results + :param include_user_assist_info: True if User Assist should be included + in results + :param include_powershell_info: True if Powershell Info should be included + in results + :return object_data: {dict} Dictionary of requested parameters + """ + + object_data = {} + if include_sys_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, event_id, FORENSIC_DATA_TYPES.get("include_sys_info") + ) + ) + # Verify result. + self.validate_response(result) + + host_info = self.parser.build_siemplify_forensic_host_info_object( + result.json() + ) + object_data["host_info"] = host_info + + if include_prefetch_files_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_prefetch_files_info"), + ) + ) + # Verify result. + self.validate_response(result) + + prefetch_files_info = ( + self.parser.build_siemplify_forensic_prefetch_info_object(result.json()) + ) + object_data["prefetch_info"] = prefetch_files_info + + if include_add_remove: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_add_remove"), + ) + ) + # Verify result. + self.validate_response(result) + + add_remove_info = self.parser.build_siemplify_forensic_add_remove_object( + result.json() + ) + object_data["include_add_remove"] = add_remove_info + + if include_startup_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_startup_info"), + ) + ) + # Verify result. + self.validate_response(result) + + include_startup_info = self.parser.build_siemplify_forensic_startup_object( + result.json() + ) + object_data["include_startup_info"] = include_startup_info + + if include_running_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_running_info"), + ) + ) + # Verify result. + self.validate_response(result) + + include_running_info = ( + self.parser.build_siemplify_forensic_runningprocesses_object( + result.json() + ) + ) + object_data["include_running_info"] = include_running_info + + if include_user_assist_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_user_assist_info"), + ) + ) + # Verify result. + self.validate_response(result) + + include_user_assist_info = ( + self.parser.build_siemplify_forensic_userassist_object(result.json()) + ) + object_data["user_assist_info"] = include_user_assist_info + + if include_powershell_info: + result = self.session.get( + GET_FORENSIC_DATA_QUERY.format( + self.api_root, + event_id, + FORENSIC_DATA_TYPES.get("include_powershell_info"), + ) + ) + # Verify result. + self.validate_response(result) + + include_powershell_info = ( + self.parser.build_siemplify_forensic_powershell_object(result.json()) + ) + object_data["powershell_history"] = include_powershell_info + + return object_data + + def enrich_entity(self, host_entity_name): + """ + Function that gets details about a host + :param host_entity_name: {str} Name of the host to enrich + :return: {HostObject} Siemplify HostObject + """ + + result = self.session.get( + ENRICH_ENTITIES_QUERY.format(self.api_root, host_entity_name) + ) + # Verify result. + self.validate_response(result) + + return self.parser.build_siemplify_host_object(result.json()) + + def _get_full_url(self, url_id, **kwargs): + """ + Get full url from url identifier. + :param url_id: {str} The id of url + :param general_api: {bool} whether to use general api or not + :param kwargs: {dict} Variables passed for string formatting + :return: {str} The full url + """ + return urllib.parse.urljoin( + self.api_root, API_ENDPOINTS[url_id].format(**kwargs) + ) + + def get_incidents(self, start_date): + """ + Get incidents + :param start_date: {str} Start time to fetch incidents + :return: {list} List of Incident + """ + return sorted( + self._paginate_results( + method="GET", + url=self._get_full_url("get_incidents"), + parser_method="build_siemplify_incident_object", + params={"start_date": start_date}, + ), + key=lambda x: x.timestamp, + ) + + def _paginate_results( + self, + method, + url, + parser_method, + params=None, + body=None, + limit=None, + err_msg="Unable to get results", + page_size=100, + ): + """ + Paginate the results of a job + :param method: {str} The method of the request (GET, POST, PUT, DELETE, PATCH) + :param url: {str} The url to send request to + :param parser_method: {str} The name of parser method to build the result + :param params: {dict} The params of the request + :param body: {dict} The json payload of the request + :param limit: {int} The limit of the results to fetch + :param err_msg: {str} The message to display on error + :param page_size: {int} Items per page + :return: {list} List of results + """ + + params = params or {} + offset = 0 + params["limit"] = page_size + params.update({"offset": offset}) + + response = None + results = [] + + while True: + if response: + if limit and len(results) >= limit: + break + + params.update({"offset": params["offset"] + page_size}) + + response = self.session.request(method, url, params=params, json=body) + + self.validate_response(response, err_msg) + current_items = [ + getattr(self.parser, parser_method)(item_json) + for item_json in response.json() + ] + results.extend(current_items) + if len(current_items) < page_size: + break + return results[:limit] if limit else results + + def get_incident_timeline(self, incident_id): + """ + Get incident timeline + :param incident_id: {str} Incident if to load timeline + :return: {list} List of BaseModel + """ + response = self.session.get( + self._get_full_url("timeline"), params={"incident_id": incident_id} + ) + self.validate_response( + response, f"Unable to load timeline for incident {incident_id}" + ) + return self.parser.build_incident_events(response.json()) + + def get_deceptive_users(self, deceptive_state, limit=None): + """ + Get deceptive users list + :param deceptive_state: {str} deceptive state + :param limit: {int} Items to return + :return: {list} List of DeceptiveUser + """ + response = self.session.get( + GET_DECEPTIVE_USERS_QUERY.format(self.api_root, deceptive_state) + ) + self.validate_response(response) + + return self.parser.build_siemplify_deceptive_user_obj_list( + response.json(), limit + ) + + def get_deceptive_servers(self, deceptive_state, limit=None): + """ + Get deceptive servers list + :param deceptive_state: {str} deceptive state + :param limit: {int} Items to return + :return: {list} List of DeceptiveServer + """ + response = self.session.get( + GET_DECEPTIVE_SERVERS_QUERY.format(self.api_root, deceptive_state) + ) + self.validate_response(response) + + return self.parser.build_siemplify_deceptive_server_obj_list( + response.json(), limit + ) + + def get_deceptive_server(self, host_name): + """ + Get deceptive server by host name + :param host_name: {str} + :return: {DeceptiveServer or None} + """ + response = self.session.get( + self._get_full_url("deceptive_server"), params={"hostName": host_name} + ) + self.validate_response(response) + + return ( + self.parser.build_siemplify_deceptive_server_obj(response.json()) + if response.content + else None + ) + + def remove_deceptive_server(self, hosts): + """ + Remove deceptive server by hosts names + :param hosts: {list} List of host names + :return: {bool} + """ + response = self.session.delete( + self._get_full_url("deceptive_servers"), params={"deceptive_hosts": hosts} + ) + self.validate_response(response) + + return True + + def add_deceptive_server(self, host, server_types, policy_names): + """ + Add deceptive server or raise if host already exist + :param host: {str} + :param server_types: {list} + :param policy_names: {list} + :return: {bool} + """ + payload = [ + {"host": host, "policyNames": policy_names, "serviceTypes": server_types} + ] + response = self.session.post( + self._get_full_url("deceptive_servers"), json=payload + ) + self.validate_response(response) + + return True + + def get_deceptive_server_or_raise(self, host_name): + """ + Get deceptive server by host name or raise + :param host_name: {str} + :return: {DeceptiveServer} + """ + deceptive_server = self.get_deceptive_server(host_name) + + if deceptive_server is not None: + return deceptive_server + + raise ManagerNotFoundException( + f'Deceptive server "{host_name}" doesn\'t exist.' + ) + + def get_deceptive_user(self, username): + """ + Get deceptive user by username + :param username: {str} + :return: {DeceptiveUser or None} + """ + response = self.session.get( + self._get_full_url("deceptive_user"), params={"userName": username} + ) + self.validate_response(response) + + return ( + self.parser.build_siemplify_deceptive_user_obj(response.json()) + if response.content + else None + ) + + def add_deceptive_user(self, dns_domain, username, password, policy_names): + """ + Add deceptive user or raise if user already exist + :param dns_domain: {str} + :param username: {str} + :param password: {str} + :param policy_names: {list} + :return: {bool} + """ + payload = [ + { + "domainName": dns_domain, + "username": username, + "password": password, + "policyNames": policy_names, + } + ] + response = self.session.post( + self._get_full_url("deceptive_users"), json=payload + ) + self.validate_response(response) + + return True + + def get_deceptive_user_or_raise(self, username): + """ + Get deceptive user by username or raise + :param username: {str} + :return: {DeceptiveUser} + """ + deceptive_user = self.get_deceptive_user(username) + + if deceptive_user is not None: + return deceptive_user + + raise ManagerNotFoundException(f'Deceptive user "{username}" doesn\'t exist.') + + def remove_deceptive_user(self, usernames): + """ + Remove deceptive users by usernames + :param usernames: {list} List of usernames + :return: {bool} + """ + response = self.session.delete( + self._get_full_url("deceptive_users"), params={"deceptive_users": usernames} + ) + self.validate_response(response) + + return True diff --git a/content/response_integrations/google/illusive_networks/core/IllusiveNetworksParser.py b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksParser.py new file mode 100755 index 000000000..ad088adc5 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/IllusiveNetworksParser.py @@ -0,0 +1,278 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from .datamodels import * +from .Utils import transform_string_response_to_json +from TIPCommon import SiemplifySession + + +class IllusiveNetworksParser: + + @staticmethod + def build_siemplify_host_object(raw_data): + return HostObject( + raw_data=raw_data.get("content") if raw_data.get("content") else None, + machine_name=( + raw_data.get("content")[0].get("machineName") + if raw_data.get("content") + else None + ), + is_healthy=( + raw_data.get("content")[0].get("isHealthy") + if raw_data.get("content") + else None + ), + host=( + raw_data.get("content")[0].get("host") + if raw_data.get("content") + else None + ), + distinguested_name=( + raw_data.get("content")[0].get("distinguishedName") + if raw_data.get("content") + else None + ), + source_discovery_name=( + raw_data.get("content")[0].get("sourceDiscoveryName") + if raw_data.get("content") + else None + ), + policy_name=( + raw_data.get("content")[0].get("policyName") + if raw_data.get("content") + else None + ), + operating_system_name=( + raw_data.get("content")[0].get("operatingSystemName") + if raw_data.get("content") + else None + ), + agent_version=( + raw_data.get("content")[0].get("agentVersion") + if raw_data.get("content") + else None + ), + logged_in_user_name=( + raw_data.get("content")[0].get("loggedInUserName") + if raw_data.get("content") + else None + ), + machine_exe_status=( + raw_data.get("content")[0].get("machineExecutionUnifiedStatus") + if raw_data.get("content") + else None + ), + bitness=( + raw_data.get("content")[0].get("bitness") + if raw_data.get("content") + else None + ), + ) + + @staticmethod + def build_siemplify_forensic_host_info_object(raw_data): + value_data = transform_string_response_to_json(raw_data=raw_data) + + return ForensicHostInfo( + raw_data=value_data, + os_name=value_data.get("osName"), + machine_type=value_data.get("machineType"), + host=value_data.get("host"), + logged_in_user=value_data.get("loggedInUser"), + user_profiles=", ".join(value_data.get("userProfiles")), + operating_system_type=value_data.get("operatingSystemType"), + ) + + @staticmethod + def build_siemplify_forensic_prefetch_info_object(raw_data): + prefetch_info_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicPreFetchInfo( + raw_data=prefetch_info, + file_name=prefetch_info.get("fileName") if prefetch_info else None, + last_exe_time=( + prefetch_info.get("details", {}).get("lastExecutionTime") + if prefetch_info + else None + ), + file_modify_time=( + prefetch_info.get("details", {}).get("fileModificationTime") + if prefetch_info + else None + ), + prefetchfile_name=( + prefetch_info.get("details", {}).get("prefetchFileName") + if prefetch_info + else None + ), + ) + for prefetch_info in prefetch_info_data + ] + + @staticmethod + def build_siemplify_forensic_add_remove_object(raw_data): + add_remove_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicProgramsInfo( + raw_data=add_remove, + display_name=( + add_remove.get("details", {}).get("displayName") + if add_remove + else None + ), + file_name=add_remove.get("fileName") if add_remove else None, + ) + for add_remove in add_remove_data + ] + + @staticmethod + def build_siemplify_forensic_startup_object(raw_data): + startup_processes_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicStartupProcess( + raw_data=startup_process, + name=startup_process.get("Name") if startup_process else None, + command=startup_process.get("Command") if startup_process else None, + location=startup_process.get("Location") if startup_process else None, + user=startup_process.get("User") if startup_process else None, + ) + for startup_process in startup_processes_data + ] + + @staticmethod + def build_siemplify_forensic_runningprocesses_object(raw_data): + running_processes_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicRunningProcesses( + raw_data=running_process, + user=running_process.get("user") if running_process else None, + admin_privileges=( + running_process.get("administratorPrivilleges") + if running_process + else None + ), + command_line=( + running_process.get("commandline") if running_process else None + ), + process_id=( + running_process.get("processID") if running_process else None + ), + process_name=( + running_process.get("processName") if running_process else None + ), + start_time=( + running_process.get("startTime") if running_process else None + ), + ) + for running_process in running_processes_data + ] + + @staticmethod + def build_siemplify_forensic_userassist_object(raw_data): + user_assist_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicUserAssistInfo( + raw_data=running_process, + file_name=running_process.get("fileName") if running_process else None, + user_name=running_process.get("userName") if running_process else None, + last_updated_date=( + running_process.get("lastUsedDate") if running_process else None + ), + ) + for running_process in user_assist_data + ] + + @staticmethod + def build_siemplify_forensic_powershell_object(raw_data): + powershell_processes_data = transform_string_response_to_json(raw_data=raw_data) + + return [ + ForensicPowershellInfo( + raw_data=powershell_process, + user_name=( + powershell_process.get("userName") if powershell_process else None + ), + command=( + powershell_process.get("command") if powershell_process else None + ), + ) + for powershell_process in powershell_processes_data + ] + + @staticmethod + def build_siemplify_incident_object(incident_json): + return Incident( + raw_data=incident_json, + incident_time_UTC=incident_json.get("incidentTimeUTC"), + incident_types=incident_json.get("incidentTypes", []), + incident_id=incident_json.get("incidentId"), + ) + + @staticmethod + def build_incident_events(raw_json): + return [ + BaseModel(raw_data=event_json) + for event_json in raw_json + if event_json.get("type") == "EVENT" + ] + + @staticmethod + def build_siemplify_deceptive_user_obj_list(raw_json, limit=None): + return [ + IllusiveNetworksParser.build_siemplify_deceptive_user_obj(deceptive_user) + for deceptive_user in raw_json[:limit] + ] + + @staticmethod + def build_siemplify_deceptive_user_obj(raw_json): + password = raw_json.get("password", "") + if password: + raw_json["password"] = SiemplifySession().encode_data(password) + + return DeceptiveUser( + raw_data=raw_json, + username=raw_json.get("username", ""), + password=raw_json.get("password", ""), + domain=raw_json.get("domainName", ""), + policies=raw_json.get("policyNames", []), + ad_user=raw_json.get("adUser", ""), + active_user=raw_json.get("activeUser", ""), + deceptive_state=raw_json.get("deceptiveState", ""), + ) + + @staticmethod + def build_siemplify_deceptive_server_obj(raw_json): + return DeceptiveServer( + raw_data=raw_json, + host=raw_json.get("host", ""), + services=raw_json.get("serviceTypes", []), + policies=raw_json.get("policyNames", []), + ad_host=raw_json.get("adHost", ""), + deceptive_state=raw_json.get("deceptiveState", ""), + ) + + @staticmethod + def build_siemplify_deceptive_server_obj_list(raw_data, limit=None): + return [ + IllusiveNetworksParser.build_siemplify_deceptive_server_obj( + deceptive_server + ) + for deceptive_server in raw_data[:limit] + ] diff --git a/content/response_integrations/google/illusive_networks/core/Utils.py b/content/response_integrations/google/illusive_networks/core/Utils.py new file mode 100755 index 000000000..3eb6b6b77 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/Utils.py @@ -0,0 +1,330 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +import os +import datetime +import json +import requests +from soar_sdk.SiemplifyUtils import utc_now, convert_datetime_to_unix_time, unix_now +from EnvironmentCommon import EnvironmentHandle +from TIPCommon import validate_map_file + + +def transform_string_response_to_json(raw_data): + processes_data = json.dumps(raw_data) + processes_data = json.loads(processes_data) + + processes_data_value = processes_data.get("value") + + return json.loads(processes_data_value) if processes_data_value else [] + + +UNIX_FORMAT = 1 +DATETIME_FORMAT = 2 +STORED_IDS_LIMIT = 1000 +TIMEOUT_THRESHOLD = 0.9 + + +# Move to TIPCommon +def is_approaching_timeout( + python_process_timeout, connector_starting_time, timeout_threshold=0.9 +): + """ + Check if a timeout is approaching. + :param python_process_timeout: {int} The python process timeout + :param connector_starting_time: {int} The connector start unix time + :param timeout_threshold: {int} Determines which part of the execution time is + available for execution + :return: {bool} True if timeout is close, False otherwise + """ + processing_time_ms = unix_now() - connector_starting_time + return processing_time_ms > python_process_timeout * 1000 * timeout_threshold + + +# Move to TIPCommon +def get_last_success_time( + siemplify, + offset_with_metric, + time_format=DATETIME_FORMAT, + print_value=True, + date_time_format=None, +): + """ + Get last success time datetime + :param siemplify: {siemplify} Siemplify object + :param offset_with_metric: {dict} metric and value. Ex {'hours': 1} + :param time_format: {int} The format of the output time. Ex DATETIME, UNIX + :param print_value: {bool} Whether log the value or not + :param date_time_format: {str} Datetime format to return data + :return: {time} If first run, return current time minus offset time, else return + timestamp from file + """ + last_run_timestamp = siemplify.fetch_timestamp(datetime_format=True) + offset = datetime.timedelta(**offset_with_metric) + current_time = utc_now() + # Check if first run + raw_datetime = ( + current_time - offset + if current_time - last_run_timestamp > offset + else last_run_timestamp + ) + unix_result = convert_datetime_to_unix_time(raw_datetime) + datetime_result = ( + raw_datetime + if not date_time_format + else raw_datetime.strftime(date_time_format) + ) + + if print_value: + siemplify.LOGGER.info( + f"Last success time. Date time:{datetime_result}. Unix:{unix_result}" + ) + return unix_result if time_format == UNIX_FORMAT else datetime_result + + +def validate_response(response, error_msg="An error occurred"): + """ + Validate response + :param response: {requests.Response} The response to validate + :param error_msg: {unicode} Default message to display on error + """ + try: + response.raise_for_status() + + except requests.HTTPError as error: + raise Exception(f"{error_msg}: {error} {error.response.content}") + + return True + + +# Move to TIPCommon +def get_environment_common( + siemplify, environment_field_name, environment_regex_pattern, map_file="map.json" +): + """ + Get environment common + :param siemplify: {siemplify} Siemplify object + :param environment_field_name: {string} The environment field name + :param environment_regex_pattern: {string} The environment regex pattern + :param map_file: {string} The map file + :return: {EnvironmentHandle} + """ + map_file_path = os.path.join(siemplify.run_folder, map_file) + validate_map_file(siemplify, map_file_path) + return EnvironmentHandle( + map_file_path, + siemplify.LOGGER, + environment_field_name, + environment_regex_pattern, + siemplify.context.connector_info.environment, + ) + + +# Move to TIPCommon +def filter_old_alerts(logger, alerts, existing_ids, id_key="alert_id"): + """ + Filter alerts that were already processed + :param logger: {SiemplifyLogger} Siemplify logger + :param alerts: {list} The alerts to filter + :param existing_ids: {list} The ids to filter + :param id_key: {unicode} The key of identifier + :return: {list} The filtered alerts + """ + filtered_alerts = [] + for alert in alerts: + id = getattr(alert, id_key) + if id not in existing_ids: + filtered_alerts.append(alert) + else: + logger.info(f"The alert {id} skipped since it has been fetched before") + + return filtered_alerts + + +# Move to TIPCommon +def read_ids(siemplify, ids_file_name="ids.json"): + """ + Read existing alerts IDs from ids file (from last 24h only) + :param siemplify: {Siemplify} Siemplify object. + :param ids_file_name: {str} The name of the ids file + :return: {list} List of ids + """ + ids_file_path = os.path.join(siemplify.run_folder, ids_file_name) + if not os.path.exists(ids_file_path): + return [] + + try: + with open(ids_file_path, "r") as f: + return json.loads(f.read()) + except Exception as e: + siemplify.LOGGER.error(f"Unable to read ids file: {e}") + siemplify.LOGGER.exception(e) + return [] + + +# Move to TIPCommon +def write_ids(siemplify, ids, ids_file_name="ids.json"): + """ + Write ids to the ids file + :param siemplify: {Siemplify} Siemplify object. + :param ids: {list} The ids to write to the file + :param ids_file_name: {unicode} The name of the ids file. + :return: {bool} + """ + ids = ids[-STORED_IDS_LIMIT:] + try: + ids_file_path = os.path.join(siemplify.run_folder, ids_file_name) + if not os.path.exists(os.path.dirname(ids_file_path)): + os.makedirs(os.path.dirname(ids_file_path)) + + with open(ids_file_path, "w") as f: + try: + for chunk in json.JSONEncoder().iterencode(ids): + f.write(chunk) + except: + # Move seeker to start of the file + f.seek(0) + # Empty the content of the file (the partially written content + # that was written before the exception) + f.truncate() + # Write an empty dict to the events data file + f.write("[]") + raise + siemplify.LOGGER.info(f"Write ids. Total ids={len(ids)}") + return True + except Exception as err: + siemplify.LOGGER.error(f"Failed writing IDs to IDs file, ERROR: {err}") + siemplify.LOGGER.exception(err) + return False + + +# Move to TIPCommon +def is_overflowed(siemplify, alert_info, is_test_run): + """ + Check if overflowed + :param siemplify: {Siemplify} Siemplify object. + :param alert_info: {AlertInfo} + :param is_test_run: {bool} Whether test run or not. + :return: {bool} + """ + try: + return siemplify.is_overflowed_alert( + environment=alert_info.environment, + alert_identifier=alert_info.ticket_id, + alert_name=alert_info.rule_generator, + product=alert_info.device_product, + ) + + except Exception as err: + siemplify.LOGGER.error(f"Error validation connector overflow, ERROR: {err}") + siemplify.LOGGER.exception(err) + if is_test_run: + raise + + return False + + +# Move to TIPCommon +def save_timestamp( + siemplify, + alerts, + timestamp_key="timestamp", + incrementation_value=0, + log_timestamp=True, +): + """ + Save last timestamp for given alerts + :param siemplify: {Siemplify} Siemplify object + :param alerts: {list} The list of alerts to find the last timestamp + :param timestamp_key: {str} key for getting timestamp from alert + :param incrementation_value: {int} The value to increment last timestamp + by milliseconds + :param log_timestamp: {bool} Whether log timestamp or not + :return: {bool} Is timestamp updated + """ + if not alerts: + siemplify.LOGGER.info("Timestamp is not updated since no alerts fetched") + return False + alerts = sorted(alerts, key=lambda alert: int(getattr(alert, timestamp_key))) + last_timestamp = int(getattr(alerts[-1], timestamp_key)) + incrementation_value + if log_timestamp: + siemplify.LOGGER.info(f"Last timestamp is: {last_timestamp}") + + siemplify.save_timestamp(new_timestamp=last_timestamp) + return True + + +def priority_text_to_value(priority_text): + supported_priorities = { + "info": -1, + "low": 40, + "medium": 60, + "high": 80, + "critical": 100, + } + priority_value = supported_priorities.get(priority_text.lower()) + if not priority_value: + raise Exception(f"Severity {priority_text} not supported") + + return priority_value + + +# Move to TIPCommon +WHITELIST_FILTER = 1 +BLACKLIST_FILTER = 2 + + +def pass_whitelist_filter( + siemplify, whitelist_as_a_blacklist, model, model_key, whitelist=None +): + # whitelist filter + whitelist = whitelist or siemplify.whitelist + whitelist_filter_type = ( + BLACKLIST_FILTER if whitelist_as_a_blacklist else WHITELIST_FILTER + ) + model_value = getattr(model, model_key) + model_values = model_value if isinstance(model_value, list) else [model_value] + if whitelist: + for value in model_values: + if whitelist_filter_type == BLACKLIST_FILTER and value in whitelist: + siemplify.LOGGER.info(f"'{value}' did not pass blacklist filter.") + return False + + if whitelist_filter_type == WHITELIST_FILTER and value not in whitelist: + siemplify.LOGGER.info(f"'{value}' did not pass whitelist filter.") + return False + + return True + + +def string_to_multi_value(string_value, delimiter=",", only_unique=False): + """ + String to multi value. + :param string_value: {str} String value to convert multi value. + :param delimiter: {str} Delimiter to extract multi values from single value string. + :param only_unique: {bool} include only uniq values + :return: {dict} fixed dictionary. + """ + if not string_value: + return [] + values = [ + single_value.strip() + for single_value in string_value.split(delimiter) + if single_value.strip() + ] + if only_unique: + seen = set() + return [value for value in values if not (value in seen or seen.add(value))] + return values diff --git a/content/response_integrations/google/illusive_networks/core/__init__.py b/content/response_integrations/google/illusive_networks/core/__init__.py new file mode 100644 index 000000000..9f71a2dc3 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/content/response_integrations/google/illusive_networks/core/constants.py b/content/response_integrations/google/illusive_networks/core/constants.py new file mode 100755 index 000000000..b97a74865 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/constants.py @@ -0,0 +1,69 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +INTEGRATION_NAME = "IllusiveNetworks" +PRODUCT_NAME = "Illusive Networks" +PING_ACTION = f"{INTEGRATION_NAME} - Ping" +RUN_FORENSIC_SCAN_ACTION = f"{INTEGRATION_NAME} - Run Forensic Scan" +ENRICH_ENTITIES_ACTION = f"{INTEGRATION_NAME} - Enrich Entities" +CONNECTOR_NAME = f"{INTEGRATION_NAME} Detection Connector" +LIST_DECEPTIVE_ITEMS_ACTION = f"{INTEGRATION_NAME} - List Deceptive Items" +REMOVE_DECEPTIVE_SERVER_SCRIPT_NAME = f"{INTEGRATION_NAME} - Remove Deceptive Server" +ADD_DECEPTIVE_SERVER_SCRIPT_NAME = f"{INTEGRATION_NAME} - Add Deceptive Server" +ADD_DECEPTIVE_USER_SCRIPT_NAME = f"{INTEGRATION_NAME} - Add Deceptive User" +REMOVE_DECEPTIVE_USER_SCRIPT_NAME = f"{INTEGRATION_NAME} - Remove Deceptive User" + +PING_QUERY = "{}/api/v1/incidents?limit=1" +ENRICH_ENTITIES_QUERY = "{}/api/v2/monitoring/hosts?host_names={}" +FORENSIC_SCAN_QUERY = "{}/api/v1/event/create-external-event?hostNameOrIp={}" +GET_INCIDENT_ID_QUERY = "{}/api/v1/incidents/id?event_id={}" +GET_FORENSIC_DATA_QUERY = "{}/api/v1GET_INCIDENT_ID_QUERY/forensics?event_id={}&type={}" +GET_DECEPTIVE_USERS_QUERY = "{}/api/v1/deceptive-entities/users?deceptive_user_type={}" +GET_DECEPTIVE_SERVERS_QUERY = ( + "{}/api/v1/deceptive-entities/servers?deceptive_server_type={}" +) + +CA_CERTIFICATE_FILE_PATH = "cacert.pem" +DEFAULT_ITEMS = 50 +TIMEOUT_THRESHOLD = 0.9 + +FORENSIC_DATA_TYPES = { + "include_sys_info": "HOST_INFO", + "include_prefetch_files_info": "PREFETCH_INFO", + "include_add_remove": "INSTALLED_PROGRAMS_INFO", + "include_startup_info": "STARTUP_PROCESSES", + "include_running_info": "RUNNING_PROCESSES", + "include_user_assist_info": "USER_ASSIST_INFO", + "include_powershell_info": "POWER_SHELL_HISTORY", +} + +ILLUSIVE_NETWORKS_PREFIX = "ILLNET" + +ALL = "All" +SUGGESTED = "Only Suggested" +APPROVED = "Only Approved" +ONLY_USERS = "Only Users" +ONLY_SERVERS = "Only Servers" + +DECEPTIVE_STATE_MAPPING = {ALL: "ALL", SUGGESTED: "SUGGESTED", APPROVED: "APPROVED"} + +DECEPTIVE_USERS_TABLE_NAME = "Deceptive Users" +DECEPTIVE_SERVERS_TABLE_NAME = "Deceptive Servers" + + +DEFAULT_DEVICE_PRODUCT = "Illusive Networks" +DEFAULT_DEVICE_VENDOR = "Illusive Networks" + +RATE_LIMIT_ERROR_IDENTIFIER = "Rate limit error" diff --git a/content/response_integrations/google/illusive_networks/core/datamodels.py b/content/response_integrations/google/illusive_networks/core/datamodels.py new file mode 100755 index 000000000..9fbe09b9f --- /dev/null +++ b/content/response_integrations/google/illusive_networks/core/datamodels.py @@ -0,0 +1,395 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from TIPCommon import dict_to_flat, add_prefix_to_dict +from soar_sdk.SiemplifyUtils import convert_string_to_unix_time +from .constants import ILLUSIVE_NETWORKS_PREFIX + + +class BaseModel: + """ + Base model for inheritance + """ + + def __init__(self, raw_data): + self.raw_data = raw_data + + def to_json(self): + return self.raw_data + + def to_flat_dict(self): + return dict_to_flat(self.to_json()) + + def to_csv(self): + return dict_to_flat(self.to_json()) + + def to_enrichment_data(self, prefix=None): + data = dict_to_flat(self.raw_data) + return add_prefix_to_dict(data, prefix) if prefix else data + + +class ForensicHostInfo(BaseModel): + def __init__( + self, + raw_data, + os_name, + machine_type, + host, + logged_in_user, + user_profiles, + operating_system_type, + ): + super(ForensicHostInfo, self).__init__(raw_data) + self.os_name = os_name + self.machine_type = machine_type + self.host = host + self.logged_in_user = logged_in_user + self.user_profiles = user_profiles + self.operating_system_type = operating_system_type + + def as_enrichment_data(self): + enrichment_data = {} + if self.os_name: + enrichment_data["osName"] = self.os_name + if self.machine_type: + enrichment_data["machineType"] = self.machine_type + if self.host: + enrichment_data["host"] = self.host + if self.logged_in_user: + enrichment_data["loggedInUser"] = self.logged_in_user + if self.user_profiles: + enrichment_data["userProfiles"] = self.user_profiles + if self.operating_system_type: + enrichment_data["operatingSystemType"] = self.operating_system_type + + return add_prefix_to_dict( + dict_to_flat(enrichment_data), ILLUSIVE_NETWORKS_PREFIX + ) + + def to_table(self): + """ + Function that prepares the users's data to be used on the table + :return {list} List containing dict of users's data + """ + + table_data_list = [] + + table_data = {} + if self.os_name: + table_data["OS Name"] = self.os_name + + if self.machine_type: + table_data["Machine Type"] = self.machine_type + + if self.host: + table_data["Host"] = self.host + + if self.logged_in_user: + table_data["Logged In User"] = self.logged_in_user + + if self.user_profiles: + table_data["User Profiles"] = self.user_profiles + + if self.operating_system_type: + table_data["Operating System Type"] = self.operating_system_type + + for key, value in table_data.items(): + table_data_list.append({"Key": key, "Value": value}) + + return table_data_list + + +class ForensicPreFetchInfo(BaseModel): + def __init__( + self, raw_data, file_name, last_exe_time, file_modify_time, prefetchfile_name + ): + super(ForensicPreFetchInfo, self).__init__(raw_data) + self.file_name = file_name + self.last_exe_time = last_exe_time + self.file_modify_time = file_modify_time + self.prefetchfile_name = prefetchfile_name + + def to_table(self): + table = { + "File Name": self.file_name, + "Last Execution Time": self.last_exe_time, + "File Modification Time": self.file_modify_time, + "Prefetch File Name": self.prefetchfile_name, + } + return table + + +class ForensicProgramsInfo(BaseModel): + def __init__(self, raw_data, display_name, file_name): + super(ForensicProgramsInfo, self).__init__(raw_data) + self.display_name = display_name + self.file_name = file_name + + def to_table(self): + """ + Function that prepares the users's data to be used on the table + :return {list} List containing dict of users's data + """ + + table = {"File Name": self.file_name, "Display Name": self.display_name} + return table + + +class ForensicStartupProcess(BaseModel): + def __init__(self, raw_data, name, command, location, user): + super(ForensicStartupProcess, self).__init__(raw_data) + self.name = name + self.command = command + self.location = location + self.user = user + + def to_table(self): + table = { + "User": self.user, + "Location": self.location, + "Command": self.command, + "Name": self.name, + } + return table + + +class ForensicRunningProcesses(BaseModel): + def __init__( + self, + raw_data, + user, + admin_privileges, + command_line, + process_id, + process_name, + start_time, + ): + super(ForensicRunningProcesses, self).__init__(raw_data) + self.user = user + self.admin_privileges = admin_privileges + self.command_line = command_line + self.process_id = process_id + self.process_name = process_name + self.start_time = start_time + + def to_table(self): + table = { + "User": self.user, + "Admin Privileges": self.admin_privileges, + "Command": self.command_line, + "Process ID": self.process_id, + "Process Name": self.process_name, + "Start Time": self.start_time, + } + return table + + +class ForensicUserAssistInfo(BaseModel): + def __init__(self, raw_data, file_name, user_name, last_updated_date): + super(ForensicUserAssistInfo, self).__init__(raw_data) + self.file_name = file_name + self.user_name = user_name + self.last_updated_date = last_updated_date + + def to_table(self): + table = { + "File Name": self.file_name, + "User Name": self.user_name, + "Last Used Date": self.last_updated_date, + } + return table + + +class ForensicPowershellInfo(BaseModel): + def __init__(self, raw_data, user_name, command): + super(ForensicPowershellInfo, self).__init__(raw_data) + self.user_name = user_name + self.command = command + + def to_table(self): + table = {"User Name": self.user_name, "Command": self.command} + return table + + +class HostObject(BaseModel): + + def __init__( + self, + raw_data, + machine_name, + is_healthy, + host, + distinguested_name, + source_discovery_name, + policy_name, + operating_system_name, + agent_version, + logged_in_user_name, + machine_exe_status, + bitness, + ): + super(HostObject, self).__init__(raw_data) + self.machine_name = machine_name + self.is_healthy = is_healthy + self.host = host + self.distinguested_name = distinguested_name + self.source_discovery_name = source_discovery_name + self.policy_name = policy_name + self.operating_system_name = operating_system_name + self.agent_version = agent_version + self.logged_in_user_name = logged_in_user_name + self.machine_exe_status = machine_exe_status + self.bitness = bitness + + def as_enrichment_data(self): + enrichment_data = {} + if self.machine_name: + enrichment_data["machine_name"] = self.machine_name + if self.is_healthy: + enrichment_data["is_healthy"] = self.is_healthy + if self.host: + enrichment_data["host"] = self.host + if self.distinguested_name: + enrichment_data["distinguested_name"] = self.distinguested_name + if self.source_discovery_name: + enrichment_data["source_discovery_name"] = self.source_discovery_name + if self.policy_name: + enrichment_data["policy_name"] = self.policy_name + if self.operating_system_name: + enrichment_data["operating_system_name"] = self.operating_system_name + if self.agent_version: + enrichment_data["agent_version"] = self.agent_version + if self.logged_in_user_name: + enrichment_data["logged_in_user_name"] = self.logged_in_user_name + if self.machine_exe_status: + enrichment_data["machine_exe_status"] = self.machine_exe_status + if self.bitness: + enrichment_data["bitness"] = self.bitness + + return add_prefix_to_dict( + dict_to_flat(enrichment_data), ILLUSIVE_NETWORKS_PREFIX + ) + + def to_table(self): + """ + Function that prepares the users's data to be used on the table + :return {list} List containing dict of users's data + """ + + table_data_list = [] + + table_data = {} + if self.machine_name: + table_data["Machine Name"] = self.machine_name + + if self.is_healthy: + table_data["Is Healthy"] = self.is_healthy + + if self.host: + table_data["Host"] = self.host + + if self.distinguested_name: + table_data["Distinguested Name"] = self.distinguested_name + + if self.source_discovery_name: + table_data["Source Discovery Name"] = self.source_discovery_name + + if self.policy_name: + table_data["Policy Name"] = self.policy_name + + if self.operating_system_name: + table_data["Operating System Name"] = self.operating_system_name + + if self.agent_version: + table_data["Agent Version"] = self.agent_version + + if self.logged_in_user_name: + table_data["Loggedin User Name"] = self.logged_in_user_name + + if self.bitness: + table_data["bitness"] = self.bitness + + for key, value in table_data.items(): + table_data_list.append({"Key": key, "Value": value}) + + return table_data_list + + +class DeceptiveUser(BaseModel): + def __init__( + self, + raw_data, + username, + password, + domain, + policies, + ad_user, + active_user, + deceptive_state, + ): + super(DeceptiveUser, self).__init__(raw_data) + self.username = username + self.password = password + self.domain = domain + self.policies = policies + self.ad_user = ad_user + self.active_user = active_user + self.deceptive_state = deceptive_state + + def to_table(self): + return { + "Username": self.username, + "Password": self.password, + "Domain": self.domain, + "Policies": ", ".join(self.policies), + "AD User": self.ad_user, + "Active": self.active_user, + "State": self.deceptive_state, + } + + +class DeceptiveServer(BaseModel): + def __init__(self, raw_data, host, policies, services, ad_host, deceptive_state): + super(DeceptiveServer, self).__init__(raw_data) + self.domain = host + self.policies = policies + self.services = services + self.ad_host = ad_host + self.deceptive_state = deceptive_state + + def to_table(self): + return { + "Host": self.domain, + "Services": ", ".join(self.services), + "Policies": ", ".join(self.policies), + "AD Server": self.ad_host, + "State": self.deceptive_state, + } + + +class Incident(BaseModel): + def __init__(self, raw_data, incident_time_UTC, incident_id, incident_types): + super(Incident, self).__init__(raw_data) + self.incident_time_UTC = incident_time_UTC + self.timestamp = convert_string_to_unix_time(incident_time_UTC) + self.incident_id = incident_id + self.incident_types = incident_types + self.events = [] + + def set_event(self, events): + self.events = events + + def to_events(self): + return [dict_to_flat(event.to_json()) for event in self.events] diff --git a/content/response_integrations/google/illusive_networks/definition.yaml b/content/response_integrations/google/illusive_networks/definition.yaml new file mode 100644 index 000000000..7f17b7ed0 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/definition.yaml @@ -0,0 +1,47 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +identifier: IllusiveNetworks +name: Illusive Networks +parameters: +- name: API Root + default_value: http://x.x.x.x + type: string + description: '' + is_mandatory: true + integration_identifier: IllusiveNetworks +- name: API Key + default_value: '' + type: password + description: '' + is_mandatory: true + integration_identifier: IllusiveNetworks +- name: CA Certificate File + default_value: '' + type: string + description: '' + is_mandatory: false + integration_identifier: IllusiveNetworks +- name: Verify SSL + default_value: false + type: boolean + description: '' + is_mandatory: true + integration_identifier: IllusiveNetworks +documentation_link: https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/illusive-networks +categories: +- Network Security +- Security +svg_logo_path: resources/logo.svg +image_path: resources/image.png diff --git a/content/response_integrations/google/illusive_networks/pyproject.toml b/content/response_integrations/google/illusive_networks/pyproject.toml new file mode 100644 index 000000000..4f852efbf --- /dev/null +++ b/content/response_integrations/google/illusive_networks/pyproject.toml @@ -0,0 +1,34 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[project] +name = "IllusiveNetworks" +version = "8.0" +description = "Shrink your organization’s attack surface. Find and eliminate the vulnerable credentials and connections that attackers use to escalate privileges and move laterally. Agentless, undetectable deception technology that creates a hostile environment for attackers, stopping lateral movement and access to your critical assets. Get actionable, real-time or on-demand forensic attack insight to accelerate blocking and remediation." +requires-python = ">=3.11" +dependencies = [ "requests==2.32.4", "tipcommon",] + +[dependency-groups] +dev = [ "pytest>=9.0.2", "pytest-json-report>=1.5.0", "soar-sdk",] + +[tool.uv] +[[tool.uv.index]] +url = "https://pypi.org/simple" +default = true + +[tool.uv.sources.tipcommon] +path = "../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" + +[tool.uv.sources.soar-sdk] +git = "https://github.com/chronicle/soar-sdk.git" diff --git a/content/response_integrations/google/illusive_networks/release_notes.yaml b/content/response_integrations/google/illusive_networks/release_notes.yaml new file mode 100644 index 000000000..9b5308dd7 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/release_notes.yaml @@ -0,0 +1,109 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- description: New integration "Illusive Networks". + integration_version: 1.0 + item_name: IllusiveNetworks + item_type: Integration + publish_time: '2021-03-24' + ticket_number: TIPG-7398 + new: true + regressive: false + deprecated: false + removed: false +- description: Illusive Networks - Incidents Connector - Improved description for + 'Max Hours Backwards' parameter. + integration_version: 2.0 + item_name: IllusiveNetworks + item_type: Connector + publish_time: '2024-08-08' + ticket_number: '333133526' + new: false + regressive: false + deprecated: false + removed: false +- description: 'Illusive Networks integration - Important - Updated the integration + code to work with Python version 3.11. To ensure compatibility and avoid disruptions, + follow the upgrade best practices described in the following document: https://cloud.google.com/chronicle/docs/soar/respond/integrations-setup/upgrade-python-versions' + integration_version: 3.0 + item_name: IllusiveNetworks + item_type: Integration + publish_time: '2024-09-19' + ticket_number: '331751541' + new: false + regressive: false + deprecated: false + removed: false +- description: Enrich Entities, Run Forensic Scan - Added Predefined Widgets. + integration_version: 4.0 + item_name: Enrich Entities, Run Forensic Scan + item_type: Widget + publish_time: '2024-11-05' + ticket_number: '346745176' + new: true + regressive: false + deprecated: false + removed: false +- description: List Deceptive Items - Added Predefined Widget. + integration_version: 4.0 + item_name: List Deceptive Items + item_type: Widget + publish_time: '2024-11-05' + ticket_number: '353158439' + new: true + regressive: false + deprecated: false + removed: false +- description: Updated integration metadata + integration_version: 5.0 + item_name: IllusiveNetworks + item_type: Integration + publish_time: '2025-10-29' + ticket_number: '' + new: false + regressive: false + deprecated: false + removed: false +- description: List Deceptive Items - Updated Predefined Widget. + integration_version: 6.0 + item_name: List Deceptive Items + item_type: Widget + publish_time: '2026-03-20' + ticket_number: '492135438' + new: false + regressive: false + deprecated: false + removed: false +- description: Run Forensic Scan, Enrich Entities - Updated Predefined Widgets. + integration_version: 7.0 + item_name: Run Forensic Scan, Enrich Entities + item_type: Widget + publish_time: '2026-03-27' + ticket_number: '496451947' + new: false + regressive: false + deprecated: false + removed: false + +- description: 'Integration - Source code for the integration is now available publicly + on Github. Link to repo: https://github.com/chronicle/content-hub' + integration_version: 8.0 + item_name: IllusiveNetworks + item_type: Integration + publish_time: '2026-04-20' + new: false + regressive: false + deprecated: false + removed: false + ticket_number: '495762513' diff --git a/content/response_integrations/google/illusive_networks/resources/ai/actions_ai_description.yaml b/content/response_integrations/google/illusive_networks/resources/ai/actions_ai_description.yaml new file mode 100644 index 000000000..11de36f17 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/resources/ai/actions_ai_description.yaml @@ -0,0 +1,562 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Add Deceptive Server: + ai_description: >- + ### General Description + + Adds a new deceptive server to the Illusive Networks platform. This action enables + security teams to programmatically deploy decoys designed to detect unauthorized + lateral movement within the network. It checks for the existence of the server + before attempting creation to avoid duplicates. + + + ### Parameters Description + + | Parameter | Type | Mandatory | Description | + + | :--- | :--- | :--- | :--- | + + | Server Name | String | Yes | The hostname or identifier for the new deceptive + server to be created. | + + | Service Types | String | Yes | A comma-separated list of service types (e.g., + DB, RDP, HTTP) to be configured on the new deceptive server. | + + | Policy Names | String | No | A comma-separated list of policies to apply to + the new deceptive server. If left empty, the action defaults to applying all available + policies. | + + + ### Flow Description + + 1. **Connection Setup**: Initializes the Illusive Networks manager using the API + root and credentials. + + 2. **Duplicate Check**: Performs a lookup to see if a deceptive server with the + provided 'Server Name' already exists in the system. + + 3. **Server Creation**: If the server does not exist, the action sends a request + to the Illusive Networks API to create the server with the specified 'Service + Types' and 'Policy Names'. + + 4. **Completion**: Returns a success message upon successful creation or an error + if the server already exists or the API request fails. + + + ### Additional Notes + + - This action does not run on entities; it relies entirely on the provided input + parameters. + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: true + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: >- + Creates a new deceptive server configuration in the Illusive Networks platform + via a POST request. + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Add Deceptive User: + ai_description: >- + ### General Description + + Adds a new deceptive user to the Illusive Networks platform. This action is used + to create honey-tokens or decoy accounts within the network to detect lateral + movement and unauthorized access by attackers. It allows for the specification + of credentials, domain association, and the application of specific security policies. + + + ### Parameters Description + + | Parameter | Type | Mandatory | Description | + + | :--- | :--- | :--- | :--- | + + | **Username** | String | Yes | The username for the new deceptive user to be + created. | + + | **Password** | Password | Yes | The password associated with the new deceptive + user. | + + | **DNS Domain** | String | No | The DNS domain name where the deceptive user + will be registered. | + + | **Policy Names** | String | No | A comma-separated list of Illusive Networks + policies to apply to the user. If left empty, the system applies all available + policies by default. | + + + ### Additional Notes + + - The action first checks if a user with the same username already exists in Illusive + Networks. If the user exists, the action will fail with a 'ManagerAlreadyExistException' + to prevent accidental overwrites. + + - This action does not run on entities; it relies entirely on the provided input + parameters. + + + ### Flow Description + + 1. **Initialization**: Extracts integration configuration (API Root, API Key, + SSL settings) and action parameters (Username, Password, DNS Domain, Policy Names). + + 2. **Manager Setup**: Initializes the Illusive Networks API manager. + + 3. **Existence Check**: Queries the Illusive Networks API to determine if the + specified username already exists as a deceptive user. + + 4. **User Creation**: If the user does not exist, sends a POST request to the + Illusive Networks API to create the deceptive user with the provided credentials + and policies. + + 5. **Completion**: Returns a success message if the user is created or an error + message if the user already exists or if the API call fails. + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: true + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: >- + Creates a new deceptive user account in the Illusive Networks platform via a + POST request. + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Enrich Entities: + ai_description: "### General Description\nEnriches Hostname entities with detailed\ + \ information retrieved from Illusive Networks. This action queries the Illusive\ + \ Networks monitoring API to gather metadata about specific hosts, such as operating\ + \ system details, health status, agent versions, and active user sessions. The\ + \ gathered data is used to update the entity's profile within Google SecOps and\ + \ provide context through data tables.\n\n### Parameters\n| Parameter Name | Type\ + \ | Mandatory | Description |\n| :--- | :--- | :--- | :--- |\n| API Root | String\ + \ | Yes | The base URL of the Illusive Networks instance. |\n| API Key | String\ + \ | Yes | The API key used for authentication with the Illusive Networks service.\ + \ |\n| CA Certificate File | String | No | Base64 encoded CA certificate file\ + \ for SSL verification. |\n| Verify SSL | Boolean | Yes | If enabled (True), the\ + \ action will verify the SSL certificate of the Illusive Networks server. Default\ + \ is False. |\n\n### Flow Description\n1. **Initialization**: The action extracts\ + \ configuration parameters and initializes the Illusive Networks manager, performing\ + \ a connectivity test.\n2. **Entity Filtering**: It identifies all `HOSTNAME`\ + \ entities within the current context.\n3. **Data Retrieval**: For each identified\ + \ hostname, the action sends a GET request to the Illusive Networks API (`/api/v2/monitoring/hosts`)\ + \ to fetch host-specific metadata.\n4. **Internal Enrichment**: \n * Updates\ + \ the entity's `is_enriched` status to True.\n * Populates the entity's `additional_properties`\ + \ with data such as OS name, machine type, and health status.\n * Attaches\ + \ a detailed data table to each entity containing the retrieved host information.\n\ + 5. **Finalization**: Updates the entities in the Google SecOps platform and returns\ + \ a JSON result containing the raw data for all successfully enriched hosts." + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: false + can_mutate_internal_data: true + can_update_entities: true + external_data_mutation_explanation: null + fetches_data: true + internal_data_mutation_explanation: >- + Updates entity attributes with host details and adds data tables to the entities. + categories: + enrichment: true + entity_usage: + entity_types: + - HOSTNAME + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: true + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +List Deceptive Items: + ai_description: "### General Description\nThe **List Deceptive Items** action retrieves\ + \ a comprehensive list of deceptive users and servers currently configured within\ + \ the Illusive Networks environment. This action is primarily used for auditing\ + \ and visibility, allowing security teams to see which decoys are active, suggested,\ + \ or approved without modifying any external configurations.\n\n### Parameters\ + \ Description\n| Parameter | Type | Mandatory | Description |\n| :--- | :--- |\ + \ :--- | :--- |\n| **Deceptive Type** | String (Enum) | Yes | Determines the category\ + \ of deceptive items to fetch. Options include `All`, `Only Users`, or `Only Servers`.\ + \ |\n| **Deceptive State** | String (Enum) | Yes | Filters the results based on\ + \ the lifecycle state of the deceptive item. Options include `All`, `Only Approved`,\ + \ or `Only Suggested`. |\n| **Max Items To Return** | Integer | No | Limits the\ + \ number of items returned for each category. If left empty, the action defaults\ + \ to 50 items. |\n\n### Flow Description\n1. **Parameter Extraction**: The action\ + \ retrieves the user-defined filters for deceptive type, state, and the result\ + \ limit.\n2. **API Interaction**: \n - If the type is `All` or `Only Users`,\ + \ it queries the Illusive Networks API for deceptive user accounts.\n - If\ + \ the type is `All` or `Only Servers`, it queries the API for deceptive server\ + \ hosts.\n3. **Data Processing**: The retrieved items are filtered by the specified\ + \ `Deceptive State` (Approved/Suggested) and truncated based on the `Max Items\ + \ To Return` value.\n4. **Output Generation**: \n - Results are formatted into\ + \ two separate data tables: \"Deceptive Users\" and \"Deceptive Servers\".\n \ + \ - A full JSON payload containing the raw details of all identified items is\ + \ attached to the action result for further playbook processing." + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: false + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: null + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Ping: + ai_description: "### General Description\nThe **Ping** action is a diagnostic tool\ + \ designed to verify the connectivity between Google SecOps and the **Illusive\ + \ Networks** platform. It validates that the integration's configuration parameters\u2014\ + such as the API Root, API Key, and SSL settings\u2014are correct and that the\ + \ external service is reachable.\n\n### Parameters Description\n| Parameter |\ + \ Type | Mandatory | Description |\n| :--- | :--- | :--- | :--- |\n| **API Root**\ + \ | String | Yes | The base URL of the Illusive Networks API instance. |\n| **API\ + \ Key** | String | Yes | The API key used for authentication with the Illusive\ + \ Networks server. |\n| **CA Certificate File** | String | No | A base64 encoded\ + \ CA certificate file used for SSL verification if a custom certificate is required.\ + \ |\n| **Verify SSL** | Boolean | Yes | If enabled, the action will verify the\ + \ SSL certificate of the Illusive Networks server. Defaults to `False`. |\n\n\ + ### Additional Notes\n- This action does not interact with or process any SecOps\ + \ entities.\n- It is typically used during the initial setup of the integration\ + \ or for troubleshooting connection issues.\n\n### Flow Description\n1. **Parameter\ + \ Extraction:** The action retrieves the connection details (API Root, API Key,\ + \ CA Certificate, and SSL verification flag) from the integration configuration.\n\ + 2. **Manager Initialization:** It initializes the `IllusiveNetworksManager` using\ + \ the extracted credentials and network settings.\n3. **Connectivity Test:** The\ + \ manager executes a `GET` request to the `/api/v1/incidents` endpoint (limiting\ + \ the result to 1) to confirm the API is responsive and the credentials are valid.\n\ + 4. **Execution Outcome:** If the API call succeeds, the action completes with\ + \ a success status. If an error occurs (e.g., timeout, 401 Unauthorized), it catches\ + \ the exception and reports a failure." + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: false + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: null + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Remove Deceptive Server: + ai_description: >- + ### General Description + + Removes a deceptive server configuration from the Illusive Networks platform. + This action is used to decommission or clean up specific deceptive assets that + are no longer required in the environment. + + + ### Parameters Description + + | Parameter | Type | Mandatory | Description | + + | :--- | :--- | :--- | :--- | + + | **Server Name** | String | Yes | The specific name of the deceptive server that + needs to be removed from the Illusive Networks instance. | + + + ### Additional Notes + + * This action does not operate on SecOps entities; it requires the manual input + of a server name. + + * The action first verifies the existence of the server before attempting the + removal process. + + + ### Flow Description + + 1. **Initialization**: Connects to the Illusive Networks API using the provided + credentials and configuration. + + 2. **Verification**: Queries the external system to retrieve the details of the + deceptive server specified by the 'Server Name' parameter. + + 3. **Removal**: If the server is found, the action sends a DELETE request to the + Illusive Networks API to remove the server configuration. + + 4. **Completion**: Reports a success message if the server was successfully removed + or an error message if the server was not found or the deletion failed. + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: true + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: >- + Performs a DELETE request to the Illusive Networks API to remove a deceptive + server configuration from the external system. + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Remove Deceptive User: + ai_description: >- + Removes a deceptive user from the Illusive Networks platform. This action is used + to manage and clean up deceptive identities that are no longer required for active + deception campaigns. + + + ### Parameters + + | Parameter | Type | Mandatory | Description | + + | :--- | :--- | :--- | :--- | + + | Username | String | Yes | Specify the username of the deceptive user that needs + to be removed from the Illusive Networks system. | + + + ### Flow Description + + 1. **Initialization**: The action initializes the Illusive Networks manager using + the provided API credentials and configuration. + + 2. **User Verification**: It first attempts to retrieve the deceptive user details + from the external system using the provided username to ensure the user exists. + + 3. **Removal**: If the user is found, the action sends a DELETE request to the + Illusive Networks API to remove the specified deceptive user. + + 4. **Completion**: The action reports a success message if the user is successfully + removed or an error message if the user is not found or if the API request fails. + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: true + can_mutate_internal_data: false + can_update_entities: false + external_data_mutation_explanation: >- + This action performs a DELETE request to the Illusive Networks API to remove + a deceptive user from the external system. + fetches_data: true + internal_data_mutation_explanation: null + categories: + enrichment: false + entity_usage: + entity_types: [] + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: false + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false +Run Forensic Scan: + ai_description: >- + ### General Description + + Initiates and retrieves a comprehensive forensic scan for **HOSTNAME** and **ADDRESS** + entities using the Illusive Networks platform. This action is asynchronous; it + first triggers a scan on the target endpoint and then polls for the results. Once + the scan is complete, it fetches detailed forensic artifacts based on the user's + configuration, such as system information, running processes, and PowerShell history. + + + ### Parameters Description + + | Parameter | Type | Mandatory | Description | + + | :--- | :--- | :--- | :--- | + + | **Include System Information** | Boolean | Yes | If enabled, retrieves OS name, + machine type, host details, and logged-in user information. | + + | **Include Prefetch Files Information** | Boolean | Yes | If enabled, retrieves + details about Windows prefetch files, including last execution times. | + + | **Include Add-Remove Programs Information** | Boolean | Yes | If enabled, retrieves + the list of installed programs from the endpoint. | + + | **Include Startup Processes Information** | Boolean | Yes | If enabled, retrieves + information about processes configured to start automatically. | + + | **Include Running Processes Information** | Boolean | No | If enabled, retrieves + a snapshot of currently active processes on the endpoint. | + + | **Include User-Assist Programs Information** | Boolean | Yes | If enabled, retrieves + registry-based information about user program execution. | + + | **Include Powershell History Information** | Boolean | Yes | If enabled, retrieves + the command history for PowerShell sessions. | + + | **Max Items To Return** | Integer | No | Specifies the maximum number of items + to return for each forensic category (e.g., processes, files). Defaults to 50 + if not specified. | + + + ### Additional Notes + + * **Requirement:** At least one of the "Include..." parameters must be set to + `True` for the action to execute successfully. + + * **Asynchronous Execution:** The action will remain in an "In Progress" state + while waiting for Illusive Networks to generate the forensic report. + + + ### Flow Description + + 1. **Initialization:** Validates that at least one forensic data category is selected + and checks the connectivity to Illusive Networks. + + 2. **Scan Initiation:** Sends a POST request to Illusive Networks to create an + external forensic scan event for each target entity. + + 3. **Status Polling:** Periodically checks the status of the initiated events + until the forensic data is ready for retrieval. + + 4. **Data Retrieval:** Once ready, the action fetches the specific forensic modules + (System Info, Prefetch, etc.) requested via the parameters. + + 5. **Internal Enrichment:** Updates the entities with host information and attaches + detailed CSV-formatted data tables to the case for each retrieved forensic category. + capabilities: + can_create_case_comments: false + can_create_insight: false + can_modify_alert_data: false + can_mutate_external_data: true + can_mutate_internal_data: true + can_update_entities: true + external_data_mutation_explanation: >- + Initiates a new forensic scan request on the target endpoint within the Illusive + Networks platform via a POST request to the external event creation API. + fetches_data: true + internal_data_mutation_explanation: >- + Updates entity attributes with host information and attaches multiple data tables + (e.g., Prefetch, Startup Processes, Running Processes) to the case for each + enriched entity. + categories: + enrichment: false + entity_usage: + entity_types: + - HOSTNAME + - ADDRESS + filters_by_additional_properties: false + filters_by_alert_identifier: false + filters_by_case_identifier: false + filters_by_creation_time: false + filters_by_entity_type: true + filters_by_identifier: false + filters_by_is_artifact: false + filters_by_is_enriched: false + filters_by_is_internal: false + filters_by_is_pivot: false + filters_by_is_suspicious: false + filters_by_is_vulnerable: false + filters_by_modification_time: false diff --git a/content/response_integrations/google/illusive_networks/resources/enrich_entities_JsonResult_example.json b/content/response_integrations/google/illusive_networks/resources/enrich_entities_JsonResult_example.json new file mode 100644 index 000000000..c2ec99937 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/resources/enrich_entities_JsonResult_example.json @@ -0,0 +1,47 @@ +[ + { + "Entity": "MSSQL-XXX", + "EntityResult": { + "content": [ + { + "machineId": "32de351a-755c-40c7-806b-8xxx", + "machineName": "MSSQL-XXX", + "isHealthy": "false", + "lastDeploymentMethodType": "WMI", + "distinguishedName": "CN=MSSQL-XXX,CN=Computers,dc=lab,dc=local", + "groupName": null, + "sourceDiscoveryName": "lab.local", + "collectData": true, + "policyName": null, + "assignmentStatus": "ANALYSIS", + "operatingSystemType": "Windows", + "operatingSystemName": "Windows Server 2016 Standard Evaluation", + "operatingSystemVersion": "10.0 (14393)", + "agentVersion": null, + "bitness": null, + "loggedInUserName": null, + "lastLogonTime": 1610487457236, + "succeededDeceptionFamilies": 0, + "shouldBeUninstalledDeceptionFamilies": 0, + "desiredDeceptionFamilies": 0, + "deceptionFamiliesPercentages": null, + "lastExecutionType": "AGENT", + "machineLastExecutionPhaseType": "CONNECTION", + "machineLastExecutionPhaseStatus": "FAILURE", + "machineLastExecutionPhaseErrorMessage": "Unreachable - no ping", + "mitigationStatusType": null, + "machineExecutionUnifiedStatus": "FAILURE_CONNECTION", + "machineLastExecutionPhaseFinishDate": "2021-03-02T21:13:04.636Z", + "endpointTrapHealthCheckHostStatus": "NotTested", + "endpointTrapHealthCheckHostStatusLastUpdated": null, + "failedDeceptionFamilies": 0, + "inProgressDeceptionFamilies": 0, + "notDeployedDeceptionFamilies": 0, + "policyId": null, + "ghost": false + } + ], + "totalQueryResults": 1 + } + } +] \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/resources/image.png b/content/response_integrations/google/illusive_networks/resources/image.png new file mode 100644 index 0000000000000000000000000000000000000000..3a955a1fbf875fd706e9c3e801f5e3669884ad91 GIT binary patch literal 2054 zcmbuA`#%$o9>(W3E62BHle=kd5xK?Wwq)2OnUw2sS%t{uB(sh!6sKuMb8VPwlIB`U zG5Q)t$fcFAkSJnqjS%Dbo*&LX@crTWJg@iX`Q`cTNpW>?ls>3<5C8y3W6&tKeJ|XX z8wj|6%WK0M_U%9v+AA6Wkb?Xr5kOvn{QgrU+RgC{pr&8>$3BP$+n%!p0P54Fb^;{; z0Lgp|%GRAAvRHsjgYCA|+2U}s5RE~C1{``OwLlh{klff9*S@`rpWx>s5sh)Nk&~a7HZt`PeSNL; zTLQ+$w&3>jY}Yc7g5gDbvg9$VuM!8-35jfVzv30L+~2{_9;35}AouNES+5#a(?6-C zHRilrGj%@4`l(UOMcO;6035kBv`#ohT=mp9`0OT3Se75c#GPMS9|!8dT#_Z*k|+b2 z3#xt>gb%cz-R7x%Yp-8v?X8J>AX~OzhTe+OVkLR0D0zGgm$c~hHoS%Nu9n`s7k2G# z79^PM^Rh3nSOUT~9~%$aBH1^@7g2MWVb}kcg}w7%QbkE=jhHIz!OA>0*Cw8ZH|YOM z!ZNLL-lr=gd=5;z?ot3U(7!+b@-8Ntd2KJIy4Fnhck_X0irMa?*W*>@;KIfDqszdy z?w`XoA>OeD8xufOMKUi^=@H8uqedCg>hHO6wReI*3ZOeBbbkOneIl)3^l1>NV~~yA zBUwEZtlWC%%$CBt?IPBH<-<6eT-KE>R2f^Zwa$@suz*WPzX0vUY-gU2~W$7xP3H80dW{N zrZ>Xwx@)3G+v#?%sG5*wt^YIrI(wm71ClX&Yz1o{E88RnDrYE)a>7D(bOq-9=x1Z;{Khb zJI#Y!JJv9I`;p)YZIolCF?+6ve9DnGG&hgyfRR@WDSQSoh;-aT$tf_c5Q${OH<1>7EcvjrbsR#I(W zwYJ69MSpoDAsP4|^jBnDl_D&HC@eFXD&g`QP>Pt#bLm2jw1cm{Ml8QamL$D3V+*xODk~$5oBgT02UWDUv5FJW_moVdhFl*g!1P>3wnmzM{*%&UuE86Cl5-Gvc@+nJu^^}<-vZ!C)&tPg6 z;|`aT@$>g=F6cA18!My*;`cwpb{u@;S^im+RN)dMb2TO);K}g~bFkkCafo}HX&=(= zO%mO!mi;E9SxcDa_W#@334VVu=4MVP15Mzdn4Q6+qSD#?v#UmdDL>_16RROpMw-erdrhM4 zPrjQ-dL=#oB3kCP{R8%z7ZBm0v^ZGO!lGRoPSY1vcd7Gntl&*>d!^h`Ak7*yH36~J z%2ioOYPl`DgDlJE1CQb1O%SUp!$m}~Rf)-ztuH5D1V01JLl)9LK`G*%KrCjB(UY4p z+C#Sm`R|nNQQfrUSBsD?*QMk(&@i<+W7f~NlQ^6YVxV)^yD6juv^v?atSijBpT)ei zaykE7+KM(rcsx?@IqFTC$P{6{JF^G%GrPvV`dnK10vT842V%mbOBw`$y#fv+w; z``aPLInv@!ndmBH9T2We+6kDgbPmUjQH^LVzP&idogf& zcC)5Sr+8%gN4u8IQ+B5fjTj*4n6)^n9^k&akT0Ad#GRz@MvAI&ZNfU(qa6>&+FsMw zg?Z5y(TRV&oV6Emxc@Fmfn>Z8^IPnT2>TpcEGJd#kcOj-pk?k8)MAM5{PlY;k>7E1 z@`Qs$x2I0zHN8XqrVMnB*-f@Nxu(lh-5=CM>ww$IGcyQQ@J9Np!}(z_Q*^z z<gbRyUS& z!ID^rQxtIme*`Y$b>Z(LakQyq6S)qlzG<+5E1^RQs`~#9e+OCOJQsRL^T=}N{%iv< M_AaQJGXZ!03$BphQ2+n{ literal 0 HcmV?d00001 diff --git a/content/response_integrations/google/illusive_networks/resources/list_deceptive_items_JsonResult_example.json b/content/response_integrations/google/illusive_networks/resources/list_deceptive_items_JsonResult_example.json new file mode 100644 index 000000000..7f50d67df --- /dev/null +++ b/content/response_integrations/google/illusive_networks/resources/list_deceptive_items_JsonResult_example.json @@ -0,0 +1,40 @@ +{ + "users": [ + { + "username": "Administrator", + "password": "xxxxx", + "domainName": "siemplifylab.xxxx", + "policyNames": [ + "Full Protection" + ], + "adUser": true, + "activeUser": true, + "deceptiveState": "APPROVED" + }, + { + "username": "accountpriv", + "password": "xxxxxx", + "domainName": "siemplifylab.xxxx", + "policyNames": [], + "adUser": false, + "activeUser": false, + "deceptiveState": "SUGGESTED" + } + ], + "servers": [ + { + "host": "ex001.xxxx.local", + "serviceTypes": [ + "WEB", + "SSH", + "SHARE", + "DB" + ], + "policyNames": [ + "Full Protection" + ], + "adHost": true, + "deceptiveState": "APPROVED" + } + ] +} \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/resources/logo.svg b/content/response_integrations/google/illusive_networks/resources/logo.svg new file mode 100644 index 000000000..ab306f559 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/resources/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/resources/run_forensic_scan_JsonResult_example.json b/content/response_integrations/google/illusive_networks/resources/run_forensic_scan_JsonResult_example.json new file mode 100644 index 000000000..125990dec --- /dev/null +++ b/content/response_integrations/google/illusive_networks/resources/run_forensic_scan_JsonResult_example.json @@ -0,0 +1,153 @@ +[ + { + "Entity": "some_entity", + "EntityResult": { + "host_info": { + "osName": "Windows 10", + "machineType": "Workstation", + "host": "xxx.yyy.local", + "loggedInUser": "No User is Logged-in", + "userProfiles": [ + "admin", + "Administrator" + ], + "operatingSystemType": "Windows", + "timeDeviationInHours": -8.0, + "daylightSavingDeviationInHours": 0.0 + }, + "prefetch_info": [ + { + "details": { + "MD5": "N/A", + "SHA256": "N/A", + "dataSource": "Prefetch", + "isFileExists": "No", + "lastExecutionTime": "2021-03-08T02:32:07.123-08:00", + "lastRunTimes": [ + "2021-03-01T02:25:26.533-08:00", + "2021-02-18T18:23:10.450-08:00", + "2021-02-18T17:09:42.157-08:00", + "2021-02-17T18:23:58.376-08:00" + ], + "prefetchFileName": "C:\\Windows\\Prefetch\\AM_DELTA.EXE-3A6EE7FD.pf", + "relatedModules": [ + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\MPSIGSTUB.EXE", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\NTDLL.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SOFTWAREDISTRIBUTION\\DOWNLOAD\\INSTALL\\AM_DELTA.EXE", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\KERNEL32.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\KERNELBASE.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\LOCALE.NLS", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\ADVAPI32.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\MSVCRT.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\SECHOST.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\RPCRT4.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\VERSION.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\BCRYPTPRIMITIVES.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\$MFT" + ], + "runCount": 0, + "version": 30 + }, + "fileName": "C:\\WINDOWS\\SOFTWAREDISTRIBUTION\\DOWNLOAD\\INSTALL\\AM_DELTA.EXE" + }, + { + "details": { + "MD5": "N/A", + "SHA256": "N/A", + "dataSource": "Prefetch", + "isFileExists": "No", + "lastExecutionTime": "2021-02-15T18:23:20.701-08:00", + "lastRunTimes": [], + "prefetchFileName": "C:\\Windows\\Prefetch\\AM_DELTA_PATCH_1.331.1067.0.E-C66E0B1B.pf", + "relatedModules": [ + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\NTDLL.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SOFTWAREDISTRIBUTION\\DOWNLOAD\\INSTALL\\AM_DELTA_PATCH_1.331.1067.0.EXE", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\KERNEL32.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\KERNELBASE.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\LOCALE.NLS", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\ADVAPI32.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\MSVCRT.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\SECHOST.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\RPCRT4.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\VERSION.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\BCRYPTPRIMITIVES.DLL", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\SYSTEM32\\MPSIGSTUB.EXE", + "\\VOLUME{01d7035e6f98cb77-266fcfdc}\\WINDOWS\\APPPATCH\\SYSMAIN.SDB" + ], + "runCount": 0, + "version": 30 + }, + "fileName": "C:\\WINDOWS\\SOFTWAREDISTRIBUTION\\DOWNLOAD\\INSTALL\\AM_DELTA_PATCH_1.331.1067.0.EXE" + } + ], + "installed_programs_info": [ + { + "details": { + "MD5": "N/A", + "SHA256": "N/A", + "dataSource": "Add Remove Programs", + "displayName": "Microsoft Update Health Tools", + "isFileExists": "No", + "lastWriteTime": "2021-02-20T14:18:49.409-08:00", + "version": "2.75.0.0" + }, + "fileName": "MsiExec.exe /X{BAB9FCC5-1506-4B4F-BFCA-EDE0BDB86C21}" + } + ], + "startup_processes": [ + { + "Caption": "OneDriveSetup", + "Command": "C:\\Windows\\SysWOW64\\OneDriveSetup.exe /thfirstsetup", + "Description": "OneDriveSetup", + "Location": "HKU\\S-1-5-19\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + "Name": "OneDriveSetup", + "SettingID": "", + "User": "NT AUTHORITY\\LOCAL SERVICE", + "UserSID": "S-1-5-19" + }, + { + "Caption": "OneDriveSetup", + "Command": "C:\\Windows\\SysWOW64\\OneDriveSetup.exe /thfirstsetup", + "Description": "OneDriveSetup", + "Location": "HKU\\S-1-5-20\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + "Name": "OneDriveSetup", + "SettingID": "", + "User": "NT AUTHORITY\\NETWORK SERVICE", + "UserSID": "S-1-5-20" + } + ], + "running_processes": [ + { + "MD5": "N/A", + "SHA256": "N/A", + "administratorPrivilleges": "True", + "commandline": "", + "processID": 4, + "processName": "N/A", + "processParent": "N/A", + "processPath": "N/A", + "session": "RDP-Tcp (0)", + "sessionId": 0, + "startTime": "2021-02-22T02:04:29.428-08:00", + "user": "NT AUTHORITY\\SYSTEM", + "userSid": "S-1-5-18" + }, + { + "MD5": "N/A", + "SHA256": "5BE0DE7F915BA819D4BA048DB7A2A87F6F3253FDD4865DC418181A0D6A031CAA", + "administratorPrivilleges": "True", + "commandline": "C:\\Windows\\system32\\svchost.exe -k DcomLaunch -p -s LSM", + "processID": 8, + "processName": "svchost.exe", + "processParent": "N/A", + "processPath": "C:\\Windows\\System32\\svchost.exe", + "session": "RDP-Tcp (0)", + "sessionId": 0, + "startTime": "2021-02-22T02:06:35.629-08:00", + "user": "NT AUTHORITY\\SYSTEM", + "userSid": "S-1-5-18" + } + ] + } + } +] \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/tests/__init__.py b/content/response_integrations/google/illusive_networks/tests/__init__.py new file mode 100644 index 000000000..9f71a2dc3 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/content/response_integrations/google/illusive_networks/tests/common.py b/content/response_integrations/google/illusive_networks/tests/common.py new file mode 100644 index 000000000..a31d1e94f --- /dev/null +++ b/content/response_integrations/google/illusive_networks/tests/common.py @@ -0,0 +1,22 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations +from integration_testing.common import get_def_file_content +import json +from integration_testing.common import get_def_file_content +import pathlib +INTEGRATION_PATH: pathlib.Path = pathlib.Path(__file__).parent.parent +CONFIG_PATH: pathlib.Path = pathlib.Path(__file__).parent / 'config.json' +CONFIG: dict = get_def_file_content(CONFIG_PATH) if CONFIG_PATH.exists() else {} \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/tests/config.json b/content/response_integrations/google/illusive_networks/tests/config.json new file mode 100644 index 000000000..6473bdc90 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/tests/config.json @@ -0,0 +1,6 @@ +{ + "API Root": "http://x.x.x.x", + "API Key": "", + "CA Certificate File": "", + "Verify SSL": false +} \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/tests/test_defaults/__init__.py b/content/response_integrations/google/illusive_networks/tests/test_defaults/__init__.py new file mode 100644 index 000000000..9f71a2dc3 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/tests/test_defaults/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/content/response_integrations/google/illusive_networks/tests/test_defaults/test_imports.py b/content/response_integrations/google/illusive_networks/tests/test_defaults/test_imports.py new file mode 100644 index 000000000..1243de833 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/tests/test_defaults/test_imports.py @@ -0,0 +1,57 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import importlib +import pathlib + +from .. import common + + +VALID_SUFFIXES = (".py",) + + +def import_all_integration_modules(integration: pathlib.Path) -> None: + if not integration.exists(): + msg: str = f"Cannot find integration {integration.name}" + raise AssertionError(msg) + + imports: list[str] = _get_integration_modules_import_strings(integration) + for import_ in imports: + importlib.import_module(import_) + + +def _get_integration_modules_import_strings(integration: pathlib.Path) -> list[str]: + results: list[str] = [] + for package in integration.iterdir(): + if not package.is_dir(): + continue + + for module in package.iterdir(): + if not module.is_file() or module.suffix not in VALID_SUFFIXES: + continue + + import_: str = _get_import_string(integration.stem, package.stem, module.stem) + results.append(import_) + + return results + + +def _get_import_string(integration: str, package: str, module: str) -> str: + return f"{integration}.{package}.{module}" + + +def test_imports() -> None: + import_all_integration_modules(common.INTEGRATION_PATH) diff --git a/content/response_integrations/google/illusive_networks/uv.lock b/content/response_integrations/google/illusive_networks/uv.lock new file mode 100644 index 000000000..b08b58bd6 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/uv.lock @@ -0,0 +1,570 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[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 = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[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" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "chardet" +version = "7.4.0.post1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/38/fe380893cbba72febb24d5dc0c2f9ac99f437153c36a409a8e254ed77bb6/chardet-7.4.0.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2769be12361a6c7873392e435c708eca88c9f0fb6a647af75fa1386db64032d6", size = 851312, upload-time = "2026-03-26T19:17:20.183Z" }, + { url = "https://files.pythonhosted.org/packages/5e/24/3c1522d777b66e2e3615ee33d1d4291c47b0ec258a9471b559339b01fac5/chardet-7.4.0.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e1eaa942ae81d43d535092ff3ba660c967344178cc3876b54834a56c1207f3a", size = 837425, upload-time = "2026-03-26T19:17:21.848Z" }, + { url = "https://files.pythonhosted.org/packages/3b/0d/be32abacdb6ed59b5e53b55da04102946b03eadac8a0bb107e359b22e257/chardet-7.4.0.post1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941af534a9b77d4b912e173e98680225340cf8827537e465bd6498b3e98d0cb8", size = 857193, upload-time = "2026-03-26T19:17:23.186Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a2/dab58511fbeef06dd88866568ea1a11b2f15654223cafc2681e2da84b1f2/chardet-7.4.0.post1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad98a6c2e61624b1120919353d222121b8f5848b9d33c885d949fe0235575682", size = 863486, upload-time = "2026-03-26T19:17:24.529Z" }, + { url = "https://files.pythonhosted.org/packages/e1/3a/f392d9b8465575140f250a8571e6cc643b08c8b650d84d0b499b542a0f2f/chardet-7.4.0.post1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6cddf6f1a0834ab5a6894819a6f4c3cd8c2cc86a12fc4efdc87eadb9ce7186ab", size = 858813, upload-time = "2026-03-26T19:17:25.855Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/7747b1bd30b8686088581382e1465463f40d27d25db94eccfd872f088ac7/chardet-7.4.0.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db07ed10259c0e93a55e3c285d2d78111b000e574aa4f94d89de53c72fb28127", size = 853961, upload-time = "2026-03-26T19:17:27.174Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/de2ba4786ada1c10147612cb7ff469ac8b835a47e9e5a772ce15735a8f4a/chardet-7.4.0.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86d7ca798051fce39b980241f4e93a40e3ef1fb568e282afcdcdbf6efa56bada", size = 837780, upload-time = "2026-03-26T19:17:28.82Z" }, + { url = "https://files.pythonhosted.org/packages/29/59/133001a6a7549dd34a3c28d1358122b0e68a59f27840efb2102b72eb73cf/chardet-7.4.0.post1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45adca296eddec38b803ce352bffcc5fff40576246e74fcd2aa358f9c813ffe0", size = 859436, upload-time = "2026-03-26T19:17:30.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/00/2eec7b47263524f204b3225d023f7d75e9c06a0a75c06a4c85faf2aec246/chardet-7.4.0.post1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f03aef24d91168232db8b01e4d6726676708be503e6aa07e28ab808e6d0fe606", size = 868655, upload-time = "2026-03-26T19:17:31.871Z" }, + { url = "https://files.pythonhosted.org/packages/00/a2/36e5b1a46a36293cac237fa5c61f9e11497e025ec2e4b10e8d187dede9b9/chardet-7.4.0.post1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5aec4e167de05470a3e2466a1ae751d7f0ad15510f63633fdd01a0405515df7a", size = 862406, upload-time = "2026-03-26T19:17:33.529Z" }, + { url = "https://files.pythonhosted.org/packages/e9/32/83a15c6077e7f240834ffd9ed78ef12f20f6e1924d7d7986d33f3d2af905/chardet-7.4.0.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdb3785c8700b3d0b354553827a166480a439f9754f7366f795bbe8b42d6daf", size = 853792, upload-time = "2026-03-26T19:17:35.057Z" }, + { url = "https://files.pythonhosted.org/packages/83/d3/80554c1cc15631446c9b90aec6fe63b7310aa0b82d3004f7ba38bd8a8270/chardet-7.4.0.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6285d35f79d0cdc8838d3cb01876f979c8419a74662e8de39444e40639e0b2b", size = 837634, upload-time = "2026-03-26T19:17:36.706Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4d/e9bbe23cec7394ed1190f5af688efd1b41dea8515371f0b1ee6ad4c09682/chardet-7.4.0.post1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c23262011179db48e600012e296133ab577d8e9682c91a19221164732ccb4427", size = 858692, upload-time = "2026-03-26T19:17:38.027Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3b/6103194ea934f1c3a4ea080905c8849f71e83de455c16cb625d25f49b779/chardet-7.4.0.post1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:329aa8766c4917d3acc1b1d0462f0b2e820e24e9f341d0f858aee85396ae3002", size = 867879, upload-time = "2026-03-26T19:17:39.332Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/317627e347072507e448e0515b736cdb650826c57c7217ce1361615d7a85/chardet-7.4.0.post1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cbfa21c8526022a62d1ebd1799b6e2c3598779231c0397372b6e26a4b1f4d28", size = 862092, upload-time = "2026-03-26T19:17:40.654Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/74e49a9fa914afe7e751263969894c61e2c49b7e71c0047bcaab64c8117f/chardet-7.4.0.post1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:70af3a05ca9476461160474f1324612d04b885101216894e60266c8c16510084", size = 853233, upload-time = "2026-03-26T19:17:42.496Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a3/a358e5fb4144da67fe9572b4506ce3a065dc24e9cc030d1b049c6b1e914e/chardet-7.4.0.post1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79c96d41ee63b098205d071caeee272968fe7a81a65151af2c02d1f51d2e2b4f", size = 837902, upload-time = "2026-03-26T19:17:44.398Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8e/488e166cc97bf7544d246c628a1abd7af1e369c4068cedb02a5f0abe8a74/chardet-7.4.0.post1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2eb2b2ed1662e74bebcdcd8f3872fd23378605601065a94ca265040026a11b3", size = 860652, upload-time = "2026-03-26T19:17:46.498Z" }, + { url = "https://files.pythonhosted.org/packages/19/0e/d3ed1a607f64c831ff2c35adc43d1a5db1cce4494585d494d3465e4b593b/chardet-7.4.0.post1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:069f4ff169142c45198b513702d7596b60944d3d8ebcfed2626e7716430e90e0", size = 868211, upload-time = "2026-03-26T19:17:48.314Z" }, + { url = "https://files.pythonhosted.org/packages/8d/6a/9ad635e2b75e6b058c1104a2821a1ed444161279f112df2c0f914193dff1/chardet-7.4.0.post1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e801976901fb7bc48f13653a7ca78e81fa65fe615d0d33c9383385c73e571927", size = 862775, upload-time = "2026-03-26T19:17:49.581Z" }, + { url = "https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl", hash = "sha256:57a62ef50f69bc2fb3a3ea1ffffec6d10f3d2112d3b05d6e3cb15c2c9b55f6cc", size = 624666, upload-time = "2026-03-26T19:17:51.248Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[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.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, + { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, + { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, + { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, + { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, + { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, +] + +[[package]] +name = "google-auth" +version = "2.49.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" }, +] + +[[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 = "illusivenetworks" +version = "8.0" +source = { virtual = "." } +dependencies = [ + { name = "requests" }, + { name = "tipcommon" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-json-report" }, + { name = "soar-sdk" }, +] + +[package.metadata] +requires-dist = [ + { name = "requests", specifier = "==2.32.4" }, + { name = "tipcommon", path = "../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=9.0.2" }, + { name = "pytest-json-report", specifier = ">=1.5.0" }, + { name = "soar-sdk", git = "https://github.com/chronicle/soar-sdk.git" }, +] + +[[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 = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[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 = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[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 = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[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 = "26.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/11/a62e1d33b373da2b2c2cd9eb508147871c80f12b1cacde3c5d314922afdd/pyopenssl-26.0.0.tar.gz", hash = "sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc", size = 185534, upload-time = "2026-03-15T14:28:26.353Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/7d/d4f7d908fa8415571771b30669251d57c3cf313b36a856e6d7548ae01619/pyopenssl-26.0.0-py3-none-any.whl", hash = "sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81", size = 57969, upload-time = "2026-03-15T14:28:24.864Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.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/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[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 = "2026.1.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[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 = "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 = "soar-sdk" +version = "0.2.0" +source = { git = "https://github.com/chronicle/soar-sdk.git#5c563da488afa729eeba2195d3569de1370ab106" } +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 = "tipcommon" +version = "1.0.10" +source = { path = "../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" } +dependencies = [ + { name = "chardet" }, + { name = "requests" }, +] +wheels = [ + { filename = "tipcommon-1.0.10-py3-none-any.whl", hash = "sha256:da0469b727d65b51bff0c43dce28f5c0c0463530d9bb2aab0bc8047c5cca1ea0" }, +] + +[package.metadata] +requires-dist = [ + { name = "chardet" }, + { 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.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] diff --git a/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.html b/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.html new file mode 100644 index 000000000..e9938b44f --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.html @@ -0,0 +1,1155 @@ + + + + + + + + + + + Illusive Networks - Enrich Entities + + + + +
+
+
+
+
+

+

+ +
+
+
+
+
+ + + diff --git a/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.yaml b/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.yaml new file mode 100644 index 000000000..3d2e22e58 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/EnrichEntities.yaml @@ -0,0 +1,44 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +title: Illusive Networks - Enrich Entities +type: html +scope: alert +action_identifier: Enrich Entities +description: This widget highlights the most important items in Enrich Entities +data_definition: + html_height: 400 + safe_rendering: false + widget_definition_scope: both + type: html +condition_group: + conditions: + - field_name: '[{stepInstanceName}.JsonResult]' + value: '[' + match_type: starts_with + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{' + match_type: contains + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: Entity + match_type: contains + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: EntityResult + match_type: contains + custom_operator_name: null + logical_operator: and +default_size: half_width diff --git a/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.html b/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.html new file mode 100644 index 000000000..7566846d8 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.html @@ -0,0 +1,1087 @@ + + + + + + + + + + + + Illusive Networks - List Deceptive Items + + + + +
+ +
+
+
+ + + diff --git a/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.yaml b/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.yaml new file mode 100644 index 000000000..2bb7176ad --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/ListDeceptiveItems.yaml @@ -0,0 +1,44 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +title: Illusive Networks - List Deceptive Items +type: html +scope: alert +action_identifier: List Deceptive Items +description: This widget highlights the most important items in List Deceptive Items +data_definition: + html_height: 400 + safe_rendering: false + widget_definition_scope: both + type: html +condition_group: + conditions: + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{stepInstanceName}' + match_type: not_contains + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: '' + match_type: is_not_empty + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{}' + match_type: not_equal + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: '[]' + match_type: not_equal + custom_operator_name: null + logical_operator: and +default_size: half_width diff --git a/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.html b/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.html new file mode 100644 index 000000000..4a96f6eec --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.html @@ -0,0 +1,1155 @@ + + + + + + + + + + + Illusive Networks - Run Forensic Scan + + + + +
+
+
+
+
+

+

+ +
+
+
+
+
+ + + diff --git a/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.yaml b/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.yaml new file mode 100644 index 000000000..595222f77 --- /dev/null +++ b/content/response_integrations/google/illusive_networks/widgets/RunForensicScan.yaml @@ -0,0 +1,44 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +title: Illusive Networks - Run Forensic Scan +type: html +scope: alert +action_identifier: Run Forensic Scan +description: This widget highlights the most important items in Run Forensic Scan +data_definition: + html_height: 400 + safe_rendering: false + widget_definition_scope: both + type: html +condition_group: + conditions: + - field_name: '[{stepInstanceName}.JsonResult]' + value: '[' + match_type: starts_with + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{' + match_type: contains + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: Entity + match_type: contains + custom_operator_name: null + - field_name: '[{stepInstanceName}.JsonResult]' + value: EntityResult + match_type: contains + custom_operator_name: null + logical_operator: and +default_size: half_width diff --git a/content/response_integrations/google/ruff.toml b/content/response_integrations/google/ruff.toml index 69bc65a26..49f33c4c7 100644 --- a/content/response_integrations/google/ruff.toml +++ b/content/response_integrations/google/ruff.toml @@ -84,6 +84,7 @@ preview = true "stealthwatch_v610/**" = ["ALL"] "symantec_content_analysis/**" = ["ALL"] "symantec_icdx/**" = ["ALL"] +"illusive_networks/**" = ["ALL"] [format] # Like Black, use double quotes for strings. From cf82d638a32cfb47d86a7c1f0c1ce7454f2fc3de Mon Sep 17 00:00:00 2001 From: haggit-eliyahu Date: Mon, 20 Apr 2026 12:20:17 +0300 Subject: [PATCH 2/2] fixes --- .../google/illusive_networks/pyproject.toml | 2 +- .../google/illusive_networks/tests/common.py | 3 +- .../google/illusive_networks/uv.lock | 355 +++++++++--------- 3 files changed, 184 insertions(+), 176 deletions(-) diff --git a/content/response_integrations/google/illusive_networks/pyproject.toml b/content/response_integrations/google/illusive_networks/pyproject.toml index 4f852efbf..002270e12 100644 --- a/content/response_integrations/google/illusive_networks/pyproject.toml +++ b/content/response_integrations/google/illusive_networks/pyproject.toml @@ -20,7 +20,7 @@ requires-python = ">=3.11" dependencies = [ "requests==2.32.4", "tipcommon",] [dependency-groups] -dev = [ "pytest>=9.0.2", "pytest-json-report>=1.5.0", "soar-sdk",] +dev = [ "pytest>=9.0.3", "pytest-json-report>=1.5.0", "soar-sdk",] [tool.uv] [[tool.uv.index]] diff --git a/content/response_integrations/google/illusive_networks/tests/common.py b/content/response_integrations/google/illusive_networks/tests/common.py index a31d1e94f..cb340e5ef 100644 --- a/content/response_integrations/google/illusive_networks/tests/common.py +++ b/content/response_integrations/google/illusive_networks/tests/common.py @@ -13,10 +13,9 @@ # limitations under the License. from __future__ import annotations -from integration_testing.common import get_def_file_content +import pathlib import json from integration_testing.common import get_def_file_content -import pathlib INTEGRATION_PATH: pathlib.Path = pathlib.Path(__file__).parent.parent CONFIG_PATH: pathlib.Path = pathlib.Path(__file__).parent / 'config.json' CONFIG: dict = get_def_file_content(CONFIG_PATH) if CONFIG_PATH.exists() else {} \ No newline at end of file diff --git a/content/response_integrations/google/illusive_networks/uv.lock b/content/response_integrations/google/illusive_networks/uv.lock index b08b58bd6..7b3ca167e 100644 --- a/content/response_integrations/google/illusive_networks/uv.lock +++ b/content/response_integrations/google/illusive_networks/uv.lock @@ -96,119 +96,128 @@ wheels = [ [[package]] name = "chardet" -version = "7.4.0.post1" +version = "7.4.3" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/b6/9df434a8eeba2e6628c465a1dfa31034228ef79b26f76f46278f4ef7e49d/chardet-7.4.3.tar.gz", hash = "sha256:cc1d4eb92a4ec1c2df3b490836ffa46922e599d34ce0bb75cf41fd2bf6303d56", size = 784800, upload-time = "2026-04-13T21:33:39.803Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/38/fe380893cbba72febb24d5dc0c2f9ac99f437153c36a409a8e254ed77bb6/chardet-7.4.0.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2769be12361a6c7873392e435c708eca88c9f0fb6a647af75fa1386db64032d6", size = 851312, upload-time = "2026-03-26T19:17:20.183Z" }, - { url = "https://files.pythonhosted.org/packages/5e/24/3c1522d777b66e2e3615ee33d1d4291c47b0ec258a9471b559339b01fac5/chardet-7.4.0.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e1eaa942ae81d43d535092ff3ba660c967344178cc3876b54834a56c1207f3a", size = 837425, upload-time = "2026-03-26T19:17:21.848Z" }, - { url = "https://files.pythonhosted.org/packages/3b/0d/be32abacdb6ed59b5e53b55da04102946b03eadac8a0bb107e359b22e257/chardet-7.4.0.post1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941af534a9b77d4b912e173e98680225340cf8827537e465bd6498b3e98d0cb8", size = 857193, upload-time = "2026-03-26T19:17:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a2/dab58511fbeef06dd88866568ea1a11b2f15654223cafc2681e2da84b1f2/chardet-7.4.0.post1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad98a6c2e61624b1120919353d222121b8f5848b9d33c885d949fe0235575682", size = 863486, upload-time = "2026-03-26T19:17:24.529Z" }, - { url = "https://files.pythonhosted.org/packages/e1/3a/f392d9b8465575140f250a8571e6cc643b08c8b650d84d0b499b542a0f2f/chardet-7.4.0.post1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6cddf6f1a0834ab5a6894819a6f4c3cd8c2cc86a12fc4efdc87eadb9ce7186ab", size = 858813, upload-time = "2026-03-26T19:17:25.855Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/7747b1bd30b8686088581382e1465463f40d27d25db94eccfd872f088ac7/chardet-7.4.0.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db07ed10259c0e93a55e3c285d2d78111b000e574aa4f94d89de53c72fb28127", size = 853961, upload-time = "2026-03-26T19:17:27.174Z" }, - { url = "https://files.pythonhosted.org/packages/2e/24/de2ba4786ada1c10147612cb7ff469ac8b835a47e9e5a772ce15735a8f4a/chardet-7.4.0.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86d7ca798051fce39b980241f4e93a40e3ef1fb568e282afcdcdbf6efa56bada", size = 837780, upload-time = "2026-03-26T19:17:28.82Z" }, - { url = "https://files.pythonhosted.org/packages/29/59/133001a6a7549dd34a3c28d1358122b0e68a59f27840efb2102b72eb73cf/chardet-7.4.0.post1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45adca296eddec38b803ce352bffcc5fff40576246e74fcd2aa358f9c813ffe0", size = 859436, upload-time = "2026-03-26T19:17:30.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/00/2eec7b47263524f204b3225d023f7d75e9c06a0a75c06a4c85faf2aec246/chardet-7.4.0.post1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f03aef24d91168232db8b01e4d6726676708be503e6aa07e28ab808e6d0fe606", size = 868655, upload-time = "2026-03-26T19:17:31.871Z" }, - { url = "https://files.pythonhosted.org/packages/00/a2/36e5b1a46a36293cac237fa5c61f9e11497e025ec2e4b10e8d187dede9b9/chardet-7.4.0.post1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5aec4e167de05470a3e2466a1ae751d7f0ad15510f63633fdd01a0405515df7a", size = 862406, upload-time = "2026-03-26T19:17:33.529Z" }, - { url = "https://files.pythonhosted.org/packages/e9/32/83a15c6077e7f240834ffd9ed78ef12f20f6e1924d7d7986d33f3d2af905/chardet-7.4.0.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdb3785c8700b3d0b354553827a166480a439f9754f7366f795bbe8b42d6daf", size = 853792, upload-time = "2026-03-26T19:17:35.057Z" }, - { url = "https://files.pythonhosted.org/packages/83/d3/80554c1cc15631446c9b90aec6fe63b7310aa0b82d3004f7ba38bd8a8270/chardet-7.4.0.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6285d35f79d0cdc8838d3cb01876f979c8419a74662e8de39444e40639e0b2b", size = 837634, upload-time = "2026-03-26T19:17:36.706Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4d/e9bbe23cec7394ed1190f5af688efd1b41dea8515371f0b1ee6ad4c09682/chardet-7.4.0.post1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c23262011179db48e600012e296133ab577d8e9682c91a19221164732ccb4427", size = 858692, upload-time = "2026-03-26T19:17:38.027Z" }, - { url = "https://files.pythonhosted.org/packages/d1/3b/6103194ea934f1c3a4ea080905c8849f71e83de455c16cb625d25f49b779/chardet-7.4.0.post1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:329aa8766c4917d3acc1b1d0462f0b2e820e24e9f341d0f858aee85396ae3002", size = 867879, upload-time = "2026-03-26T19:17:39.332Z" }, - { url = "https://files.pythonhosted.org/packages/72/06/317627e347072507e448e0515b736cdb650826c57c7217ce1361615d7a85/chardet-7.4.0.post1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cbfa21c8526022a62d1ebd1799b6e2c3598779231c0397372b6e26a4b1f4d28", size = 862092, upload-time = "2026-03-26T19:17:40.654Z" }, - { url = "https://files.pythonhosted.org/packages/6f/11/74e49a9fa914afe7e751263969894c61e2c49b7e71c0047bcaab64c8117f/chardet-7.4.0.post1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:70af3a05ca9476461160474f1324612d04b885101216894e60266c8c16510084", size = 853233, upload-time = "2026-03-26T19:17:42.496Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a3/a358e5fb4144da67fe9572b4506ce3a065dc24e9cc030d1b049c6b1e914e/chardet-7.4.0.post1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79c96d41ee63b098205d071caeee272968fe7a81a65151af2c02d1f51d2e2b4f", size = 837902, upload-time = "2026-03-26T19:17:44.398Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8e/488e166cc97bf7544d246c628a1abd7af1e369c4068cedb02a5f0abe8a74/chardet-7.4.0.post1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2eb2b2ed1662e74bebcdcd8f3872fd23378605601065a94ca265040026a11b3", size = 860652, upload-time = "2026-03-26T19:17:46.498Z" }, - { url = "https://files.pythonhosted.org/packages/19/0e/d3ed1a607f64c831ff2c35adc43d1a5db1cce4494585d494d3465e4b593b/chardet-7.4.0.post1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:069f4ff169142c45198b513702d7596b60944d3d8ebcfed2626e7716430e90e0", size = 868211, upload-time = "2026-03-26T19:17:48.314Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6a/9ad635e2b75e6b058c1104a2821a1ed444161279f112df2c0f914193dff1/chardet-7.4.0.post1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e801976901fb7bc48f13653a7ca78e81fa65fe615d0d33c9383385c73e571927", size = 862775, upload-time = "2026-03-26T19:17:49.581Z" }, - { url = "https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl", hash = "sha256:57a62ef50f69bc2fb3a3ea1ffffec6d10f3d2112d3b05d6e3cb15c2c9b55f6cc", size = 624666, upload-time = "2026-03-26T19:17:51.248Z" }, + { url = "https://files.pythonhosted.org/packages/19/52/505c207f334d51e937cbaa27ff95776e16e2d120e13cbe491cd7b3a70b50/chardet-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:25a862cddc6a9ac07023e808aedd297115345fbaabc2690479481ddc0f980e09", size = 870747, upload-time = "2026-04-13T21:32:56.916Z" }, + { url = "https://files.pythonhosted.org/packages/14/4b/d3c79495dee4831b8bebca2790e72cb90f0c5849c940570a7c7e5b70b952/chardet-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7005c88da26fd95d8abb8acbe6281d833e9a9181b03cf49b4546c4555389bd97", size = 853210, upload-time = "2026-04-13T21:32:58.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/99/f6a822ad1bde25a4c38dc3e770485e78e0893dfd871cd6e18ed3ea3a795e/chardet-7.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc50f28bad067393cce0af9091052c3b8df7a23115afd8ba7b2e0947f0cef1f8", size = 873625, upload-time = "2026-04-13T21:32:59.606Z" }, + { url = "https://files.pythonhosted.org/packages/b1/10/31932775c94a86814f76b41c4a772b52abfb0e6125324f32c6da1196c297/chardet-7.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3da294de1a681097848ab58bd3f2771a674f8039d2d87a5538b28856b815e9", size = 883436, upload-time = "2026-04-13T21:33:01.351Z" }, + { url = "https://files.pythonhosted.org/packages/6c/63/0f43e3acf2c436fdb32a0f904aeb03a2904d2126eed34a042a194d235926/chardet-7.4.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c45e116dd51b66226a53ade3f9f635e870de5399b90e00ce45dcc311093bf4", size = 876589, upload-time = "2026-04-13T21:33:02.636Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a6/e9b8f8a3e99602792b01fa7d0a731737615ab56d8bfd0b52935a0ef88b85/chardet-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:ccc1f83ab4bcfb901cf39e0c4ba6bc6e726fc6264735f10e24ceb5cb47387578", size = 941866, upload-time = "2026-04-13T21:33:04.282Z" }, + { url = "https://files.pythonhosted.org/packages/61/33/29de185079e6675c3f375546e30a559b7ddc75ce972f18d6e566cd9ea4eb/chardet-7.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:75d3c65cc16bddf40b8da1fd25ba84fca5f8070f2b14e86083653c1c85aee971", size = 874870, upload-time = "2026-04-13T21:33:05.977Z" }, + { url = "https://files.pythonhosted.org/packages/9c/2f/4c5af01fd1a7506a1d5375403d68925eac70289229492db5aa68b58103d8/chardet-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:29af5999f654e8729d251f1724a62b538b1262d9292cccaefddf8a02aae1ef6a", size = 854859, upload-time = "2026-04-13T21:33:07.381Z" }, + { url = "https://files.pythonhosted.org/packages/36/21/edb36ad5dfa48d7f8eed97ab43931ecdaa8c15166c21b1d614967e49d681/chardet-7.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:626f00299ad62dfe937058a09572beed442ccc7b58f87aa667949b20fd3db235", size = 875032, upload-time = "2026-04-13T21:33:08.741Z" }, + { url = "https://files.pythonhosted.org/packages/e5/59/a32a241d861cf180853a11c8e5a67641cb1b2af13c3a5ccce83ec07e2c9f/chardet-7.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a4904dd5f071b7a7d7f50b4a67a86db3c902d243bf31708f1d5cde2f68239cb", size = 888283, upload-time = "2026-04-13T21:33:10.213Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/e1ee6a77abf3782c00e05b89c4d4328c8353bf9500661c4348df1dd68614/chardet-7.4.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5d2879598bc220689e8ce509fe9c3f37ad2fca53a36be9c9bd91abdd91dd364f", size = 879974, upload-time = "2026-04-13T21:33:11.448Z" }, + { url = "https://files.pythonhosted.org/packages/32/60/fca69c534602a7ced04280c952a246ad1edde2a6ca3a164f65d32ac41fe7/chardet-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:4b2799bd58e7245cfa8d4ab2e8ad1d76a5c3a5b1f32318eb6acca4c69a3e7101", size = 943973, upload-time = "2026-04-13T21:33:12.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/43/79ac9b4db5bc87020c9dbc419125371d80882d1d197e9c4765ba8682b605/chardet-7.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e4486df251b8962e86ea9f139ca235aa6e0542a00f7844c9a04160afb99aa9", size = 873769, upload-time = "2026-04-13T21:33:14.002Z" }, + { url = "https://files.pythonhosted.org/packages/55/5f/25bdec773905bff0ff6cf35ca73b17bd05593b4f87bd8c5fa43705f7167d/chardet-7.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4fbff1907925b0c5a1064cffb5e040cd5e338585c9c552625f30de6bc2f3107a", size = 853991, upload-time = "2026-04-13T21:33:15.564Z" }, + { url = "https://files.pythonhosted.org/packages/b4/07/a29380ee0b215d23d77733b5ad60c5c0c7969650e080c667acdf9462040d/chardet-7.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:365135eaf37ba65a828f8e668eb0a8c38c479dcbec724dc25f4dfd781049c357", size = 874024, upload-time = "2026-04-13T21:33:16.915Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b1/3338e121cbd4c8a126b8ccb1061170c2ce51a53f678c502793ea49c6fd6d/chardet-7.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfc134b70c846c21ead8e43ada3ae1a805fff732f6922f8abcf2ff27b8f6493d", size = 887410, upload-time = "2026-04-13T21:33:18.368Z" }, + { url = "https://files.pythonhosted.org/packages/63/1c/44a9a9e0c59c185a5d307ceaeee8768afa1558f0a24f7a4b5fa11b67586b/chardet-7.4.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9acd9988a93e09390f3cd231201ea7166c415eb8da1b735928990ffc05cb9fbb", size = 879269, upload-time = "2026-04-13T21:33:20.377Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b3/5d0e77ea774bd3224321c248880ea0c0379000ac5c2bb6d77609549de247/chardet-7.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:e1b98790c284ff813f18f7cf7de5f05ea2435a080030c7f1a8318f3a4f80b131", size = 944155, upload-time = "2026-04-13T21:33:21.694Z" }, + { url = "https://files.pythonhosted.org/packages/70/a8/bf0811d859e13801279a2ae64f37a408027b282f2047bc0001c75dd356ad/chardet-7.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d892d3dcd652fdef53e3d6327d39b17c0df40a899dfc919abaeb64c974497531", size = 872887, upload-time = "2026-04-13T21:33:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/51/ac/b9d68ebddfe1b02c77af5bf81120e12b036b4432dc6af7a303d90e2bc38b/chardet-7.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:acc46d1b8b7d5783216afe15db56d1c179b9a40e5a1558bc13164c4fd20674c4", size = 853964, upload-time = "2026-04-13T21:33:24.724Z" }, + { url = "https://files.pythonhosted.org/packages/2a/81/17fa103ea9caf5d325a5e4051ab2ba65996fd66baa60b81ee41af1f54e10/chardet-7.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ac3bf11c645734a1701a3804e43eabd98851838192267d08c353a834ab79fea", size = 876006, upload-time = "2026-04-13T21:33:26.098Z" }, + { url = "https://files.pythonhosted.org/packages/c2/20/193faab46a68ea550587331a698c3dca8099f8901d10937c4443135c7ed9/chardet-7.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e3bd9f936e04bae89c254262af08d9e5b98f805175ba1e29d454e6cba3107b7", size = 887680, upload-time = "2026-04-13T21:33:27.49Z" }, + { url = "https://files.pythonhosted.org/packages/40/c6/94a3c673327392652ee8bdea9a45bc8a5f5365197a7387d68f0eed007115/chardet-7.4.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:27cc23da03630cdecc9aa81a895aa86629c211f995cd57651f0fbc280717bf93", size = 879865, upload-time = "2026-04-13T21:33:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2c/cad8b5e3623a987f3c930b68e2bdd06cfc388cd91cd42ed05f1227701b73/chardet-7.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:b95c934b9ad59e2ba8abb9be49df70d3ad1b0d95d864b9fdb7588d4fa8bd921c", size = 939594, upload-time = "2026-04-13T21:33:31.391Z" }, + { url = "https://files.pythonhosted.org/packages/33/e0/d06e42fd6f02a58e5e227e5106587751cb38adcff0aaf949add744b78b6e/chardet-7.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c77867f0c1cb8bd819502249fcdc500364aedb07881e11b743726fa2148e7b6e", size = 889714, upload-time = "2026-04-13T21:33:32.772Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ed/40d091954d48abea037baae6be8fb79905e5f78d34d12ea955132c7d8011/chardet-7.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cf1efeaf65a6ef2f5b9cc3a1df6f08ba2831b369ccaa4c7018eaf90aa757bb11", size = 872319, upload-time = "2026-04-13T21:33:34.427Z" }, + { url = "https://files.pythonhosted.org/packages/bb/77/82a46821dbfbdfe062710d2bf2ede13426304e3567a23c57d919c0c31630/chardet-7.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f3504c139a2ad544077dd2d9e412cd08b01786843d76997cd43bb6de311723c", size = 892021, upload-time = "2026-04-13T21:33:35.766Z" }, + { url = "https://files.pythonhosted.org/packages/49/57/42d30c562bda5b4a839766c1aad8d5856b798ad2a1c3247b72a679afec94/chardet-7.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457f619882ba66327d4d8d14c6c342269bdb1e4e1c38e8117df941d14d351b04", size = 902509, upload-time = "2026-04-13T21:33:37.096Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/0a40afdb50a0fe041ab95553b835a8160b6cf0e81edf2ae2fe9f5224cbf9/chardet-7.4.3-py3-none-any.whl", hash = "sha256:1173b74051570cf08099d7429d92e4882d375ad4217f92a6e5240ccfb26f231e", size = 626562, upload-time = "2026-04-13T21:33:38.559Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.6" +version = "3.4.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, - { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, - { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, - { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, - { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, - { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, - { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, - { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, - { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, - { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, - { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, - { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, - { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, - { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, - { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, - { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, - { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, - { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, - { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, - { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, - { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, - { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, - { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, - { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, - { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, - { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, - { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, - { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, - { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, - { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, - { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, - { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, - { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, - { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, - { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, - { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, - { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, - { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, - { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, - { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, - { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, - { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, - { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, - { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, - { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, - { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] [[package]] @@ -222,74 +231,74 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.6" +version = "46.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, - { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, - { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, - { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, - { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, - { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, - { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, - { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, - { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, - { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, - { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, - { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, - { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, - { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, - { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, - { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, - { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, - { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, - { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, - { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, - { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, - { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, + { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, + { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, + { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, + { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, + { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, + { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, + { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, + { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, + { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" }, ] [[package]] name = "google-auth" -version = "2.49.1" +version = "2.49.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/fc/e925290a1ad95c975c459e2df070fac2b90954e13a0370ac505dff78cb99/google_auth-2.49.2.tar.gz", hash = "sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409", size = 333958, upload-time = "2026-04-10T00:41:21.888Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" }, + { url = "https://files.pythonhosted.org/packages/73/76/d241a5c927433420507215df6cac1b1fa4ac0ba7a794df42a84326c68da8/google_auth-2.49.2-py3-none-any.whl", hash = "sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5", size = 240638, upload-time = "2026-04-10T00:41:14.501Z" }, ] [[package]] @@ -320,12 +329,12 @@ dev = [ [package.metadata] requires-dist = [ { name = "requests", specifier = "==2.32.4" }, - { name = "tipcommon", path = "../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" }, + { name = "tipcommon", path = "../../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" }, ] [package.metadata.requires-dev] dev = [ - { name = "pytest", specifier = ">=9.0.2" }, + { name = "pytest", specifier = ">=9.0.3" }, { name = "pytest-json-report", specifier = ">=1.5.0" }, { name = "soar-sdk", git = "https://github.com/chronicle/soar-sdk.git" }, ] @@ -341,11 +350,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] @@ -389,11 +398,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] @@ -411,7 +420,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -420,9 +429,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] [[package]] @@ -527,7 +536,7 @@ dependencies = [ [[package]] name = "tipcommon" version = "1.0.10" -source = { path = "../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" } +source = { path = "../../../../../packages/tipcommon/whls/TIPCommon-1.0.10-py3-none-any.whl" } dependencies = [ { name = "chardet" }, { name = "requests" }, @@ -553,11 +562,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.3" +version = "2026.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, ] [[package]]