diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..730c7ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,179 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python + +test_asset.json +*.tgz diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe86072..88cb369 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,7 @@ repos: exclude: "README.md" # Central hooks - repo: https://github.com/phantomcyber/dev-cicd-tools - rev: v2.0.9 + rev: v2.1.0 hooks: - id: build-docs language: python @@ -70,9 +70,6 @@ repos: language: python additional_dependencies: ["local-hooks"] args: ['.'] - - id: package-app-dependencies - language: python - additional_dependencies: ["local-hooks"] - id: notice-file language: python additional_dependencies: ["local-hooks"] diff --git a/NOTICE b/NOTICE index f5dd57e..fefa18d 100644 --- a/NOTICE +++ b/NOTICE @@ -1,32 +1,3 @@ -Splunk SOAR App: HTTP -Copyright (c) 2016-2025 Splunk Inc. +Splunk SOAR App: http +Copyright (c) 2025 Splunk Inc. Third Party Software Attributions: - -@@@@============================================================================ - -Library: validators - 0.35.0 -Homepage: https://python-validators.github.io/validators -License: MIT License -License Text: - -The MIT License (MIT) - -Copyright (c) 2013 - 2025 Konsta Vesterinen - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index 61d3383..75afb5c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # HTTP Publisher: Splunk
-Connector Version: 3.7.9
+Connector Version: 4.0.0
Product Vendor: Generic
Product Name: HTTP
-Minimum Product Version: 6.2.1 +Minimum Product Version: 6.4.0 This App facilitates making HTTP requests as actions @@ -34,30 +34,31 @@ VARIABLE | REQUIRED | TYPE | DESCRIPTION **oauth_token_url** | optional | string | URL to fetch oauth token from | **client_id** | optional | string | Client ID (for OAuth) | **client_secret** | optional | password | Client Secret (for OAuth) | +**scope** | optional | string | Scope for OAuth | **timeout** | optional | numeric | Timeout for HTTP calls | **test_http_method** | optional | string | HTTP Method for Test Connectivity | ### Supported Actions -[test connectivity](#action-test-connectivity) - Validate connection using the configured credentials
-[put data](#action-put-data) - Perform a REST PUT call to the server
-[patch data](#action-patch-data) - Perform a REST PATCH call to the server
-[delete data](#action-delete-data) - Perform a REST DELETE call to the server
-[get headers](#action-get-headers) - Perform a REST HEAD call to the server
-[get options](#action-get-options) - Perform a REST OPTIONS call to the server
-[get data](#action-get-data) - Perform a REST GET call to the server
-[post data](#action-post-data) - Perform a REST POST call to the server
-[get file](#action-get-file) - Retrieve a file from the endpoint and save it to the vault
-[put file](#action-put-file) - Put a file from the vault to another location +[test connectivity](#action-test-connectivity) - Validate connection using the configured credentials.
+[get data](#action-get-data) - Perform a REST GET call to the server.
+[post data](#action-post-data) - Perform a REST POST call to the server.
+[put data](#action-put-data) - Perform a REST PUT call to the server.
+[patch data](#action-patch-data) - Perform a REST PATCH call to the server.
+[delete data](#action-delete-data) - Perform a REST DELETE call to the server.
+[get headers](#action-get-headers) - Perform a REST HEAD call to the server.
+[get options](#action-get-options) - Perform a REST OPTIONS call to the server.
+[put file](#action-put-file) - Put a file from the vault to another location.
+[get file](#action-get-file) - Retrieve a file from the endpoint and save it to the vault. ## action: 'test connectivity' -Validate connection using the configured credentials +Validate connection using the configured credentials. Type: **test**
Read only: **True** -This action will perform a GET on the configured base_url. The action will succeed if given a status code between 200 and 399 (inclusive). Therefore, if the given base URL requires no authentication, this action may pass even if the supplied credentials are incorrect. Alternately, if the given base URL does not point to a valid endpoint, this action could fail even if other actions may succeed with valid location parameters. +Basic test for app. #### Action Parameters @@ -65,21 +66,25 @@ No parameters are required for this action #### Action Output -No Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failure | +action_result.message | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -## action: 'put data' +## action: 'get data' -Perform a REST PUT call to the server +Perform a REST GET call to the server. -Type: **generic**
-Read only: **False** +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | -**body** | required | PATCH body (query string, JSON, etc.) | string | | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | @@ -87,25 +92,23 @@ PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.body | string | | {"name": "Bad IP"} | -action_result.parameter.headers | string | | {"Content-Type": "application/json"} | -action_result.parameter.location | string | `endpoint` | /rest/assets | -action_result.parameter.verify_certificate | boolean | | False True | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | -action_result.data.\*.method | string | | PUT | -action_result.data.\*.parsed_response_body | string | | {"failed": true, "message": "Requested item not found"} | +action_result.data.\*.method | string | | POST | action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | Not Found | -action_result.summary.status_code | numeric | | 404 | -action_result.message | string | | Can't process response from server. Status Code: 404 Data from server: {"failed": true, "message": "Requested item not found"} | +action_result.summary.status_code | numeric | | 200 | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 0 | +summary.total_objects_successful | numeric | | 1 | -## action: 'patch data' +## action: 'post data' -Perform a REST PATCH call to the server +Perform a REST POST call to the server. Type: **generic**
Read only: **False** @@ -114,34 +117,33 @@ Read only: **False** PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | -**body** | optional | PATCH body (query string, JSON, etc.) | string | | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | +**body** | optional | POST body (query string, JSON, etc.) | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.body | string | | {"name": "Bad IP"} | -action_result.parameter.headers | string | | {"Content-Type": "application/json"} | -action_result.parameter.location | string | `endpoint` | /rest/assets | -action_result.parameter.verify_certificate | boolean | | False True | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | +action_result.parameter.body | string | | | action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | -action_result.data.\*.method | string | | PATCH | -action_result.data.\*.parsed_response_body | string | | {"failed": true, "message": "Requested item not found"} | +action_result.data.\*.method | string | | POST | action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | Not Found | -action_result.summary.status_code | numeric | | 404 | -action_result.message | string | | Can't process response from server. Status Code: 404 Data from server: {"failed": true, "message": "Requested item not found"} | +action_result.summary.status_code | numeric | | 200 | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 0 | +summary.total_objects_successful | numeric | | 1 | -## action: 'delete data' +## action: 'put data' -Perform a REST DELETE call to the server +Perform a REST PUT call to the server. Type: **generic**
Read only: **False** @@ -150,100 +152,103 @@ Read only: **False** PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | -**body** | optional | DELETE body (query string, JSON, etc.) | string | | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | +**body** | optional | PATCH body (query string, JSON, etc.) | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.body | string | | {"name": "Bad IP"} | -action_result.parameter.headers | string | | {"Content-Type": "application/json"} | -action_result.parameter.location | string | `endpoint` | /rest/assets | -action_result.parameter.verify_certificate | boolean | | False True | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | +action_result.parameter.body | string | | | action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | -action_result.data.\*.method | string | | DELETE | -action_result.data.\*.parsed_response_body | string | | {"failed": true, "message": "Requested item not found"} | +action_result.data.\*.method | string | | POST | action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | Not Found | -action_result.summary.status_code | numeric | | 404 | -action_result.message | string | | Can't process response from server. Status Code: 404 Data from server: {"failed": true, "message": "Requested item not found"} | +action_result.summary.status_code | numeric | | 200 | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 0 | +summary.total_objects_successful | numeric | | 1 | -## action: 'get headers' +## action: 'patch data' -Perform a REST HEAD call to the server +Perform a REST PATCH call to the server. -Type: **investigate**
-Read only: **True** +Type: **generic**
+Read only: **False** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | +**body** | optional | PATCH body (query string, JSON, etc.) | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.headers | string | | {"User-Agent": "automation"} | -action_result.parameter.location | string | `endpoint` | /rest/cont | -action_result.parameter.verify_certificate | boolean | | False True | -action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/cont | -action_result.data.\*.method | string | | HEAD | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | +action_result.parameter.body | string | | | +action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | +action_result.data.\*.method | string | | POST | +action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | OK | action_result.summary.status_code | numeric | | 200 | -action_result.message | string | | Status code: 200, Reason: OK | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get options' +## action: 'delete data' -Perform a REST OPTIONS call to the server +Perform a REST DELETE call to the server. -Type: **investigate**
-Read only: **True** +Type: **generic**
+Read only: **False** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | +**body** | optional | DELETE body (query string, JSON, etc.) | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | action_result.parameter.headers | string | | | -action_result.parameter.location | string | `endpoint` | /rest/cont | -action_result.parameter.verify_certificate | boolean | | False True | -action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/cont | -action_result.data.\*.method | string | | OPTIONS | -action_result.data.\*.parsed_response_body | string | | GET,HEAD,PUT,POST,DELETE,PATCH | -action_result.data.\*.response_body | string | | GET,HEAD,PUT,POST,DELETE,PATCH | +action_result.parameter.body | string | | | +action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | +action_result.data.\*.method | string | | POST | +action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | OK | action_result.summary.status_code | numeric | | 200 | -action_result.message | string | | Status code: 200, Reason: OK | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get data' +## action: 'get headers' -Perform a REST GET call to the server +Perform a REST HEAD call to the server. Type: **investigate**
Read only: **True** @@ -252,7 +257,7 @@ Read only: **True** PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**location** | required | Location (e.g. path/to/endpoint?query=string) | string | `endpoint` | +**location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | @@ -260,34 +265,32 @@ PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.headers | string | | {"Content-Type": "application/json"} | -action_result.parameter.location | string | `endpoint` | /rest/cont | -action_result.parameter.verify_certificate | boolean | | False True | -action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/cont | -action_result.data.\*.method | string | | GET | -action_result.data.\*.parsed_response_body | string | | {"failed": true, "message": "Requested item not found"} | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | +action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | +action_result.data.\*.method | string | | POST | action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | Not Found | -action_result.summary.status_code | numeric | | 404 | -action_result.message | string | | Can't process response from server. Status Code: 404 Data from server: {"failed": true, "message": "Requested item not found"} | +action_result.summary.status_code | numeric | | 200 | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 0 | +summary.total_objects_successful | numeric | | 1 | -## action: 'post data' +## action: 'get options' -Perform a REST POST call to the server +Perform a REST OPTIONS call to the server. -Type: **generic**
-Read only: **False** +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- **location** | required | Location (e.g. path/to/endpoint) | string | `endpoint` | -**body** | optional | POST body (query string, JSON, etc.) | string | | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | **headers** | optional | Additional headers (JSON object with headers) | string | | @@ -295,88 +298,87 @@ PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.body | string | | {"name": "Bad IP"} | -action_result.parameter.headers | string | | {"Content-Type": "application/json"} | -action_result.parameter.location | string | `endpoint` | /rest/assets | -action_result.parameter.verify_certificate | boolean | | False True | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.location | string | `endpoint` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.parameter.headers | string | | | action_result.data.\*.location | string | `url` | http://192.168.1.26/rest/assets | action_result.data.\*.method | string | | POST | -action_result.data.\*.parsed_response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_body | string | | {"failed": true, "message": "Requested item not found"} | action_result.data.\*.response_headers | string | | | -action_result.summary.reason | string | | Not Found | -action_result.summary.status_code | numeric | | 404 | -action_result.message | string | | Can't process response from server. Status Code: 404 Data from server: {"failed": true, "message": "Requested item not found"} | +action_result.summary.status_code | numeric | | 200 | +action_result.summary.reason | string | | Request successful | summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 0 | +summary.total_objects_successful | numeric | | 1 | -## action: 'get file' +## action: 'put file' -Retrieve a file from the endpoint and save it to the vault +Put a file from the vault to another location. -Type: **investigate**
-Read only: **True** +Type: **generic**
+Read only: **False** -Provide the file path and file name to download into the vault. For example, /web_storage/file.tgz. +Provide the path to store the file on the file server. For example, /web_storage/test_repo/. #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**hostname** | optional | Hostname to execute command on | string | `host name` | -**file_path** | required | Path of the file to download (include filename) | string | `file path` | +**host** | optional | Hostname/IP with port number to execute command on | string | `host name` | +**vault_id** | required | Vault ID of file | string | `vault id` | +**file_destination** | required | File destination path (exclude filename) | string | `file path` | +**file_name** | optional | Name of the file to be put on endpoint | string | | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.file_path | string | `file path` | /web_storage/file.tgz | -action_result.parameter.verify_certificate | boolean | | False True | -action_result.parameter.hostname | string | `host name` | http://192.168.0.1 | -action_result.data | string | | | -action_result.summary.exit_status | numeric | | 0 | -action_result.summary.name | string | | file.tgz | -action_result.summary.size | numeric | | 412 | -action_result.summary.vault_id | string | `vault id` | dc871f816c4d179f3a3cea24b4bc81a67562c | -action_result.message | string | | Transferred file | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.host | string | `host name` | | +action_result.parameter.vault_id | string | `vault id` | | +action_result.parameter.file_destination | string | `file path` | | +action_result.parameter.file_name | string | | | +action_result.parameter.verify_certificate | boolean | | | +action_result.data.\*.file_sent | string | | | +action_result.summary.file_sent | string | | http://192.168.1.26/web_storage/test_repo/test.txt | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'put file' +## action: 'get file' -Put a file from the vault to another location +Retrieve a file from the endpoint and save it to the vault. -Type: **generic**
-Read only: **False** +Type: **investigate**
+Read only: **True** -Provide the path to store the file on the file server. For example, /web_storage/test_repo/. +Provide the file path and file name to download into the vault. For example, /web_storage/file.tgz. #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**host** | optional | Hostname/IP with port number to execute command on | string | `host name` | -**vault_id** | required | Vault ID of file | string | `vault id` | -**file_destination** | required | File destination path (exclude filename) | string | `file path` | -**file_name** | optional | Name of the file to be put on endpoint | string | | +**hostname** | optional | Hostname to execute command on | string | `hostname` | +**file_path** | required | Path of the file to download (include filename) | string | `file path` | **verify_certificate** | optional | Verify certificates (if using HTTPS) | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.file_name | string | | test.txt | -action_result.parameter.file_destination | string | `file path` | /web_storage/test_repo/ | -action_result.parameter.host | string | `host name` | http://192.168.0.1:8001 | -action_result.parameter.verify_certificate | boolean | | False True | -action_result.parameter.vault_id | string | `vault id` | dc871f816c4d179f3a3cea24b4bc81a67562c | -action_result.data | string | | | -action_result.summary.file_sent | string | `file path` | http://192.168.0.1:8001/web_storage/test_repo/file.tgz | -action_result.message | string | | File sent: http://192.168.0.1:8001/web_storage/test_repo/file.tgz | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.hostname | string | `hostname` | | +action_result.parameter.file_path | string | `file path` | | +action_result.parameter.verify_certificate | boolean | | | +action_result.data.\*.vault_id | string | | | +action_result.data.\*.file_name | string | | | +action_result.summary.vault_id | string | | 1234567890 | +action_result.summary.name | string | | test.txt | +action_result.summary.exit_status | numeric | | | +action_result.summary.size | numeric | | 100 | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 41abd5e..0000000 --- a/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# File: __init__.py -# -# Copyright (c) 2016-2025 Splunk Inc. -# -# 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/http.json b/http.json deleted file mode 100644 index 7438ea0..0000000 --- a/http.json +++ /dev/null @@ -1,1427 +0,0 @@ -{ - "appid": "290b7499-0374-4930-9cdc-5e9b05d65827", - "name": "HTTP", - "description": "This App facilitates making HTTP requests as actions", - "package_name": "phantom_http", - "publisher": "Splunk", - "contributors": [ - { - "name": "Ricardo Moreira" - } - ], - "type": "information", - "main_module": "http_connector.py", - "app_version": "3.7.9", - "utctime_updated": "2025-09-05T18:58:17.618520Z", - "product_vendor": "Generic", - "product_name": "HTTP", - "product_version_regex": ".*", - "min_phantom_version": "6.2.1", - "license": "Copyright (c) 2016-2025 Splunk Inc.", - "logo": "logo_splunk.svg", - "logo_dark": "logo_splunk_dark.svg", - "python_version": "3.9, 3.13", - "fips_compliant": true, - "latest_tested_versions": [ - "Tested with Crowdstrike on 12th September 2022" - ], - "configuration": { - "base_url": { - "data_type": "string", - "order": 0, - "description": "Base URL for making queries. (e.g. https://myservice/)", - "required": true - }, - "test_path": { - "data_type": "string", - "order": 1, - "description": "Endpoint for test connectivity. (e.g. /some/specific/endpoint , appended to Base URL)" - }, - "auth_token_name": { - "data_type": "string", - "order": 2, - "description": "Type of authentication token", - "default": "ph-auth-token" - }, - "auth_token": { - "data_type": "password", - "order": 3, - "description": "Value of authentication token" - }, - "username": { - "data_type": "string", - "order": 4, - "description": "Username (for HTTP basic auth)" - }, - "password": { - "data_type": "password", - "order": 5, - "description": "Password (for HTTP basic auth)" - }, - "oauth_token_url": { - "data_type": "string", - "order": 6, - "description": "URL to fetch oauth token from" - }, - "placeholder": { - "data_type": "ph", - "order": 7, - "description": "" - }, - "client_id": { - "data_type": "string", - "order": 8, - "description": "Client ID (for OAuth)" - }, - "client_secret": { - "data_type": "password", - "order": 9, - "description": "Client Secret (for OAuth)" - }, - "timeout": { - "data_type": "numeric", - "order": 10, - "description": "Timeout for HTTP calls" - }, - "test_http_method": { - "data_type": "string", - "order": 11, - "description": "HTTP Method for Test Connectivity", - "default": "GET", - "value_list": [ - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "OPTIONS", - "TRACE", - "PATCH" - ] - } - }, - "actions": [ - { - "action": "test connectivity", - "identifier": "test_connectivity", - "description": "Validate connection using the configured credentials", - "verbose": "This action will perform a GET on the configured base_url. The action will succeed if given a status code between 200 and 399 (inclusive). Therefore, if the given base URL requires no authentication, this action may pass even if the supplied credentials are incorrect. Alternately, if the given base URL does not point to a valid endpoint, this action could fail even if other actions may succeed with valid location parameters.", - "type": "test", - "read_only": true, - "parameters": {}, - "output": [], - "versions": "EQ(*)" - }, - { - "action": "put data", - "description": "Perform a REST PUT call to the server", - "type": "generic", - "identifier": "http_put", - "read_only": false, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "body": { - "description": "PATCH body (query string, JSON, etc.)", - "data_type": "string", - "order": 1, - "required": true - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 2 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "{\"name\": \"Bad IP\"}" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"Content-Type\": \"application/json\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/assets" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/assets" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "PUT" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "Not Found" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 404 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Can't process response from server. Status Code: 404 Data from server: {\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 0 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "patch data", - "description": "Perform a REST PATCH call to the server", - "type": "generic", - "identifier": "http_patch", - "read_only": false, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "body": { - "description": "PATCH body (query string, JSON, etc.)", - "data_type": "string", - "order": 1 - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 2 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "{\"name\": \"Bad IP\"}" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"Content-Type\": \"application/json\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/assets" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/assets" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "PATCH" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "Not Found" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 404 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Can't process response from server. Status Code: 404 Data from server: {\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 0 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "delete data", - "description": "Perform a REST DELETE call to the server", - "type": "generic", - "identifier": "http_delete", - "read_only": false, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "body": { - "description": "DELETE body (query string, JSON, etc.)", - "data_type": "string", - "order": 1 - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 2 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "{\"name\": \"Bad IP\"}" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"Content-Type\": \"application/json\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/assets" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/assets" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "DELETE" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "Not Found" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 404 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Can't process response from server. Status Code: 404 Data from server: {\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 0 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "get headers", - "description": "Perform a REST HEAD call to the server", - "type": "investigate", - "identifier": "http_head", - "read_only": true, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 1 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"User-Agent\": \"automation\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/cont" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/cont" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "HEAD" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "OK" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 200 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Status code: 200, Reason: OK" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "get options", - "description": "Perform a REST OPTIONS call to the server", - "type": "investigate", - "identifier": "http_options", - "read_only": true, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 1 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string" - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/cont" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/cont" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "OPTIONS" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "GET,HEAD,PUT,POST,DELETE,PATCH" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "GET,HEAD,PUT,POST,DELETE,PATCH" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "OK" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 200 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Status code: 200, Reason: OK" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "get data", - "description": "Perform a REST GET call to the server", - "type": "investigate", - "identifier": "http_get", - "read_only": true, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint?query=string)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 1 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 2 - } - }, - "render": { - "type": "json", - "width": 10, - "height": 5, - "title": "GET DATA" - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"Content-Type\": \"application/json\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/cont" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/cont" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "GET" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "Not Found" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 404 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Can't process response from server. Status Code: 404 Data from server: {\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 0 - ] - } - ], - "versions": "EQ(*)" - }, - { - "action": "post data", - "description": "Perform a REST POST call to the server", - "type": "generic", - "identifier": "http_post", - "read_only": false, - "parameters": { - "location": { - "description": "Location (e.g. path/to/endpoint)", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "endpoint" - ], - "required": true - }, - "body": { - "description": "POST body (query string, JSON, etc.)", - "data_type": "string", - "order": 1 - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 2 - }, - "headers": { - "description": "Additional headers (JSON object with headers)", - "data_type": "string", - "order": 3 - } - }, - "render": { - "type": "json", - "width": 10, - "height": 5, - "title": "POST DATA" - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "{\"name\": \"Bad IP\"}" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"Content-Type\": \"application/json\"}" - ] - }, - { - "data_path": "action_result.parameter.location", - "data_type": "string", - "contains": [ - "endpoint" - ], - "example_values": [ - "/rest/assets" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.location", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "http://192.168.1.26/rest/assets" - ] - }, - { - "data_path": "action_result.data.*.method", - "data_type": "string", - "example_values": [ - "POST" - ] - }, - { - "data_path": "action_result.data.*.parsed_response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_body", - "data_type": "string", - "example_values": [ - "{\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "action_result.data.*.response_headers", - "data_type": "string" - }, - { - "data_path": "action_result.summary.reason", - "data_type": "string", - "example_values": [ - "Not Found" - ] - }, - { - "data_path": "action_result.summary.status_code", - "data_type": "numeric", - "example_values": [ - 404 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Can't process response from server. Status Code: 404 Data from server: {\"failed\": true, \"message\": \"Requested item not found\"}" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 0 - ] - } - ], - "versions": "EQ(*)" - }, - { - "action": "get file", - "description": "Retrieve a file from the endpoint and save it to the vault", - "verbose": "Provide the file path and file name to download into the vault. For example, /web_storage/file.tgz.", - "type": "investigate", - "identifier": "get_file", - "read_only": true, - "parameters": { - "hostname": { - "data_type": "string", - "order": 0, - "description": "Hostname to execute command on", - "contains": [ - "host name" - ], - "primary": true - }, - "file_path": { - "data_type": "string", - "order": 1, - "description": "Path of the file to download (include filename)", - "contains": [ - "file path" - ], - "required": true, - "primary": true - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 2 - } - }, - "render": { - "width": 12, - "title": "Get File", - "type": "table", - "height": 5 - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.file_path", - "data_type": "string", - "contains": [ - "file path" - ], - "example_values": [ - "/web_storage/file.tgz" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.parameter.hostname", - "data_type": "string", - "contains": [ - "host name" - ], - "example_values": [ - "http://192.168.0.1" - ] - }, - { - "data_path": "action_result.data", - "data_type": "string" - }, - { - "data_path": "action_result.summary.exit_status", - "data_type": "numeric", - "example_values": [ - 0 - ] - }, - { - "data_path": "action_result.summary.name", - "data_type": "string", - "example_values": [ - "file.tgz" - ] - }, - { - "data_path": "action_result.summary.size", - "data_type": "numeric", - "example_values": [ - 412 - ] - }, - { - "data_path": "action_result.summary.vault_id", - "data_type": "string", - "contains": [ - "vault id" - ], - "column_order": 1, - "column_name": "Vault ID", - "example_values": [ - "dc871f816c4d179f3a3cea24b4bc81a67562c" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "column_order": 0, - "column_name": "Result Message", - "example_values": [ - "Transferred file" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "versions": "EQ(*)" - }, - { - "action": "put file", - "description": "Put a file from the vault to another location", - "type": "generic", - "identifier": "put_file", - "verbose": "Provide the path to store the file on the file server. For example, /web_storage/test_repo/.", - "read_only": false, - "parameters": { - "host": { - "data_type": "string", - "order": 0, - "description": "Hostname/IP with port number to execute command on", - "contains": [ - "host name" - ], - "primary": true - }, - "vault_id": { - "data_type": "string", - "order": 1, - "contains": [ - "vault id" - ], - "description": "Vault ID of file", - "required": true, - "primary": true - }, - "file_destination": { - "description": "File destination path (exclude filename)", - "data_type": "string", - "order": 2, - "contains": [ - "file path" - ], - "required": true, - "primary": true - }, - "file_name": { - "description": "Name of the file to be put on endpoint", - "data_type": "string", - "order": 3 - }, - "verify_certificate": { - "description": "Verify certificates (if using HTTPS)", - "data_type": "boolean", - "order": 4 - } - }, - "render": { - "width": 12, - "title": "Put File", - "type": "table", - "height": 5 - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.file_name", - "data_type": "string", - "example_values": [ - "test.txt" - ] - }, - { - "data_path": "action_result.parameter.file_destination", - "data_type": "string", - "contains": [ - "file path" - ], - "example_values": [ - "/web_storage/test_repo/" - ] - }, - { - "data_path": "action_result.parameter.host", - "data_type": "string", - "contains": [ - "host name" - ], - "example_values": [ - "http://192.168.0.1:8001" - ] - }, - { - "data_path": "action_result.parameter.verify_certificate", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.parameter.vault_id", - "data_type": "string", - "contains": [ - "vault id" - ], - "column_order": 0, - "column_name": "Vault ID", - "example_values": [ - "dc871f816c4d179f3a3cea24b4bc81a67562c" - ] - }, - { - "data_path": "action_result.data", - "data_type": "string" - }, - { - "data_path": "action_result.summary.file_sent", - "data_type": "string", - "column_order": 1, - "column_name": "File Transferred", - "contains": [ - "file path" - ], - "example_values": [ - "http://192.168.0.1:8001/web_storage/test_repo/file.tgz" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "column_order": 2, - "column_name": "Result Message", - "example_values": [ - "File sent: http://192.168.0.1:8001/web_storage/test_repo/file.tgz" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "versions": "EQ(*)" - } - ], - "pip39_dependencies": { - "wheel": [ - { - "module": "validators", - "input_file": "wheels/py3/validators-0.35.0-py3-none-any.whl" - } - ] - }, - "pip313_dependencies": { - "wheel": [ - { - "module": "validators", - "input_file": "wheels/py3/validators-0.35.0-py3-none-any.whl" - } - ] - } -} diff --git a/http_connector.py b/http_connector.py deleted file mode 100644 index a8a4ed2..0000000 --- a/http_connector.py +++ /dev/null @@ -1,927 +0,0 @@ -# File: http_connector.py -# -# Copyright (c) 2016-2025 Splunk Inc. -# -# 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. - -import json -import os -import re -import shutil -import socket -import sys -import uuid -from urllib.parse import quote, unquote_plus, urlparse - -import encryption_helper -import magic -import phantom.app as phantom -import phantom.rules as ph_rules -import requests -import validators -import xmltodict -from bs4 import BeautifulSoup, UnicodeDammit -from phantom.action_result import ActionResult -from phantom.base_connector import BaseConnector -from phantom.vault import Vault as Vault -from requests.auth import HTTPBasicAuth - -from http_consts import * - - -class RetVal(tuple): - def __new__(cls, val1, val2=None): - return tuple.__new__(RetVal, (val1, val2)) - - -class HttpConnector(BaseConnector): - MAGIC_FORMATS = [ - (re.compile("^PE.* Windows"), ["pe file"], ".exe"), - (re.compile("^MS-DOS executable"), ["pe file"], ".exe"), - (re.compile("^PDF "), ["pdf"], ".pdf"), - (re.compile("^MDMP crash"), ["process dump"], ".dmp"), - (re.compile("^Macromedia Flash"), ["flash"], ".flv"), - (re.compile("^tcpdump capture"), ["pcap"], ".pcap"), - ] - - def __init__(self): - super().__init__() - - self._state = None - self._base_url = None - self._test_path = None - self._timeout = None - self._token_name = None - self._token = None - self._username = None - self._password = None - self._oauth_token_url = None - self._client_id = None - self._client_secret = None - self._access_token = None - self.access_token_retry = True - - def encrypt_state(self, encrypt_var, token_name): - """Handle encryption of token. - :param encrypt_var: Variable needs to be encrypted - :return: encrypted variable - """ - self.debug_print(HTTP_ENCRYPT_TOKEN.format(token_name)) # nosemgrep - return encryption_helper.encrypt(encrypt_var, self.get_asset_id()) - - def decrypt_state(self, decrypt_var, token_name): - """Handle decryption of token. - :param decrypt_var: Variable needs to be decrypted - :return: decrypted variable - """ - self.debug_print(HTTP_DECRYPT_TOKEN.format(token_name)) # nosemgrep - return encryption_helper.decrypt(decrypt_var, self.get_asset_id()) - - def _get_error_message_from_exception(self, e): - """This function is used to get appropriate error message from the exception. - :param e: Exception object - :return: error message - """ - error_msg = HTTP_ERR_MSG - error_code = HTTP_ERR_CODE_MSG - try: - if hasattr(e, "args"): - if len(e.args) > 1: - error_code = e.args[0] - error_msg = e.args[1] - elif len(e.args) == 1: - error_code = HTTP_ERR_CODE_MSG - error_msg = e.args[0] - except Exception as ex: - self.error_print("Exception occurred.", ex) - - return f"Error Code: {error_code}. Error Message: {error_msg}" - - def _validate_integers(self, action_result, parameter, key, allow_zero=False): - """This method is to check if the provided input parameter value - is a non-zero positive integer and returns the integer value of the parameter itself. - :param action_result: Action result or BaseConnector object - :param parameter: input parameter - :return: integer value of the parameter or None in case of failure - """ - try: - if not float(parameter).is_integer(): - self.set_status(phantom.APP_ERROR, HTTP_VALIDATE_INTEGER_MSG.format(key=key)) - return None - parameter = int(parameter) - - except Exception as ex: - self.error_print("Exception occurred.", ex) - self.set_status(phantom.APP_ERROR, HTTP_VALIDATE_INTEGER_MSG.format(key=key)) - return None - - if parameter < 0: - self.set_status( - phantom.APP_ERROR, - f"Please provide a valid non-negative integer value in the {key} parameter", - ) - return None - if not allow_zero and parameter == 0: - self.set_status( - phantom.APP_ERROR, - f"Please provide a positive integer value in the {key} parameter", - ) - return None - - return parameter - - def initialize(self): - self._state = self.load_state() - - if not isinstance(self._state, dict): - self.debug_print("Resetting the state file with the default format") - self._state = {"app_version": self.get_app_json().get("app_version")} - - config = self.get_config() - self._base_url = config["base_url"].strip("/") - self._token_name = config.get("auth_token_name", "ph-auth-token") - self._token = config.get("auth_token") - - self._username = config.get("username") - self._password = config.get("password", "") - self._test_http_method = config.get("test_http_method", "get").lower() - - self._oauth_token_url = config.get("oauth_token_url") - if self._oauth_token_url: - self._oauth_token_url = self._oauth_token_url.strip("/") - self._client_id = config.get("client_id") - self._client_secret = config.get("client_secret") - self._access_token = self._state.get(HTTP_JSON_ACCESS_TOKEN) - - if "test_path" in config: - try: - if not config["test_path"].startswith("/"): - self._test_path = "/" + config["test_path"] - else: - self._test_path = config["test_path"] - except Exception as e: - error_message = self._get_error_message_from_exception(e) - return self.set_status( - phantom.APP_ERROR, - f"Given endpoint value is invalid: {error_message}", - ) - - if "timeout" in config: - self._timeout = self._validate_integers(self, config.get("timeout"), "timeout") - if self._timeout is None: - return self.get_status() - - parsed = urlparse(self._base_url) - - if not parsed.scheme or not parsed.hostname: - return self.set_status( - phantom.APP_ERROR, - f'Failed to parse URL ({self._base_url}). Should look like "http(s)://location/optional_path"', - ) - - # Make sure base_url isn't 127.0.0.1 - addr = parsed.hostname - try: - unpacked = socket.gethostbyname(addr) - except Exception as ex: - self.error_print("Exception occurred.", ex) - try: - packed = socket.inet_aton(addr) - unpacked = socket.inet_aton(packed) - except Exception as ex: - self.error_print("Exception occurred.", ex) - # gethostbyname can fail even when the addr is a hostname - # If that happens, I think we can assume that it isn't localhost - unpacked = "" - - if unpacked.startswith("127."): - return self.set_status(phantom.APP_ERROR, "Accessing 127.0.0.1 is not allowed") - - if self._state.get(HTTP_STATE_IS_ENCRYPTED): - try: - if self._access_token: - self._access_token = self.decrypt_state(self._access_token, "access") - except Exception as e: - self.error_print(HTTP_DECRYPTION_ERR, e) - return self.set_status(phantom.APP_ERROR, HTTP_DECRYPTION_ERR) - - return phantom.APP_SUCCESS - - def finalize(self): - try: - if self._access_token: - self._state[HTTP_JSON_ACCESS_TOKEN] = self.encrypt_state(self._access_token, "access") - self._state[HTTP_STATE_IS_ENCRYPTED] = True - except Exception as e: - self.error_print(HTTP_ENCRYPTION_ERR, e) - return self.set_status(phantom.APP_ERROR, HTTP_ENCRYPTION_ERR) - - self.save_state(self._state) - return phantom.APP_SUCCESS - - def _process_empty_reponse(self, response, action_result): - if 200 <= response.status_code < 400: - if response.headers.get("Content-Type") == "application/octet-stream": - return RetVal(phantom.APP_SUCCESS, "Response includes a file") - return RetVal(phantom.APP_SUCCESS, None) - return RetVal( - action_result.set_status(phantom.APP_ERROR, "Empty response and no information in the header"), - None, - ) - - def _process_html_response(self, response, action_result): - status_code = response.status_code - - try: - soup = BeautifulSoup(response.text, "html.parser") - for element in soup(["script", "style", "footer", "nav"]): - element.extract() - error_text = soup.text - split_lines = error_text.split("\n") - split_lines = [x.strip() for x in split_lines if x.strip()] - error_text = "\n".join(split_lines) - except Exception as ex: - self.error_print("Exception occurred.", ex) - error_text = "Cannot parse error details" - - response_data = error_text - if 200 <= response.status_code < 400: - return RetVal(phantom.APP_SUCCESS, response_data) - - message = f"Status Code: {status_code}. Data from server:\n{unquote_plus(error_text)}\n" - - message = message.replace("{", "{{").replace("}", "}}") - - return RetVal(action_result.set_status(phantom.APP_ERROR, message), response_data) - - def _process_json_response(self, response, action_result): - resp_json = {} - try: - if response: - resp_json = response.json() - except Exception as e: - error_message = self._get_error_message_from_exception(e) - self.error_print("Unable to parse the response into a dictionary", error_message) - return RetVal( - action_result.set_status( - phantom.APP_ERROR, - f"Unable to parse JSON response. Error: {error_message}", - ) - ) - - if 200 <= response.status_code < 400: - return RetVal(phantom.APP_SUCCESS, resp_json) - - message_template = "Error from server. Status Code: {0} Data from server: {1}" - message = message_template.format(response.status_code, response.text.replace("{", "{{").replace("}", "}}")) - - error_field_name = "error" - message_field_name = "message" - if error_field_name in resp_json: - error_field = resp_json[error_field_name] - - if isinstance(error_field, dict) and message_field_name in error_field: - error_message = error_field[message_field_name] - else: - error_message = error_field - - message = message_template.format(response.status_code, error_message) - - return RetVal(action_result.set_status(phantom.APP_ERROR, message), resp_json) - - def _process_xml_response(self, r, action_result): - resp_json = None - try: - if r.text: - resp_json = xmltodict.parse(r.text) - except Exception as e: - error_message = self._get_error_message_from_exception(e) - return RetVal( - action_result.set_status( - phantom.APP_ERROR, - f"Unable to parse XML response. Error: {error_message}", - ) - ) - - if 200 <= r.status_code < 400: - return RetVal(phantom.APP_SUCCESS, resp_json) - - message = "Error from server. Status Code: {} Data from server: {}".format(r.status_code, r.text.replace("{", "{{").replace("}", "}}")) - - return RetVal(action_result.set_status(phantom.APP_ERROR, message), resp_json) - - def _process_response(self, r, action_result): - if r.headers.get("Content-Type", "") == "application/octet-stream": - r_text = "" - else: - r_text = r.text - - if hasattr(action_result, "add_debug_data"): - action_result.add_debug_data({"r_status_code": r.status_code}) - action_result.add_debug_data({"r_text": r_text}) - action_result.add_debug_data({"r_headers": r.headers}) - - if not r_text: - return self._process_empty_reponse(r, action_result) - if "xml" in r.headers.get("Content-Type", ""): - return self._process_xml_response(r, action_result) - if "json" in r.headers.get("Content-Type", "") or "javascript" in r.headers.get("Content-Type", ""): - return self._process_json_response(r, action_result) - if "html" in r.headers.get("Content-Type", ""): - return self._process_html_response(r, action_result) - if 200 <= r.status_code < 400: - return RetVal(phantom.APP_SUCCESS, r_text) - - message = "Can't process response from server. Status Code: {} Data from server: {}".format( - r.status_code, r.text.replace("{", "{{").replace("}", "}}") - ) - - return RetVal(action_result.set_status(phantom.APP_ERROR, message), r.text) - - def _make_http_call( - self, - action_result, - endpoint="", - method="get", - headers=None, - params=None, - verify=False, - data=None, - files=None, - use_default_endpoint=False, - ): - auth = None - headers = {} if not headers else headers - access_token = "" - if self._username: - self.save_progress("Using HTTP Basic auth to authenticate") - auth = (self._username, self._password) - elif self._oauth_token_url and self._client_id: - self.save_progress("Using OAuth to authenticate") - access_token = self._generate_api_token(action_result) - if not access_token: - return action_result.get_status(), None - headers["Authorization"] = f"Bearer {access_token}" - elif self._token_name: - self.save_progress("Using provided token to authenticate") - if self._token and self._token_name not in headers: - headers[self._token_name] = self._token - else: - return action_result.set_status(phantom.APP_ERROR, "No authentication method set"), None - - if self.get_action_identifier() == "get_file" or self.get_action_identifier() == "put_file": - url = endpoint - if not use_default_endpoint: - auth = None - else: - url = self._base_url + endpoint - - try: - r = requests.request( - method=method, - url=url, - auth=auth, - params=params, - data=UnicodeDammit(data).unicode_markup.encode("utf-8") if isinstance(data, str) else data, - verify=verify, - headers=headers, - files=files, - timeout=self._timeout, - ) - - except Exception as e: - error_message = self._get_error_message_from_exception(e) - return action_result.set_status( - phantom.APP_ERROR, - f"Error Connecting to server. Details: {error_message}", - ), None - - # fetch new token if old one has expired - if access_token and r.status_code == 401 and self.access_token_retry: - self.save_progress(f"Got error: {r.status_code}") - self._access_token = None - self._state.pop("access_token") - self.access_token_retry = False # make it to false to avoid getting access token after one time (prevents recursive loop) - return self._make_http_call( - action_result, - endpoint=endpoint, - method=method, - headers=headers, - params=params, - verify=verify, - data=data, - files=files, - use_default_endpoint=use_default_endpoint, - ) - - # Return success for get headers action as it returns empty response body - if self.get_action_identifier() == "http_head" and r.status_code == 200: - resp_data = {"method": method.upper(), "location": url} - try: - resp_data["response_headers"] = dict(r.headers) - except Exception: - pass - action_result.add_data(resp_data) - action_result.update_summary({"status_code": r.status_code, "reason": r.reason}) - self.access_token_retry = True - return action_result.set_status(phantom.APP_SUCCESS), None - - ret_val, parsed_body = self._process_response(r, action_result) - - if self.get_action_identifier() == "get_file" or self.get_action_identifier() == "put_file": - return ret_val, r - - content_type = r.headers.get("Content-Type", "") - if "json" not in content_type and "javascript" not in content_type: - response_body = r.text - else: - response_body = parsed_body - - resp_data = { - "method": method.upper(), - "location": url, - "parsed_response_body": parsed_body, - "response_body": response_body, - } - try: - resp_data["response_headers"] = dict(r.headers) - except Exception: - pass - action_result.add_data(resp_data) - action_result.update_summary({"status_code": r.status_code, "reason": r.reason}) - - if self.get_action_identifier() == "test_connectivity": - self.save_progress(f"Got status code {r.status_code}") - - if phantom.is_fail(ret_val): - return ret_val, None - - return action_result.set_status(phantom.APP_SUCCESS), None - - def _get_headers(self, action_result, headers): - # Not to be confused with the action "get headers" - if headers is None: - return RetVal(phantom.APP_SUCCESS) - - if hasattr(headers, "decode"): - headers = headers.decode("utf-8") - - try: - headers = json.loads(headers) - except Exception as e: - error_message = self._get_error_message_from_exception(e) - return RetVal( - action_result.set_status( - phantom.APP_ERROR, - f"Failed to parse headers as JSON object. error: {error_message}, headers: {headers}", - ) - ) - - return RetVal(phantom.APP_SUCCESS, headers) - - def _generate_api_token(self, action_result, new_token=False): - """This function is used to generate token - - :param action_result: object of ActionResult class - :param new_token: boolean. weather to fetch new token or fetch old access token - :return: access token - """ - self.save_progress("Fetching access token") - - if self._access_token and not new_token: - self.save_progress("Using old token") - return self._access_token - - payload = {"grant_type": "client_credentials"} - - self.save_progress("Fetching new token") - # Querying endpoint to generate token - response = requests.post( - self._oauth_token_url, - auth=HTTPBasicAuth(self._client_id, self._client_secret), # nosemgrep - data=payload, - timeout=DEFAULT_REQUEST_TIMEOUT, - ) - if response.status_code not in [200, 201]: - action_result.set_status( - phantom.APP_ERROR, - f"Error fetching token from {self._oauth_token_url}. Server returned {response.status_code}", - ) - return None - - try: - self._access_token = json.loads(response.text).get(HTTP_JSON_ACCESS_TOKEN) - except Exception: - action_result.set_status( - phantom.APP_ERROR, - "Error parsing response from server while fetching token", - ) - return None - - if not self._access_token: - action_result.set_status(phantom.APP_ERROR, "Access token not found in response body") - return None - - self._state[HTTP_JSON_ACCESS_TOKEN] = self._access_token - return self._access_token - - def _handle_test_connectivity(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - test_path = self._test_path - - if test_path: - self.save_progress(f"Querying base url, {self._base_url}{self._test_path}, to test credentials") - ret_val, _ = self._make_http_call(action_result, test_path, method=self._test_http_method) - else: - self.save_progress(f"Querying base url, {self._base_url}, to test credentials") - ret_val, _ = self._make_http_call(action_result, method=self._test_http_method) - - if phantom.is_fail(ret_val): - self.save_progress("Test Connectivity Failed") - else: - self.save_progress("Test Connectivity Passed") - - return ret_val - - def _verb(self, param, method): - # These three are all the same thing except for the 'method' - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - location = param["location"] - body = param.get("body") - - if not location.startswith("/"): - location = "/" + location - - if hasattr(location, "decode"): - location = location.decode("utf-8") - - ret_val, headers = self._get_headers(action_result, param.get("headers")) - - if phantom.is_fail(ret_val): - return ret_val - - ret_val, _ = self._make_http_call( - action_result, - endpoint=location, - method=method, - headers=headers, - verify=param.get("verify_certificate", False), - data=body, - ) - return ret_val - - def _handle_get_file(self, param, method): - action_result = ActionResult(dict(param)) - self.add_action_result(action_result) - - file_path = param[HTTP_JSON_FILE_PATH] - file_path = file_path.strip(" ") - file_path = file_path.rstrip("/") - hostname = param.get(HTTP_JSON_HOSTNAME) - if not hostname: - hostname = self._base_url - use_default_endpoint = True - else: - use_default_endpoint = False - hostname = hostname.strip(" ") - hostname = hostname.strip("/") - - if file_path == "": - return action_result.set_status(phantom.APP_ERROR, HTTP_INVALID_PATH_ERR) - - encoded_file_path = quote(file_path) - endpoint = f"{hostname}/{file_path}" - validate_endpoint = f"{hostname}/{encoded_file_path}" - - if not validators.url(validate_endpoint): - return action_result.set_status(phantom.APP_ERROR, HTTP_INVALID_URL_ERR) - file_name = file_path.split("/")[-1] - file_name = unquote_plus(file_name) - try: - ret_val, r = self._make_http_call( - action_result, - endpoint=endpoint, - method=method, - verify=param.get("verify_certificate", False), - use_default_endpoint=use_default_endpoint, - ) - except Exception as e: - err = self._get_error_message_from_exception(e) - return action_result.set_status(phantom.APP_ERROR, HTTP_SERVER_CONNECTION_ERR_MSG.format(error=err)) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if r.status_code == 200: - return self._save_file_to_vault(action_result, r, file_name) - else: - return action_result.set_status( - phantom.APP_ERROR, - HTTP_SERVER_CONNECTION_ERR_MSG.format(error=r.status_code), - ) - - def _handle_put_file(self, param, method): - action_result = ActionResult(dict(param)) - self.add_action_result(action_result) - - input_filename = param.get(HTTP_JSON_FILE_NAME, "") - - # fetching phantom vault details - try: - success, message, vault_meta_info = ph_rules.vault_info(vault_id=param[HTTP_JSON_VAULT_ID]) - if not success or not vault_meta_info: - error_msg = f" Error Details: {unquote_plus(message)}" if message else "" - return action_result.set_status( - phantom.APP_ERROR, - f"{HTTP_UNABLE_TO_RETRIEVE_VAULT_ITEM_ERR_MSG}.{error_msg}", - ) - vault_meta_info = list(vault_meta_info) - except Exception as e: - err = self._get_error_message_from_exception(e) - return action_result.set_status( - phantom.APP_ERROR, - f"{HTTP_UNABLE_TO_RETRIEVE_VAULT_ITEM_ERR_MSG}. {err}", - ) - - file_is_present = False - if input_filename: - input_filename = input_filename.strip() - for vault_data in vault_meta_info: - if vault_data.get("name") == input_filename: - file_is_present = True - break - if not file_is_present: - return action_result.set_status(phantom.APP_ERROR, HTTP_ERR_FILENAME_NOT_IN_VAULT) - else: - vault_meta_info[0]["name"] = input_filename - - # phantom vault file path - vault_path = vault_meta_info[0].get("path") - try: - f = open(vault_path, "rb") - files = {"file": f} - except Exception as e: - return action_result.set_status(phantom.APP_ERROR, self._get_error_message_from_exception(e)) - - # phantom vault file name - dest_file_name = vault_meta_info[0].get("name") - file_dest = param[HTTP_JSON_FILE_DEST] - endpoint = param.get(HTTP_JSON_HOST) - if not endpoint: - endpoint = self._base_url - use_default_endpoint = True - else: - use_default_endpoint = False - - file_dest = file_dest.strip("/") - endpoint = endpoint.rstrip("/") - - # encoding input file name - dest_file_name = dest_file_name.strip("/") - validate_dest_file_name = quote(dest_file_name) - - # Returning an error if the filename is included in the file_destination path - if dest_file_name in file_dest: - return action_result.set_status(phantom.APP_ERROR, HTTP_EXCLUDE_FILENAME_ERR_MSG) - - destination_path = f"{endpoint}/{file_dest}/{dest_file_name}" - validate_destination_path = f"{endpoint}/{file_dest}/{validate_dest_file_name}" - - if not validators.url(validate_destination_path): - return action_result.set_status(phantom.APP_ERROR, HTTP_INVALID_URL_ERR) - - params = {"file_path": file_dest} - - try: - ret_val, response = self._make_http_call( - action_result, - endpoint=destination_path, - method=method, - params=params, - verify=param.get("verify_certificate", False), - files=files, - use_default_endpoint=use_default_endpoint, - ) - except Exception as e: - err = self._get_error_message_from_exception(e) - return action_result.set_status(phantom.APP_ERROR, HTTP_SERVER_CONNECTION_ERR_MSG.format(error=err)) - finally: - f.close() - - if response.status_code == 200: - summary = {"file_sent": destination_path} - action_result.update_summary(summary) - return action_result.set_status(phantom.APP_SUCCESS) - else: - return action_result.set_status( - phantom.APP_ERROR, - HTTP_SERVER_CONNECTION_ERR_MSG.format(error=response.status_code), - ) - - def _save_file_to_vault(self, action_result, response, file_name): - # Create a tmp directory on the vault partition - - guid = uuid.uuid4() - if hasattr(Vault, "get_vault_tmp_dir"): - temp_dir = Vault.get_vault_tmp_dir() - else: - temp_dir = "/vault/tmp" - - local_dir = temp_dir + f"/{guid}" - self.save_progress(f"Using temp directory: {guid}") - try: - os.makedirs(local_dir) - except Exception as e: - return action_result.set_status( - phantom.APP_ERROR, - f"Unable to create temporary folder {temp_dir}.", - e, - ) - file_path = f"{local_dir}/{file_name}" - # open and download the file - with open(file_path, "wb") as f: - f.write(response.content) - contains = [] - file_ext = "" - magic_str = magic.from_file(file_path) - for regex, cur_contains, extension in self.MAGIC_FORMATS: - if regex.match(magic_str): - contains.extend(cur_contains) - if not file_ext: - file_ext = extension - file_name = f"{file_name}{file_ext}" - - # move the file to the vault - status, vault_ret_message, vault_id = ph_rules.vault_add( - file_location=file_path, - container=self.get_container_id(), - file_name=file_name, - metadata={"contains": contains}, - ) - curr_data = {} - - if status: - curr_data[phantom.APP_JSON_VAULT_ID] = vault_id - curr_data[phantom.APP_JSON_NAME] = file_name - if contains: - curr_data["file_type"] = ",".join(contains) - action_result.add_data(curr_data) - action_result.update_summary(curr_data) - action_result.set_status(phantom.APP_SUCCESS, "File successfully retrieved and added to vault") - else: - action_result.set_status(phantom.APP_ERROR, phantom.APP_ERR_FILE_ADD_TO_VAULT) - action_result.append_to_message(vault_ret_message) - - # remove the /tmp/<> temporary directory - shutil.rmtree(local_dir) - - return action_result.get_status() - - def _handle_http_get(self, param): - return self._verb(param, "get") - - def _handle_http_post(self, param): - return self._verb(param, "post") - - def _handle_http_patch(self, param): - return self._verb(param, "patch") - - def _handle_http_put(self, param): - return self._verb(param, "put") - - def _handle_http_delete(self, param): - return self._verb(param, "delete") - - def _handle_http_head(self, param): - return self._verb(param, "head") - - def _handle_http_options(self, param): - return self._verb(param, "options") - - def handle_action(self, param): - ret_val = phantom.APP_SUCCESS - - action_id = self.get_action_identifier() - - self.debug_print("action_id", self.get_action_identifier()) - - if action_id == "test_connectivity": - ret_val = self._handle_test_connectivity(param) - - elif action_id == "http_get": - ret_val = self._handle_http_get(param) - - elif action_id == "http_post": - ret_val = self._handle_http_post(param) - - elif action_id == "http_put": - ret_val = self._handle_http_put(param) - - elif action_id == "http_patch": - ret_val = self._handle_http_patch(param) - - elif action_id == "http_delete": - ret_val = self._handle_http_delete(param) - - elif action_id == "http_head": - ret_val = self._handle_http_head(param) - - elif action_id == "http_options": - ret_val = self._handle_http_options(param) - - elif action_id == "get_file": - ret_val = self._handle_get_file(param, "get") - - elif action_id == "put_file": - ret_val = self._handle_put_file(param, "post") - - return ret_val - - -if __name__ == "__main__": - import argparse - - import pudb - - pudb.set_trace() - - argparser = argparse.ArgumentParser() - - argparser.add_argument("input_test_json", help="Input Test JSON file") - argparser.add_argument("-u", "--username", help="username", required=False) - argparser.add_argument("-p", "--password", help="password", required=False) - argparser.add_argument( - "-v", - "--verify", - action="store_true", - help="verify", - required=False, - default=False, - ) - - args = argparser.parse_args() - verify = args.verify - session_id = None - verify = args.verify - - if args.username and args.password: - login_url = BaseConnector._get_phantom_base_url() + "login" - try: - print("Accessing the Login page") - r = requests.get(login_url, verify=verify, timeout=DEFAULT_REQUEST_TIMEOUT) - csrftoken = r.cookies["csrftoken"] - data = { - "username": args.username, - "password": args.password, - "csrfmiddlewaretoken": csrftoken, - } - headers = { - "Cookie": f"csrftoken={csrftoken}", - "Referer": login_url, - } - - print("Logging into Platform to get the session id") - r2 = requests.post( - login_url, - verify=verify, - data=data, - headers=headers, - timeout=DEFAULT_REQUEST_TIMEOUT, - ) - session_id = r2.cookies["sessionid"] - - except Exception as e: - print(f"Unable to get session id from the platform. Error: {e!s}") - sys.exit(1) - - if len(sys.argv) < 2: - print("No test json specified as input") - sys.exit(0) - - with open(sys.argv[1]) as f: - in_json = f.read() - in_json = json.loads(in_json) - print(json.dumps(in_json, indent=4)) - - connector = HttpConnector() - connector.print_progress_message = True - - if session_id is not None: - in_json["user_session_token"] = session_id - - ret_val = connector._handle_action(json.dumps(in_json), None) - print(json.dumps(json.loads(ret_val), indent=4)) - - sys.exit(0) diff --git a/http_consts.py b/http_consts.py deleted file mode 100644 index 9122885..0000000 --- a/http_consts.py +++ /dev/null @@ -1,46 +0,0 @@ -# File: http_consts.py -# -# Copyright (c) 2016-2025 Splunk Inc. -# -# 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. - -HTTP_VALIDATE_INTEGER_MSG = "Please provide a valid integer value in the {key} parameter" -HTTP_ERR_CODE_MSG = "Error code unavailable" -HTTP_ERR_MSG = "Unknown error occurred. Please check the asset configuration and|or action parameters" -TYPE_ERR_MSG = "Error occurred while connecting to the HTTP server. Please check the asset configuration and|or the action parameters" -HTTP_SERVER_CONNECTION_ERR_MSG = "Error Connecting to file server. Error:{error}" -HTTP_ERR_FILE_ADD_TO_VAULT = "Error while adding the file to Vault" -HTTP_FILE_NOT_FOUND_ERR_MSG = "Please verify the file destination and make sure the filename is not included in it" -HTTP_UNABLE_TO_RETRIEVE_VAULT_ITEM_ERR_MSG = "Unable to retrieve vault item details" -HTTP_EXCLUDE_FILENAME_ERR_MSG = "Error: Do not include filename in the file destination" -HTTP_PUT_FILE_ERR_MSG = "Error putting file. {error}" -HTTP_JSON_FILE_PATH = "file_path" -HTTP_INVALID_PATH_ERR = "Invalid file path, please enter a valid file path" -HTTP_INVALID_URL_ERR = "Malformed URL, please enter hostname and filepath in proper format" - -HTTP_JSON_HOSTNAME = "hostname" -HTTP_JSON_ACCESS_TOKEN = "access_token" -HTTP_JSON_HOST = "host" -HTTP_JSON_VAULT_ID = "vault_id" -HTTP_JSON_FILE_DEST = "file_destination" -HTTP_JSON_FILE_NAME = "file_name" -UNKNOWN_ERR_MSG = "UNKNOWN ERR MSG" -UNKNOWN_ERR_CODE_MSG = "UNKNOWN ERR CODE MSG" -HTTP_ERR_FILENAME_NOT_IN_VAULT = "Could not find file with specified filename in vault, please provide a valid filename" -DEFAULT_REQUEST_TIMEOUT = 30 # in seconds -HTTP_STATE_IS_ENCRYPTED = "is_encrypted" - -# For encryption and decryption -HTTP_ENCRYPT_TOKEN = "Encrypting the {} token" -HTTP_DECRYPT_TOKEN = "Decrypting the {} token" -HTTP_ENCRYPTION_ERR = "Error occurred while encrypting the state file" -HTTP_DECRYPTION_ERR = "Error occurred while decrypting the state file" diff --git a/logo_splunk.svg b/logo.svg similarity index 100% rename from logo_splunk.svg rename to logo.svg diff --git a/logo_splunk_dark.svg b/logo_dark.svg similarity index 100% rename from logo_splunk_dark.svg rename to logo_dark.svg diff --git a/pyproject.toml b/pyproject.toml index a816e31..bab55a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,37 +1,103 @@ -# Ruff linting +[project] +name = "http" +version = "4.0.0" +description = "This App facilitates making HTTP requests as actions" +license = "Copyright (c) 2025 Splunk Inc." +requires-python = ">=3.9, <3.14" +authors = [ +] +dependencies = [ + "splunk-soar-sdk>=2.3.3", + "validators", + "xmltodict", +] + +[dependency-groups] +dev = [ + "coverage>=7.6.7,<8", + "mypy>=1.2.0,<2", + "pre-commit>=4.2.0,<5", + "pytest>=7.4.2,<8", + "pytest-mock>=3.14.0,<4", + "pytest-watch>=4.2.0,<5", + "ruff>=0.11.6,<1", +] + +[tool.soar.app] +main_module = "src.app:app" + +[[tool.uv.index]] +url = "https://pypi.python.org/simple" + +[tool.uv] +environments = [ + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.9'", + "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.9'", + "sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version == '3.9'", + "sys_platform == 'darwin' and platform_machine == 'arm64' and python_version == '3.9'", + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.13'", + "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.13'", + "sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version == '3.13'", + "sys_platform == 'darwin' and platform_machine == 'arm64' and python_version == '3.13'", +] +required-environments = [ + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.9'", + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.13'", +] + [tool.ruff] -line-length = 145 +output-format = "full" # +fix = true target-version = "py39" [tool.ruff.lint] -select = [ # Auto-fixable rules only - "I", # isort +select = [ + "ERA", # commented out code + "YTT", # bad use of sys.version_info + "S", # security issues + "B", # bugbear + "A", # builtins + "DTZ", # datetime footguns + "T10", # breakpoints + "ISC", # implicit string concatenation + "PT", # pytest style + "SIM", # simplify + "PTH", # use pathlib + "E", # pycodestyle errors + "F", # pyflakes + "W", # pycodestyle warnings + "PL", # pylint "UP", # pyupgrade - "F401", # unused imports - "RUF" # ruff rules + "RUF", # ruff's own rules ] - ignore = [ - "RUF012", # Not auto-fixable (remove eventually) - "RUF001" # Not auto-fixable (remove eventually) + "E402", # Module level import not at top of file. + "E501", # Line too long. Format covers this. + "PT006", # Opinions about types for pytest.parametrize + "PT007", # Opinions about types for pytest.parametrize + "PTH123", # Allow builtin open() + "PLR", # pylint refactors ] -[tool.ruff.lint.per-file-ignores] -"__init__.py" = ["F401", "UP035"] # __init__.py file exceptions +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -# Keep complexity below 28 -[tool.ruff.lint.mccabe] -max-complexity = 28 +[tool.ruff.lint.per-file-ignores] +"tests/**/*" = [ + "ANN", # Disable flake8-annotations rules for test files + "S", # Disable flake8-bandit rules for test files +] +"src/**/*" = [ + "PT", # Disable pytest rules for src files +] -[tool.ruff.lint.isort] -combine-as-imports = true -lines-after-imports = 2 +[tool.ruff.lint.pyupgrade] +# Preserve types, even if a file imports `from __future__ import annotations`. +# This is necessary since we rely on runtime type annotations in our code and in pydantic +keep-runtime-typing = true [tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" +docstring-code-format = true # HTML linting [tool.djlint] @@ -58,22 +124,26 @@ config = [ "r/typescript.react.security.audit.react-dangerouslysetinnerhtml.react-dangerouslysetinnerhtml" # TypeScript React security rule ] ignore-patterns = [ - "node_modules/", - "build/", "dist/", "vendor/", "env/", ".env/", "venv/", ".venv/", - ".tox/", - "*.min.js", "test/", "tests/", - "*_test.go", ".semgrep", "wheels/", ".html", - "*.md", - "*.svg" + ".md", + ".svg" ] + +[tool.black] +line-length = 145 +target-version = ['py39'] +verbose = true + +[tool.isort] +line_length = 145 +profile = "black" diff --git a/release_notes/unreleased.md b/release_notes/unreleased.md index fbcb2fd..36aa4d0 100644 --- a/release_notes/unreleased.md +++ b/release_notes/unreleased.md @@ -1 +1,4 @@ **Unreleased** + +* Converts the HTTP app to use the new connector SDK (https://pypi.org/project/splunk-soar-sdk/) +* Adds scope to the asset configuration for OAuth \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c7ff900..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -validators==0.35.0 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..1010bb8 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 . import app + +__ALL__ = [app] diff --git a/src/actions/action_delete.py b/src/actions/action_delete.py new file mode 100644 index 0000000..0d0e1f2 --- /dev/null +++ b/src/actions/action_delete.py @@ -0,0 +1,47 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient +from soar_sdk.params import Param + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + + +class DeleteDataOutput(BaseHttpOutput): + pass + + +class DeleteDataParams(BaseHttpParams): + body: str = Param( + description="DELETE body (query string, JSON, etc.)", required=False + ) + + +def delete_data( + params: DeleteDataParams, soar: SOARClient, asset: Asset +) -> DeleteDataOutput: + """Perform a REST DELETE call to the server.""" + logger.info("In action handler for: delete_data") + return make_request( + asset=asset, + soar=soar, + method="DELETE", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + output=DeleteDataOutput, + body=None, + ) diff --git a/src/actions/action_get.py b/src/actions/action_get.py new file mode 100644 index 0000000..71343c4 --- /dev/null +++ b/src/actions/action_get.py @@ -0,0 +1,42 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + + +class GetDataOutput(BaseHttpOutput): + pass + + +class GetDataParams(BaseHttpParams): + pass + + +def get_data(params: GetDataParams, soar: SOARClient, asset: Asset) -> GetDataOutput: + """Perform a REST GET call to the server.""" + logger.info("In action handler for: get_data") + return make_request( + asset=asset, + soar=soar, + method="GET", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + output=GetDataOutput, + body=None, + ) diff --git a/src/actions/action_head.py b/src/actions/action_head.py new file mode 100644 index 0000000..d482413 --- /dev/null +++ b/src/actions/action_head.py @@ -0,0 +1,44 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + + +class GetHeadersOutput(BaseHttpOutput): + pass + + +class GetHeadersParams(BaseHttpParams): + pass + + +def get_headers( + params: GetHeadersParams, soar: SOARClient, asset: Asset +) -> GetHeadersOutput: + """Perform a REST HEAD call to the server.""" + logger.info("In action handler for: head_data") + return make_request( + asset=asset, + soar=soar, + method="HEAD", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + output=GetHeadersOutput, + body=None, + ) diff --git a/src/actions/action_options.py b/src/actions/action_options.py new file mode 100644 index 0000000..872320d --- /dev/null +++ b/src/actions/action_options.py @@ -0,0 +1,44 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + + +class GetOptionsParams(BaseHttpParams): + pass + + +class GetOptionsOutput(BaseHttpOutput): + pass + + +def get_options( + params: GetOptionsParams, soar: SOARClient, asset: Asset +) -> GetOptionsOutput: + """Perform a REST OPTIONS call to the server.""" + logger.info("In action handler for: get_options") + return make_request( + asset=asset, + soar=soar, + method="OPTIONS", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + output=GetOptionsOutput, + body=None, + ) diff --git a/src/actions/action_patch.py b/src/actions/action_patch.py new file mode 100644 index 0000000..1dd79ee --- /dev/null +++ b/src/actions/action_patch.py @@ -0,0 +1,49 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient +from soar_sdk.params import Param + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + +action_type = "generic" + + +class PatchDataParams(BaseHttpParams): + body: str = Param( + description="PATCH body (query string, JSON, etc.)", required=False + ) + + +class PatchDataOutput(BaseHttpOutput): + pass + + +def patch_data( + params: PatchDataParams, soar: SOARClient, asset: Asset +) -> PatchDataOutput: + """Perform a REST PATCH call to the server.""" + logger.info("In action handler for: patch_data") + return make_request( + asset=asset, + soar=soar, + method="PATCH", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + output=PatchDataOutput, + body=None, + ) diff --git a/src/actions/action_post.py b/src/actions/action_post.py new file mode 100644 index 0000000..7e2f8fa --- /dev/null +++ b/src/actions/action_post.py @@ -0,0 +1,45 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient +from soar_sdk.params import Param + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + + +class PostDataOutput(BaseHttpOutput): + pass + + +class PostDataParams(BaseHttpParams): + body: str = Param( + description="POST body (query string, JSON, etc.)", required=False + ) + + +def post_data(params: PostDataParams, soar: SOARClient, asset: Asset) -> PostDataOutput: + """Perform a REST POST call to the server.""" + logger.info("In action handler for: http_post") + return make_request( + asset=asset, + soar=soar, + method="POST", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + body=params.body, + output=PostDataOutput, + ) diff --git a/src/actions/action_put.py b/src/actions/action_put.py new file mode 100644 index 0000000..1cbacad --- /dev/null +++ b/src/actions/action_put.py @@ -0,0 +1,47 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient +from soar_sdk.params import Param + +from ..asset import Asset +from ..common import logger +from ..request_maker import make_request +from ..schemas import BaseHttpOutput, BaseHttpParams + +action_type = "generic" + + +class PutDataOutput(BaseHttpOutput): + pass + + +class PutDataParams(BaseHttpParams): + body: str = Param( + description="PATCH body (query string, JSON, etc.)", required=False + ) + + +def put_data(params: PutDataParams, soar: SOARClient, asset: Asset) -> PutDataOutput: + """Perform a REST PUT call to the server.""" + logger.info("In action handler for: put_data") + return make_request( + asset=asset, + soar=soar, + method="PUT", + location=params.location, + headers=params.headers, + verify=params.verify_certificate, + body=params.body, + output=PutDataOutput, + ) diff --git a/src/actions/get_file.py b/src/actions/get_file.py new file mode 100644 index 0000000..aec3e5f --- /dev/null +++ b/src/actions/get_file.py @@ -0,0 +1,117 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 urllib.parse import quote, unquote_plus + +import requests +import validators +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.exceptions import ActionFailure +from soar_sdk.params import Param, Params + +from ..asset import Asset +from ..common import logger +from ..schemas import GetFileSummary + +VERBOSE = "Provide the file path and file name to download into the vault. For example, /web_storage/file.tgz." + + +class GetFileParams(Params): + hostname: str = Param( + description="Hostname to execute command on", + primary=True, + cef_types=["hostname"], + required=False, + ) + file_path: str = Param( + required=True, + description="Path of the file to download (include filename)", + primary=True, + cef_types=["file path"], + ) + verify_certificate: bool = Param( + description="Verify certificates (if using HTTPS)", + default=False, + required=False, + ) + + +class GetFileOutput(ActionOutput): + vault_id: str + file_name: str + + +def get_file(params: GetFileParams, soar: SOARClient, asset: Asset) -> GetFileOutput: + """Retrieve a file from the endpoint and save it to the vault.""" + logger.info("In action handler for: get_file") + + hostname = params.hostname.strip(" ").strip("/") or asset.base_url + file_path = params.file_path.strip() + encoded_file_path = quote(file_path) + validate_url = f"{hostname}/{encoded_file_path}" + + if not validators.url(validate_url): + raise ActionFailure( + f"Invalid URL constructed based on hostname and file_path: {validate_url}" + ) + full_url = f"{hostname}/{file_path}" + + file_name = unquote_plus(file_path.split("/")[-1]) + + from ..app import get_auth_method + + auth_strategy = get_auth_method(asset, soar) + auth_object, final_headers = auth_strategy.create_auth({}) + + try: + response = requests.get( + full_url, + auth=auth_object, + headers=final_headers, + verify=params.verify_certificate, + timeout=asset.timeout, + ) + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise ActionFailure( + f"Failed to download file from {full_url}. Details: {e}" + ) from e + + try: + file_content = response.content + if not file_content: + raise ActionFailure("Downloaded file is empty.") + + container_id_to_use = soar.get_executing_container_id() + + if not ( + new_vault_id := soar.vault.create_attachment( + file_content=file_content, + file_name=file_name, + container_id=container_id_to_use, + metadata=None, + ) + ): + raise ActionFailure("Failed to add file to vault.") + logger.info(f"Saving file to vault. name: {file_name}") + except Exception as e: + raise ActionFailure( + f"An error occurred while saving the file to the vault. Details: {e}" + ) from e + + logger.info("File successfully retrieved and added to vault") + get_summary = GetFileSummary(vault_id=new_vault_id, name=file_name) + soar.set_summary(get_summary) + soar.set_message(get_summary.get_message()) + return GetFileOutput(vault_id=new_vault_id, file_name=file_name) diff --git a/src/actions/put_file.py b/src/actions/put_file.py new file mode 100644 index 0000000..c7eab64 --- /dev/null +++ b/src/actions/put_file.py @@ -0,0 +1,124 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 urllib.parse import quote + +import requests +import validators +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.exceptions import ActionFailure +from soar_sdk.params import Param, Params +from ..schemas import PutFileSummary +from ..asset import Asset +from ..common import logger + +VERBOSE = "Provide the path to store the file on the file server. For example, /web_storage/test_repo/." + + +class PutFileParams(Params): + host: str = Param( + description="Hostname/IP with port number to execute command on", + primary=True, + cef_types=["host name"], + required=False, + ) + vault_id: str = Param( + description="Vault ID of file", + primary=True, + cef_types=["vault id"], + required=True, + ) + file_destination: str = Param( + description="File destination path (exclude filename)", + primary=True, + cef_types=["file path"], + required=True, + ) + file_name: str = Param( + description="Name of the file to be put on endpoint", required=False + ) + verify_certificate: bool = Param( + description="Verify certificates (if using HTTPS)", + default=False, + required=False, + ) + + +class PutFileOutput(ActionOutput): + file_sent: str + + +def put_file(params: PutFileParams, soar: SOARClient, asset: Asset) -> PutFileOutput: + """Put a file from the vault to another location.""" + logger.info("In action handler for: put_file") + try: + logger.info(f"Fetching phantom vault details for vault_id: {params.vault_id}") + if not (attachments := soar.vault.get_attachment(vault_id=params.vault_id)): + raise ActionFailure( + f"File with vault_id '{params.vault_id}' not found in vault." + ) + vault_attachment = attachments[0] + file_name_to_send = params.file_name or vault_attachment.name + if params.file_name and vault_attachment.name != params.file_name: + logger.warning( + f"Provided file_name '{params.file_name}' does not match the name in vault '{vault_attachment.name}'. Using provided name." + ) + if file_name_to_send in params.file_destination: + raise ActionFailure( + "The filename should be excluded from the 'location' (file destination) parameter." + ) + + base_url = params.host or asset.base_url + + # Handle root directory case + destination_path = params.file_destination.lstrip("/") + if destination_path: + full_url = ( + f"{base_url.rstrip('/')}/{destination_path}/{quote(file_name_to_send)}" + ) + else: + full_url = f"{base_url.rstrip('/')}/{quote(file_name_to_send)}" + + if not validators.url(full_url): + raise ActionFailure(f"Invalid URL constructed: {full_url}") + + with vault_attachment.open("rb") as f: + from ..app import get_auth_method + + auth_strategy = get_auth_method(asset, soar) + auth_object, final_headers = auth_strategy.create_auth({}) + files_payload = {"file": f} + query_params = {"file_path": params.file_destination} + logger.info(f"Uploading file '{file_name_to_send}' to: {full_url}") + response = requests.post( + full_url, + auth=auth_object, + headers=final_headers, + params=query_params, + files=files_payload, + verify=params.verify_certificate, + timeout=asset.timeout, + ) + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise ActionFailure(f"Failed to upload file to {full_url}. Details: {e}") from e + except Exception as e: + raise ActionFailure(f"An unexpected error occurred. Details: {e}") from e + logger.info(f"File successfully uploaded. Server status: {response.status_code}") + put_summary = PutFileSummary(file_sent=full_url) + soar.set_summary(put_summary) + soar.set_message(put_summary.get_message()) + return PutFileOutput( + file_sent=full_url, + ) diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..faed44c --- /dev/null +++ b/src/app.py @@ -0,0 +1,134 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.abstract import SOARClient +from soar_sdk.app import App + +from .actions import ( + action_delete, + action_get, + action_head, + action_options, + action_patch, + action_post, + action_put, + get_file, + put_file, +) +from .asset import Asset +from .common import logger +from .schemas import EmptyOutput, HttpSummary, PutFileSummary, GetFileSummary +from .auth import BasicAuth, TokenAuth, OAuth, NoAuth +from .request_maker import make_request + +app = App( + name="HTTP", + app_type="generic", + logo="logo.svg", + logo_dark="logo_dark.svg", + product_vendor="Generic", + product_name="HTTP", + publisher="Splunk", + appid="290b7499-0374-4930-9cdc-5e9b05d65827", + fips_compliant=False, + asset_cls=Asset, +) + + +@app.test_connectivity() +def test_connectivity(soar: SOARClient, asset: Asset) -> None: + """Validate connection using the configured credentials.""" + + logger.info("In action handler for: test_connectivity") + + make_request( + asset=asset, + soar=soar, + method=asset.test_http_method, + location=asset.test_path if asset.test_path else "", + output=EmptyOutput, + verify=False, + headers=None, + body=None, + ) + + logger.info("Test connectivity passed!") + + +app.register_action( + action_get.get_data, action_type="investigate", summary_type=HttpSummary +) +app.register_action( + action_post.post_data, + action_type="generic", + read_only=False, + summary_type=HttpSummary, +) +app.register_action( + action_put.put_data, + action_type="generic", + read_only=False, + summary_type=HttpSummary, +) +app.register_action( + action_patch.patch_data, + action_type="generic", + read_only=False, + summary_type=HttpSummary, +) +app.register_action( + action_delete.delete_data, + action_type="generic", + read_only=False, + summary_type=HttpSummary, +) +app.register_action( + action_head.get_headers, action_type="investigate", summary_type=HttpSummary +) +app.register_action( + action_options.get_options, action_type="investigate", summary_type=HttpSummary +) +app.register_action( + put_file.put_file, + action_type="generic", + read_only=False, + verbose=put_file.VERBOSE, + summary_type=PutFileSummary, +) +app.register_action( + get_file.get_file, + action_type="investigate", + verbose=get_file.VERBOSE, + summary_type=GetFileSummary, +) + + +def get_auth_method(asset: Asset, soar_client: SOARClient): + """ + Factory function to select and instantiate the appropriate auth strategy. + + Based on the provided asset configuration, this function determines which + authentication method to use (Basic, Token, OAuth, or None) and returns + an instance of the corresponding strategy class. + """ + if asset.username and asset.password: + return BasicAuth(asset) + elif asset.auth_token_name and asset.auth_token: + return TokenAuth(asset) + elif asset.oauth_token_url and asset.client_id: + return OAuth(asset, soar_client, app.actions_manager) + return NoAuth(asset) + + +if __name__ == "__main__": + app.cli() diff --git a/src/asset.py b/src/asset.py new file mode 100644 index 0000000..448dfb3 --- /dev/null +++ b/src/asset.py @@ -0,0 +1,63 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.asset import AssetField, BaseAsset + + +class Asset(BaseAsset): + base_url: str = AssetField( + required=True, + description="Base URL for making queries. (e.g. https://myservice/)", + ) + test_path: str = AssetField( + required=False, + description="Endpoint for test connectivity. (e.g. /some/specific/endpoint , appended to Base URL)", + ) + auth_token_name: str = AssetField( + required=False, + description="Type of authentication token", + default="ph-auth-token", + ) + auth_token: str = AssetField( + required=False, description="Value of authentication token", sensitive=True + ) + username: str = AssetField( + required=False, description="Username (for HTTP basic auth)" + ) + password: str = AssetField( + required=False, description="Password (for HTTP basic auth)", sensitive=True + ) + oauth_token_url: str = AssetField( + required=False, description="URL to fetch oauth token from" + ) + client_id: str = AssetField(required=False, description="Client ID (for OAuth)") + client_secret: str = AssetField( + required=False, description="Client Secret (for OAuth)", sensitive=True + ) + scope: str = AssetField(required=False, description="Scope for OAuth") + timeout: float = AssetField(required=False, description="Timeout for HTTP calls") + test_http_method: str = AssetField( + required=False, + description="HTTP Method for Test Connectivity", + default="GET", + value_list=[ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "OPTIONS", + "TRACE", + "PATCH", + ], + ) diff --git a/src/auth.py b/src/auth.py new file mode 100644 index 0000000..945a0f9 --- /dev/null +++ b/src/auth.py @@ -0,0 +1,171 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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. +import json +from abc import ABC, abstractmethod +from typing import Optional + +import requests +from requests.auth import AuthBase, HTTPBasicAuth +from soar_sdk.exceptions import ActionFailure + +from .common import logger + + +class Authorization(ABC): + """ + Abstract base class for defining an authentication strategy. + + Each strategy must implement the `create_auth` method, which is responsible + for preparing the necessary authentication objects and headers for a request. + """ + + @abstractmethod + def create_auth(self, headers) -> tuple[Optional[AuthBase], dict]: + """ + Prepares authentication components for an HTTP request. + + Args: + headers (dict): The initial dictionary of headers for the request. + + Returns: + Tuple[Optional[AuthBase], dict]: A tuple containing: + - An optional `requests.auth.AuthBase` object. + - The updated headers dictionary. + """ + pass + + +class BasicAuth(Authorization): + """ + Implements HTTP Basic Authentication using username and password. + """ + + def __init__(self, asset): + self.username = asset.username + self.password = asset.password + + def create_auth(self, headers): + logger.info("Using HTTP Basic auth to authenticate") + return (self.username, self.password), headers + + +class TokenAuth(Authorization): + """ + Implements authentication using a static token in a specified header. + """ + + def __init__(self, asset): + self.auth_token_name = asset.auth_token_name + self.auth_token = asset.auth_token + + def create_auth(self, headers): + logger.info("Using provided token to authenticate") + if self.auth_token and self.auth_token_name not in headers: + headers[self.auth_token_name] = self.auth_token + return None, headers + + +class OAuth(Authorization): + """ + Implements OAuth 2.0 Client Credentials Grant Flow. + + This strategy fetches an access token from a token URL, caches it in the + app's authentication state, and adds it to the request as a Bearer token. + """ + + def __init__(self, asset, soar_client, actions_manager): + self.asset = asset + self.soar = soar_client + self.state_key = f"oauth_token_{self.soar.get_asset_id()}" + self.actions_manager = actions_manager + + def _generate_new_token(self): + """ + Fetches a new OAuth access token and saves it to the app's auth_state. + """ + logger.info("Fetching new token") + token_url = self.asset.oauth_token_url + client_id = self.asset.client_id + client_secret = self.asset.client_secret + scope = self.asset.scope + + payload = {"grant_type": "client_credentials"} + if scope: + payload["scope"] = scope + + try: + response = requests.post( + token_url, + auth=HTTPBasicAuth(client_id, client_secret), + data=payload, + timeout=30, + ) + response.raise_for_status() + + access_token = json.loads(response.text).get("access_token") + + except requests.exceptions.RequestException as e: + raise ActionFailure( + f"Error fetching OAuth token from {token_url}. Details: {e}" + ) from e + except json.JSONDecodeError as e: + raise ActionFailure( + "Error parsing response from server while fetching token" + ) from e + + if not access_token: + raise ActionFailure("Access token not found in response body") + + self.actions_manager.auth_state[self.state_key] = access_token + logger.info("Successfully fetched and saved new OAuth token to auth_state.") + return access_token + + def get_token(self, force_new: bool = False) -> str: + """ + Retrieves a token, either from the cached state or by fetching a new one. + + Args: + force_new (bool): If True, forces a new token to be fetched, + ignoring any cached token. + """ + logger.info("Fetching access token") + cached_token = self.actions_manager.auth_state.get(self.state_key) + + if cached_token and not force_new: + logger.info("Using old token") + return cached_token + + return self._generate_new_token() + + def create_auth(self, headers: dict) -> tuple[None, dict]: + logger.info("Using OAuth to authenticate") + + access_token = self.get_token() + + headers["Authorization"] = f"Bearer {access_token}" + + return None, headers + + +class NoAuth(Authorization): + """ + Represents an anonymous request with no authentication. + """ + + def __init__(self, asset): + pass + + def create_auth(self, headers): + logger.info("No authentication method configured. Making an anonymous request.") + return None, headers diff --git a/src/common.py b/src/common.py new file mode 100644 index 0000000..9e007a5 --- /dev/null +++ b/src/common.py @@ -0,0 +1,16 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 soar_sdk.logging import getLogger + +logger = getLogger() diff --git a/src/helpers.py b/src/helpers.py new file mode 100644 index 0000000..1628aa1 --- /dev/null +++ b/src/helpers.py @@ -0,0 +1,130 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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. +import json +from typing import Optional + +import xmltodict +from bs4 import BeautifulSoup +from pydantic import ValidationError +from soar_sdk.exceptions import ActionFailure + +from .common import logger +from .schemas import ParsedResponseBody + + +def process_xml_response(response) -> dict: + try: + return xmltodict.parse(response.text) + except Exception as e: + raise ActionFailure(f"Unable to parse XML response. Error: {e}") from e + + +def process_json_response(response) -> dict: + try: + return ParsedResponseBody(**response.json()) + except json.JSONDecodeError as e: + raise ActionFailure( + f"Server claimed JSON but failed to parse. Error: {e}" + ) from e + except ValidationError as e: + raise ActionFailure( + f"Response JSON did not match expected structure. Details: {e}" + ) from e + + +def process_html_response(response) -> ParsedResponseBody: + try: + soup = BeautifulSoup(response.text, "html.parser") + for element in soup(["script", "style", "footer", "nav"]): + element.extract() + error_text_lines = [x.strip() for x in soup.text.split("\n") if x.strip()] + cleaned_text = "\n".join(error_text_lines) + data_dict = {"cleaned_html_text": cleaned_text} + return ParsedResponseBody(**data_dict) + + except Exception as e: + raise ActionFailure(f"Unable to parse HTML response. Error: {e}") from e + + +def process_empty_response(content_type) -> dict: + message = ( + "Response includes a file" + if "octet-stream" in content_type + else "Empty response body" + ) + return {"message": message} + + +def process_text_response(response) -> ParsedResponseBody: + data_dict = {"raw_text": response.text} + return ParsedResponseBody(**data_dict) + + +RESPONSE_HANDLERS = { + "json": process_json_response, + "javascript": process_json_response, + "xml": process_xml_response, + "html": process_html_response, +} + + +def parse_headers(headers_str: Optional[str]) -> dict: + """ + Parses a JSON string of headers into a dictionary. + Returns an empty dictionary if the input is empty or None. + Raises ActionFailure on parsing or validation errors. + """ + if headers_str is None: + return {} + + try: + parsed_headers = json.loads(headers_str) + + except json.JSONDecodeError as e: + error_message = ( + f"Failed to parse headers. Ensure it's a valid JSON object. Error: {e}" + ) + logger.error(error_message) + raise ActionFailure(error_message) from e + + if not isinstance(parsed_headers, dict): + raise ActionFailure( + "Headers parameter must be a valid JSON object (dictionary)." + ) + + return parsed_headers + + +def handle_various_response(response): + """ + Analyzes the response, routes it to the correct parser, and returns both + the parsed body and a raw text body for the final output. + """ + content_type = response.headers.get("Content-Type", "").lower() + if not response.text.strip() or ("application/octet-stream" in content_type): + return process_empty_response(content_type), "" + + parser = process_text_response + for key, handler in RESPONSE_HANDLERS.items(): + if key in content_type: + parser = handler + break + + parsed_body = parser(response) + + if isinstance(parsed_body, (dict, list)): + raw_body = json.dumps(parsed_body, indent=4) + else: + raw_body = response.text + return parsed_body, raw_body diff --git a/src/request_maker.py b/src/request_maker.py new file mode 100644 index 0000000..e67b66c --- /dev/null +++ b/src/request_maker.py @@ -0,0 +1,110 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 typing import Optional + +import requests +from bs4 import UnicodeDammit +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.exceptions import ActionFailure + +from . import helpers +from .asset import Asset +from .auth import OAuth +from .common import logger +from .schemas import HttpSummary + + +def make_request( + asset: Asset, + soar: SOARClient, + method: str, + location: str, + output: type[ActionOutput], + verify: bool, + headers: Optional[str], + body: Optional[str], +) -> ActionOutput: + """ + The central engine for making all HTTP requests. + + This function handles parameter parsing, URL construction, authentication, + request execution (with OAuth token refresh logic), and response processing. + """ + + logger.info(f"Preparing to make {method} http request.") + parsed_headers = helpers.parse_headers(headers) + + full_url = asset.base_url.rstrip("/") + "/" + location.lstrip("/") + + logger.info(f"Making {method} request to: {full_url}") + + body = ( + UnicodeDammit(body).unicode_markup.encode("utf-8") + if isinstance(body, str) + else body + ) + + retries = 1 + response = None + + while retries >= 0: + from .app import get_auth_method + + auth_method = get_auth_method(asset, soar) + auth_object, final_headers = auth_method.create_auth(parsed_headers) + + try: + response = requests.request( + method=method, + url=full_url, + auth=auth_object, + data=body, + verify=verify, + headers=final_headers, + timeout=asset.timeout, + ) + response.raise_for_status() + + break + + except requests.exceptions.RequestException as e: + if isinstance(auth_method, OAuth) and retries > 0: + logger.warning( + "Request failed with 401, token might be expired. Forcing a refresh." + ) + auth_method.get_token(force_new=True) + retries -= 1 + continue + else: + raise ActionFailure( + f"Request failed for {full_url}. Details: {e}" + ) from e + + parsed_body, raw_body = helpers.handle_various_response(response) + logger.info(f"Successfully processed data. Status: {response.status_code}") + summary = HttpSummary( + status_code=response.status_code, + reason="Request successful" if response.status_code < 300 else response.reason, + ) + soar.set_summary(summary) + + return output( + status_code=response.status_code, + location=full_url, + method=method, + parsed_response_body=parsed_body, + response_body=raw_body, + response_headers=str(dict(response.headers)), + ) diff --git a/src/schemas.py b/src/schemas.py new file mode 100644 index 0000000..44b7902 --- /dev/null +++ b/src/schemas.py @@ -0,0 +1,101 @@ +# Copyright (c) 2025 Splunk Inc. +# +# 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 pydantic import Extra +from soar_sdk.action_results import ActionOutput, OutputField +from soar_sdk.params import Param, Params +from typing import Optional + + +class EmptyOutput(ActionOutput): + """Empty output for test_connectivity.""" + + pass + + +class ParsedResponseBody(ActionOutput): + """A flexible model for any JSON response.""" + + class Config: + extra = Extra.allow + + +class BaseHttpOutput(ActionOutput): + """Base class for all standard action outputs.""" + + location: str = OutputField( + cef_types=["url"], example_values=["http://192.168.1.26/rest/assets"] + ) + method: str = OutputField(example_values=["POST"]) + parsed_response_body: ParsedResponseBody = OutputField( + example_values=['{"failed": true, "message": "Requested item not found"}'] + ) + response_body: str = OutputField( + example_values=['{"failed": true, "message": "Requested item not found"}'] + ) + response_headers: str + + def generate_action_summary_message(self) -> str: + return f"Status code: {self.status_code}" + + +class BaseHttpParams(Params): + """Base class for all standard action parameters.""" + + location: str = Param( + description="Location (e.g. path/to/endpoint)", + primary=True, + cef_types=["endpoint"], + required=True, + ) + verify_certificate: bool = Param( + description="Verify certificates (if using HTTPS)", + default=False, + required=False, + ) + headers: str = Param( + description="Additional headers (JSON object with headers)", required=False + ) + + +class HttpSummary(ActionOutput): + """Summary for HTTP actions.""" + + status_code: int = OutputField(example_values=[200]) + reason: str = OutputField(example_values=["Request successful"]) + + def get_message(self) -> str: + return f"Status code: {self.status_code}, Reason: {self.reason}" + + +class PutFileSummary(ActionOutput): + """Summary for Put File action.""" + + file_sent: str = OutputField( + example_values=["http://192.168.1.26/web_storage/test_repo/test.txt"] + ) + + def get_message(self) -> str: + return f"File sent: {self.file_sent}" + + +class GetFileSummary(ActionOutput): + """Summary for Get File action.""" + + vault_id: str = OutputField(example_values=["1234567890"]) + name: str = OutputField(example_values=["test.txt"]) + exit_status: int = 0 + size: Optional[int] = OutputField(example_values=[100]) + + def get_message(self) -> str: + return f"Vault ID: {self.vault_id}, Name: {self.name}, Exit Status: {self.exit_status}" diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..01cd81c --- /dev/null +++ b/uv.lock @@ -0,0 +1,1003 @@ +version = 1 +revision = 3 +requires-python = ">=3.9, <3.14" +resolution-markers = [ + "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", +] +supported-markers = [ + "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", +] +required-markers = [ + "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'", +] + +[[package]] +name = "anyio" +version = "4.10.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "idna", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "sniffio", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.10.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "soupsieve", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/69/daeee6d8f22c997e522cdbeb59641c4d31ab120aba0f2c799500f7456b7e/beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891", size = 399890, upload-time = "2021-09-08T00:16:07.708Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/bf/f0f194d3379d3f3347478bd267f754fc68c11cbf2fe302a6ab69447b1417/beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf", size = 97398, upload-time = "2021-09-08T00:16:06.464Z" }, +] + +[[package]] +name = "bleach" +version = "6.2.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "webencodings", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083, upload-time = "2024-10-29T18:30:40.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406, upload-time = "2024-10-29T18:30:38.186Z" }, +] + +[[package]] +name = "build" +version = "1.3.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyproject-hooks", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tomli", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/1c/23e33405a7c9eac261dff640926b8b5adaed6a6eb3e1767d441ed611d0c0/build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", size = 48544, upload-time = "2025-08-01T21:27:09.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4", size = 23382, upload-time = "2025-08-01T21:27:07.844Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, + { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, + { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, + { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, + { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, + { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, + { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, + { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, + { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, + { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, + { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", size = 207520, upload-time = "2025-08-09T07:57:11.026Z" }, + { url = "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", size = 147307, upload-time = "2025-08-09T07:57:12.4Z" }, + { url = "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", size = 160448, upload-time = "2025-08-09T07:57:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/04/9a/914d294daa4809c57667b77470533e65def9c0be1ef8b4c1183a99170e9d/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", size = 157758, upload-time = "2025-08-09T07:57:14.979Z" }, + { url = "https://files.pythonhosted.org/packages/b0/a8/6f5bcf1bcf63cb45625f7c5cadca026121ff8a6c8a3256d8d8cd59302663/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", size = 152487, upload-time = "2025-08-09T07:57:16.332Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/d3d0e9592f4e504f9dea08b8db270821c909558c353dc3b457ed2509f2fb/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", size = 150054, upload-time = "2025-08-09T07:57:17.576Z" }, + { url = "https://files.pythonhosted.org/packages/20/30/5f64fe3981677fe63fa987b80e6c01042eb5ff653ff7cec1b7bd9268e54e/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", size = 161703, upload-time = "2025-08-09T07:57:20.012Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ef/dd08b2cac9284fd59e70f7d97382c33a3d0a926e45b15fc21b3308324ffd/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", size = 159096, upload-time = "2025-08-09T07:57:21.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/8c/dcef87cfc2b3f002a6478f38906f9040302c68aebe21468090e39cde1445/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", size = 153852, upload-time = "2025-08-09T07:57:22.608Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.python.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 = "coverage" +version = "7.10.6" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025, upload-time = "2025-08-29T15:32:57.169Z" }, + { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419, upload-time = "2025-08-29T15:32:59.908Z" }, + { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992, upload-time = "2025-08-29T15:33:03.239Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851, upload-time = "2025-08-29T15:33:04.603Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891, upload-time = "2025-08-29T15:33:06.176Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786, upload-time = "2025-08-29T15:33:08.965Z" }, + { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, + { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, + { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, + { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, + { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/91/70/f73ad83b1d2fd2d5825ac58c8f551193433a7deaf9b0d00a8b69ef61cd9a/coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352", size = 217009, upload-time = "2025-08-29T15:34:57.381Z" }, + { url = "https://files.pythonhosted.org/packages/01/e8/099b55cd48922abbd4b01ddd9ffa352408614413ebfc965501e981aced6b/coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612", size = 217400, upload-time = "2025-08-29T15:34:58.985Z" }, + { url = "https://files.pythonhosted.org/packages/01/f9/82c6c061838afbd2172e773156c0aa84a901d59211b4975a4e93accf5c89/coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144", size = 245658, upload-time = "2025-08-29T15:35:02.135Z" }, + { url = "https://files.pythonhosted.org/packages/81/6a/35674445b1d38161148558a3ff51b0aa7f0b54b1def3abe3fbd34efe05bc/coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b", size = 247433, upload-time = "2025-08-29T15:35:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/98c99e7cafb288730a93535092eb433b5503d529869791681c4f2e2012a8/coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862", size = 245315, upload-time = "2025-08-29T15:35:05.629Z" }, + { url = "https://files.pythonhosted.org/packages/67/52/d57a42502aef05c6325f28e2e81216c2d9b489040132c18725b7a04d1448/coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78", size = 244343, upload-time = "2025-08-29T15:35:09.55Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "distro" +version = "1.8.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/89/eaa3a3587ebf8bed93e45aa79be8c2af77d50790d15b53f6dfc85b57f398/distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8", size = 59428, upload-time = "2022-10-10T15:30:33.395Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/2c/c90a3adaf0ddb70afe193f5ebfb539612af57cffe677c3126be533df3098/distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff", size = 20315, upload-time = "2022-10-10T15:30:26.903Z" }, +] + +[[package]] +name = "docopt" +version = "0.6.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "http" +version = "4.0.0" +source = { virtual = "." } +dependencies = [ + { name = "splunk-soar-sdk", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "validators", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "xmltodict", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "mypy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pre-commit", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest-mock", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest-watch", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "ruff", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[package.metadata] +requires-dist = [ + { name = "splunk-soar-sdk", specifier = ">=2.3.3" }, + { name = "validators" }, + { name = "xmltodict" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7.6.7,<8" }, + { name = "mypy", specifier = ">=1.2.0,<2" }, + { name = "pre-commit", specifier = ">=4.2.0,<5" }, + { name = "pytest", specifier = ">=7.4.2,<8" }, + { name = "pytest-mock", specifier = ">=3.14.0,<4" }, + { name = "pytest-watch", specifier = ">=4.2.0,<5" }, + { name = "ruff", specifier = ">=0.11.6,<1" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "certifi", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "h11", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "anyio", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "certifi", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpcore", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "idna", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "humanize" +version = "4.13.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/1d/3062fcc89ee05a715c0b9bfe6490c00c576314f27ffee3a704122c6fd259/humanize-4.13.0.tar.gz", hash = "sha256:78f79e68f76f0b04d711c4e55d32bebef5be387148862cb1ef83d2b58e7935a0", size = 81884, upload-time = "2025-08-25T09:39:20.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/c7/316e7ca04d26695ef0635dc81683d628350810eb8e9b2299fc08ba49f366/humanize-4.13.0-py3-none-any.whl", hash = "sha256:b810820b31891813b1673e8fec7f1ed3312061eab2f26e3fa192c393d11ed25f", size = 128869, upload-time = "2025-08-25T09:39:18.54Z" }, +] + +[[package]] +name = "identify" +version = "2.6.14" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/c4/62963f25a678f6a050fb0505a65e9e726996171e6dbe1547f79619eefb15/identify-2.6.14.tar.gz", hash = "sha256:663494103b4f717cb26921c52f8751363dc89db64364cd836a9bf1535f53cd6a", size = 99283, upload-time = "2025-09-06T19:30:52.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/ae/2ad30f4652712c82f1c23423d79136fbce338932ad166d70c1efb86a5998/identify-2.6.14-py2.py3-none-any.whl", hash = "sha256:11a073da82212c6646b1f39bb20d4483bfb9543bd5566fec60053c4bb309bf2e", size = 99172, upload-time = "2025-09-06T19:30:51.759Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "zipp", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "mdurl", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mypy" +version = "1.17.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "mypy-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pathspec", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tomli", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" }, + { url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" }, + { url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.python.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 = "pre-commit" +version = "4.3.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cfgv", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "identify", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nodeenv", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyyaml", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "virtualenv", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, +] + +[[package]] +name = "pydantic" +version = "1.10.22" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/57/5996c63f0deec09e9e901a2b838247c97c6844999562eac4e435bcb83938/pydantic-1.10.22.tar.gz", hash = "sha256:ee1006cebd43a8e7158fb7190bb8f4e2da9649719bff65d0c287282ec38dec6d", size = 356771, upload-time = "2025-04-24T13:38:43.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/92/91eb5c75a1460292e1f2f3e577122574ebb942fbac19ad2369ff00b9eb24/pydantic-1.10.22-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:57889565ccc1e5b7b73343329bbe6198ebc472e3ee874af2fa1865cfe7048228", size = 2852481, upload-time = "2025-04-24T13:36:55.045Z" }, + { url = "https://files.pythonhosted.org/packages/08/f3/dd54b49fc5caaed06f5a0d0a5ec35a81cf722cd6b42455f408dad1ef3f7d/pydantic-1.10.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90729e22426de79bc6a3526b4c45ec4400caf0d4f10d7181ba7f12c01bb3897d", size = 2585586, upload-time = "2025-04-24T13:36:58.453Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9b/48d10180cc614ffb66da486e99bc1f8b639fb44edf322864f2fb161e2351/pydantic-1.10.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8684d347f351554ec94fdcb507983d3116dc4577fb8799fed63c65869a2d10", size = 3336974, upload-time = "2025-04-24T13:37:00.652Z" }, + { url = "https://files.pythonhosted.org/packages/38/c5/c776d03ec374f22860802b2cee057b41e866be3c80826b53d4c001692db3/pydantic-1.10.22-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4148232aded8dd1dd13cf910a01b32a763c34bd79a0ab4d1ee66164fcb0b7b9d", size = 3485878, upload-time = "2025-04-24T13:37:06.102Z" }, + { url = "https://files.pythonhosted.org/packages/42/03/e435ed85a9abda29e3fbdb49c572fe4131a68c6daf3855a01eebda9e1b27/pydantic-1.10.22-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e530a8da353f791ad89e701c35787418605d35085f4bdda51b416946070e938", size = 2845682, upload-time = "2025-04-24T13:37:10.142Z" }, + { url = "https://files.pythonhosted.org/packages/72/ea/4a625035672f6c06d3f1c7e33aa0af6bf1929991e27017e98b9c2064ae0b/pydantic-1.10.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:654322b85642e9439d7de4c83cb4084ddd513df7ff8706005dada43b34544946", size = 2553286, upload-time = "2025-04-24T13:37:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f0/424ad837746e69e9f061ba9be68c2a97aef7376d1911692904d8efbcd322/pydantic-1.10.22-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8bece75bd1b9fc1c32b57a32831517943b1159ba18b4ba32c0d431d76a120ae", size = 3141232, upload-time = "2025-04-24T13:37:14.394Z" }, + { url = "https://files.pythonhosted.org/packages/92/35/dffc1b29cb7198aadab68d75447191e59bdbc1f1d2d51826c9a4460d372f/pydantic-1.10.22-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffe02767d27c39af9ca7dc7cd479c00dda6346bb62ffc89e306f665108317a2", size = 3244258, upload-time = "2025-04-24T13:37:20.929Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a3/ec66239ed7c9e90edfb85b23b6b18eb290ed7aa05f54837cdcb6a14faa98/pydantic-1.10.22-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:92d0f97828a075a71d9efc65cf75db5f149b4d79a38c89648a63d2932894d8c9", size = 2794865, upload-time = "2025-04-24T13:37:25.087Z" }, + { url = "https://files.pythonhosted.org/packages/49/6a/99cf3fee612d93210c85f45a161e98c1c5b45b6dcadb21c9f1f838fa9e28/pydantic-1.10.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af5a2811b6b95b58b829aeac5996d465a5f0c7ed84bd871d603cf8646edf6ff", size = 2534212, upload-time = "2025-04-24T13:37:26.848Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e6/0f8882775cd9a60b221103ee7d6a89e10eb5a892d877c398df0da7140704/pydantic-1.10.22-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cf06d8d40993e79af0ab2102ef5da77b9ddba51248e4cb27f9f3f591fbb096e", size = 2994027, upload-time = "2025-04-24T13:37:28.683Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6e/b64deccb8a7304d584088972437ea3091e9d99d27a8e7bf2bd08e29ae84e/pydantic-1.10.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:82d9a3da1686443fb854c8d2ab9a473251f8f4cdd11b125522efb4d7c646e7bc", size = 3123560, upload-time = "2025-04-24T13:37:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/47/8f/67befe3607b342dd6eb80237134ebcc6e8db42138609306eaf2b30e1f273/pydantic-1.10.22-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b259dc89c9abcd24bf42f31951fb46c62e904ccf4316393f317abeeecda39978", size = 2797042, upload-time = "2025-04-24T13:37:38.753Z" }, + { url = "https://files.pythonhosted.org/packages/aa/91/bfde7d301f8e1c4cff949b3f1eb2c9b27bdd4b2368da0fe88e7350bbe4bc/pydantic-1.10.22-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9238aa0964d80c0908d2f385e981add58faead4412ca80ef0fa352094c24e46d", size = 2538572, upload-time = "2025-04-24T13:37:41.653Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/1b0097ece420354df77d2f01c72278fb43770c8ed732d6b7a303c0c70875/pydantic-1.10.22-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8029f05b04080e3f1a550575a1bca747c0ea4be48e2d551473d47fd768fc1b", size = 2986271, upload-time = "2025-04-24T13:37:43.551Z" }, + { url = "https://files.pythonhosted.org/packages/1a/47/2d55ec452c9a87347234bbbc70df268e1f081154b1851f0db89638558a1c/pydantic-1.10.22-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:815f0a73d5688d6dd0796a7edb9eca7071bfef961a7b33f91e618822ae7345b7", size = 3117572, upload-time = "2025-04-24T13:37:49.339Z" }, + { url = "https://files.pythonhosted.org/packages/01/6f/9658e94018bc7c4e71863fb0f1ea8d30f8b3439e17df7aa710b2bb72dbca/pydantic-1.10.22-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec54c89b2568b258bb30d7348ac4d82bec1b58b377fb56a00441e2ac66b24587", size = 2854460, upload-time = "2025-04-24T13:38:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b3/5184ec7d3423a37c193ffaeced03921f4c34d226f3c6852653784f37d38b/pydantic-1.10.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8f1d1a1532e4f3bcab4e34e8d2197a7def4b67072acd26cfa60e92d75803a48", size = 2587418, upload-time = "2025-04-24T13:38:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/25/27d769c5dc7491df5faebfc49a26f83ca2e070a9a788c67fde4c4e51d68b/pydantic-1.10.22-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ad83ca35508c27eae1005b6b61f369f78aae6d27ead2135ec156a2599910121", size = 3331289, upload-time = "2025-04-24T13:38:29.262Z" }, + { url = "https://files.pythonhosted.org/packages/e9/87/eb3408e1c040a6d9f703e089d26a723d6c41f23a192e86bd7584d037d576/pydantic-1.10.22-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8aee040e25843f036192b1a1af62117504a209a043aa8db12e190bb86ad7e611", size = 3483434, upload-time = "2025-04-24T13:38:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e0/1ed151a56869be1588ad2d8cda9f8c1d95b16f74f09a7cea879ca9b63a8b/pydantic-1.10.22-py3-none-any.whl", hash = "sha256:343037d608bcbd34df937ac259708bfc83664dadf88afe8516c4f282d7d471a9", size = 166503, upload-time = "2025-04-24T13:38:41.374Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.python.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 = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "pytest" +version = "7.4.4" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "iniconfig", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pluggy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tomli", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "pytest", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf", size = 33838, upload-time = "2025-09-04T20:57:48.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f", size = 10050, upload-time = "2025-09-04T20:57:47.274Z" }, +] + +[[package]] +name = "pytest-watch" +version = "4.2.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "colorama", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "docopt", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "watchdog", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/47/ab65fc1d682befc318c439940f81a0de1026048479f732e84fe714cd69c0/pytest-watch-4.2.0.tar.gz", hash = "sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9", size = 16340, upload-time = "2018-05-20T19:52:16.194Z" } + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "certifi", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "charset-normalizer", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "idna", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "urllib3", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "responses" +version = "0.22.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "requests", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "toml", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "types-toml", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "urllib3", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/cc/c15e28fdbc81909ac18a4c22bebe8517bbafef43947a072da6f042fbd4d5/responses-0.22.0.tar.gz", hash = "sha256:396acb2a13d25297789a5866b4881cf4e46ffd49cc26c43ab1117f40b973102e", size = 71785, upload-time = "2022-10-11T16:34:46.926Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/d0/2b9030eedf7061ac60828fcd92a4b04dc9a7dd07f316300f2841c41421a0/responses-0.22.0-py3-none-any.whl", hash = "sha256:dcf294d204d14c436fddcc74caefdbc5764795a40ff4e6a7740ed8ddbf3294be", size = 51148, upload-time = "2022-10-11T16:34:46.926Z" }, +] + +[[package]] +name = "rich" +version = "14.1.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "markdown-it-py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pygments", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, +] + +[[package]] +name = "ruff" +version = "0.12.12" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, + { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, + { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, + { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, + { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, + { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.8" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, +] + +[[package]] +name = "splunk-soar-sdk" +version = "2.3.3" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "beautifulsoup4", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "bleach", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "build", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "click", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "distro", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpx", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "humanize", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "jinja2", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pydantic", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "requests", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "responses", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "setuptools", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "toml", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tqdm", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typer", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/ac/da69ddd1bd22bf876f43017a765b15c17143b986857a395679aaa82b1655/splunk_soar_sdk-2.3.3.tar.gz", hash = "sha256:dfbb79daf7aac10350a8d147ecb502123ff5bac2ef67938ab3aef4d1a8ce4cef", size = 522970, upload-time = "2025-10-10T19:19:52.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/ba/8e96ea2bb231336b5c9de906001fbf7d727f3973254a7da4854d42ba9f84/splunk_soar_sdk-2.3.3-py3-none-any.whl", hash = "sha256:0f07ea57af5ed513e4c22c28d5e9af8140cb945677f0964958c1c584b09d6774", size = 159258, upload-time = "2025-10-10T19:19:51.585Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "typer" +version = "0.17.4" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "click", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "rich", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "shellingham", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580", size = 103734, upload-time = "2025-09-05T18:14:40.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824", size = 46643, upload-time = "2025-09-05T18:14:39.166Z" }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392, upload-time = "2024-03-10T02:18:37.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777, upload-time = "2024-03-10T02:18:36.568Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.python.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 = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "validators" +version = "0.35.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399, upload-time = "2025-05-01T05:42:06.7Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712, upload-time = "2025-05-01T05:42:04.203Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.34.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "distlib", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "filelock", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "platformdirs", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390, upload-time = "2024-11-01T14:06:49.325Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386, upload-time = "2024-11-01T14:06:50.536Z" }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017, upload-time = "2024-11-01T14:06:51.717Z" }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903, upload-time = "2024-11-01T14:06:57.052Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381, upload-time = "2024-11-01T14:06:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, +] + +[[package]] +name = "xmltodict" +version = "0.15.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/ee/b30fdb281b39da57053bd7012870989de6f066d6ef1476d78de8fc427324/xmltodict-0.15.0.tar.gz", hash = "sha256:c6d46b4e3413d1e4fc3e5016f0f1c7a5c10f8ce39efaa0cb099af986ecfc9a53", size = 60285, upload-time = "2025-09-05T00:35:45.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/56/507a207b96e3aa7365c28bb6702011e7c76c899c1737966b25852eaef3e8/xmltodict-0.15.0-py2.py3-none-any.whl", hash = "sha256:8887783bf1faba1754fc45fdf3fe03fbb3629c811ae57f91c018aace4c58d4ed", size = 10965, upload-time = "2025-09-05T00:35:44.583Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] diff --git a/wheels/py3/validators-0.35.0-py3-none-any.whl b/wheels/py3/validators-0.35.0-py3-none-any.whl deleted file mode 100644 index 92db5ce..0000000 Binary files a/wheels/py3/validators-0.35.0-py3-none-any.whl and /dev/null differ