pax_global_header00006660000000000000000000000064152443323230014513gustar00rootroot0000000000000052 comment=7a57a8955af8b3c2f6cf28a6497511c414472056 censys-censys-python-7a57a89/000077500000000000000000000000001524433232300162075ustar00rootroot00000000000000censys-censys-python-7a57a89/.coveragerc000066400000000000000000000000731524433232300203300ustar00rootroot00000000000000[run] include = censys/* [report] show_missing = true censys-censys-python-7a57a89/.github/000077500000000000000000000000001524433232300175475ustar00rootroot00000000000000censys-censys-python-7a57a89/.github/CODE_OF_CONDUCT.md000066400000000000000000000125341524433232300223530ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at support@censys.io. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][mozilla coc]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][faq]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [mozilla coc]: https://github.com/mozilla/diversity [faq]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations censys-censys-python-7a57a89/.github/CONTRIBUTING.md000066400000000000000000000017311524433232300220020ustar00rootroot00000000000000# Contributing ## Working on your first Pull Request? You can learn how from this _free_ series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) ## Commit Messages Please use the following format for your commits: ```commit : is one of: - feat: A new feature - fix: A bug fix - docs: Documentation only changes - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) - refactor: A code change that neither fixes a bug nor adds a feature - perf: A code change that improves performance - test: Adding missing tests or correcting existing tests - chore: Changes to the build process or auxiliary files - revert: Revert to a commit - WIP: Work in progress is a short, imperative tense description of the change ``` [Full details](https://conventionalcommits.org) censys-censys-python-7a57a89/.github/ISSUE_TEMPLATE/000077500000000000000000000000001524433232300217325ustar00rootroot00000000000000censys-censys-python-7a57a89/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000011561524433232300244270ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "" labels: "type: bug" assignees: "" --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Run '...' 2. Then '....' **Expected behavior** A clear and concise description of what you expected to happen. **Device (please complete the following information):** - OS [e.g. debian, macOS]: - Censys Python Version [e.g. 2.2.1]: - Shell [e.g. bash, zsh]: **Additional context** Add any other context about the problem here. censys-censys-python-7a57a89/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011471524433232300254620ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "" labels: "type: feature request" assignees: "" --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. censys-censys-python-7a57a89/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000003201524433232300241160ustar00rootroot00000000000000--- name: Question about: Ask a question about this project title: "" labels: "type: question" assignees: "" --- **What is your question?** A clear and concise question. Ex. What can I use \_\_\_ for? [...] censys-censys-python-7a57a89/.github/PULL_REQUEST_TEMPLATE/000077500000000000000000000000001524433232300230265ustar00rootroot00000000000000censys-censys-python-7a57a89/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md000066400000000000000000000012431524433232300277670ustar00rootroot00000000000000# Description # Changes # Checklist - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes censys-censys-python-7a57a89/.github/dependabot.yml000066400000000000000000000011701524433232300223760ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" time: "08:00" timezone: "US/Michigan" labels: - "type: dependencies" reviewers: - "censys/dev-oss" censys-censys-python-7a57a89/.github/stale.yml000066400000000000000000000013531524433232300214040ustar00rootroot00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security - "priority: critical" - "type: feature request" # Label to use when marking an issue as stale staleLabel: "type: stale" # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: true censys-censys-python-7a57a89/.github/workflows/000077500000000000000000000000001524433232300216045ustar00rootroot00000000000000censys-censys-python-7a57a89/.github/workflows/pypi.yml000066400000000000000000000007021524433232300233070ustar00rootroot00000000000000name: Upload Python Package on: workflow_dispatch: push: tags: - "*" jobs: deploy: runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v6 with: enable-cache: true - name: Build run: uv build - name: Publish run: uv publish --token ${{ secrets.PYPI_TOKEN }} censys-censys-python-7a57a89/.github/workflows/python-ci.yml000066400000000000000000000017241524433232300242450ustar00rootroot00000000000000name: Python CI on: push: branches: - main paths-ignore: - "**.md" pull_request: branches: - main jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest] python: ["3.9", "3.13"] runs-on: ${{ matrix.os }} timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v6 with: enable-cache: true python-version: ${{ matrix.python }} - name: Install dependencies run: uv sync --group dev - name: Lint with ruff if: startsWith(matrix.os, 'ubuntu') run: | uv run ruff check . uv run ruff format --check . - name: Type checking with mypy if: startsWith(matrix.os, 'ubuntu') run: | uv run mypy censys - name: Test with pytest run: | uv run pytest --cov --cov-fail-under=100 censys-censys-python-7a57a89/.github/workflows/stale.yml000066400000000000000000000013041524433232300234350ustar00rootroot00000000000000# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. # # You can adjust the behavior by modifying this file. # For more information, see: # https://github.com/actions/stale name: Mark stale issues and pull requests on: workflow_dispatch: schedule: - cron: "30 12 * * *" jobs: stale: runs-on: ubuntu-latest timeout-minutes: 5 permissions: issues: write pull-requests: write steps: - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 60 days-before-close: 7 stale-issue-label: "stale" stale-pr-label: "stale" censys-censys-python-7a57a89/.gitignore000066400000000000000000000023721524433232300202030ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # 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 # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Sphinx documentation docs/_build/ # IPython profile_default/ ipython_config.py # 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 # pyenv .python-version # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # mypy .mypy_cache/ .dmypy.json dmypy.json # IDE .vscode/ .idea/ # Config *.cfg # Scripts scripts/*.py # Output censys-query-output.* censys-view-* *.json *.csv *.txt *.xml # Autocomplete !censys/cli/data/*_autocomplete.json censys-censys-python-7a57a89/.pre-commit-config.yaml000066400000000000000000000022441524433232300224720ustar00rootroot00000000000000# Install pre-commit hook # pre-commit install # Apply to all files without committing: # pre-commit run --all-files # Update this file: # pre-commit autoupdate repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-case-conflict - id: check-toml - id: check-yaml - id: detect-private-key - id: end-of-file-fixer - id: mixed-line-ending - id: trailing-whitespace - repo: local hooks: - id: system name: ruff entry: uv run ruff check . --fix pass_filenames: false language: system types: [python] require_serial: true - id: system name: ruff-format entry: uv run ruff format . pass_filenames: false language: system types: [python] - id: system name: pytest entry: uv run pytest --cov --cov-fail-under=100 pass_filenames: false language: system types: [python] - id: system name: mypy entry: uv run mypy censys pass_filenames: false language: system types: [python] require_serial: true censys-censys-python-7a57a89/.readthedocs.yml000066400000000000000000000012641524433232300213000ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: builder: html configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF formats: - pdf build: os: ubuntu-24.04 tools: python: "3.13" python: install: - method: uv command: sync groups: - docs # Set a custom search rank over pages matching a pattern search: ranking: censys.asm.html: 5 usage-asm.html: 5 censys.search.v2.html: 5 usage-v2.html: 5 censys.search.v1.html: -1 usage-v1.html: -1 censys-censys-python-7a57a89/LICENSE000066400000000000000000000236761524433232300172320ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS censys-censys-python-7a57a89/README.md000066400000000000000000000121521524433232300174670ustar00rootroot00000000000000# Censys Python Library [![PyPI](https://img.shields.io/pypi/v/censys?color=orange&logo=pypi&logoColor=orange)](https://pypi.org/project/censys/) [![Python Version](https://img.shields.io/badge/python-3.9%2B-blue?logo=python)](https://www.python.org/downloads/) [![Read the Docs (version)](https://img.shields.io/readthedocs/censys-python/latest?logo=read%20the%20docs)](https://censys-python.readthedocs.io/en/stable/?badge=stable) [![GitHub Discussions](https://img.shields.io/badge/GitHub-Discussions-brightgreen?logo=github)](https://github.com/censys/censys-python/discussions) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-organge.svg?logo=git&logoColor=organge)](http://makeapullrequest.com) [![License](https://img.shields.io/github/license/censys/censys-python?logo=apache)](https://github.com/censys/censys-python/blob/main/LICENSE) > [!WARNING] > **DEPRECATION NOTICE**: The Censys Search v1 and v2 APIs will be deprecated soon. Please migrate to the new **Censys Platform**. > > - 📖 **New API Documentation**: [https://docs.censys.com/reference/get-started#/](https://docs.censys.com/reference/get-started#/) > - 📦 **New Python SDK**: Install with `pip install censys-platform` - [https://pypi.org/project/censys-platform/](https://pypi.org/project/censys-platform/) > - 🖥️ **New CLI Tool** (`cencli`): [https://docs.censys.com/docs/platform-cli](https://docs.censys.com/docs/platform-cli) > - ⚠️ **If you plan to use the new CLI, you will need to uninstall this package's CLI first** as both use the `censys` command name > > The **Censys ASM APIs in this library are NOT deprecated** and will continue to be supported. An easy-to-use and lightweight API wrapper for Censys APIs ([censys.io](https://censys.io/)). Python 3.9+ is currently supported. This library has been tested on Python 3.9 and 3.x (Currently version 3.13). ## Features - [Search Censys data](https://censys-python.readthedocs.io/en/stable/usage-v2.html) - [Bulk Certificate lookups](https://censys-python.readthedocs.io/en/stable/usage-v2.html#bulk-view) - [Download Bulk Data](https://censys-python.readthedocs.io/en/stable/usage-v1.html#data) - [Manage assets, events, and seeds in Censys ASM](https://censys-python.readthedocs.io/en/stable/usage-asm.html) - [Command-line interface](https://censys-python.readthedocs.io/en/stable/cli.html) ## Getting Started The library can be installed using `pip`. ```sh pip install censys ``` To upgraded using `pip`. ```sh pip install --upgrade censys ``` Alternatively, you can install the library using `uv`. ```sh git clone https://github.com/censys/censys-python.git cd censys-python/ uv install ``` Optionally, you can enable tab completion for the CLI by adding this line to your `~/.bashrc`, `~/.zshrc`, or equivalent. > Please note that autocomplete is supported for field names in the `search` command. ```sh eval "$(register-python-argcomplete censys)" ``` To configure your search credentials run `censys config` or set both `CENSYS_API_ID` and `CENSYS_API_SECRET` environment variables. ```sh $ censys config Censys API ID: XXX Censys API Secret: XXX Do you want color output? [y/n]: y Successfully authenticated for your@email.com ``` If you have a Censys ASM account, you can configure your ASM credentials by running `censys asm config` or set both `CENSYS_ASM_API_KEY` environment variables. ```sh $ censys asm config Censys ASM API Key: XXX Do you want color output? [y/n]: y Successfully authenticated ``` ## Examples The examples located in the [`examples/`](examples/) directory are a great place to start. You can also find more examples in the [usage documentation](https://censys-python.readthedocs.io/en/stable/usage-v2.html). ## [Documentation](https://censys-python.readthedocs.io/) [![Read the Docs](https://raw.githubusercontent.com/censys/censys-python/main/docs/_static/readthedocs.png)](https://censys-python.readthedocs.io/) ## Resources - [Source](https://github.com/censys/censys-python) - [Issue Tracker](https://github.com/censys/censys-python/issues) - [Changelog](https://github.com/censys/censys-python/releases) - [Documentation](https://censys-python.rtfd.io) - [Discussions](https://github.com/censys/censys-python/discussions) - [Censys Homepage](https://censys.com/) - [Censys Search](https://search.censys.io/) ## Contributing All contributions (no matter how small) are always welcome. See [Contributing to Censys Python](.github/CONTRIBUTING.md) ## Development This project uses [uv](https://docs.astral.sh/uv/) for dependency management. Please ensure you have [installed the latest version](https://docs.astral.sh/uv/getting-started/installation/). ```sh git clone git@github.com:censys/censys-python.git cd censys-python/ uv install ``` ## Testing ```sh # Run tests uv run pytest # With coverage report uv run pytest --cov-report html ``` ## License This software is licensed under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - Copyright (C) 2026 Censys, Inc. censys-censys-python-7a57a89/censys/000077500000000000000000000000001524433232300175135ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/__main__.py000066400000000000000000000001341524433232300216030ustar00rootroot00000000000000"""Main function for when executed with python -m censys.""" from .cli import main main() censys-censys-python-7a57a89/censys/asm/000077500000000000000000000000001524433232300202735ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/asm/__init__.py000066400000000000000000000015161524433232300224070ustar00rootroot00000000000000"""An easy-to-use and lightweight API wrapper for Censys ASM (app.censys.io).""" from .assets import ( Assets, CertificatesAssets, DomainsAssets, HostsAssets, ObjectStoragesAssets, SubdomainsAssets, WebEntitiesAssets, ) from .beta import Beta from .client import AsmClient from .clouds import Clouds from .inventory import InventorySearch from .logbook import Events, Logbook from .risks import Risks from .saved_queries import SavedQueries from .seeds import Seeds __all__ = [ "AsmClient", "Assets", "Beta", "CertificatesAssets", "Clouds", "DomainsAssets", "Events", "HostsAssets", "InventorySearch", "Logbook", "Risks", "SavedQueries", "Seeds", "SubdomainsAssets", "WebEntitiesAssets", "ObjectStoragesAssets", ] censys-censys-python-7a57a89/censys/asm/api.py000066400000000000000000000070141524433232300214200ustar00rootroot00000000000000"""Base for interacting with the Censys ASM API.""" import os from collections.abc import Iterator from math import inf from typing import Optional from requests.models import Response from censys.common.base import CensysAPIBase from censys.common.config import DEFAULT, get_config from censys.common.exceptions import ( CensysAsmException, CensysException, CensysExceptionMapper, ) class CensysAsmAPI(CensysAPIBase): """This is the base class for ASM's Seeds, Assets, and Events classes.""" DEFAULT_URL: str = "https://app.censys.io/api" """Default ASM API base URL.""" def __init__(self, api_key: Optional[str] = None, **kwargs): """Inits CensysAsmAPI. Args: api_key (str): Optional; The API Key provided by Censys. **kwargs: Arbitrary keyword arguments. Raises: CensysException: Base Exception Class for the Censys API. """ url = kwargs.pop("url", self.DEFAULT_URL) CensysAPIBase.__init__(self, url=url, **kwargs) # Gets config file config = get_config() # Try to get credentials self._api_key = api_key or os.getenv("CENSYS_ASM_API_KEY") or config.get(DEFAULT, "asm_api_key") if not self._api_key: raise CensysException("No ASM API key configured.") self._session.headers.update({"Content-Type": "application/json", "Censys-Api-Key": self._api_key}) def _get_exception_class( # type: ignore self, res: Response ) -> type[CensysAsmException]: return CensysExceptionMapper.ASM_EXCEPTIONS.get(res.json().get("errorCode"), CensysAsmException) def _get_page( self, path: str, page_number: int = 1, page_size: Optional[int] = None, args: Optional[dict] = None, keyword: str = "assets", ) -> Iterator[dict]: """Fetches paginated ASM resource API results. Args: path (str): The API url endpoint. page_number (int): Optional; Page number to begin at when getting results. page_size (int): Optional; Number of results to return per HTTP request. Defaults to 500. args (dict): Optional; URL args that are mapped to params. keyword (str): Optional; The keyword to iterate over in the results. Yields: dict: The resource result returned. """ total_pages = inf args = args or {} while page_number <= total_pages: args.update({"pageNumber": page_number, "pageSize": page_size or 500}) res = self._get(path, args=args) page_number = int(res["pageNumber"]) + 1 total_pages = int(res["totalPages"]) yield from res[keyword] def _get_logbook_page(self, path: str, args: Optional[dict] = None) -> Iterator[dict]: """Fetches paginated ASM logbook API events. Args: path (str): The API url endpoint. args (dict): Optional; URL args that are mapped to params (cursor). Yields: dict: The event result returned. """ end_of_events = False while not end_of_events: res = self._get(path, args=args) end_of_events = res["endOfEvents"] args = {"cursor": res["nextCursor"]} yield from res["events"] def get_workspace_id(self) -> str: """Get the workspace ID. Returns: str: The workspace ID. """ return self._get("/integrations/v1/account")["workspaceId"] censys-censys-python-7a57a89/censys/asm/assets/000077500000000000000000000000001524433232300215755ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/asm/assets/__init__.py000066400000000000000000000007511524433232300237110ustar00rootroot00000000000000"""Interact with the Censys Assets API.""" from .assets import Assets from .certificates import CertificatesAssets from .domains import DomainsAssets from .hosts import HostsAssets from .object_storages import ObjectStoragesAssets from .subdomains import SubdomainsAssets from .web_entities import WebEntitiesAssets __all__ = [ "Assets", "CertificatesAssets", "DomainsAssets", "HostsAssets", "ObjectStoragesAssets", "SubdomainsAssets", "WebEntitiesAssets", ] censys-censys-python-7a57a89/censys/asm/assets/assets.py000066400000000000000000000143731524433232300234610ustar00rootroot00000000000000"""Base for interacting with the Censys Assets API.""" import re from collections.abc import Iterator from typing import Any, Optional from censys.common.exceptions import CensysInvalidColorException from ..api import CensysAsmAPI HEX_REGEX = re.compile(r"^#(?:[0-9a-fA-F]{3}){1,2}$") class Assets(CensysAsmAPI): """Assets API class.""" asset_type: str def __init__(self, asset_type: str, *args, **kwargs): """Inits Assets. Args: asset_type (str): Type of asset to interact with. *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ CensysAsmAPI.__init__(self, *args, **kwargs) api_version = kwargs.get("api_version", "v1") self.base_path = f"/{api_version}/assets/{asset_type}" self.asset_type = asset_type def _format_asset_id(self, asset_id: str) -> str: """Formats asset ID. Args: asset_id (str): Asset ID to format. Returns: str: Formatted asset ID. """ return asset_id def get_assets( self, page_number: int = 1, page_size: Optional[int] = None, tag: Optional[list[str]] = None, tag_operator: Optional[str] = None, source: Optional[list[str]] = None, discovery_trail: Optional[bool] = None, ) -> Iterator[dict]: """Requests assets data. Args: page_number (int): Optional; Page number to begin at when searching. page_size (int): Optional; Page size for retrieving assets. tag (list): Optional; List of tags to search for. tag_operator (str): Optional; Operator to use when searching for tags. source (list): Optional; List of sources to search for. discovery_trail (bool): Optional; Bool indicating whether to return discovery trail. Yields: dict: The assets result returned. """ args: dict[str, Any] = {} if tag: args["tag"] = tag if tag_operator: args["tagOperator"] = tag_operator if source: args["source"] = source if discovery_trail: args["discoveryTrail"] = discovery_trail yield from self._get_page(self.base_path, page_number=page_number, page_size=page_size, args=args) def get_asset_by_id(self, asset_id: str) -> dict: """Requests asset data by ID. Args: asset_id (str): Requested asset ID. Returns: dict: Asset search result. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}" return self._get(path) def get_comments( self, asset_id: str, page_number: int = 1, page_size: Optional[int] = None, ) -> Iterator[dict]: """Requests comments on a specified asset. Args: asset_id (str): Asset ID for requested comments. page_number (int): Optional; Page number to begin at when searching. page_size (int): Optional; Page size for retrieving comments. Returns: generator: Comment search results. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/comments" return self._get_page(path, page_number=page_number, page_size=page_size, keyword="comments") def get_comment_by_id(self, asset_id: str, comment_id: int) -> dict: """Requests a comment on a specified asset by comment ID. Args: asset_id (str): Asset ID for requested comments. comment_id (int): Requested comment ID. Returns: dict: Comment search result. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/comments/{comment_id}" return self._get(path) def add_comment(self, asset_id: str, comment: str) -> dict: """Adds a comment to a specified asset on the ASM platform. Args: asset_id (str): Asset ID to add comment to. comment (str): New comment text. Returns: dict: Added comment results. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/comments" data = {"markdown": str(comment)} return self._post(path, data=data) def delete_comment(self, asset_id: str, comment_id: int) -> dict: """Deletes a comment from a specified asset on the ASM platform by comment ID. Args: asset_id (str): Asset ID to delete comment from. comment_id (int): Comment ID to delete. Returns: dict: Deleted comment results. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/comments/{comment_id}" return self._delete(path) def add_tag(self, asset_id: str, name: str, color: Optional[str] = None) -> dict: """Adds a tag to a specified asset on the ASM platform. Args: asset_id (str): Asset ID to add tag to. name (str): New tag name. color (str): Optional; New tag color (hex). Returns: dict: Added tag results. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/tags" data = format_tag(name, color) return self._post(path, data=data) def delete_tag(self, asset_id: str, name: str) -> dict: """Deletes a tag from a specified asset on the ASM platform by tag name. Args: asset_id (str): Asset ID to delete tag from. name (str): Tag name to delete. Returns: dict: Deleted tag results. """ path = f"{self.base_path}/{self._format_asset_id(asset_id)}/tags/{name}" return self._delete(path) def format_tag(name: str, color: Optional[str] = None) -> dict: """Formats tag name and color request data. Args: name (str): Tag name. color (str): Optional; Tag color (hex). Raises: CensysInvalidColorException: Raised if color is not a valid hex color. Returns: dict: Formatted tag request data. """ if color: if not HEX_REGEX.match(color): raise CensysInvalidColorException(10037, f"{color} is not a valid color.") return {"name": str(name), "color": str(color)} return {"name": str(name)} censys-censys-python-7a57a89/censys/asm/assets/certificates.py000066400000000000000000000006511524433232300246160ustar00rootroot00000000000000"""Interact with the Censys Certificate Assets API.""" from .assets import Assets class CertificatesAssets(Assets): """Certificates Assets API class.""" def __init__(self, *args, **kwargs): """Inits CertificatesAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("certificates", *args, **kwargs) censys-censys-python-7a57a89/censys/asm/assets/domains.py000066400000000000000000000021541524433232300236030ustar00rootroot00000000000000"""Interact with the Censys Domain Assets API.""" from collections.abc import Iterator from typing import Optional from .assets import Assets class DomainsAssets(Assets): """Domains Assets API class.""" def __init__(self, *args, **kwargs): """Inits DomainsAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("domains", *args, **kwargs) def get_subdomains(self, domain: str, page_number: int = 1, page_size: Optional[int] = None) -> Iterator[dict]: """List all subdomains of the parent domain. Args: domain: (str): Parent domain to query. page_number (int): Optional; Page number to begin at when searching. page_size (int): Optional; Page size for retrieving assets. Yields: dict: The assets result returned. """ yield from self._get_page( f"{self.base_path}/{domain}/subdomains", page_number=page_number, page_size=page_size, keyword="subdomains", ) censys-censys-python-7a57a89/censys/asm/assets/hosts.py000066400000000000000000000006061524433232300233110ustar00rootroot00000000000000"""Interact with the Censys Host Assets API.""" from .assets import Assets class HostsAssets(Assets): """Hosts Assets API class.""" def __init__(self, *args, **kwargs): """Inits HostsAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("hosts", *args, **kwargs) censys-censys-python-7a57a89/censys/asm/assets/object_storages.py000066400000000000000000000015231524433232300253250ustar00rootroot00000000000000"""Interact with the Censys Object Storage Assets API.""" from urllib.parse import quote from .assets import Assets class ObjectStoragesAssets(Assets): """Object Storage Assets API class. Please note that the Object Storage Assets API is currently in beta and is subject to change. """ def __init__(self, *args, **kwargs): """Inits ObjectStoragesAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("object-storages", api_version="beta", *args, **kwargs) def _format_asset_id(self, asset_id: str) -> str: """Formats asset ID. Args: asset_id (str): Asset ID to format. Returns: str: Formatted asset ID. """ return quote(asset_id, safe="") censys-censys-python-7a57a89/censys/asm/assets/subdomains.py000066400000000000000000000036301524433232300243150ustar00rootroot00000000000000"""Interact with the Censys Subdomain Assets API.""" from collections.abc import Iterator from typing import Any, Optional from .assets import Assets class SubdomainsAssets(Assets): """Subdomains Assets API class.""" def __init__(self, *args, **kwargs): """Inits SubdomainsAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("subdomains", *args, **kwargs) def get_assets( self, page_number: int = 1, page_size: Optional[int] = None, tag: Optional[list[str]] = None, tag_operator: Optional[str] = None, source: Optional[list[str]] = None, discovery_trail: Optional[bool] = None, ) -> Iterator[dict]: """Requests assets data. Override for subdomains due to it return value in a different key. Args: page_number (int): Optional; Page number to begin at when searching. page_size (int): Optional; Page size for retrieving assets. tag (list): Optional; List of tags to search for. tag_operator (str): Optional; Operator to use when searching for tags. source (list): Optional; List of sources to search for. discovery_trail (bool): Optional; Bool indicating whether to return discovery trail. Yields: dict: The assets result returned. """ args: dict[str, Any] = {} if tag: args["tag"] = tag if tag_operator: args["tagOperator"] = tag_operator if source: args["source"] = source if discovery_trail: args["discoveryTrail"] = discovery_trail yield from self._get_page( self.base_path, page_number=page_number, page_size=page_size, args=args, keyword="subdomains", ) censys-censys-python-7a57a89/censys/asm/assets/web_entities.py000066400000000000000000000034131524433232300246310ustar00rootroot00000000000000"""Interact with the Censys Web Entities Assets API.""" from collections.abc import Iterator from typing import Optional from .assets import Assets class WebEntitiesAssets(Assets): """Web Entities Assets API class.""" def __init__(self, *args, **kwargs): """Inits WebEntitiesAssets. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__("web-entities", *args, **kwargs) def get_assets(self, *args, **kwargs): """Requests assets data. This method is not implemented for web entities. Please see the inventory search and aggregation API. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. Raises: NotImplementedError: This method is not implemented. """ raise NotImplementedError("This method is not implemented.") def get_instances( self, name_and_port: str, page_size: Optional[int] = None, cursor: Optional[str] = None, ) -> Iterator[dict]: """List all instances of the web entity. Args: name_and_port: (str): Web entity to query. page_size (int): Optional; Page size for retrieving assets. cursor (str): Optional; Cursor to use for pagination. Yields: dict: The assets result returned. """ args = {"pageSize": page_size, "cursor": cursor} while True: res = self._get(f"{self.base_path}/{name_and_port}/instances", args=args) yield from res.get("instances", []) cursor = res.get("cursor") if not cursor: break args["cursor"] = cursor censys-censys-python-7a57a89/censys/asm/beta.py000066400000000000000000000064101524433232300215610ustar00rootroot00000000000000"""Interact with miscellaneous Censys Beta APIs.""" from typing import Optional from ..common.types import Datetime from ..common.utils import format_iso8601 from .api import CensysAsmAPI class Beta(CensysAsmAPI): """Beta API class.""" base_path = "/beta" def get_logbook_data(self, filters: Optional[dict] = None, cursor: Optional[str] = None): """Retrieve logbook data. Args: filters (dict): Optional; Filter parameters. cursor (str): Optional; Cursor for pagination. Returns: dict: Logbook data result. """ data = {"filters": filters, "nextWindowCursor": cursor} return self._post(f"{self.base_path}/logbook/getLogbookData", data=data) def add_cloud_assets( self, cloud_connector_uid: str, cloud_assets: list[dict], ): """Add cloud assets. Args: cloud_connector_uid (str): Cloud connector UID. cloud_assets (List[dict]): Cloud assets. Returns: dict: Add cloud assets result. """ data = { "cloudConnectorUid": cloud_connector_uid, "cloudAssets": cloud_assets, } return self._post(f"{self.base_path}/cloudConnector/addCloudAssets", data=data) def get_input_assets(self, page_number: int = 1, page_size: Optional[int] = None): """Retrieve input assets. Args: page_number (int): Optional; Page number to begin at when searching. page_size (int): Optional; Page size for retrieving assets. Returns: dict: Input assets result. """ return self._get( f"{self.base_path}/assets/inputAssets", params={"pageNumber": page_number, "pageSize": page_size}, ) def get_asset_counts(self, since: Datetime, environment: str, asset_type: str): """Retrieve asset counts. Args: since (Datetime): Date to include assets from. environment (str): Environment to include assets from. asset_type (str): Asset type to include. Returns: dict: Asset count result. """ params = { "since": format_iso8601(since), "environment": environment, "assetType": asset_type, } return self._get( f"{self.base_path}/assets/counts", params=params, ) def get_host_counts_by_country(self, since: Datetime, environment: str): """Retrieve host counts by country. Args: since (Datetime): Date to include hosts from. environment (str): Environment to include hosts from. Returns: dict: Host count result. """ params = { "since": format_iso8601(since), "environment": environment, } return self._get( f"{self.base_path}/assets/hostCountsByCountry", params=params, ) def get_user_workspaces(self, user_uuid: str): """Retrieve user workspaces. Args: user_uuid (str): User UUID. Returns: dict: User workspaces result. """ return self._get( f"{self.base_path}/users/{user_uuid}/workspaces", ) censys-censys-python-7a57a89/censys/asm/client.py000066400000000000000000000030241524433232300221220ustar00rootroot00000000000000"""Interact with the Censys Seeds, Assets, and Logbook APIs.""" from typing import Optional from .assets import ( CertificatesAssets, DomainsAssets, HostsAssets, ObjectStoragesAssets, SubdomainsAssets, WebEntitiesAssets, ) from .beta import Beta from .clouds import Clouds from .inventory import InventorySearch from .logbook import Logbook from .risks import Risks from .saved_queries import SavedQueries from .seeds import Seeds class AsmClient: """Client ASM API class.""" def __init__(self, api_key: Optional[str] = None, **kwargs): """Inits AsmClient. Args: api_key (str): Optional; The API Key provided by Censys. **kwargs: Arbitrary keyword arguments. """ self.seeds = Seeds(api_key, **kwargs) self.hosts = HostsAssets(api_key, **kwargs) self.certificates = CertificatesAssets(api_key, **kwargs) self.domains = DomainsAssets(api_key, **kwargs) self.subdomains = SubdomainsAssets(api_key, **kwargs) self.logbook = Logbook(api_key, **kwargs) self.events = self.logbook self.clouds = Clouds(api_key, **kwargs) self.risks = Risks(api_key, **kwargs) self.inventory = InventorySearch(api_key, **kwargs) self.object_storages = ObjectStoragesAssets(api_key, **kwargs) self.web_entities = WebEntitiesAssets(api_key, **kwargs) self.beta = Beta(api_key, **kwargs) self.saved_queries = SavedQueries(api_key, **kwargs) censys-censys-python-7a57a89/censys/asm/clouds.py000066400000000000000000000040071524433232300221370ustar00rootroot00000000000000"""Interact with the Censys Clouds API.""" from ..common.types import Datetime from ..common.utils import format_iso8601 from .api import CensysAsmAPI class Clouds(CensysAsmAPI): """Clouds API class.""" base_path = "/v1/clouds" def get_host_counts( self, since: Datetime, ) -> dict: """Retrieve host counts by cloud. Hosts found after the date provided in the `since` parameter will be included in the new asset counts. Args: since (Datetime): Date to include hosts from. Returns: dict: Host count result. """ since = format_iso8601(since) return self._get(f"{self.base_path}/hostCounts/{since}") def get_domain_counts(self, since: Datetime) -> dict: """Retrieve domain counts by cloud. Args: since (Datetime): Date to include domains from. Returns: dict: Domain count result. """ since = format_iso8601(since) return self._get(f"{self.base_path}/domainCounts/{since}") def get_object_store_counts(self, since: Datetime) -> dict: """Retrieve object store counts by cloud. Args: since (Datetime): Date to include object stores from. Returns: dict: Object store count result. """ since = format_iso8601(since) return self._get(f"{self.base_path}/objectStoreCounts/{since}") def get_subdomain_counts(self, since: Datetime) -> dict: """Retrieve subdomain counts by cloud. Args: since (Datetime): Date to include subdomains from. Returns: dict: Subdomain count result. """ since = format_iso8601(since) return self._get(f"{self.base_path}/subdomainCounts/{since}") def get_unknown_counts(self) -> dict: """Retrieve known and unknown counts for hosts by cloud. Returns: dict: Unknown count result. """ return self._get(f"{self.base_path}/unknownCounts") censys-censys-python-7a57a89/censys/asm/inventory.py000066400000000000000000000076621524433232300227150ustar00rootroot00000000000000"""Interact with the Censys Inventory Search API.""" import warnings from typing import Optional from .api import CensysAsmAPI class InventorySearch(CensysAsmAPI): """Inventory Search API class.""" base_path = "/inventory/v1" def search( self, workspaces: Optional[list[str]] = None, query: Optional[str] = None, page_size: Optional[int] = None, cursor: Optional[str] = None, sort: Optional[list[str]] = None, fields: Optional[list[str]] = None, pages: Optional[int] = None, ) -> dict: """Search inventory data. Args: workspaces (List[str], optional): List of workspace IDs to search. Deprecated. The workspace associated with `CENSYS-API-KEY` will be used automatically. query (str, optional): Query string. page_size (int, optional): Number of results to return. Defaults to 50. cursor (str, optional): Cursor to start search from. sort (List[str], optional): List of fields to sort by. fields (List[str], optional): List of fields to return. pages (int, optional): Number of pages of results to return (when set to -1 returns all pages available). Returns: dict: Inventory search results. """ if workspaces is None: workspaces = [self.get_workspace_id()] else: warnings.warn( "The field 'workspaces' is being deprecated. The workspace associated with `CENSYS-API-KEY` will be used automatically.", category=DeprecationWarning, stacklevel=2, ) if page_size is None: page_size = 50 if pages is None: pages = 1 args = { "workspaces": workspaces, "pageSize": page_size, } if query: args["query"] = query if cursor: args["cursor"] = cursor if sort: args["sort"] = sort if fields: args["fields"] = fields page = 1 next_cursor = None hits = [] resp = self._get(self.base_path, args=args) next_cursor = resp.get("nextCursor") hits.extend(resp.get("hits", [])) # Fetch additional pages if next_cursor is available AND additional pages are requested # Loop will exit if next_cursor is None or if the number of pages requested is reached # Loop will exit if non-200 status code is returned while next_cursor and (pages == -1 or page < pages): args["cursor"] = next_cursor resp = self._get(self.base_path, args=args) next_cursor = resp.get("nextCursor") if "nextCursor" in resp else None hits.extend(resp.get("hits", [])) page += 1 resp["hits"] = hits return resp def aggregate( self, workspaces: list[str], query: Optional[str] = None, aggregation: Optional[dict] = None, ) -> dict: """Aggregate inventory data. Args: workspaces (List[str]): List of workspace IDs to search. query (str, optional): Query string. aggregation (dict, optional): Aggregation object. Returns: dict: Inventory aggregation results. """ body = { "workspaces": workspaces, "query": query, "aggregation": aggregation, } return self._post(f"{self.base_path}/aggregate", data=body) def fields(self, fields: Optional[list[str]] = None) -> dict: """List inventory fields. If no fields are specified, all fields will be returned. Args: fields (List[str], optional): List of fields to return. Returns: dict: Inventory field results. """ args = {"fields": fields} return self._get(f"{self.base_path}/fields", args=args) Inventory = InventorySearch censys-censys-python-7a57a89/censys/asm/logbook.py000066400000000000000000000050461524433232300223060ustar00rootroot00000000000000"""Interact with the Censys Logbook API.""" import datetime from collections.abc import Iterator from typing import Optional, Union from .api import CensysAsmAPI class Logbook(CensysAsmAPI): """Logbook API class.""" base_path = "/v1/logbook" def get_cursor( self, start: Optional[Union[datetime.datetime, int]] = None, filters: Optional[list[str]] = None, ) -> str: """Requests a logbook cursor. Args: start ([datetime.datetime, int]): Optional; Timestamp or event ID to begin searching. filters (list): Optional; List of filters applied to logbook search results. Returns: str: Cursor result. """ path = f"{self.base_path}-cursor" data = format_data(start=start, filters=filters) return self._post(path, data=data)["cursor"] def get_events(self, cursor: Optional[str] = None) -> Iterator[dict]: """Requests logbook events from inception or from the provided cursor. Args: cursor (str): Optional; Logbook cursor. Yields: dict: Logbook event. """ args = {"cursor": cursor} yield from self._get_logbook_page(self.base_path, args) # Alias for backwards compatibility Events = Logbook class Filters: """Logbook filters class.""" CERT = "CERT" CERT_RISK = "CERT_RISK" DOMAIN = "DOMAIN" DOMAIN_EXPIRATION_DATE = "DOMAIN_EXPIRATION_DATE" DOMAIN_MAIL_EXCHANGE_SERVER = "DOMAIN_MAIL_EXCHANGE_SERVER" DOMAIN_NAME_SERVER = "DOMAIN_NAME_SERVER" DOMAIN_REGISTRAR = "DOMAIN_REGISTRAR" DOMAIN_RISK = "DOMAIN_RISK" DOMAIN_SUBDOMAIN = "DOMAIN_SUBDOMAIN" HOST = "HOST" HOST_CERT = "HOST_CERT" HOST_PORT = "HOST_PORT" HOST_PROTOCOL = "HOST_PROTOCOL" HOST_RISK = "HOST_RISK" HOST_SOFTWARE = "HOST_SOFTWARE" HOST_VULNERABILITY = "HOST_VULNERABILITY" def format_data( start: Optional[Union[datetime.datetime, int]] = None, filters: Optional[list[str]] = None, ) -> dict: """Formats cursor request data into a start date/id and filter list. Args: start ([datetime.datetime, int]): Optional; Timestamp or event ID to begin searching. filters (list): Optional; List of filters applied to logbook search results. Returns: dict: Formatted logbook cursor request data """ data: dict = {} if filters: data["filter"] = {"type": filters} if isinstance(start, int): data["idFrom"] = start elif start: data["dateFrom"] = start return data censys-censys-python-7a57a89/censys/asm/risks.py000066400000000000000000000133311524433232300220010ustar00rootroot00000000000000"""Interact with the Censys Risks API.""" import urllib.parse from typing import Any, Optional from .api import CensysAsmAPI class Risks(CensysAsmAPI): """Risks API class.""" base_path = "/v2/risk" risk_events_path = f"{base_path}-events" risk_instances_path = f"{base_path}-instances" risk_types_path = f"{base_path}-types" def get_risk_events( self, start: Optional[str] = None, end: Optional[str] = None, after_id: Optional[int] = None, limit: Optional[int] = None, cursor: Optional[str] = None, accept: Optional[str] = None, ) -> dict: """Retrieve risk events. Args: start (str): Optional; Starting event time, inclusive (in RFC3339 format). end (str): Optional; Ending event time, inclusive (in RFC3339 format). after_id (int): Optional; Risk event ID to query for events after. limit (int): Optional; Max number of events to return. cursor (str): Optional; Cursor value to continue collecting events started in a previous request. accept (str): Optional; Accept header. Returns: dict: Risk events result. """ args: dict[str, Any] = {} if start: args["start"] = start if end: args["end"] = end if after_id: args["afterID"] = after_id if limit: args["limit"] = limit if cursor: args["cursor"] = cursor return self._get( self.risk_events_path, args=args, headers={"Accept": accept} if accept else None, ) def get_risk_instances(self, include_events: Optional[bool] = None, accept: Optional[str] = None) -> dict: """Retrieve risk instances. Args: include_events (bool): Optional; Whether to include events. accept (str): Optional; Accept header. Returns: dict: Risk instances result. """ args = {"includeEvents": include_events} return self._get( self.risk_instances_path, args=args, headers={"Accept": accept} if accept else None, ) def patch_risk_instances(self, data: dict) -> dict: """Patch risk instances. Args: data (dict): Risk instances data. Returns: dict: Risk instances result. """ return self._patch(self.risk_instances_path, data=data) def search_risk_instances(self, data: dict, accept: Optional[str] = None) -> dict: """Search risk instances. Args: data (dict): Query data. accept (str): Optional; Accept header. Returns: dict: Risk instances result. """ return self._post( f"{self.risk_instances_path}/search", data=data, headers={"Accept": accept} if accept else None, ) def get_risk_instance(self, risk_instance_id: int, include_events: Optional[bool] = None) -> dict: """Retrieve a risk instance. Args: risk_instance_id (int): Risk instance ID. include_events (bool): Optional; Whether to include events. Returns: dict: Risk instance result. """ args = {"includeEvents": include_events} return self._get(f"{self.risk_instances_path}/{risk_instance_id}", args=args) def patch_risk_instance(self, risk_instance_id: int, data: dict) -> dict: """Patch a risk instance. Args: risk_instance_id (int): Risk instance ID. data (dict): Risk instance data. Returns: dict: Risk instance result. """ return self._patch(f"{self.risk_instances_path}/{risk_instance_id}", data=data) def get_risk_types( self, limit: Optional[int] = None, page: Optional[int] = None, sort: Optional[list[str]] = None, include_events: Optional[bool] = None, accept: Optional[str] = None, ) -> dict: """Retrieve risk types. Args: limit (int, optional): Maximum number of results to return. Defaults to 1000. page (int, optional): Page number to begin at when searching. Defaults to 1. sort (list): Optional; Sort by field(s). include_events (bool): Optional; Whether to include events. accept (str): Optional; Accept header. Returns: dict: Risk types result. """ args: dict[str, Any] = {"sort": sort, "includeEvents": include_events} if page: args["page"] = page if limit: args["limit"] = limit return self._get( self.risk_types_path, args=args, headers={"Accept": accept} if accept else None, ) def get_risk_type(self, risk_type: str, include_events: Optional[bool] = None) -> dict: """Retrieve a risk type. Args: risk_type (str): Risk type. include_events (bool): Optional; Whether to include events. Returns: dict: Risk type result. """ escaped_risk_type = urllib.parse.quote(risk_type, safe="") args = {"includeEvents": include_events} return self._get(f"{self.risk_types_path}/{escaped_risk_type}", args=args) def patch_risk_type(self, risk_type: str, data: dict) -> dict: """Patch a risk type. Args: risk_type (str): Risk type. data (dict): Risk type data. Returns: dict: Risk type result. """ escaped_risk_type = urllib.parse.quote(risk_type, safe="") return self._patch(f"{self.risk_types_path}/{escaped_risk_type}", data=data) censys-censys-python-7a57a89/censys/asm/saved_queries.py000066400000000000000000000061131524433232300235050ustar00rootroot00000000000000"""Interact with the Censys Saved Queries API.""" from typing import Optional from .api import CensysAsmAPI class SavedQueries(CensysAsmAPI): """Saved Queries API class.""" base_path = "/inventory/v1/saved-query" def get_saved_queries( self, query_name_prefix: Optional[str] = None, page_size: Optional[int] = None, page: Optional[int] = None, filter_term: Optional[str] = None, ) -> dict: """Get saved queries. Args: query_name_prefix (str, optional): Prefix for the saved query name. page_size (int, optional): Number of results to return. Defaults to 50. page (int, optional): Page number to begin at when searching. Defaults to 1. filter_term (str, optional): Term used to filter the list of saved query names and the saved queries. Returns: dict: Saved queries results. """ if page_size is None: page_size = 50 if page is None: page = 1 args: dict = { "pageSize": page_size, "page": page, } if query_name_prefix: args["queryNamePrefix"] = query_name_prefix if filter_term: args["filterTerm"] = filter_term return self._get(self.base_path, args=args) def add_saved_query( self, query: str, query_name: str, ) -> dict: """Add a new saved query to the ASM platform. Args: query (str): Query string. query_name (str): Saved query name. Returns: dict: Added saved query results. """ body = { "query": query, "queryName": query_name, } return self._post(self.base_path, data=body) def get_saved_query_by_id( self, query_id: str, ) -> dict: """Get saved query by query ID. Args: query_id (str): The saved query's ID. Returns: dict: Saved query result. """ return self._get(f"{self.base_path}/{query_id}") def edit_saved_query_by_id( self, query_id: str, query: str, query_name: str, ) -> dict: """Edit an existing saved query by query ID. Args: query_id (str): The saved query's ID. query (str): New query string. query_name (str): New saved query name. Returns: dict: Edited saved query result. """ body = { "query": query, "queryName": query_name, } return self._put(f"{self.base_path}/{query_id}", data=body) def delete_saved_query_by_id( self, query_id: str, ) -> dict: """Delete saved query by query ID. Args: query_id (str): The saved query's ID. Returns: dict: Delete results. """ return self._delete(f"{self.base_path}/{query_id}") censys-censys-python-7a57a89/censys/asm/seeds.py000066400000000000000000000053211524433232300217510ustar00rootroot00000000000000"""Interact with the Censys Seeds API.""" from typing import Optional from .api import CensysAsmAPI SEED_TYPES = ["IP_ADDRESS", "DOMAIN_NAME", "CIDR", "ASN"] class Seeds(CensysAsmAPI): """Seeds API class.""" base_path = "/v1/seeds" def get_seeds(self, seed_type: Optional[str] = None, label: Optional[str] = None) -> list[dict]: """Requests seed data. Args: seed_type (str): Optional; Seed type ['IP_ADDRESS', 'DOMAIN_NAME', 'CIDR', 'ASN']. label (str): Optional; Seed label. Returns: List[dict]: Seed search results. """ args = {} if seed_type: args["type"] = seed_type if label: args["label"] = label return self._get(self.base_path, args=args)["seeds"] def get_seed_by_id(self, seed_id: int) -> dict: """Requests seed data by ID. Args: seed_id (int): Seed ID to get. Returns: dict: Seed search result. """ path = f"{self.base_path}/{seed_id}" return self._get(path) def add_seeds(self, seeds: list, force: Optional[bool] = None) -> dict: """Add seeds to the ASM platform. Args: seeds (list): List of seed objects to add. force (bool, optional): Forces replace operation. Returns: dict: Added seeds results. """ data = {"seeds": seeds} args = {"force": force} return self._post(self.base_path, args=args, data=data) def replace_seeds_by_label(self, label: str, seeds: list, force: Optional[bool] = None) -> dict: """Replace seeds in the ASM platform by label. Args: label (str): Label name to replace by. seeds (list): List of seed objects to add. force (bool): Optional; Forces replace operation. Returns: dict: Added and removed seeds results. """ data = {"seeds": seeds} args = {"label": label, "force": force} return self._put(self.base_path, args=args, data=data) def delete_seeds_by_label(self, label: str) -> dict: """Delete seeds in the ASM platform by label. Args: label (str): Label name to delete by. Returns: dict: Delete results. """ args = {"label": label} return self._delete(self.base_path, args=args) def delete_seed_by_id(self, seed_id: int) -> dict: """Delete a seed in the ASM platform by id. Args: seed_id (int): Seed ID to delete by. Returns: dict: Delete results. """ path = f"{self.base_path}/{seed_id}" return self._delete(path) censys-censys-python-7a57a89/censys/cli/000077500000000000000000000000001524433232300202625ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/cli/__init__.py000066400000000000000000000012451524433232300223750ustar00rootroot00000000000000#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK """Interact with the Censys Search API through the command line.""" import sys import argcomplete from censys.common.version import __version__ from .args import get_parser def main(): """Main cli function.""" parser = get_parser() # Adds autocomplete argcomplete.autocomplete(parser) # Executes by subcommand args = parser.parse_args() if args.version: print(f"Censys Python Version: {__version__}") sys.exit(0) try: args.func(args) except KeyboardInterrupt: # pragma: no cover sys.exit(1) if __name__ == "__main__": # pragma: no cover main() censys-censys-python-7a57a89/censys/cli/args.py000066400000000000000000000035571524433232300216020ustar00rootroot00000000000000"""Interact with argparser.""" import argparse import os from censys.common.config import DEFAULT, get_config from . import commands def get_parser() -> argparse.ArgumentParser: """Gets ArgumentParser for CLI. Returns: argparse.ArgumentParser """ config = get_config() auth = argparse.ArgumentParser(add_help=False) auth.add_argument( "--api-id", default=os.getenv("CENSYS_API_ID") or config.get(DEFAULT, "api_id"), required=False, help="a Censys API ID \ (alternatively you can use the env variable CENSYS_API_ID)", ) auth.add_argument( "--api-secret", default=os.getenv("CENSYS_API_SECRET") or config.get(DEFAULT, "api_secret"), required=False, help="a Censys API SECRET \ (alternatively you can use the env variable CENSYS_API_SECRET)", ) asm_auth = argparse.ArgumentParser(add_help=False) asm_auth.add_argument( "--api-key", default=os.getenv("CENSYS_ASM_API_KEY") or config.get(DEFAULT, "asm_api_key"), required=False, help="a Censys ASM API Key \ (alternatively you can use the env variable CENSYS_ASM_API_KEY)", ) parser = argparse.ArgumentParser() parser.add_argument( "-v", "--version", action="store_true", default=False, help="display version", ) def print_help(_: argparse.Namespace): """Prints help.""" parser.print_help() parser.exit() parser.set_defaults(func=print_help) subparsers = parser.add_subparsers() parents = { "auth": auth, "asm_auth": asm_auth, } for command in commands.__dict__.values(): try: include_func = command.include except AttributeError: continue include_func(subparsers, parents) return parser censys-censys-python-7a57a89/censys/cli/commands/000077500000000000000000000000001524433232300220635ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/cli/commands/__init__.py000066400000000000000000000002571524433232300242000ustar00rootroot00000000000000"""Censys CLI commands.""" from . import account, asm, config, hnri, search, subdomains, view __all__ = ["account", "asm", "config", "hnri", "search", "subdomains", "view"] censys-censys-python-7a57a89/censys/cli/commands/account.py000066400000000000000000000040251524433232300240720ustar00rootroot00000000000000"""Censys config CLI.""" import argparse import sys from rich import box from rich.table import Table from censys.cli.utils import console from censys.common.exceptions import CensysUnauthorizedException from censys.search.v2.api import CensysSearchAPIv2 def cli_account(args: argparse.Namespace): # pragma: no cover """Account subcommand. Args: args: Argparse Namespace. """ try: client = CensysSearchAPIv2(args.api_id, args.api_secret) account = client.account() if args.json: console.print_json(data=account) else: table = Table("Key", "Value", show_header=False, box=box.SQUARE, highlight=True) table.add_row("Email", account["email"]) table.add_row("Login ID", account["login"]) table.add_row("First Login", account["first_login"]) table.add_row("Last Login", account["last_login"][:-7]) quota = account["quota"] table.add_row( "Query Quota", f"{quota['used']} / {quota['allowance']} ({quota['used'] / quota['allowance'] * 100:.2f}%)", # noqa ) table.add_row("Quota Resets At", quota["resets_at"]) console.print(table) sys.exit(0) except CensysUnauthorizedException: console.print("Failed to authenticate") sys.exit(1) def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ account_parser = parent_parser.add_parser( "account", description="Check Censys account details and quota", help="check Censys account details and quota", parents=[parents["auth"]], ) account_parser.add_argument("-j", "--json", action="store_true", help="Output in JSON format") account_parser.set_defaults(func=cli_account) censys-censys-python-7a57a89/censys/cli/commands/asm.py000066400000000000000000000766511524433232300232340ustar00rootroot00000000000000"""Censys ASM CLI.""" import argparse import concurrent.futures import csv import json import sys import threading from typing import Union from xml.etree import ElementTree from rich.progress import Progress, TaskID from rich.prompt import Confirm, Prompt from censys.asm.api import CensysAsmAPI from censys.asm.inventory import InventorySearch from censys.asm.saved_queries import SavedQueries from censys.asm.seeds import SEED_TYPES, Seeds from censys.cli.utils import console from censys.common.config import DEFAULT, get_config, write_config from censys.common.exceptions import ( CensysAsmException, CensysSeedNotFoundException, CensysUnauthorizedException, ) def cli_asm_config(_: argparse.Namespace): # pragma: no cover """Config asm subcommand. Args: _: Argparse Namespace. """ api_key_prompt = "Censys ASM API Key" config = get_config() api_key = config.get(DEFAULT, "asm_api_key") if api_key: key_len = len(api_key) - 4 redacted_api_key = api_key.replace(api_key[:key_len], key_len * "*") api_key_prompt = f"{api_key_prompt} [cyan]({redacted_api_key})[/cyan]" api_key = Prompt.ask(api_key_prompt, console=console) or api_key try: c = CensysAsmAPI(api_key) w = c.get_workspace_id() console.print(f"Successfully authenticated to workspace {w}") except CensysUnauthorizedException: console.print("Failed to authenticate") sys.exit(1) if not api_key: console.print("Please enter valid credentials") sys.exit(1) color = Confirm.ask("Do you want color output?", default=True, show_default=False, console=console) config.set(DEFAULT, "color", "auto" if color else "") try: # Assumes that login was successfully config.set(DEFAULT, "asm_api_key", api_key) write_config(config) console.print("\nSuccessfully configured credentials") sys.exit(0) except CensysUnauthorizedException: console.print("Failed to authenticate") sys.exit(1) def get_seeds_from_xml(file: str) -> list[dict[str, str]]: """Get seeds from nmap xml. Args: file (str): Nmap xml file. Returns: List[Dict[str, str]]: List of seeds. """ tree = ElementTree.parse(file) root = tree.getroot() ips = set() domains = set() for element in root.findall("host"): address_element = element.find("address") hostnames_element = element.find("hostnames") if address_element is not None and address_element.get("addrtype") == "ipv4": ip_address = address_element.get("addr") if ip_address: ips.add(ip_address) if hostnames_element is not None: hostname_elements = hostnames_element.findall("hostname") for hostname_element in hostname_elements: hostname_type = hostname_element.get("type") if hostname_type != "user": continue hostname = hostname_element.get("name") if hostname: domains.add(hostname) return [{"value": ip, "type": "IP_ADDRESS"} for ip in ips] + [ {"value": domain, "type": "DOMAIN_NAME"} for domain in domains ] def get_seeds_from_params(args: argparse.Namespace, command_name: str) -> list[dict[str, Union[str, int]]]: """Get seeds from params. Args: args (Namespace): Argparse Namespace. command_name (str): The name of the command getting the seeds to be processed. Returns: List[Dict[str, str]]: List of seeds. """ is_csv = args.csv if args.input_file or args.json: json_data = None if args.input_file: if args.input_file == "-": file = sys.stdin else: if not args.csv and args.input_file.endswith(".csv"): is_csv = True file = open(args.input_file) # noqa: SIM115 if is_csv: seeds = [] csv_reader = csv.DictReader(file, delimiter=",") if csv_reader.fieldnames: # Lowercase the field names csv_reader.fieldnames = [string.lower() for string in csv_reader.fieldnames] for row in csv_reader: seeds.append(row) else: json_data = file.read() else: json_data = args.json if json_data: try: seeds = json.loads(json_data) except json.decoder.JSONDecodeError as e: console.print(f"Invalid json {e}") sys.exit(1) elif args.nmap_xml: try: seeds = get_seeds_from_xml(args.nmap_xml) except ElementTree.ParseError as e: console.print(f"Invalid xml {e}") sys.exit(1) seeds_to_add = [] for seed in seeds: if isinstance(seed, str): seed = {"value": seed} if not isinstance(seed, dict): console.print(f"Invalid seed {seed}") sys.exit(1) if command_name != "delete-seeds" and "type" not in seed: seed["type"] = args.default_type if command_name != "replace-labeled-seeds" and "label" not in seed and "label" in args: seed["label"] = args.label # The back end is really picky about sending extra fields, so we'll prune out anything it won't like. # This makes it possible to output to CSV, edit, and then pipe the same CSV back into add-seeds, where # we will strip the id, source, createdOn, and any other junk that might be there # valid_params = ["type", "value"] if command_name == "add-seeds": valid_params.append("label") elif command_name == "delete-seeds" and "value" not in seed: valid_params.append("id") filtered_seeds = {key: seed[key] for key in seed if key in valid_params} seeds_to_add.append(filtered_seeds) return seeds_to_add def console_clear_line(): """Clear previous line in console.""" sys.stdout.write("\033[F") # Move the cursor to the previous line sys.stdout.write("\033[K") # Clear the line def cli_add_seeds(args: argparse.Namespace): """Add seed subcommand. Args: args (Namespace): Argparse Namespace. """ seeds_to_add = get_seeds_from_params(args, "add-seeds") s = Seeds(args.api_key) to_add_count = len(seeds_to_add) res = s.add_seeds(seeds_to_add) added_seeds = res["addedSeeds"] added_count = len(added_seeds) if not added_count: console.print("No seeds were added. (Run with -v to get more info)") if not args.verbose: sys.exit(1) else: console.print(f"Added {added_count} seeds.") if added_count < to_add_count: console.print(f"Seeds not added: {to_add_count - added_count}") if args.verbose: # pragma: no cover console.print("The following seed(s) were not able to be added as they already exist or are reserved.") for seed in seeds_to_add: if not any(s for s in added_seeds if seed["value"] == s["value"]): console.print(f"{seed}") def cli_delete_seeds(args: argparse.Namespace): """Delete seeds subcommand. Args: args (Namespace): Argparse Namespace. """ seeds_to_delete = get_seeds_from_params(args, "delete-seeds") s = Seeds(args.api_key) # Get all seeds into a dict indexed by value (for later lookups) console.print("Getting seeds...") seeds = s.get_seeds() console_clear_line() seeds_dict_indexed_by_value = {} for seed in seeds: seeds_dict_indexed_by_value[seed["value"]] = seed seed_ids_to_delete = [] seed_ids_not_found = [] for seed_to_delete in seeds_to_delete: if seed_id := seed_to_delete.get("id"): seed_ids_to_delete.append(seed_id) elif seed_value := seed_to_delete.get("value"): # value may not be in current seeds - can't delete if seed_value in seeds_dict_indexed_by_value: seed_ids_to_delete.append(seeds_dict_indexed_by_value[seed_value]["id"]) else: seed_ids_not_found.append(seed_value) else: console.print("Error, no seed id or value for seed.") if len(seed_ids_to_delete) == 0: console.print("No seeds to delete.") sys.exit(0) seed_ids_deleted = [] # Create a lock to protect shared variables lock = threading.Lock() def delete_seed(seed_id: int, progress: Progress, task_id: TaskID): try: s.delete_seed_by_id(seed_id) with lock: nonlocal seed_ids_deleted seed_ids_deleted.append(seed_id) except CensysSeedNotFoundException: # pragma: no cover with lock: nonlocal seed_ids_not_found seed_ids_not_found.append(seed_id) progress.update(task_id, advance=1) # Create a rich Progress instance with Progress() as progress: progress_task_id = progress.add_task("[cyan]Deleting[/cyan]", total=len(seeds_to_delete)) tasks = [] with concurrent.futures.ThreadPoolExecutor(max_workers=25) as executor: # Submit requests using the executor for seed_id in seed_ids_to_delete: if isinstance(seed_id, str): # pragma: no cover console.print(f"Invalid seed id {seed_id}") continue task = executor.submit(delete_seed, seed_id, progress, progress_task_id) tasks.append(task) # Wait for all requests to complete concurrent.futures.wait(tasks) console.print(f"Deleted {len(seed_ids_deleted)} seeds.") if len(seed_ids_not_found) > 0: console.print(f"Unable to delete {len(seed_ids_not_found)} seeds because they were not present.") def cli_delete_all_seeds(args: argparse.Namespace): """Delete all seeds subcommand. Args: args (Namespace): Argparse Namespace. """ if not args.force: delete_all = Confirm.ask( "Are you sure you want to delete all seeds?", default=False, show_default=True, console=console, ) console_clear_line() if not delete_all: sys.exit(1) s = Seeds(args.api_key) console.print("Getting seeds...") seeds = s.get_seeds() console_clear_line() # Create a lock to protect shared variables lock = threading.Lock() seeds_delete_count = 0 def delete_seed(seed_id: int, progress: Progress, task_id: TaskID): s.delete_seed_by_id(seed_id) with lock: nonlocal seeds_delete_count seeds_delete_count += 1 progress.update(task_id, advance=1) # Create a rich Progress instance with Progress() as progress: progress_task_id = progress.add_task("[cyan]Deleting[/cyan]", total=len(seeds)) tasks = [] with concurrent.futures.ThreadPoolExecutor(max_workers=25) as executor: # Submit requests using the executor for seed in seeds: task = executor.submit(delete_seed, seed["id"], progress, progress_task_id) tasks.append(task) # Wait for all requests to complete concurrent.futures.wait(tasks) console.print(f"Deleted {seeds_delete_count} of {len(seeds)} total seeds.") def cli_delete_seeds_with_label(args: argparse.Namespace): """Delete seeds with label subcommand. Args: args (Namespace): Argparse Namespace. """ s = Seeds(args.api_key) s.delete_seeds_by_label(args.label) console.print(f'Deleted seeds with label "{args.label}".') def cli_replace_seeds_with_label(args: argparse.Namespace): """Replace seeds with label subcommand. Args: args (Namespace): Argparse Namespace. """ seeds_to_add = get_seeds_from_params(args, "replace-labeled-seeds") s = Seeds(args.api_key) res = s.replace_seeds_by_label(args.label, seeds_to_add, True) console.print( f"Removed {len(res['removedSeeds'])} seeds. Added {len(res['addedSeeds'])} seeds. Skipped {len(res['skippedReservedSeeds'])} reserved seeds." ) if args.verbose: # pragma: no cover added_seeds = res.get("addedSeeds", []) if len(added_seeds) > 0: console.print("The following seed(s) were added.") for added_seed in added_seeds: console.print(f" {added_seed}") removed_seeds = res.get("removedSeeds", []) if len(removed_seeds) > 0: console.print("The following seed(s) were removed.") for removed_seed in removed_seeds: console.print(f" {removed_seed}") skipped_seeds = res.get("skippedReservedSeeds", []) if len(skipped_seeds) > 0: console.print("The following seed(s) were not added because they are reserved.") for skipped_seed in skipped_seeds: console.print(f" {skipped_seed}") def cli_list_seeds(args: argparse.Namespace): """List seeds subcommand. Args: args (Namespace): Argparse Namespace. """ s = Seeds(args.api_key) res = s.get_seeds(args.type, args.label) if args.csv: console.print("id,type,value,label,source,createdOn") for seed in res: console.print( f"{seed['id']},{seed['type']},{seed['value']},{seed['label']},{seed['source']},{seed['createdOn']}" # noqa: E231 ) else: console.print_json(json.dumps(res)) def add_seed_arguments(parser: argparse._SubParsersAction, is_delete=False) -> None: """Add seed arguments to parser. Args: parser (argparse._SubParsersAction): Parser. is_delete (Boolean): Is this a delete command. """ if not is_delete: parser.add_argument( # type: ignore[attr-defined] "--default-type", help="type of the seed(s) if type is not already provided (default: %(default)s)", choices=SEED_TYPES, default="IP_ADDRESS", ) parser.add_argument("--csv", help="process input in CSV format", action="store_true") # type: ignore[attr-defined] seeds_group = parser.add_mutually_exclusive_group(required=True) # type: ignore[attr-defined] seeds_group.add_argument( "--input-file", "-i", help="input file name containing valid seeds in JSON format, unless --csv is specified (use - for stdin)", type=str, ) seeds_group.add_argument("--json", "-j", help="input string containing valid json seeds", type=str) seeds_group.add_argument("--nmap-xml", help="input file name containing valid xml nmap output", type=str) def cli_list_saved_queries(args: argparse.Namespace): """List saved queries subcommand. Args: args (Namespace): Argparse Namespace. """ s = SavedQueries(args.api_key) try: res = s.get_saved_queries(args.query_name_prefix, args.page_size, args.page, args.filter_term) if args.csv: console.print("queryId,queryName,query,createdAt") for query in res["results"]: console.print( f"{query['queryId']},{query['queryName']},{query['query']},{query['createdAt']}" # noqa: E231 ) else: console.print_json(json.dumps(res)) except KeyError: console.print("Failed to get saved queries.") sys.exit(1) def cli_add_saved_query(args: argparse.Namespace): """Add saved query subcommand. Args: args (Namespace): Argparse Namespace. """ s = SavedQueries(args.api_key) res = s.add_saved_query(args.query, args.query_name) try: console.print( f"Added saved query.\nQuery name: {res['result']['queryName']}\nQuery: {res['result']['query']}\nQuery ID: {res['result']['queryId']}\nCreated at: {res['result']['createdAt']}" ) except (KeyError, CensysAsmException): console.print("Failed to add saved query.") sys.exit(1) def cli_get_saved_query_by_id(args: argparse.Namespace): """Get saved query by id subcommand. Args: args (Namespace): Argparse Namespace. """ s = SavedQueries(args.api_key) res = s.get_saved_query_by_id(args.query_id) try: console.print( f"Query name: {res['result']['queryName']}\nQuery: {res['result']['query']}\nQuery ID: {res['result']['queryId']}\nCreated at: {res['result']['createdAt']}" ) except (KeyError, CensysAsmException): console.print("Failed to get saved query.") sys.exit(1) def cli_edit_saved_query_by_id(args: argparse.Namespace): """Edit saved query by id subcommand. Args: args (Namespace): Argparse Namespace. """ s = SavedQueries(args.api_key) res = s.edit_saved_query_by_id(args.query_id, args.query, args.query_name) try: console.print( f"Edited saved query.\nQuery name: {res['result']['queryName']}\nQuery: {res['result']['query']}\nQuery ID: {res['result']['queryId']}\nCreated at: {res['result']['createdAt']}" ) except (KeyError, CensysAsmException): console.print("Failed to edit saved query.") sys.exit(1) def cli_delete_saved_query_by_id(args: argparse.Namespace): """Delete saved query by id subcommand. Args: args (Namespace): Argparse Namespace. """ s = SavedQueries(args.api_key) res = s.delete_saved_query_by_id(args.query_id) if res == {}: console.print(f"Deleted saved query with ID {args.query_id}.") else: console.print("Failed to delete saved query.") sys.exit(1) def cli_execute_saved_query_by_name(args: argparse.Namespace): """Execute saved query by name subcommand. Args: args (Namespace): Argparse Namespace. """ # do some sanity checking on page size before anything else if args.page_size > 1000: console.print("page size must be within [0,1000]. To fetch all pages, specify --pages -1 with any legal page size") sys.exit(1) s = InventorySearch(args.api_key) q = SavedQueries(args.api_key) # get query from name queries = q.get_saved_queries(args.query_name, 1, 1) results = queries.get("results") if not results: console.print("No saved query found with that name.") sys.exit(1) query = results[0]["query"] try: res = s.search(None, query, args.page_size, None, args.sort, args.fields, args.pages) console.print_json(json.dumps(res)) except CensysAsmException: console.print("Failed to execute saved query.") sys.exit(1) def cli_execute_saved_query_by_id(args: argparse.Namespace): """Execute saved query by id subcommand. Args: args (Namespace): Argparse Namespace. """ # do some sanity checking on page size before anything else if args.page_size > 1000: console.print("page size must be within [0,1000]. To fetch all pages, specify --pages -1 with any legal page size") sys.exit(1) s = InventorySearch(args.api_key) q = SavedQueries(args.api_key) try: query_json = q.get_saved_query_by_id(args.query_id) query = query_json["result"]["query"] except (KeyError, CensysAsmException): console.print("No saved query found with that ID.") sys.exit(1) try: res = s.search(None, query, args.page_size, None, args.sort, args.fields, args.pages) console.print_json(json.dumps(res)) except CensysAsmException: console.print("Failed to execute saved query.") sys.exit(1) def cli_search(args: argparse.Namespace): """Inventory search subcommand. Args: args (Namespace): Argparse Namespace. """ s = InventorySearch(args.api_key) try: res = s.search( args.workspaces, args.query, args.page_size, args.cursor, args.sort, args.fields, args.pages, ) console.print_json(json.dumps(res)) except CensysAsmException: console.print("Failed to execute query.") sys.exit(1) def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ asm_parser = parent_parser.add_parser("asm", description="Interact with the Censys ASM API", help="interact with ASM") def add_verbose(parser): parser.add_argument( "-v", "--verbose", help="verbose output", action="store_true", ) asm_subparser = asm_parser.add_subparsers() # Add config command config_parser = asm_subparser.add_parser( "config", description="Configure Censys ASM API Settings", help="configure Censys ASM API settings", ) config_parser.set_defaults(func=cli_asm_config) add_parser = asm_subparser.add_parser( "add-seeds", description="Add seeds to ASM", help="add seeds", parents=[parents["asm_auth"]], ) add_verbose(add_parser) add_seed_arguments(add_parser) add_parser.add_argument( "-l", "--label", help='label to apply to seeds without label (default: "")', type=str, default="", ) add_parser.set_defaults(func=cli_add_seeds) delete_parser = asm_subparser.add_parser( "delete-seeds", description="Delete ASM seeds", help="delete seeds", parents=[parents["asm_auth"]], ) add_verbose(delete_parser) add_seed_arguments(delete_parser, True) delete_parser.set_defaults(func=cli_delete_seeds) delete_all_parser = asm_subparser.add_parser( "delete-all-seeds", description="Delete all ASM seeds", help="delete all seeds", parents=[parents["asm_auth"]], ) delete_all_parser.add_argument( "-f", "--force", help="force delete all (no confirmation prompt)", action="store_true", ) add_verbose(delete_all_parser) delete_all_parser.set_defaults(func=cli_delete_all_seeds) delete_with_label_parser = asm_subparser.add_parser( "delete-labeled-seeds", description="Delete all ASM seeds with specified label", help="delete all seeds having label", parents=[parents["asm_auth"]], ) delete_with_label_parser.add_argument( "-l", "--label", help="label for which to delete all seeds", type=str, default="", required=True, ) add_verbose(delete_with_label_parser) delete_with_label_parser.set_defaults(func=cli_delete_seeds_with_label) replace_with_label_parser = asm_subparser.add_parser( "replace-labeled-seeds", description="Replace all ASM seeds with specified label with new seeds", help="replace all seeds having label", parents=[parents["asm_auth"]], ) replace_with_label_parser.add_argument( "-l", "--label", help="label for which to replace all seeds", type=str, default="", required=True, ) add_verbose(replace_with_label_parser) add_seed_arguments(replace_with_label_parser) replace_with_label_parser.set_defaults(func=cli_replace_seeds_with_label) list_parser = asm_subparser.add_parser( "list-seeds", description="List all ASM seeds, optionally filtered by label and type", help="list seeds", parents=[parents["asm_auth"]], ) list_parser.add_argument( "-t", "--type", help="type of the seed to list, if not specified, all types returned", choices=SEED_TYPES, default="", ) list_parser.add_argument( "-l", "--label", help="label of seeds to list, if not specified, all labels returned", type=str, default="", ) list_parser.add_argument("--csv", help="output in CSV format (otherwise JSON)", action="store_true") add_verbose(list_parser) list_parser.set_defaults(func=cli_list_seeds) list_saved_queries_parser = asm_subparser.add_parser( "list-saved-queries", description="List all ASM saved queries, optionally filtered by query name prefix and filter term", help="list saved queries", parents=[parents["asm_auth"]], ) list_saved_queries_parser.add_argument( "--query-name-prefix", help="Prefix for the saved query name to filter by", type=str, default="", ) list_saved_queries_parser.add_argument( "--filter-term", help="Term used to filter the list of saved query names and the saved queries", type=str, default="", ) list_saved_queries_parser.add_argument( "--page-size", help="Number of results to return. Defaults to 50.", type=int, default=50, ) list_saved_queries_parser.add_argument( "--page", help="Page number to begin at when searching. Defaults to 1.", type=int, default=1, ) list_saved_queries_parser.add_argument("--csv", help="output in CSV format (otherwise JSON)", action="store_true") add_verbose(list_saved_queries_parser) list_saved_queries_parser.set_defaults(func=cli_list_saved_queries) add_saved_query_parser = asm_subparser.add_parser( "add-saved-query", description="Add a saved query to ASM", help="add saved query", parents=[parents["asm_auth"]], ) add_saved_query_parser.add_argument( "--query-name", help="Name of the saved query", type=str, required=True, ) add_saved_query_parser.add_argument( "--query", help="Query string", type=str, required=True, ) add_verbose(add_saved_query_parser) add_saved_query_parser.set_defaults(func=cli_add_saved_query) get_saved_query_by_id_parser = asm_subparser.add_parser( "get-saved-query-by-id", description="Get a saved query by ID", help="get saved query by ID", parents=[parents["asm_auth"]], ) get_saved_query_by_id_parser.add_argument( "--query-id", help="ID of the saved query", type=str, required=True, ) add_verbose(get_saved_query_by_id_parser) get_saved_query_by_id_parser.set_defaults(func=cli_get_saved_query_by_id) edit_saved_query_by_id_parser = asm_subparser.add_parser( "edit-saved-query-by-id", description="Edit a saved query by ID", help="edit saved query by ID", parents=[parents["asm_auth"]], ) edit_saved_query_by_id_parser.add_argument( "--query-id", help="ID of the saved query", type=str, required=True, ) edit_saved_query_by_id_parser.add_argument( "--query-name", help="Name of the saved query", type=str, required=True, ) edit_saved_query_by_id_parser.add_argument( "--query", help="Query string", type=str, required=True, ) add_verbose(edit_saved_query_by_id_parser) edit_saved_query_by_id_parser.set_defaults(func=cli_edit_saved_query_by_id) delete_saved_query_by_id_parser = asm_subparser.add_parser( "delete-saved-query-by-id", description="Delete a saved query by ID", help="delete saved query by ID", parents=[parents["asm_auth"]], ) delete_saved_query_by_id_parser.add_argument( "--query-id", help="ID of the saved query", type=str, required=True, ) add_verbose(delete_saved_query_by_id_parser) delete_saved_query_by_id_parser.set_defaults(func=cli_delete_saved_query_by_id) execute_saved_query_by_name_parser = asm_subparser.add_parser( "execute-saved-query-by-name", description="Execute a saved query by name in inventory search", help="execute saved query by name", parents=[parents["asm_auth"]], ) execute_saved_query_by_name_parser.add_argument( "--query-name", help="Query name", type=str, required=True, ) execute_saved_query_by_name_parser.add_argument( "--page-size", help="Number of results to return. Defaults to 50.", type=int, default=50, ) execute_saved_query_by_name_parser.add_argument( "--sort", help="Sort order for results", type=str, nargs="+", default=[], ) execute_saved_query_by_name_parser.add_argument( "--fields", help="Fields to include in results", type=str, nargs="+", default=[], ) execute_saved_query_by_name_parser.add_argument( "--pages", help="Number of pages to return. Defaults to 1.", type=int, default=1, ) add_verbose(execute_saved_query_by_name_parser) execute_saved_query_by_name_parser.set_defaults(func=cli_execute_saved_query_by_name) execute_saved_query_by_id_parser = asm_subparser.add_parser( "execute-saved-query-by-id", description="Execute a saved query by id in inventory search", help="execute saved query by id", parents=[parents["asm_auth"]], ) execute_saved_query_by_id_parser.add_argument( "--query-id", help="Query ID", type=str, required=True, ) execute_saved_query_by_id_parser.add_argument( "--page-size", help="Number of results to return. Defaults to 50.", type=int, default=50, ) execute_saved_query_by_id_parser.add_argument( "--sort", help="Sort order for results", type=str, nargs="+", default=[], ) execute_saved_query_by_id_parser.add_argument( "--fields", help="Fields to include in results", type=str, nargs="+", default=[], ) execute_saved_query_by_id_parser.add_argument( "--pages", help="Number of pages to return. Defaults to 1.", type=int, default=1, ) add_verbose(execute_saved_query_by_id_parser) execute_saved_query_by_id_parser.set_defaults(func=cli_execute_saved_query_by_id) search_parser = asm_subparser.add_parser( "search", description="Execute a query in inventory search", help="execute query in inventory search", parents=[parents["asm_auth"]], ) search_parser.add_argument( "--query", help="Query string", type=str, required=True, ) search_parser.add_argument( "--page-size", help="Number of results to return. Defaults to 50.", type=int, default=50, ) search_parser.add_argument( "--cursor", help="Cursor to use for pagination", type=str, default="", ) search_parser.add_argument( "--sort", help="Sort order for results", type=str, nargs="+", default=[], ) search_parser.add_argument( "--fields", help="Fields to include in results", type=str, nargs="+", default=[], ) search_parser.add_argument( "--workspaces", help="Workspace IDs to search. Deprecated. The workspace associated with `CENSYS-API-KEY` will be used automatically.", type=str, required=False, ) search_parser.add_argument( "--pages", help="Number of pages to return. Defaults to 1.", type=int, default=1, ) add_verbose(search_parser) search_parser.set_defaults(func=cli_search) censys-censys-python-7a57a89/censys/cli/commands/config.py000066400000000000000000000061261524433232300237070ustar00rootroot00000000000000"""Censys config CLI.""" import argparse import os import sys from rich.prompt import Confirm, Prompt from censys.cli.utils import console from censys.common.config import DEFAULT, get_config, write_config from censys.common.exceptions import CensysUnauthorizedException from censys.search.v2.api import CensysSearchAPIv2 def cli_config(_: argparse.Namespace): # pragma: no cover """Config subcommand. Args: _: Argparse Namespace. """ api_id_prompt = "Censys API ID" api_secret_prompt = "Censys API Secret" config = get_config() api_id = config.get(DEFAULT, "api_id") api_secret = config.get(DEFAULT, "api_secret") api_id_env = os.getenv("CENSYS_API_ID") api_secret_env = os.getenv("CENSYS_API_SECRET") if api_id_env is not None or api_secret_env is not None: console.print( "Please note environment variables (CENSYS_API_ID & CENSYS_API_SECRET) " "will take priority over configured credentials." ) api_id = api_id_env or api_id api_secret = api_secret_env or api_secret if api_id and api_secret: redacted_id = api_id.replace(api_id[:32], 32 * "*") redacted_secret = api_secret.replace(api_secret[:28], 28 * "*") api_id_prompt = f"{api_id_prompt} [cyan]({redacted_id})[/cyan]" api_secret_prompt = f"{api_secret_prompt} [cyan]({redacted_secret})[/cyan]" api_id = Prompt.ask(api_id_prompt, console=console) or api_id api_secret = Prompt.ask(api_secret_prompt, console=console) or api_secret if not (api_id and api_secret): console.print("Please enter valid credentials") sys.exit(1) api_id = api_id.strip() api_secret = api_secret.strip() color = Confirm.ask("Do you want color output?", default=True, show_default=False, console=console) config.set(DEFAULT, "color", "auto" if color else "") try: client = CensysSearchAPIv2(api_id, api_secret) account = client.account() email = account.get("email") console.print(f"\nSuccessfully authenticated for {email}") # Assumes that login was successfully config.set(DEFAULT, "api_id", api_id) config.set(DEFAULT, "api_secret", api_secret) write_config(config) sys.exit(0) except CensysUnauthorizedException: console.print("Failed to authenticate") sys.exit(1) except PermissionError as e: console.print(e) console.print( "Cannot write config file to directory. " + "Please set the `CENSYS_CONFIG_PATH` environmental variable to a writeable location." ) sys.exit(1) def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ config_parser = parent_parser.add_parser( "config", description="Configure Censys Search API Settings", help="configure Censys search API settings", ) config_parser.set_defaults(func=cli_config) censys-censys-python-7a57a89/censys/cli/commands/hnri.py000066400000000000000000000130471524433232300234020ustar00rootroot00000000000000"""Censys HNRI CLI.""" import argparse import sys import webbrowser from typing import Any, Optional import requests from rich import box from rich.table import Table from censys.cli.utils import console from censys.common.exceptions import CensysCLIException, CensysNotFoundException from censys.search import CensysHosts class CensysHNRI: """Searches the Censys API for the user's current IP to scan for risks.""" HIGH_RISK_DEFINITION: list[str] = ["TELNET", "REDIS", "POSTGRES", "VNC"] MEDIUM_RISK_DEFINITION: list[str] = ["SSH", "HTTP", "HTTPS"] def __init__(self, api_id: Optional[str] = None, api_secret: Optional[str] = None): """Inits CensysHNRI. Args: api_id (str): Optional; The API ID provided by Censys. api_secret (str): Optional; The API secret provided by Censys. """ self.index = CensysHosts(api_id, api_secret) @staticmethod def get_current_ip() -> str: """Uses ipify.org to get the current IP address. Returns: str: IP address. """ response = requests.get("https://api.ipify.org?format=json") current_ip = str(response.json().get("ip")) return current_ip def translate_risk(self, services: list[dict]) -> tuple[list[dict], list[dict]]: """Interpret protocols to risks. Args: services (list): List of services. Returns: Tuple[list, list]: Lists of high and medium risks. """ high_risk = [] medium_risk = [] for service in services: service_name = service.get("service_name") if service_name in self.HIGH_RISK_DEFINITION: high_risk.append(service) elif service_name in self.MEDIUM_RISK_DEFINITION: medium_risk.append(service) else: medium_risk.append(service) return high_risk, medium_risk def make_risks_into_table(self, title: str, risks: list[dict]) -> Table: """Creates a table of risks. Args: title (str): Title of the table. risks (list): List of risks. Returns: Table: Table of risks. """ table = Table("Port", "Service Name", title=title, box=box.SQUARE) for risk in risks: table.add_row(str(risk.get("port")), risk.get("service_name")) return table def risks_to_string(self, high_risks: list, medium_risks: list) -> list[Any]: """Risks to printable string. Args: high_risks (list): Lists of high risks. medium_risks (list): Lists of medium risks. Raises: CensysCLIException: No information/risks found. Returns: list: Printable objects for CLI. """ len_high_risk = len(high_risks) len_medium_risk = len(medium_risks) if len_high_risk + len_medium_risk == 0: raise CensysCLIException response: list[Any] = [] if len_high_risk > 0: response.append( self.make_risks_into_table( ":exclamation: High Risks Found", high_risks, ) ) else: response.append("You don't have any High Risks in your network\n") if len_medium_risk > 0: response.append( self.make_risks_into_table( ":grey_exclamation: Medium Risks Found", medium_risks, ) ) else: response.append("You don't have any Medium Risks in your network\n") return response def view_current_ip_risks(self): """Gets protocol information for the current IP and returns any risks.""" current_ip = self.get_current_ip() try: console.print(f"Searching for information on {current_ip}...") results = self.index.view(current_ip) services = results.get("services", []) high_risk, medium_risk = self.translate_risk(services) for res in self.risks_to_string(high_risk, medium_risk): console.print(res) console.print( f"\nFor more information, please visit: https://search.censys.io/hosts/{current_ip}" # noqa: E231 ) except (CensysNotFoundException, CensysCLIException): console.print("[green]:white_check_mark: No Risks were found on your network[/green]") def cli_hnri(args: argparse.Namespace): """HNRI subcommand. Args: args (Namespace): Argparse Namespace. """ if args.open: webbrowser.open("https://search.censys.io/me") sys.exit(0) client = CensysHNRI(args.api_id, args.api_secret) client.view_current_ip_risks() def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ hnri_parser = parent_parser.add_parser( "hnri", description="Home Network Risk Identifier (H.N.R.I.)", help="home network risk identifier", parents=[parents["auth"]], ) hnri_parser.add_argument( "-O", "--open", action="store_true", help="open your IP in browser", ) hnri_parser.set_defaults(func=cli_hnri) censys-censys-python-7a57a89/censys/cli/commands/search.py000066400000000000000000000157201524433232300237070ustar00rootroot00000000000000"""Censys search CLI.""" import argparse import json import sys import webbrowser from pathlib import Path from typing import Any from urllib.parse import urlencode from censys.cli.utils import V2_INDEXES, err_console, write_file from censys.common.exceptions import CensysCLIException from censys.search import SearchClient from censys.search.v2.api import CensysSearchAPIv2 Results = list[dict] DATA_DIR = Path(__file__).parent.parent / "data" HOSTS_AUTOCOMPLETE = DATA_DIR / "hosts_autocomplete.json" CERTIFICATES_AUTOCOMPLETE = DATA_DIR / "certificates_autocomplete.json" def fields_completer(prefix: str, parsed_args: argparse.Namespace, **kwargs) -> list[str]: """Fields completer. Args: prefix (str): Prefix to complete. parsed_args (Namespace): Argparse Namespace. **kwargs: Keyword arguments. Returns: List[str]: List of fields. """ index_type = parsed_args.index_type autocomplete_json = {} if index_type == "hosts": with HOSTS_AUTOCOMPLETE.open() as autocomplete_file: autocomplete_json = json.load(autocomplete_file) elif index_type == "certificates": with CERTIFICATES_AUTOCOMPLETE.open() as autocomplete_file: autocomplete_json = json.load(autocomplete_file) else: return [] autocomplete_data = autocomplete_json.get("data", []) fields = [field_value for field in autocomplete_data if not (field_value := field["value"]).endswith(".type")] if not prefix: # Returns first 20 fields if no prefix is provided return fields[:20] return fields def cli_search(args: argparse.Namespace): """Search subcommand. Args: args (Namespace): Argparse Namespace. Raises: CensysCLIException: If invalid options are provided. """ index_type = args.index_type or args.query_type if args.open: url_query = {"q": args.query, "resource": index_type} webbrowser.open( f"https://search.censys.io/search?{urlencode(url_query)}" # noqa: E231 ) sys.exit(0) if args.timeout < 1: raise CensysCLIException("Timeout must be greater than 0.") censys_args = { "timeout": args.timeout, } if args.api_id: censys_args["api_id"] = args.api_id if args.api_secret: censys_args["api_secret"] = args.api_secret c = SearchClient(**censys_args) search_args = {} write_args = {"file_format": args.format, "file_path": args.output} results: list[dict[str, Any]] = [] index: CensysSearchAPIv2 = getattr(c.v2, index_type) search_args.update( { "pages": args.pages, "per_page": args.per_page, "fields": args.fields, } ) if index_type == "hosts": search_args.update( { "sort": args.sort_order, "virtual_hosts": args.virtual_hosts, } ) elif index_type == "certificates": search_args.update({"sort": args.sort}) if args.output and not args.output.endswith(".json"): raise CensysCLIException("JSON is the only valid file format for Search 2.0 responses.") write_args.update( { "file_format": "json" if args.output else "screen", } ) with err_console.status("Searching"): try: for page in index.search(args.query, **search_args): results.extend(page) except Exception: err_console.print_exception(max_frames=4) except KeyboardInterrupt: # pragma: no cover pass try: write_file(results, **write_args) except ValueError as error: # pragma: no cover err_console.print(f"Error writing log file. Error: {error}") def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ search_parser = parent_parser.add_parser( "search", description="Query Censys Search for resource data by providing a query \ string, the resource index, and the fields to be returned", help="query Censys search", parents=[parents["auth"]], ) search_parser.add_argument( "query", type=str, help="a string written in Censys Search syntax", ).completer = fields_completer index_metavar = "|".join(V2_INDEXES) index_default = "hosts" search_parser.add_argument( "--index-type", type=str, default=index_default, choices=V2_INDEXES, metavar=index_metavar, help="which resource index to query", ) # Backwards compatibility search_parser.add_argument( "-f", "--format", type=str, default="screen", choices=["screen", "json"], metavar="screen|json", help=argparse.SUPPRESS, ) search_parser.add_argument( "-o", "--output", type=str, help="output file path", ) search_parser.add_argument( "-O", "--open", action="store_true", help="open query in browser", ) search_parser.add_argument( "--pages", default=1, type=int, help="number of pages of results to return (when set to -1 returns all pages available)", ) search_parser.add_argument( "--per-page", default=100, type=int, help="number of results to return per page", ) search_parser.add_argument( "--timeout", default=30, type=int, help="number of seconds to wait for a response", ) search_parser.add_argument( "--fields", dest="fields", type=str, nargs="+", help="additional fields to return in the matching results", ).completer = fields_completer hosts_group = search_parser.add_argument_group("hosts specific arguments") hosts_group.add_argument( "--sort-order", dest="sort_order", type=str, default="RELEVANCE", choices=["RELEVANCE", "ASCENDING", "DESCENDING", "RANDOM"], help="sort order of results", ) hosts_group.add_argument( "--virtual-hosts", type=str, default="EXCLUDE", choices=["INCLUDE", "EXCLUDE", "ONLY"], metavar="INCLUDE|EXCLUDE|ONLY", help="whether to include virtual hosts in the results", ) certs_group = search_parser.add_argument_group("certificates specific arguments") certs_group.add_argument( "--sort", dest="sort", type=str, nargs="+", help="fields to sort by", ) search_parser.set_defaults(func=cli_search) censys-censys-python-7a57a89/censys/cli/commands/subdomains.py000066400000000000000000000063731524433232300246120ustar00rootroot00000000000000"""Censys subdomains CLI.""" import argparse import json import sys from censys.cli.utils import console, err_console from censys.common.exceptions import ( CensysException, CensysRateLimitExceededException, CensysUnauthorizedException, ) from censys.search import CensysCerts def print_subdomains(subdomains: set[str], as_json: bool = False): """Print subdomains. Args: subdomains (Set[str]): Subdomains. as_json (bool): Output in JSON format. """ if as_json: console.print_json(json.dumps(list(subdomains))) else: for subdomain in subdomains: console.print(f" - {subdomain}") # noqa: E221 def cli_subdomains(args: argparse.Namespace): # pragma: no cover """Subdomain subcommand. Args: args: Argparse Namespace. """ subdomains = set() try: client = CensysCerts(api_id=args.api_id, api_secret=args.api_secret) certificate_query = f"names: {args.domain}" with err_console.status(f"Querying {args.domain} subdomains"): query = client.search(certificate_query, per_page=100, pages=args.pages) # 100 is the max per page # Flatten the result, and remove duplicates for hits in query: for cert in hits: new_subdomains: list[str] = cert.get("names", []) subdomains.update([subdomain for subdomain in new_subdomains if subdomain.endswith(args.domain)]) # Don't make console prints if we're in json mode if not args.json: if len(subdomains) == 0: err_console.print(f"No subdomains found for {args.domain}") return console.print(f"Found {len(subdomains)} unique subdomain(s) of {args.domain}") print_subdomains(subdomains, args.json) except CensysRateLimitExceededException: err_console.print("Censys API rate limit exceeded") print_subdomains(subdomains, args.json) except CensysUnauthorizedException: err_console.print("Invalid Censys API ID or secret") sys.exit(1) except CensysException as e: err_console.print(str(e)) print_subdomains(subdomains, args.json) sys.exit(1) except KeyboardInterrupt: err_console.print("Caught Ctrl-C, exiting...") print_subdomains(subdomains, args.json) sys.exit(1) def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ subdomains_parser = parent_parser.add_parser( "subdomains", description="Enumerates subdomains using the Censys Search Certificates index", help="enumerate subdomains", parents=[parents["auth"]], ) subdomains_parser.add_argument("domain", help="The base domain to search for") subdomains_parser.add_argument("--pages", type=int, default=1, help="Max records to query") subdomains_parser.add_argument("-j", "--json", action="store_true", help="Output in JSON format") subdomains_parser.set_defaults(func=cli_subdomains) censys-censys-python-7a57a89/censys/cli/commands/view.py000066400000000000000000000073771524433232300234250ustar00rootroot00000000000000"""Censys view CLI.""" import argparse import ipaddress import sys import webbrowser from censys.cli.utils import ( V2_INDEXES, console, err_console, valid_datetime_type, write_file, ) from censys.common.exceptions import CensysCLIException from censys.search import SearchClient from censys.search.v2.api import CensysSearchAPIv2 def cli_view(args: argparse.Namespace): """Search subcommand. Args: args (Namespace): Argparse Namespace. Raises: CensysCLIException: If invalid options are provided. """ if args.open: webbrowser.open( f"https://search.censys.io/{args.index_type}/{args.document_id}" # noqa: E231 ) sys.exit(0) censys_args = {} if args.api_id: censys_args["api_id"] = args.api_id if args.api_secret: censys_args["api_secret"] = args.api_secret c = SearchClient(**censys_args) index_type = args.index_type if index_type == "hosts": try: ip_address = args.document_id if "+" in ip_address: ip_address, _ = args.document_id.split("+") ipaddress.ip_address(ip_address) except ValueError: if len(args.document_id) == 64: err_console.print("This is a SHA-256 certificate fingerprint. Switching to certificates index.") index_type = "certificates" else: raise CensysCLIException( f"Invalid IP address: {args.document_id}. Please provide a valid IPv4 or IPv6 address." ) index: CensysSearchAPIv2 = getattr(c.v2, index_type) view_args = {} write_args = { "file_format": "json" if args.output else "screen", "file_path": args.output, } if args.at_time: if index_type == "hosts": view_args["at_time"] = args.at_time else: err_console.print("The --at-time option is only supported for the hosts index. Ignoring.") document = index.view(args.document_id, **view_args) try: write_file(document, **write_args) except ValueError as error: # pragma: no cover console.print(f"Error writing log file. Error: {error}") def include(parent_parser: argparse._SubParsersAction, parents: dict): """Include this subcommand into the parent parser. Args: parent_parser (argparse._SubParsersAction): Parent parser. parents (dict): Parent arg parsers. """ view_parser = parent_parser.add_parser( "view", description="View a document in Censys Search by providing a document \ id and the resource index", help="view document", parents=[parents["auth"]], ) view_parser.add_argument( "document_id", type=str, help="a document id (IP address or SHA-256 certificate fingerprint) to view", ) view_parser.add_argument( "--index-type", type=str, default="hosts", choices=V2_INDEXES, metavar="|".join(V2_INDEXES), help="which resource index to query", ) view_parser.add_argument( "-o", "--output", type=str, help="json output file path", ) view_parser.add_argument( "-O", "--open", action="store_true", help="open document in browser", ) hosts_group = view_parser.add_argument_group("hosts specific arguments") hosts_group.add_argument( "--at-time", type=valid_datetime_type, metavar="YYYY-MM-DD (HH:mm)", help="Fetches a document at a given point in time", ) view_parser.set_defaults(func=cli_view) censys-censys-python-7a57a89/censys/cli/data/000077500000000000000000000000001524433232300211735ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/cli/data/certificates_autocomplete.json000066400000000000000000002431711524433232300273240ustar00rootroot00000000000000{ "data": [ { "data": { "category": " METADATA" }, "value": "added_at" }, { "data": { "category": " METADATA" }, "value": "labels" }, { "data": { "category": " METADATA" }, "value": "modified_at" }, { "data": { "category": " METADATA" }, "value": "parse_status" }, { "data": { "category": " CERTIFICATE TRANSPARENCY LOGS" }, "value": "ct.entries.key" }, { "data": { "category": " CERTIFICATE TRANSPARENCY LOGS" }, "value": "ct.entries.value.added_to_ct_at" }, { "data": { "category": " CERTIFICATE TRANSPARENCY LOGS" }, "value": "ct.entries.value.ct_to_censys_at" }, { "data": { "category": " CERTIFICATE TRANSPARENCY LOGS" }, "value": "ct.entries.value.index" }, { "data": { "category": " MISC" }, "value": "ever_seen_in_scan" }, { "data": { "category": " MISC" }, "value": "parent_spki_subject_fingerprint_sha256" }, { "data": { "category": " MISC" }, "value": "parsed.redacted" }, { "data": { "category": " MISC" }, "value": "parsed.serial_number_hex" }, { "data": { "category": " MISC" }, "value": "parsed.version" }, { "data": { "category": " MISC" }, "value": "precert" }, { "data": { "category": " MISC" }, "value": "spki_subject_fingerprint_sha256" }, { "data": { "category": " MISC" }, "value": "validation_level" }, { "data": { "category": " FINGERPRINT" }, "value": "fingerprint_md5" }, { "data": { "category": " FINGERPRINT" }, "value": "fingerprint_sha1" }, { "data": { "category": " FINGERPRINT" }, "value": "fingerprint_sha256" }, { "data": { "category": " FINGERPRINT" }, "value": "tbs_fingerprint_sha256" }, { "data": { "category": " FINGERPRINT" }, "value": "tbs_no_ct_fingerprint_sha256" }, { "data": { "category": " BASIC INFORMATION" }, "value": "names" }, { "data": { "category": " BASIC INFORMATION" }, "value": "parsed.issuer_dn" }, { "data": { "category": " BASIC INFORMATION" }, "value": "parsed.serial_number" }, { "data": { "category": " BASIC INFORMATION" }, "value": "parsed.subject_dn" }, { "data": { "category": " BASIC INFORMATION" }, "value": "validated_at" }, { "data": { "category": " AUTHORITY INFO ACCESS (AIA)" }, "value": "parsed.extensions.authority_info_access.issuer_urls" }, { "data": { "category": " AUTHORITY INFO ACCESS (AIA)" }, "value": "parsed.extensions.authority_info_access.ocsp_urls" }, { "data": { "category": " AUTHORITY KEY ID (AKID)" }, "value": "parsed.extensions.authority_key_id" }, { "data": { "category": " BASIC CONSTRAINTS" }, "value": "parsed.extensions.basic_constraints.is_ca" }, { "data": { "category": " BASIC CONSTRAINTS" }, "value": "parsed.extensions.basic_constraints.max_path_len" }, { "data": { "category": " CABF ORGANIZATION ID EXTENSION" }, "value": "parsed.extensions.cabf_organization_id.country" }, { "data": { "category": " CABF ORGANIZATION ID EXTENSION" }, "value": "parsed.extensions.cabf_organization_id.reference" }, { "data": { "category": " CABF ORGANIZATION ID EXTENSION" }, "value": "parsed.extensions.cabf_organization_id.scheme" }, { "data": { "category": " CABF ORGANIZATION ID EXTENSION" }, "value": "parsed.extensions.cabf_organization_id.state" }, { "data": { "category": " CERTIFICATE POLICIES" }, "value": "parsed.extensions.certificate_policies.cps" }, { "data": { "category": " CERTIFICATE POLICIES" }, "value": "parsed.extensions.certificate_policies.id" }, { "data": { "category": " CERTIFICATE POLICIES" }, "value": "parsed.extensions.certificate_policies.user_notice.explicit_text" }, { "data": { "category": " CERTIFICATE POLICIES" }, "value": "parsed.extensions.certificate_policies.user_notice.notice_reference.notice_numbers" }, { "data": { "category": " CERTIFICATE POLICIES" }, "value": "parsed.extensions.certificate_policies.user_notice.notice_reference.organization" }, { "data": { "category": " CRL DISTRIBUTION POINTS" }, "value": "parsed.extensions.crl_distribution_points" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.ct_poison" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.log_id" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.signature.hash_algorithm" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.signature.signature" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.signature.signature_algorithm" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.timestamp" }, { "data": { "category": " EMBEDDED SCTS / CT POISON" }, "value": "parsed.extensions.signed_certificate_timestamps.version" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.any" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_code_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_code_signing_development" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_code_signing_third_party" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_development_env" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_env" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_maintenance_env" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_production_env" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_qos" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_test_env" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_tier0_qos" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_tier1_qos" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_tier2_qos" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_crypto_tier3_qos" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_ichat_encryption" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_ichat_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_resource_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_software_update_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.apple_system_identity" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.client_auth" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.code_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.dvcs" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.eap_over_lan" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.eap_over_ppp" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.email_protection" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.ipsec_end_system" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.ipsec_intermediate_system_usage" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.ipsec_tunnel" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.ipsec_user" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_ca_exchange" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_cert_trust_list_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_csp_signature" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_document_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_drm" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_drm_individualization" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_efs_recovery" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_embedded_nt_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_encrypted_file_system" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_enrollment_agent" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_kernel_mode_code_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_key_recovery_21" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_key_recovery_3" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_license_server" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_licenses" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_lifetime_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_mobile_device_software" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_nt5_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_oem_whql_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_qualified_subordinate" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_root_list_signer" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_server_gated_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_sgc_serialized" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_smart_display" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_smartcard_logon" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_system_health" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_system_health_loophole" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_timestamp_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.microsoft_whql_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.netscape_server_gated_crypto" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.ocsp_signing" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.sbgp_cert_aa_service_auth" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.server_auth" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.time_stamping" }, { "data": { "category": " EXTENDED KEY USAGE" }, "value": "parsed.extensions.extended_key_usage.unknown" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.common_name" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.country" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.domain_component" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.email_address" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.given_name" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.jurisdiction_country" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.jurisdiction_locality" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.jurisdiction_province" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.locality" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.organization" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.organization_id" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.organizational_unit" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.postal_code" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.province" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.serial_number" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.street_address" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.directory_names.surname" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.dns_names" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.edi_party_names.name_assigner" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.edi_party_names.party_name" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.email_addresses" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.ip_addresses" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.other_names.id" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.other_names.value" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.registered_ids" }, { "data": { "category": " ISSUER ALTERNATE NAMES (IANS)" }, "value": "parsed.extensions.issuer_alt_name.uniform_resource_identifiers" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.certificate_sign" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.content_commitment" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.crl_sign" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.data_encipherment" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.decipher_only" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.digital_signature" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.encipher_only" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.key_agreement" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.key_encipherment" }, { "data": { "category": " KEY USAGE" }, "value": "parsed.extensions.key_usage.value" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.critical" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.common_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.country" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.domain_component" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.email_address" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.given_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_country" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_locality" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_province" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.locality" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.organization" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.organization_id" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.organizational_unit" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.postal_code" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.province" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.serial_number" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.street_address" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_directory_names.surname" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_edi_party_names.name_assigner" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_edi_party_names.party_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_email_addresses" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_ip_addresses.begin" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_ip_addresses.cidr" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_ip_addresses.end" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_ip_addresses.mask" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_names" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_registered_ids" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.excluded_uris" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.common_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.country" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.domain_component" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.email_address" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.given_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_country" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_locality" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_province" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.locality" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.organization" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.organization_id" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.organizational_unit" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.postal_code" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.province" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.serial_number" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.street_address" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_directory_names.surname" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_edi_party_names.name_assigner" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_edi_party_names.party_name" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_email_addresses" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_ip_addresses.begin" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_ip_addresses.cidr" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_ip_addresses.end" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_ip_addresses.mask" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_names" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_registered_ids" }, { "data": { "category": " NAME CONSTRAINTS" }, "value": "parsed.extensions.name_constraints.permitted_uris" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.ids" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.etsi_compliance" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.legislation.country_codes" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.limit.amount" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.limit.currency" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.limit.currency_number" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.limit.exponent" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.pds_locations.language" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.pds_locations.url" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.retention_period" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.sscd" }, { "data": { "category": " QC STATEMENTS EXTENSION" }, "value": "parsed.extensions.qc_statements.parsed.types.ids" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.common_name" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.country" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.domain_component" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.email_address" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.given_name" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.jurisdiction_country" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.jurisdiction_locality" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.jurisdiction_province" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.locality" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.organization" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.organization_id" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.organizational_unit" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.postal_code" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.province" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.serial_number" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.street_address" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.directory_names.surname" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.dns_names" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.edi_party_names.name_assigner" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.edi_party_names.party_name" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.email_addresses" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.ip_addresses" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.other_names.id" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.other_names.value" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.registered_ids" }, { "data": { "category": " SUBJECT ALTERNATE NAMES (SANS)" }, "value": "parsed.extensions.subject_alt_name.uniform_resource_identifiers" }, { "data": { "category": " SUBJECT KEY ID (SKID)" }, "value": "parsed.extensions.subject_key_id" }, { "data": { "category": " TOR SERVICE DESCRIPTORS" }, "value": "parsed.extensions.tor_service_descriptors.algorithm_name" }, { "data": { "category": " TOR SERVICE DESCRIPTORS" }, "value": "parsed.extensions.tor_service_descriptors.hash" }, { "data": { "category": " TOR SERVICE DESCRIPTORS" }, "value": "parsed.extensions.tor_service_descriptors.hash_bits" }, { "data": { "category": " TOR SERVICE DESCRIPTORS" }, "value": "parsed.extensions.tor_service_descriptors.onion" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.common_name" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.country" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.domain_component" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.email_address" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.given_name" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.jurisdiction_country" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.jurisdiction_locality" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.jurisdiction_province" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.locality" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.organization" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.organization_id" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.organizational_unit" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.postal_code" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.province" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.serial_number" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.street_address" }, { "data": { "category": " ISSUER" }, "value": "parsed.issuer.surname" }, { "data": { "category": " SIGNATURE" }, "value": "parsed.signature.self_signed" }, { "data": { "category": " SIGNATURE" }, "value": "parsed.signature.signature_algorithm.name" }, { "data": { "category": " SIGNATURE" }, "value": "parsed.signature.signature_algorithm.oid" }, { "data": { "category": " SIGNATURE" }, "value": "parsed.signature.valid" }, { "data": { "category": " SIGNATURE" }, "value": "parsed.signature.value" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.common_name" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.country" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.domain_component" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.email_address" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.given_name" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.jurisdiction_country" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.jurisdiction_locality" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.jurisdiction_province" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.locality" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.organization" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.organization_id" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.organizational_unit" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.postal_code" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.province" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.serial_number" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.street_address" }, { "data": { "category": " SUBJECT" }, "value": "parsed.subject.surname" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.dsa.g" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.dsa.p" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.dsa.q" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.dsa.y" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.b" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.curve" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.gx" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.gy" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.length" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.n" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.p" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.pub" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.x" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.ecdsa.y" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.fingerprint_sha256" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.key_algorithm.name" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.key_algorithm.oid" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.rsa.exponent" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.rsa.length" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.rsa.modulus" }, { "data": { "category": " PUBLIC KEY" }, "value": "parsed.subject_key_info.unrecognized.raw" }, { "data": { "category": " UNKNOWN EXTENSIONS" }, "value": "parsed.unknown_extensions.critical" }, { "data": { "category": " UNKNOWN EXTENSIONS" }, "value": "parsed.unknown_extensions.id" }, { "data": { "category": " UNKNOWN EXTENSIONS" }, "value": "parsed.unknown_extensions.value" }, { "data": { "category": " VALIDITY PERIOD" }, "value": "parsed.validity_period.length_seconds" }, { "data": { "category": " VALIDITY PERIOD" }, "value": "parsed.validity_period.not_after" }, { "data": { "category": " VALIDITY PERIOD" }, "value": "parsed.validity_period.not_before" }, { "data": { "category": " CRL VALIDATION" }, "value": "revocation.crl.next_update" }, { "data": { "category": " CRL VALIDATION" }, "value": "revocation.crl.reason" }, { "data": { "category": " CRL VALIDATION" }, "value": "revocation.crl.revocation_time" }, { "data": { "category": " CRL VALIDATION" }, "value": "revocation.crl.revoked" }, { "data": { "category": " CRL VALIDATION" }, "value": "revoked" }, { "data": { "category": " OCSP VALIDATION" }, "value": "revocation.ocsp.next_update" }, { "data": { "category": " OCSP VALIDATION" }, "value": "revocation.ocsp.reason" }, { "data": { "category": " OCSP VALIDATION" }, "value": "revocation.ocsp.revocation_time" }, { "data": { "category": " OCSP VALIDATION" }, "value": "revocation.ocsp.revoked" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.chains.sha256fp" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.ever_valid" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.had_trusted_path" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.has_trusted_path" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.in_revocation_set" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.is_valid" }, { "data": { "category": " APPLE VALIDATION" }, "value": "validation.apple.parents" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.chains.sha256fp" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.ever_valid" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.had_trusted_path" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.has_trusted_path" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.in_revocation_set" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.is_valid" }, { "data": { "category": " CHROME VALIDATION" }, "value": "validation.chrome.parents" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.chains.sha256fp" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.ever_valid" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.had_trusted_path" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.has_trusted_path" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.in_revocation_set" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.is_valid" }, { "data": { "category": " MICROSOFT VALIDATION" }, "value": "validation.microsoft.parents" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.chains.sha256fp" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.ever_valid" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.had_trusted_path" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.has_trusted_path" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.in_revocation_set" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.is_valid" }, { "data": { "category": " NSS (FIREFOX) VALIDATION" }, "value": "validation.nss.parents" }, { "data": { "category": " ZLINT" }, "value": "zlint.errors_present" }, { "data": { "category": " ZLINT" }, "value": "zlint.failed_lints" }, { "data": { "category": " ZLINT" }, "value": "zlint.fatals_present" }, { "data": { "category": " ZLINT" }, "value": "zlint.notices_present" }, { "data": { "category": " ZLINT" }, "value": "zlint.timestamp" }, { "data": { "category": " ZLINT" }, "value": "zlint.version" }, { "data": { "category": " ZLINT" }, "value": "zlint.warnings_present" } ] } censys-censys-python-7a57a89/censys/cli/data/hosts_autocomplete.json000066400000000000000000014730301524433232300260170ustar00rootroot00000000000000{ "data": [ { "data": { "category": " HOST INFORMATION" }, "value": "ip" }, { "data": { "category": " HOST INFORMATION" }, "value": "ip.type" }, { "data": { "category": " HOST INFORMATION" }, "value": "name" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.banner" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.banner_hex" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.extended_service_name" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.perspective_id" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.port" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.port.type" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.service_name" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.source_ip" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.source_ip.type" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.transport_protocol" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.truncated" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.truncated.type" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.banner_grab.banner" }, { "data": { "category": " SERVICE INFORMATION" }, "value": "services.banner_grab.transport" }, { "data": { "category": " HOST DNS" }, "value": "dns.names" }, { "data": { "category": " HOST DNS" }, "value": "dns.reverse_dns.names" }, { "data": { "category": " HOST DNS" }, "value": "dns.reverse_dns.resolved_at" }, { "data": { "category": " HOST DNS" }, "value": "dns.reverse_dns.resolved_at.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.city" }, { "data": { "category": " HOST LOCATION" }, "value": "location.continent" }, { "data": { "category": " HOST LOCATION" }, "value": "location.continent.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.coordinates.latitude" }, { "data": { "category": " HOST LOCATION" }, "value": "location.coordinates.latitude.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.coordinates.longitude" }, { "data": { "category": " HOST LOCATION" }, "value": "location.coordinates.longitude.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.country" }, { "data": { "category": " HOST LOCATION" }, "value": "location.country_code" }, { "data": { "category": " HOST LOCATION" }, "value": "location.country_code.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.postal_code" }, { "data": { "category": " HOST LOCATION" }, "value": "location.postal_code.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.province" }, { "data": { "category": " HOST LOCATION" }, "value": "location.registered_country" }, { "data": { "category": " HOST LOCATION" }, "value": "location.registered_country_code" }, { "data": { "category": " HOST LOCATION" }, "value": "location.registered_country_code.type" }, { "data": { "category": " HOST LOCATION" }, "value": "location.timezone" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.component_uniform_resource_identifiers" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.cpe" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.edition" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.language" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.other.key" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.other.value" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.part" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.part.type" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.product" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.source" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.sw_edition" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.target_hw" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.target_sw" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.update" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.vendor" }, { "data": { "category": " HOST OPERATING SYSTEM" }, "value": "operating_system.version" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.asn" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.asn.type" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.bgp_prefix" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.country_code" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.country_code.type" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.description" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.name" }, { "data": { "category": " HOST AUTONOMOUS SYSTEM" }, "value": "autonomous_system.organization" }, { "data": { "category": " TEAM SPECIFIC FIELDS" }, "value": "tags" }, { "data": { "category": " TLS" }, "value": "services.certificate" }, { "data": { "category": " TLS" }, "value": "services.jarm.cipher_and_version_fingerprint" }, { "data": { "category": " TLS" }, "value": "services.jarm.fingerprint" }, { "data": { "category": " TLS" }, "value": "services.jarm.observed_at" }, { "data": { "category": " TLS" }, "value": "services.jarm.observed_at.type" }, { "data": { "category": " TLS" }, "value": "services.jarm.tls_extensions_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.added_at" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.added_at.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.key" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.added_to_ct_at" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.added_to_ct_at.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.ct_to_censys_at" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.ct_to_censys_at.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.index" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ct.entries.value.index.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ever_seen_in_scan" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.ever_seen_in_scan.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.fingerprint_md5" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.fingerprint_sha1" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.modified_at" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.modified_at.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parent_spki_subject_fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parse_status" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.authority_info_access.issuer_urls" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.authority_info_access.ocsp_urls" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.authority_key_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.basic_constraints.is_ca" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.basic_constraints.is_ca.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.basic_constraints.max_path_len" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.basic_constraints.max_path_len.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.cabf_organization_id.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.cabf_organization_id.reference" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.cabf_organization_id.scheme" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.cabf_organization_id.state" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.cps" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice.explicit_text" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice.notice_reference.notice_numbers" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice.notice_reference.notice_numbers.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.certificate_policies.user_notice.notice_reference.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.crl_distribution_points" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.ct_poison" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.ct_poison.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.any" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.any.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing_development" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing_development.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing_third_party" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_code_signing_third_party.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_development_env" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_development_env.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_env" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_env.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_maintenance_env" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_maintenance_env.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_production_env" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_production_env.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_qos" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_qos.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_test_env" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_test_env.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier0_qos" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier0_qos.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier1_qos" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier1_qos.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier2_qos" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier2_qos.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier3_qos" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_crypto_tier3_qos.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_ichat_encryption" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_ichat_encryption.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_ichat_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_ichat_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_resource_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_resource_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_software_update_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_software_update_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_system_identity" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.apple_system_identity.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.client_auth" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.client_auth.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.code_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.code_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.dvcs" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.dvcs.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.eap_over_lan" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.eap_over_lan.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.eap_over_ppp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.eap_over_ppp.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.email_protection" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.email_protection.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_end_system" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_end_system.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_intermediate_system_usage" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_intermediate_system_usage.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_tunnel" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_tunnel.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_user" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ipsec_user.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_ca_exchange" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_ca_exchange.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_cert_trust_list_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_cert_trust_list_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_csp_signature" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_csp_signature.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_document_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_document_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_drm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_drm.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_drm_individualization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_drm_individualization.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_efs_recovery" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_efs_recovery.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_embedded_nt_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_embedded_nt_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_encrypted_file_system" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_encrypted_file_system.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_enrollment_agent" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_enrollment_agent.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_kernel_mode_code_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_kernel_mode_code_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_key_recovery_21" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_key_recovery_21.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_key_recovery_3" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_key_recovery_3.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_license_server" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_license_server.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_licenses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_licenses.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_lifetime_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_lifetime_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_mobile_device_software" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_mobile_device_software.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_nt5_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_nt5_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_oem_whql_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_oem_whql_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_qualified_subordinate" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_qualified_subordinate.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_root_list_signer" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_root_list_signer.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_server_gated_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_server_gated_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_sgc_serialized" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_sgc_serialized.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_smart_display" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_smart_display.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_smartcard_logon" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_smartcard_logon.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_system_health" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_system_health.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_system_health_loophole" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_system_health_loophole.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_timestamp_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_timestamp_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_whql_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.microsoft_whql_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.netscape_server_gated_crypto" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.netscape_server_gated_crypto.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ocsp_signing" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.ocsp_signing.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.sbgp_cert_aa_service_auth" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.sbgp_cert_aa_service_auth.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.server_auth" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.server_auth.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.time_stamping" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.time_stamping.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.extended_key_usage.unknown" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.directory_names.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.dns_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.edi_party_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.edi_party_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.edi_party_names.name_assigner" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.edi_party_names.party_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.email_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.ip_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.other_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.other_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.other_names.id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.other_names.value" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.registered_ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.issuer_alt_name.uniform_resource_identifiers" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.certificate_sign" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.certificate_sign.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.content_commitment" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.content_commitment.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.crl_sign" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.crl_sign.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.data_encipherment" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.data_encipherment.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.decipher_only" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.decipher_only.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.digital_signature" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.digital_signature.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.encipher_only" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.encipher_only.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.key_agreement" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.key_agreement.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.key_encipherment" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.key_encipherment.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.value" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.key_usage.value.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.critical" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.critical.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_directory_names.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_edi_party_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_edi_party_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_edi_party_names.name_assigner" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_edi_party_names.party_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_email_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses.begin" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses.cidr" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses.end" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_ip_addresses.mask" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_registered_ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.excluded_uris" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_directory_names.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_edi_party_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_edi_party_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_edi_party_names.name_assigner" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_edi_party_names.party_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_email_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses.begin" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses.cidr" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses.end" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_ip_addresses.mask" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_registered_ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.name_constraints.permitted_uris" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.etsi_compliance" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.etsi_compliance.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.legislation" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.legislation.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.legislation.country_codes" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.amount" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.amount.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.currency" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.currency_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.currency_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.exponent" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.limit.exponent.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.pds_locations" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.pds_locations.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.pds_locations.language" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.pds_locations.url" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.retention_period" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.retention_period.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.sscd" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.sscd.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.types" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.types.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.qc_statements.parsed.types.ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.log_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.signature.hash_algorithm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.signature.signature" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.signature.signature_algorithm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.timestamp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.timestamp.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.version" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.signed_certificate_timestamps.version.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.directory_names.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.dns_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.edi_party_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.edi_party_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.edi_party_names.name_assigner" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.edi_party_names.party_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.email_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.ip_addresses" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.other_names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.other_names.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.other_names.id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.other_names.value" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.registered_ids" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_alt_name.uniform_resource_identifiers" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.subject_key_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.algorithm_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.hash" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.hash_bits" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.hash_bits.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.extensions.tor_service_descriptors.onion" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.issuer_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.redacted" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.redacted.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.serial_number_hex" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.self_signed" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.self_signed.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.signature_algorithm.name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.signature_algorithm.oid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.signature.value" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.given_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject.surname" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.dsa.g" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.dsa.p" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.dsa.q" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.dsa.y" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.b" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.curve" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.gx" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.gy" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.length" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.length.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.n" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.p" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.pub" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.x" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.ecdsa.y" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.key_algorithm.name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.key_algorithm.oid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.rsa.exponent" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.rsa.exponent.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.rsa.length" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.rsa.length.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.rsa.modulus" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.subject_key_info.unrecognized.raw" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions.critical" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions.critical.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions.id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.unknown_extensions.value" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.length_seconds" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.length_seconds.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.not_after" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.not_after.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.not_before" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.validity_period.not_before.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.version" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.parsed.version.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.precert" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.precert.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.next_update" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.next_update.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.reason" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.revocation_time" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.revocation_time.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.revoked" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.crl.revoked.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.next_update" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.next_update.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.reason" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.revocation_time" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.revocation_time.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.revoked" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revocation.ocsp.revoked.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revoked" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.revoked.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.spki_subject_fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.tbs_fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.tbs_no_ct_fingerprint_sha256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validated_at" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validated_at.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.chains" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.chains.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.chains.sha256fp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.ever_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.ever_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.had_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.had_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.has_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.has_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.in_revocation_set" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.in_revocation_set.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.is_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.is_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.parents" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.apple.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.chains" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.chains.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.chains.sha256fp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.ever_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.ever_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.had_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.had_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.has_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.has_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.in_revocation_set" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.in_revocation_set.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.is_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.is_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.parents" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.chrome.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.chains" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.chains.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.chains.sha256fp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.ever_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.ever_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.had_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.had_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.has_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.has_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.in_revocation_set" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.in_revocation_set.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.is_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.is_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.parents" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.microsoft.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.chains" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.chains.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.chains.sha256fp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.ever_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.ever_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.had_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.had_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.has_trusted_path" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.has_trusted_path.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.in_revocation_set" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.in_revocation_set.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.is_valid" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.is_valid.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.parents" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation.nss.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.validation_level" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.errors_present" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.errors_present.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.failed_lints" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.fatals_present" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.fatals_present.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.notices_present" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.notices_present.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.timestamp" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.timestamp.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.version" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.version.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.warnings_present" }, { "data": { "category": " TLS" }, "value": "services.tls.certificate.zlint.warnings_present.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain.fingerprint" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain.fingerprint.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain.issuer_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain.subject_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain_fps_sha_256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.chain_fps_sha_256.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.fingerprint" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.fingerprint.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.issuer_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.names" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.pubkey_algorithm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.pubkey_bit_size" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.pubkey_bit_size.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.dsa.g" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.dsa.p" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.dsa.q" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.dsa.y" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.b" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.curve" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.curve.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.gx" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.gy" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.length" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.length.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.n" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.p" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.pub" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.x" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.ecdsa.y" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.fingerprint" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.key_algorithm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.key_algorithm.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.rsa.exponent" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.rsa.length" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.rsa.length.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.public_key.rsa.modulus" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.signature.self_signed" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.signature.self_signed.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.signature.signature_algorithm" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.signature.signature_algorithm.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.common_name" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.domain_component" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.email_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.jurisdiction_country" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.jurisdiction_locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.jurisdiction_province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.locality" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.organization" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.organization_id" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.organizational_unit" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.postal_code" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.postal_code.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.province" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.serial_number" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.serial_number.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject.street_address" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.subject_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.tbs_fingerprint" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_data.tbs_fingerprint.type" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_fp_sha_256" }, { "data": { "category": " TLS" }, "value": "services.tls.certificates.leaf_fp_sha_256.type" }, { "data": { "category": " TLS" }, "value": "services.tls.cipher_selected" }, { "data": { "category": " TLS" }, "value": "services.tls.ja3s" }, { "data": { "category": " TLS" }, "value": "services.tls.presented_chain.fingerprint" }, { "data": { "category": " TLS" }, "value": "services.tls.presented_chain.fingerprint.type" }, { "data": { "category": " TLS" }, "value": "services.tls.presented_chain.issuer_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.presented_chain.subject_dn" }, { "data": { "category": " TLS" }, "value": "services.tls.server_key_exchange.ec_params.named_curve" }, { "data": { "category": " TLS" }, "value": "services.tls.server_key_exchange.ec_params.named_curve.type" }, { "data": { "category": " TLS" }, "value": "services.tls.server_key_exchange.ec_params.public_key" }, { "data": { "category": " TLS" }, "value": "services.tls.session_ticket.length" }, { "data": { "category": " TLS" }, "value": "services.tls.session_ticket.length.type" }, { "data": { "category": " TLS" }, "value": "services.tls.session_ticket.lifetime_hint" }, { "data": { "category": " TLS" }, "value": "services.tls.session_ticket.lifetime_hint.type" }, { "data": { "category": " TLS" }, "value": "services.tls.version_selected" }, { "data": { "category": " SOFTWARE" }, "value": "services.software" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.type" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.component_uniform_resource_identifiers" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.cpe" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.edition" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.language" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.other.key" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.other.value" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.part" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.part.type" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.product" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.source" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.sw_edition" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.target_hw" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.target_sw" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.update" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.vendor" }, { "data": { "category": " SOFTWARE" }, "value": "services.software.version" }, { "data": { "category": " HTTP" }, "value": "services.http.request.body" }, { "data": { "category": " HTTP" }, "value": "services.http.request.headers" }, { "data": { "category": " HTTP" }, "value": "services.http.request.headers.type" }, { "data": { "category": " HTTP" }, "value": "services.http.request.headers.key" }, { "data": { "category": " HTTP" }, "value": "services.http.request.headers.value.headers" }, { "data": { "category": " HTTP" }, "value": "services.http.request.method" }, { "data": { "category": " HTTP" }, "value": "services.http.request.uri" }, { "data": { "category": " HTTP" }, "value": "services.http.response.body" }, { "data": { "category": " HTTP" }, "value": "services.http.response.body_hashes" }, { "data": { "category": " HTTP" }, "value": "services.http.response.body_size" }, { "data": { "category": " HTTP" }, "value": "services.http.response.body_size.type" }, { "data": { "category": " HTTP" }, "value": "services.http.response.favicons.md5_hash" }, { "data": { "category": " HTTP" }, "value": "services.http.response.favicons.md5_hash.type" }, { "data": { "category": " HTTP" }, "value": "services.http.response.favicons.name" }, { "data": { "category": " HTTP" }, "value": "services.http.response.favicons.size" }, { "data": { "category": " HTTP" }, "value": "services.http.response.favicons.size.type" }, { "data": { "category": " HTTP" }, "value": "services.http.response.headers" }, { "data": { "category": " HTTP" }, "value": "services.http.response.headers.type" }, { "data": { "category": " HTTP" }, "value": "services.http.response.headers.key" }, { "data": { "category": " HTTP" }, "value": "services.http.response.headers.value.headers" }, { "data": { "category": " HTTP" }, "value": "services.http.response.html_tags" }, { "data": { "category": " HTTP" }, "value": "services.http.response.html_title" }, { "data": { "category": " HTTP" }, "value": "services.http.response.protocol" }, { "data": { "category": " HTTP" }, "value": "services.http.response.status_code" }, { "data": { "category": " HTTP" }, "value": "services.http.response.status_code.type" }, { "data": { "category": " HTTP" }, "value": "services.http.response.status_reason" }, { "data": { "category": " HTTP" }, "value": "services.http.supports_http2" }, { "data": { "category": " HTTP" }, "value": "services.http.supports_http2.type" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.client_to_server_alg_group.cipher" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.client_to_server_alg_group.compression" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.client_to_server_alg_group.mac" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.host_key_algorithm" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.kex_algorithm" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.server_to_client_alg_group.cipher" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.server_to_client_alg_group.compression" }, { "data": { "category": " SSH" }, "value": "services.ssh.algorithm_selection.server_to_client_alg_group.mac" }, { "data": { "category": " SSH" }, "value": "services.ssh.endpoint_id.comment" }, { "data": { "category": " SSH" }, "value": "services.ssh.endpoint_id.protocol_version" }, { "data": { "category": " SSH" }, "value": "services.ssh.endpoint_id.raw" }, { "data": { "category": " SSH" }, "value": "services.ssh.endpoint_id.software_version" }, { "data": { "category": " SSH" }, "value": "services.ssh.hassh_fingerprint" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.client_to_server_ciphers" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.client_to_server_compression" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.client_to_server_languages" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.client_to_server_macs" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.first_kex_follows" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.first_kex_follows.type" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.host_key_algorithms" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.kex_algorithms" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.server_to_client_ciphers" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.server_to_client_compression" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.server_to_client_languages" }, { "data": { "category": " SSH" }, "value": "services.ssh.kex_init_message.server_to_client_macs" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.certkey_public_key" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.dsa_public_key.g" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.dsa_public_key.p" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.dsa_public_key.q" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.dsa_public_key.y" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.b" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.curve" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.curve.type" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.gx" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.gy" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.length" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.length.type" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.n" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.p" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.pub" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.x" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ecdsa_public_key.y" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.ed25519_public_key.public_bytes" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.fingerprint_sha256" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.rsa_public_key.exponent" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.rsa_public_key.length" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.rsa_public_key.length.type" }, { "data": { "category": " SSH" }, "value": "services.ssh.server_host_key.rsa_public_key.modulus" }, { "data": { "category": " TELNET" }, "value": "services.telnet.banner" }, { "data": { "category": " TELNET" }, "value": "services.telnet.do.key" }, { "data": { "category": " TELNET" }, "value": "services.telnet.do.key.type" }, { "data": { "category": " TELNET" }, "value": "services.telnet.do.value" }, { "data": { "category": " TELNET" }, "value": "services.telnet.dont.key" }, { "data": { "category": " TELNET" }, "value": "services.telnet.dont.key.type" }, { "data": { "category": " TELNET" }, "value": "services.telnet.dont.value" }, { "data": { "category": " TELNET" }, "value": "services.telnet.will.key" }, { "data": { "category": " TELNET" }, "value": "services.telnet.will.key.type" }, { "data": { "category": " TELNET" }, "value": "services.telnet.will.value" }, { "data": { "category": " TELNET" }, "value": "services.telnet.wont.key" }, { "data": { "category": " TELNET" }, "value": "services.telnet.wont.key.type" }, { "data": { "category": " TELNET" }, "value": "services.telnet.wont.value" }, { "data": { "category": " FTP" }, "value": "services.ftp.auth_ssl_response" }, { "data": { "category": " FTP" }, "value": "services.ftp.auth_tls_response" }, { "data": { "category": " FTP" }, "value": "services.ftp.banner" }, { "data": { "category": " FTP" }, "value": "services.ftp.implicit_tls" }, { "data": { "category": " FTP" }, "value": "services.ftp.implicit_tls.type" }, { "data": { "category": " FTP" }, "value": "services.ftp.status_code" }, { "data": { "category": " FTP" }, "value": "services.ftp.status_code.type" }, { "data": { "category": " FTP" }, "value": "services.ftp.status_meaning" }, { "data": { "category": " DNS" }, "value": "services.dns.additionals.name" }, { "data": { "category": " DNS" }, "value": "services.dns.additionals.response" }, { "data": { "category": " DNS" }, "value": "services.dns.additionals.type" }, { "data": { "category": " DNS" }, "value": "services.dns.answers.name" }, { "data": { "category": " DNS" }, "value": "services.dns.answers.response" }, { "data": { "category": " DNS" }, "value": "services.dns.answers.type" }, { "data": { "category": " DNS" }, "value": "services.dns.authorities.name" }, { "data": { "category": " DNS" }, "value": "services.dns.authorities.response" }, { "data": { "category": " DNS" }, "value": "services.dns.authorities.type" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.do" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.do.type" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.options" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.udp" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.udp.type" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.version" }, { "data": { "category": " DNS" }, "value": "services.dns.edns.version.type" }, { "data": { "category": " DNS" }, "value": "services.dns.questions.name" }, { "data": { "category": " DNS" }, "value": "services.dns.questions.response" }, { "data": { "category": " DNS" }, "value": "services.dns.questions.type" }, { "data": { "category": " DNS" }, "value": "services.dns.r_code" }, { "data": { "category": " DNS" }, "value": "services.dns.resolves_correctly" }, { "data": { "category": " DNS" }, "value": "services.dns.resolves_correctly.type" }, { "data": { "category": " DNS" }, "value": "services.dns.server_type" }, { "data": { "category": " DNS" }, "value": "services.dns.version" }, { "data": { "category": " AMQP" }, "value": "services.amqp.explicit_tls" }, { "data": { "category": " AMQP" }, "value": "services.amqp.explicit_tls.type" }, { "data": { "category": " AMQP" }, "value": "services.amqp.implicit_tls" }, { "data": { "category": " AMQP" }, "value": "services.amqp.implicit_tls.type" }, { "data": { "category": " AMQP" }, "value": "services.amqp.protocol_id.id" }, { "data": { "category": " AMQP" }, "value": "services.amqp.protocol_id.id.type" }, { "data": { "category": " AMQP" }, "value": "services.amqp.protocol_id.name" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.major" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.major.type" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.minor" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.minor.type" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.revision" }, { "data": { "category": " AMQP" }, "value": "services.amqp.version.revision.type" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.aggregate_auth_version" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.aggregate_auth_version.type" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.auth_methods" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.groups" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.raw" }, { "data": { "category": " ANY_CONNECT" }, "value": "services.any_connect.response_type" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.application_software_revision" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.description" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.firmware_revision" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.instance_number" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.instance_number.type" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.location" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.model_name" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.object_name" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.vendor_id" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.vendor_id.type" }, { "data": { "category": " BACNET" }, "value": "services.bacnet.vendor_name" }, { "data": { "category": " COAP" }, "value": "services.coap.code" }, { "data": { "category": " COAP" }, "value": "services.coap.message_id" }, { "data": { "category": " COAP" }, "value": "services.coap.message_id.type" }, { "data": { "category": " COAP" }, "value": "services.coap.message_type" }, { "data": { "category": " COAP" }, "value": "services.coap.payload" }, { "data": { "category": " COAP" }, "value": "services.coap.token" }, { "data": { "category": " COAP" }, "value": "services.coap.version" }, { "data": { "category": " COAP" }, "value": "services.coap.version.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.body" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.body_hashes" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.body_size" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.body_size.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.favicons.md5_hash" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.favicons.md5_hash.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.favicons.name" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.favicons.size" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.favicons.size.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.headers" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.headers.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.headers.key" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.headers.value.headers" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.html_tags" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.html_title" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.protocol" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.status_code" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.status_code.type" }, { "data": { "category": " CWMP" }, "value": "services.cwmp.http_info.status_reason" }, { "data": { "category": " DNP3" }, "value": "services.dnp3.banner" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.headers" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.headers.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.headers.key" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.headers.value.headers" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.status" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.status_code" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.http_info.status_code.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.available" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.available_in_bytes" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.available_in_bytes.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.free" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.free_in_bytes" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.free_in_bytes.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.total" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.total_in_bytes" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.filesystem.total_in_bytes.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.count" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.count.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.docs.count" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.docs.count.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.docs.deleted" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.docs.deleted.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.store.reserved_in_bytes" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.store.reserved_in_bytes.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.store.size_in_bytes" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.indices.store.size_in_bytes.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.status" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.timestamp" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.timestamp.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.cluster_combined_info.uuid" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.build_flavor" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.build_hash" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.build_type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.host" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.ingest_processors" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.ip" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.ip.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.gc" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.input_args" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.memory_pools" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.start_time" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.start_time_ms" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.start_time_ms.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.vm_name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.vm_vendor" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.jvm.vm_version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.class_name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.desc" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.elastic_version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.ext_plugins" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.has_native_ctrl" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.has_native_ctrl.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.java_version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.modules.version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.allocated_proc" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.allocated_proc.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.arch" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.available_proc" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.available_proc.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.pretty_name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.refresh_interval_ms" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.refresh_interval_ms.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.os.version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.roles" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.cluster_name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.node.attr.ml.enabled" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.node.attr.ml.machine_memory" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.node.attr.ml.max_open_jobs" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.node.attr.xpack_installed" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.settings.node.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.keep_alive" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.max" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.max.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.min" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.min.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.queue_size" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.queue_size.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.thread_pool_list.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.total_indexing_buffer" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.total_indexing_buffer.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_data.version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.node_info.nodes.node_name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.cluster_uuid" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.name" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.tagline" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_date" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_flavor" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_hash" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_snapshot" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_snapshot.type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.build_type" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.lucene_version" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.min_idx_compat_ver" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.min_wire_compat_ver" }, { "data": { "category": " ELASTICSEARCH" }, "value": "services.elasticsearch.system_info.version.number" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.api_version" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.build" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.build.type" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.headers" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.headers.type" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.headers.key" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.headers.value.headers" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.status" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.status_code" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.http_info.status_code.type" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.serial" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.status_code" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.status_code.type" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.status_msg" }, { "data": { "category": " FORTIGATE" }, "value": "services.fortigate.version" }, { "data": { "category": " FOX" }, "value": "services.fox.app_name" }, { "data": { "category": " FOX" }, "value": "services.fox.app_version" }, { "data": { "category": " FOX" }, "value": "services.fox.auth_agent_type" }, { "data": { "category": " FOX" }, "value": "services.fox.brand_id" }, { "data": { "category": " FOX" }, "value": "services.fox.host_address" }, { "data": { "category": " FOX" }, "value": "services.fox.hostid" }, { "data": { "category": " FOX" }, "value": "services.fox.hostname" }, { "data": { "category": " FOX" }, "value": "services.fox.id" }, { "data": { "category": " FOX" }, "value": "services.fox.id.type" }, { "data": { "category": " FOX" }, "value": "services.fox.language" }, { "data": { "category": " FOX" }, "value": "services.fox.os_name" }, { "data": { "category": " FOX" }, "value": "services.fox.os_version" }, { "data": { "category": " FOX" }, "value": "services.fox.station_name" }, { "data": { "category": " FOX" }, "value": "services.fox.sys_info" }, { "data": { "category": " FOX" }, "value": "services.fox.time_zone" }, { "data": { "category": " FOX" }, "value": "services.fox.version" }, { "data": { "category": " FOX" }, "value": "services.fox.vm_name" }, { "data": { "category": " FOX" }, "value": "services.fox.vm_uuid" }, { "data": { "category": " FOX" }, "value": "services.fox.vm_version" }, { "data": { "category": " IKE" }, "value": "services.ike.v1.accepted_proposal" }, { "data": { "category": " IKE" }, "value": "services.ike.v1.accepted_proposal.type" }, { "data": { "category": " IKE" }, "value": "services.ike.v1.notify_message_types" }, { "data": { "category": " IKE" }, "value": "services.ike.v1.notify_message_types.type" }, { "data": { "category": " IKE" }, "value": "services.ike.v1.vendor_ids" }, { "data": { "category": " IKE" }, "value": "services.ike.v2.accepted_proposal" }, { "data": { "category": " IKE" }, "value": "services.ike.v2.accepted_proposal.type" }, { "data": { "category": " IKE" }, "value": "services.ike.v2.notify_message_types" }, { "data": { "category": " IKE" }, "value": "services.ike.v2.notify_message_types.type" }, { "data": { "category": " IKE" }, "value": "services.ike.v2.vendor_ids" }, { "data": { "category": " IMAP" }, "value": "services.imap.banner" }, { "data": { "category": " IMAP" }, "value": "services.imap.start_tls" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.anonymous_login_enabled" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.anonymous_login_enabled.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.auth_each_message" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.auth_each_message.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.has_anonymous_users" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.has_anonymous_users.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.has_named_users" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.has_named_users.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.two_key_login_required" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.two_key_login_required.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.user_auth_disabled" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.auth_status.user_auth_disabled.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.channel_number" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.channel_number.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.completion_code.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.completion_code.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.completion_code.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.extended_capabilities.supports_ipmi_v1_5" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.extended_capabilities.supports_ipmi_v1_5.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.extended_capabilities.supports_ipmi_v2_0" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.extended_capabilities.supports_ipmi_v2_0.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.oem_data" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.oem_data.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.oem_id" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.extended" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.extended.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.md2" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.md2.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.md5" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.md5.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.none" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.none.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.oem_proprietary" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.oem_proprietary.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.password" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.password.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.capabilities.supported_auth_types.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.checksum_error" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.checksum_error.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.data" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.ipmi_command_number.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.ipmi_command_number.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.ipmi_command_number.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.logical_unit_number.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.logical_unit_number.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.logical_unit_number.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.is_request" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.is_request.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.is_response" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.is_response.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.value" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.net_fn.value.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.network_function_code.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.requestor_sequence_number" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.command_payload.requestor_sequence_number.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.class" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.class.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.is_ack" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.is_ack.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.message_class.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.sequence_number" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.sequence_number.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.version" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.rmcp_header.version.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_code" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_type.name" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_type.raw" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_type.raw.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_type.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.auth_type.type.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.session_id" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.session_id.type" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.session_sequence_number" }, { "data": { "category": " IPMI" }, "value": "services.ipmi.session_header.session_sequence_number.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.attribute_cups_version" }, { "data": { "category": " IPP" }, "value": "services.ipp.attribute_ipp_versions" }, { "data": { "category": " IPP" }, "value": "services.ipp.attribute_printer_uris" }, { "data": { "category": " IPP" }, "value": "services.ipp.attributes.name" }, { "data": { "category": " IPP" }, "value": "services.ipp.attributes.value_tag" }, { "data": { "category": " IPP" }, "value": "services.ipp.attributes.value_tag.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.body" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.body_hashes" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.body_size" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.body_size.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.favicons.md5_hash" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.favicons.md5_hash.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.favicons.name" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.favicons.size" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.favicons.size.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.headers" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.headers.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.headers.key" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.headers.value.headers" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.html_tags" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.html_title" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.protocol" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.status_code" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.status_code.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_response.status_reason" }, { "data": { "category": " IPP" }, "value": "services.ipp.cups_version" }, { "data": { "category": " IPP" }, "value": "services.ipp.major_version" }, { "data": { "category": " IPP" }, "value": "services.ipp.major_version.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.minor_version" }, { "data": { "category": " IPP" }, "value": "services.ipp.minor_version.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.body" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.body_hashes" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.body_size" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.body_size.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.favicons.md5_hash" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.favicons.md5_hash.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.favicons.name" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.favicons.size" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.favicons.size.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.headers" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.headers.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.headers.key" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.headers.value.headers" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.html_tags" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.html_title" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.protocol" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.status_code" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.status_code.type" }, { "data": { "category": " IPP" }, "value": "services.ipp.response.status_reason" }, { "data": { "category": " IPP" }, "value": "services.ipp.version_string" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.name" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.self_link" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.addresses.hostname" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.addresses.ip" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.addresses.ip.type" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.addresses.node_name" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.ports.name" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.ports.port" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.ports.port.type" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.endpoints.subsets.ports.protocol" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.kubernetes_dashboard_found" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.kubernetes_dashboard_found.type" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.addresses.address" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.addresses.address.type" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.addresses.address_type" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.architecture" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.container_runtime_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.images" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.kernel_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.kube_proxy_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.kubelet_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.name" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.operating_system" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.nodes.os_image" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.pod_names" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.roles.name" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.roles.rules.api_groups" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.roles.rules.resources" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.roles.rules.verbs" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.build_date" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.compiler" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.git_commit" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.git_tree_state" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.git_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.go_version" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.major" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.minor" }, { "data": { "category": " KUBERNETES" }, "value": "services.kubernetes.version_info.platform" }, { "data": { "category": " LDAP" }, "value": "services.ldap.allows_anonymous_bind" }, { "data": { "category": " LDAP" }, "value": "services.ldap.allows_anonymous_bind.type" }, { "data": { "category": " LDAP" }, "value": "services.ldap.attributes.name" }, { "data": { "category": " LDAP" }, "value": "services.ldap.attributes.values" }, { "data": { "category": " LDAP" }, "value": "services.ldap.resultcode" }, { "data": { "category": " LDAP" }, "value": "services.ldap.resultcode.type" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.ascii_binding_protocol_enabled" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.ascii_binding_protocol_enabled.type" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.binary_binding_protocol_enabled" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.binary_binding_protocol_enabled.type" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.responds_to_udp" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.responds_to_udp.type" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.stats" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.stats.type" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.stats.key" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.stats.value" }, { "data": { "category": " MEMCACHED" }, "value": "services.memcached.version" }, { "data": { "category": " MMS" }, "value": "services.mms.model" }, { "data": { "category": " MMS" }, "value": "services.mms.revision" }, { "data": { "category": " MMS" }, "value": "services.mms.vendor" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.exception_response.exception_function" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.exception_response.exception_function.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.exception_response.exception_type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.exception_response.exception_type.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.function" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.function.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.conformity_level" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.conformity_level.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.more_follows" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.more_follows.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.objects" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.objects.type" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.objects.key" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.mei_response.objects.value" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.unit_id" }, { "data": { "category": " MODBUS" }, "value": "services.modbus.unit_id.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.cc" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.cc_flags" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.cxx" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.cxx_flags" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.dist_arch" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.dist_mod" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.link_flags" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.target_arch" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.build_environment.target_os" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.git_version" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.build_info.version" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.is_master" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.is_master.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.logical_session_timeout_minutes" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.logical_session_timeout_minutes.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_bson_object_size" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_bson_object_size.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_message_size_bytes" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_message_size_bytes.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_wire_version" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_wire_version.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_write_batch_size" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.max_write_batch_size.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.min_wire_version" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.min_wire_version.type" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.read_only" }, { "data": { "category": " MONGODB" }, "value": "services.mongodb.is_master.read_only.type" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.connection_ack_raw" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.connection_ack_return.raw" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.connection_ack_return.raw.type" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.connection_ack_return.return_value" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.connection_ack_return.return_value.type" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.subscription_ack_return.raw" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.subscription_ack_return.raw.type" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.subscription_ack_return.return_value" }, { "data": { "category": " MQTT" }, "value": "services.mqtt.subscription_ack_return.return_value.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.encrypt_mode" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.instance_name" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.encrypt_mode" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.fed_auth_required" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.fed_auth_required.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.instance" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.mars" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.mars.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.nonce" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.build_number" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.build_number.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.major" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.major.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.minor" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.server_version.minor.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.thread_id" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.thread_id.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.trace_id" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.unknown.key" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.unknown.key.type" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.prelogin_options.unknown.value" }, { "data": { "category": " MSSQL" }, "value": "services.mssql.version" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.auth_plugin_data" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.auth_plugin_name" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.capability_flags" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.capability_flags.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.capability_flags.key" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.capability_flags.value" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.capability_flags.value.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.character_set" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.character_set.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.connection_id" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.connection_id.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.error_code" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.error_code.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.error_id" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.error_message" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.protocol_version" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.protocol_version.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.server_version" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.status_flags" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.status_flags.type" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.status_flags.key" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.status_flags.value" }, { "data": { "category": " MYSQL" }, "value": "services.mysql.status_flags.value.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.leap_indicator" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.leap_indicator.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.mode" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.mode.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.poll" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.poll.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.precision" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.precision.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.reference_id" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.stratum" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.stratum.type" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.version" }, { "data": { "category": " NTP" }, "value": "services.ntp.get_time_header.version.type" }, { "data": { "category": " OPENVPN" }, "value": "services.openvpn.accepts_v1" }, { "data": { "category": " OPENVPN" }, "value": "services.openvpn.accepts_v1.type" }, { "data": { "category": " OPENVPN" }, "value": "services.openvpn.accepts_v2" }, { "data": { "category": " OPENVPN" }, "value": "services.openvpn.accepts_v2.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.accept_version" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.accept_version.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags0" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags0.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags0.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags0.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags0.value.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags1" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags1.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags1.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags1.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.connect_flags1.value.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.did_resend" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.did_resend.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.global_service_options" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.global_service_options.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.global_service_options.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.global_service_options.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.global_service_options.value.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.nsn_service_versions" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.nsn_service_versions.type" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.nsn_service_versions.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.nsn_service_versions.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.nsn_version" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.redirect_target.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.redirect_target.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.redirect_target_raw" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_error.key" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_error.value" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_error_raw" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_reason_app" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_reason_sys" }, { "data": { "category": " ORACLE" }, "value": "services.oracle.refuse_version" }, { "data": { "category": " PC_ANYWHERE" }, "value": "services.pc_anywhere.name" }, { "data": { "category": " PC_ANYWHERE" }, "value": "services.pc_anywhere.nr" }, { "data": { "category": " PC_ANYWHERE" }, "value": "services.pc_anywhere.status.in_use" }, { "data": { "category": " PC_ANYWHERE" }, "value": "services.pc_anywhere.status.in_use.type" }, { "data": { "category": " PC_ANYWHERE" }, "value": "services.pc_anywhere.status.raw" }, { "data": { "category": " POP3" }, "value": "services.pop3.banner" }, { "data": { "category": " POP3" }, "value": "services.pop3.start_tls" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.authentication_mode.mode" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.authentication_mode.payload" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.protocol_error" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.protocol_error.type" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.protocol_error.key" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.protocol_error.value" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.startup_error" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.startup_error.type" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.startup_error.key" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.startup_error.value" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.supported_versions" }, { "data": { "category": " POSTGRES" }, "value": "services.postgres.transaction_status" }, { "data": { "category": " PPTP" }, "value": "services.pptp.bearer_message.code" }, { "data": { "category": " PPTP" }, "value": "services.pptp.bearer_message.code.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.bearer_message.meaning" }, { "data": { "category": " PPTP" }, "value": "services.pptp.error_message.code" }, { "data": { "category": " PPTP" }, "value": "services.pptp.error_message.code.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.error_message.meaning" }, { "data": { "category": " PPTP" }, "value": "services.pptp.firmware.major" }, { "data": { "category": " PPTP" }, "value": "services.pptp.firmware.major.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.firmware.minor" }, { "data": { "category": " PPTP" }, "value": "services.pptp.firmware.minor.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.framing_message.code" }, { "data": { "category": " PPTP" }, "value": "services.pptp.framing_message.code.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.framing_message.meaning" }, { "data": { "category": " PPTP" }, "value": "services.pptp.hostname" }, { "data": { "category": " PPTP" }, "value": "services.pptp.maximum_channels" }, { "data": { "category": " PPTP" }, "value": "services.pptp.maximum_channels.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.protocol.major" }, { "data": { "category": " PPTP" }, "value": "services.pptp.protocol.major.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.protocol.minor" }, { "data": { "category": " PPTP" }, "value": "services.pptp.protocol.minor.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.result_message.code" }, { "data": { "category": " PPTP" }, "value": "services.pptp.result_message.code.type" }, { "data": { "category": " PPTP" }, "value": "services.pptp.result_message.meaning" }, { "data": { "category": " PPTP" }, "value": "services.pptp.vendor" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.headers" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.headers.type" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.headers.key" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.headers.value.headers" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.status" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.status_code" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.http_info.status_code.type" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.discovered_labels.address" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.discovered_labels.job" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.discovered_labels.metrics_path" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.discovered_labels.scheme" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.health" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.labels.instance" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.labels.job" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.last_error" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.last_scrape" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.active_targets.scrape_url" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.all_versions" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.config_exposed" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.config_exposed.type" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.dropped_targets.address" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.dropped_targets.job" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.dropped_targets.metrics_path" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.dropped_targets.scheme" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.go_versions" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.prometheus_versions.go_version" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.prometheus_versions.revision" }, { "data": { "category": " PROMETHEUS" }, "value": "services.prometheus.response.prometheus_versions.version" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.internal_x509_chain_fps" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.internal_x509_chain_fps.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.key_length" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.key_length.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.magic" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.magic.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.max_bytes_datalen" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.max_bytes_datalen.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.modulus" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.modulus.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.modulus_bitlen" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.modulus_bitlen.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.public_exponent" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.public_exponent.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.signature" }, { "data": { "category": " RDP" }, "value": "services.rdp.certificate_info.proprietary_rsa_key.signature.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.connect_id" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.connect_id.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.domain_protocol_version" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.domain_protocol_version.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_channel_ids" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_channel_ids.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_mcspdu_size" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_mcspdu_size.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_provider_height" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_provider_height.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_token_ids" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_token_ids.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_user_id_channels" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.max_user_id_channels.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.min_throughput" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.min_throughput.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.num_priorities" }, { "data": { "category": " RDP" }, "value": "services.rdp.connect_response.domain_parameters.num_priorities.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.dynvc_graphics_pipeline" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.dynvc_graphics_pipeline.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.extended_client_data_supported" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.extended_client_data_supported.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.neg_resp_reserved" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.neg_resp_reserved.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.restricted_admin_mode" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.restricted_admin_mode.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.restricted_auth_mode" }, { "data": { "category": " RDP" }, "value": "services.rdp.protocol_flags.restricted_auth_mode.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.credssp" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.credssp.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.credssp_early_auth" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.credssp_early_auth.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_bad_flags" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_bad_flags.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_hybrid_required" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_hybrid_required.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_cert_missing" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_cert_missing.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_forbidden" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_forbidden.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_required" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_required.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_user_auth_required" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_ssl_user_auth_required.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_unknown" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.error_unknown.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.raw_value" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.raw_value.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.rdstls" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.rdstls.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.standard_rdp" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.standard_rdp.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.tls" }, { "data": { "category": " RDP" }, "value": "services.rdp.selected_security_protocol.tls.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.major" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.major.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.minor" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.minor.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.raw" }, { "data": { "category": " RDP" }, "value": "services.rdp.version.raw.type" }, { "data": { "category": " RDP" }, "value": "services.rdp.x224_cc_pdu_srcref" }, { "data": { "category": " RDP" }, "value": "services.rdp.x224_cc_pdu_srcref.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.arch_bits" }, { "data": { "category": " REDIS" }, "value": "services.redis.auth_response" }, { "data": { "category": " REDIS" }, "value": "services.redis.build_id" }, { "data": { "category": " REDIS" }, "value": "services.redis.commands" }, { "data": { "category": " REDIS" }, "value": "services.redis.commands_processed" }, { "data": { "category": " REDIS" }, "value": "services.redis.commands_processed.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.connections_received" }, { "data": { "category": " REDIS" }, "value": "services.redis.connections_received.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.gcc_version" }, { "data": { "category": " REDIS" }, "value": "services.redis.git_sha1" }, { "data": { "category": " REDIS" }, "value": "services.redis.info_response.key" }, { "data": { "category": " REDIS" }, "value": "services.redis.info_response.value" }, { "data": { "category": " REDIS" }, "value": "services.redis.major" }, { "data": { "category": " REDIS" }, "value": "services.redis.major.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.mem_allocator" }, { "data": { "category": " REDIS" }, "value": "services.redis.minor" }, { "data": { "category": " REDIS" }, "value": "services.redis.minor.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.mode" }, { "data": { "category": " REDIS" }, "value": "services.redis.nonexistent_response" }, { "data": { "category": " REDIS" }, "value": "services.redis.os" }, { "data": { "category": " REDIS" }, "value": "services.redis.patch_level" }, { "data": { "category": " REDIS" }, "value": "services.redis.patch_level.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.ping_response" }, { "data": { "category": " REDIS" }, "value": "services.redis.quit_response" }, { "data": { "category": " REDIS" }, "value": "services.redis.raw_command_output.output" }, { "data": { "category": " REDIS" }, "value": "services.redis.uptime" }, { "data": { "category": " REDIS" }, "value": "services.redis.uptime.type" }, { "data": { "category": " REDIS" }, "value": "services.redis.used_memory" }, { "data": { "category": " REDIS" }, "value": "services.redis.used_memory.type" }, { "data": { "category": " S7" }, "value": "services.s7.copyright" }, { "data": { "category": " S7" }, "value": "services.s7.cpu_profile" }, { "data": { "category": " S7" }, "value": "services.s7.firmware" }, { "data": { "category": " S7" }, "value": "services.s7.hardware" }, { "data": { "category": " S7" }, "value": "services.s7.location" }, { "data": { "category": " S7" }, "value": "services.s7.memory_serial_number" }, { "data": { "category": " S7" }, "value": "services.s7.module" }, { "data": { "category": " S7" }, "value": "services.s7.module_id" }, { "data": { "category": " S7" }, "value": "services.s7.module_type" }, { "data": { "category": " S7" }, "value": "services.s7.oem_id" }, { "data": { "category": " S7" }, "value": "services.s7.plant_id" }, { "data": { "category": " S7" }, "value": "services.s7.reserved_for_os" }, { "data": { "category": " S7" }, "value": "services.s7.serial_number" }, { "data": { "category": " S7" }, "value": "services.s7.system" }, { "data": { "category": " SIP" }, "value": "services.sip.code" }, { "data": { "category": " SIP" }, "value": "services.sip.code.type" }, { "data": { "category": " SIP" }, "value": "services.sip.server" }, { "data": { "category": " SIP" }, "value": "services.sip.status" }, { "data": { "category": " SIP" }, "value": "services.sip.version" }, { "data": { "category": " SKINNY" }, "value": "services.skinny.response" }, { "data": { "category": " SMB" }, "value": "services.smb.group_name" }, { "data": { "category": " SMB" }, "value": "services.smb.has_ntlm" }, { "data": { "category": " SMB" }, "value": "services.smb.has_ntlm.type" }, { "data": { "category": " SMB" }, "value": "services.smb.native_os" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.authentication_types" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.capabilities" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.capabilities.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.dialect_revision" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.dialect_revision.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.command" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.command.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.credits" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.credits.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.flags" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.flags.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.protocol_id" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.status" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.header_log.status.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.security_mode" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.security_mode.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.server_guid" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.server_start_time" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.server_start_time.type" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.system_time" }, { "data": { "category": " SMB" }, "value": "services.smb.negotiation_log.system_time.type" }, { "data": { "category": " SMB" }, "value": "services.smb.ntlm" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.command" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.command.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.credits" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.credits.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.flags" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.flags.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.protocol_id" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.status" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.header_log.status.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.negotiate_flags" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.negotiate_flags.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.setup_flags" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.setup_flags.type" }, { "data": { "category": " SMB" }, "value": "services.smb.session_setup_log.target_name" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_dfs_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_dfs_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_directory_leasing_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_directory_leasing_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_encryption_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_encryption_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_leasing_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_leasing_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_multichan_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_multichan_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_multicredit_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_multicredit_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_persistent_handle_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_capabilities.smb_persistent_handle_support.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.major" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.major.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.minor" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.minor.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.revision" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.revision.type" }, { "data": { "category": " SMB" }, "value": "services.smb.smb_version.version_string" }, { "data": { "category": " SMB" }, "value": "services.smb.smbv1_support" }, { "data": { "category": " SMB" }, "value": "services.smb.smbv1_support.type" }, { "data": { "category": " SMTP" }, "value": "services.smtp.banner" }, { "data": { "category": " SMTP" }, "value": "services.smtp.ehlo" }, { "data": { "category": " SMTP" }, "value": "services.smtp.start_tls" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_interfaces.num_ifaces" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_interfaces.num_ifaces.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.firmware_rev" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.hardware_rev" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.mfg_name" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.model_name" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.name" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.serial_num" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_physical.software_rev" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.contact" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.desc" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.init_time" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.init_time.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.location" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.name" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.object_id" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_1" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_1.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_2" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_2.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_3" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_3.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_4" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_4.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_5" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_5.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_6" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_6.type" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_7" }, { "data": { "category": " SNMP" }, "value": "services.snmp.oid_system.services.layer_7.type" }, { "data": { "category": " SSDP" }, "value": "services.ssdp.headers" }, { "data": { "category": " SSDP" }, "value": "services.ssdp.headers.type" }, { "data": { "category": " SSDP" }, "value": "services.ssdp.headers.key" }, { "data": { "category": " SSDP" }, "value": "services.ssdp.headers.value.headers" }, { "data": { "category": " SSDP" }, "value": "services.ssdp.upnp_url" }, { "data": { "category": " TEAM_VIEWER" }, "value": "services.team_viewer.response" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.device_type" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.friendly_name" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.id" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.id.type" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.manufacturer" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.manufacturer_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.model_description" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.model_name" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.model_number" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.model_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.parent_id" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.parent_id.type" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.presentation_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.serial_number" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.service_list.control_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.service_list.event_sub_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.service_list.scpd_url" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.service_list.service_id" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.service_list.service_type" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.udn" }, { "data": { "category": " UPNP" }, "value": "services.upnp.devices.upc" }, { "data": { "category": " UPNP" }, "value": "services.upnp.endpoint" }, { "data": { "category": " UPNP" }, "value": "services.upnp.headers" }, { "data": { "category": " UPNP" }, "value": "services.upnp.headers.type" }, { "data": { "category": " UPNP" }, "value": "services.upnp.headers.key" }, { "data": { "category": " UPNP" }, "value": "services.upnp.headers.value.headers" }, { "data": { "category": " UPNP" }, "value": "services.upnp.spec.major" }, { "data": { "category": " UPNP" }, "value": "services.upnp.spec.minor" }, { "data": { "category": " VNC" }, "value": "services.vnc.connection_failed_reason" }, { "data": { "category": " VNC" }, "value": "services.vnc.desktop_name" }, { "data": { "category": " VNC" }, "value": "services.vnc.pixel_encoding.name" }, { "data": { "category": " VNC" }, "value": "services.vnc.pixel_encoding.value" }, { "data": { "category": " VNC" }, "value": "services.vnc.pixel_encoding.value.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.height" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.height.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.name_len" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.name_len.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.big_endian" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.big_endian.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.bits_per_pixel" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.bits_per_pixel.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.blue_max" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.blue_max.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.blue_shift" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.blue_shift.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.depth" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.depth.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.green_max" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.green_max.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.green_shift" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.green_shift.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding1" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding1.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding2" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding2.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding3" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.padding3.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.red_max" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.red_max.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.red_shift" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.red_shift.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.true_color" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.pixel_format.true_color.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.width" }, { "data": { "category": " VNC" }, "value": "services.vnc.screen_info.width.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.security_types.name" }, { "data": { "category": " VNC" }, "value": "services.vnc.security_types.value" }, { "data": { "category": " VNC" }, "value": "services.vnc.security_types.value.type" }, { "data": { "category": " VNC" }, "value": "services.vnc.version" }, { "data": { "category": " X11" }, "value": "services.x11.refusal_reason" }, { "data": { "category": " X11" }, "value": "services.x11.requires_authentication" }, { "data": { "category": " X11" }, "value": "services.x11.requires_authentication.type" }, { "data": { "category": " X11" }, "value": "services.x11.vendor" }, { "data": { "category": " X11" }, "value": "services.x11.version" }, { "data": { "category": " MISC" }, "value": "labels" }, { "data": { "category": " MISC" }, "value": "last_updated_at" }, { "data": { "category": " MISC" }, "value": "last_updated_at.type" }, { "data": { "category": " MISC" }, "value": "service_count" }, { "data": { "category": " MISC" }, "value": "service_count.type" }, { "data": { "category": " MISC" }, "value": "services" }, { "data": { "category": " MISC" }, "value": "services.type" }, { "data": { "category": " MISC" }, "value": "services.banner_hashes" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.cookie_beacon" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.cookie_beacon.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.crypto_scheme" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.crypto_scheme.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.dns" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.dns.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_get.client" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_get.uri" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_get.verb" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_post.client" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_post.uri" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.http_post.verb" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.jitter" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.jitter.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.killdate" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.killdate.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.post_ex.x64" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.post_ex.x86" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.public_key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.sleep_time" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.sleep_time.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.ssl" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.ssl.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_bytes.key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_bytes.key.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_bytes.value" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_int.key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_int.key.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_int.value" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.unknown_int.value.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.user_agent" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.watermark" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x64.watermark.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.cookie_beacon" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.cookie_beacon.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.crypto_scheme" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.crypto_scheme.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.dns" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.dns.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_get.client" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_get.uri" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_get.verb" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_post.client" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_post.uri" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.http_post.verb" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.jitter" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.jitter.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.killdate" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.killdate.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.post_ex.x64" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.post_ex.x86" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.public_key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.sleep_time" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.sleep_time.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.ssl" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.ssl.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_bytes.key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_bytes.key.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_bytes.value" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_int.key" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_int.key.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_int.value" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.unknown_int.value.type" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.user_agent" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.watermark" }, { "data": { "category": " MISC" }, "value": "services.cobalt_strike.x86.watermark.type" }, { "data": { "category": " MISC" }, "value": "services.discovery_method" }, { "data": { "category": " MISC" }, "value": "services.labels" }, { "data": { "category": " MISC" }, "value": "services.parsed" }, { "data": { "category": " MISC" }, "value": "services.parsed.type" }, { "data": { "category": " MISC" }, "value": "services.parsed.dynamic" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.id" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.id.type" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.os" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.quic.versions" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.quic.versions.type" }, { "data": { "category": " MISC" }, "value": "services.transport_fingerprint.raw" }, { "data": { "category": " MISC" }, "value": "truncated" }, { "data": { "category": " MISC" }, "value": "truncated.type" } ] } censys-censys-python-7a57a89/censys/cli/utils.py000066400000000000000000000064041524433232300220000ustar00rootroot00000000000000"""Censys CLI utilities.""" import argparse import datetime import json import os.path import sys from typing import Any, Optional, Union from rich.console import Console from censys.common.config import DEFAULT, get_config Results = Union[list[dict], dict[str, Any]] V2_INDEXES = ["hosts", "certificates"] config = get_config() color = config.get(DEFAULT, "color") color_system = "auto" if color else None console = Console(color_system=color_system) # type: ignore err_console = Console(color_system=color_system, file=sys.stderr) # type: ignore def print_wrote_file(file_path: str): """Print wrote file confirmation. Args: file_path (str): Name of the file to write to on the disk. """ abs_file_path = os.path.abspath(file_path) console.print(f"Wrote results to file {abs_file_path}", soft_wrap=True) def _write_json(file_path: str, search_results: Results): """Write search results to a new file in JSON format. Args: file_path (str): Name of the file to write to on the disk. search_results (Results): A list of results from the query. """ with open(file_path, "w") as output_file: # Since the results are already in JSON, just write them to a file. json.dump(search_results, output_file, indent=4) print_wrote_file(file_path) def _write_screen(search_results: Results): # pragma: no cover """Writes search results to standard output. Args: search_results (Results): A list of results from the query. """ config = get_config() if config.get(DEFAULT, "color"): console.print_json(data=search_results) else: print(json.dumps(search_results, indent=4)) def write_file( results_list: Results, file_format: Optional[str] = None, file_path: Optional[str] = None, csv_fields: Optional[list[str]] = None, ): """Maps formats and writes results. Args: results_list (Results): A list of results from the API query. file_format (str): Optional; The format of the output. file_path (str): Optional; A path to write results to. csv_fields (List[str]): Optional; A list of fields to write to CSV. """ if file_format and isinstance(file_format, str): file_format = file_format.lower() if not file_path: file_path = "temp-out.json" if file_format == "json": _write_json(file_path, results_list) else: _write_screen(results_list) def valid_datetime_type(datetime_str: str) -> datetime.datetime: """Custom argparse type for user datetime values from arg. Args: datetime_str (str): A string representing a datetime. Raises: ArgumentTypeError: If the datetime string is invalid. Returns: datetime.datetime: A datetime object. """ try: return datetime.datetime.strptime(datetime_str, "%Y-%m-%d %H:%M") except ValueError: try: return datetime.datetime.strptime(datetime_str, "%Y-%m-%d") except ValueError: msg = f"Given datetime ({datetime_str}) is not valid! Expected format: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:mm'." # noqa: E231 raise argparse.ArgumentTypeError(msg) censys-censys-python-7a57a89/censys/common/000077500000000000000000000000001524433232300210035ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/common/__init__.py000066400000000000000000000001521524433232300231120ustar00rootroot00000000000000"""Common Code for the Censys Python SDK.""" from .version import __version__ __all__ = ["__version__"] censys-censys-python-7a57a89/censys/common/base.py000066400000000000000000000222641524433232300222750ustar00rootroot00000000000000"""Base for interacting with the Censys APIs.""" import json import os import warnings from functools import wraps from typing import Any, Callable, Optional import backoff import requests from requests.models import Response from .exceptions import ( CensysAPIException, CensysException, CensysInternalServerErrorException, CensysInternalServerException, CensysJSONDecodeException, CensysRateLimitExceededException, CensysTooManyRequestsException, ) from .version import __version__ # Wrapper to make max_retries configurable at runtime def _backoff_wrapper(method: Callable): @wraps(method) def _wrapper(self, *args, **kwargs): @backoff.on_exception( backoff.expo, ( CensysInternalServerException, CensysInternalServerErrorException, CensysTooManyRequestsException, CensysRateLimitExceededException, requests.exceptions.Timeout, requests.exceptions.ConnectionError, ), max_tries=self.max_retries, max_time=self.timeout, ) def _impl(): return method(self, *args, **kwargs) return _impl() return _wrapper class CensysAPIBase: """This is the base class for API queries.""" DEFAULT_TIMEOUT: int = 30 """Default API timeout.""" DEFAULT_USER_AGENT: str = f"censys-python/{__version__}" """Default API user agent.""" DEFAULT_MAX_RETRIES: int = 5 """Default max number of API retries.""" def __init__( self, url: Optional[str] = None, timeout: Optional[int] = DEFAULT_TIMEOUT, max_retries: Optional[int] = DEFAULT_MAX_RETRIES, user_agent: Optional[str] = DEFAULT_USER_AGENT, proxies: Optional[dict] = None, cookies: Optional[dict] = None, **kwargs, ): """Inits CensysAPIBase. Args: url (str): Optional; The URL to make API requests. timeout (int): Optional; Timeout for API requests in seconds. max_retries (int): Optional; Max number of times to retry failed API requests. user_agent (str): Optional; Override User-Agent string. proxies (dict): Optional; Configure HTTP proxies. cookies (dict): Optional; Configure cookies. **kwargs: Arbitrary keyword arguments. Raises: CensysException: Base Exception Class for the Censys API. """ # Get common request settings self.timeout = timeout self.max_retries = max_retries self._api_url = url or os.getenv("CENSYS_API_URL") if not self._api_url: raise CensysException("No API url configured.") # Create a session and set credentials self._session = requests.Session() if proxies: if "http" in proxies: warnings.warn("HTTP proxies will not be used.") proxies.pop("http", None) self._session.proxies.update(proxies) if cookies: self._session.cookies.update(cookies) self.request_id = kwargs.get("request_id") if kwargs.get("verify"): self._session.verify = kwargs.get("verify") if kwargs.get("cert"): self._session.cert = kwargs.get("cert") self._session.headers.update( { "accept": "application/json, */8", "User-Agent": " ".join( [ requests.utils.default_user_agent(), user_agent or kwargs.get("user_agent_identifier") or self.DEFAULT_USER_AGENT, ] ), } ) @property def request_id(self) -> Optional[str]: """The x-request-id header value for API requests. The x-request-id header is not set when the value is None. Value is None by default Returns: Type[Optional[str]]: The value of the header. """ value = self._session.headers.get("x-request-id") if not isinstance(value, str): return None return value @request_id.setter def request_id(self, value: Optional[str]): if value is None: self._session.headers.pop("x-request-id", None) return self._session.headers["x-request-id"] = value @staticmethod def _get_exception_class(_: Response) -> type[CensysAPIException]: """Maps HTTP status code or ASM error code to exception. Must be implemented by child class. Args: _ (Response): HTTP requests response object. Returns: Type[CensysAPIException]: Exception to raise. """ return CensysAPIException @backoff.on_predicate( backoff.runtime, predicate=lambda r: r.status_code in (408, 429, 502, 503) and r.headers.get("Retry-After"), value=lambda r: int(r.headers.get("Retry-After", 0)), ) def _call_method(self, method: Callable[..., Response], url: str, request_kwargs: dict) -> Response: """Make API call. Wrapper functions for all our REST API calls checking for errors and decoding the responses. Args: method (Callable): Method to send HTTP request. url (str): The URL to make API requests. request_kwargs (dict): Keyword arguments to pass to method. Returns: Response: Results from an API request. """ return method(url, **request_kwargs) @_backoff_wrapper def _make_call( self, method: Callable[..., Response], endpoint: str, args: Optional[dict] = None, data: Optional[Any] = None, **kwargs, ) -> dict: """Make API call. Wrapper functions for all our REST API calls checking for errors and decoding the responses. Args: method (Callable): Method to send HTTP request. endpoint (str): The path of API endpoint. args (dict): Optional; URL args that are mapped to params. data (Any): Optional; JSON data to serialize with request. **kwargs: Arbitrary keyword arguments to pass to method. Raises: censys_exception: Exception Class for the Censys API. CensysJSONDecodeException: Exception for decoding JSON. Returns: dict: Results from an API request. """ url = f"{self._api_url}{endpoint}" if endpoint.startswith("/") else f"{self._api_url}/{endpoint}" request_kwargs = { "params": args or {}, "timeout": self.timeout, **kwargs, } if data: request_kwargs["json"] = data res = self._call_method(method, url, request_kwargs) if res.ok: # Check for a returned json body try: json_data = res.json() if "error" not in json_data: return json_data # Successful request returned no json body in response except ValueError: return { "code": res.status_code, "status": res.reason, } try: json_data = res.json() message = json_data.get("error") or json_data.get("message") const = json_data.get("error_type") or json_data.get("status") or res.reason error_code = json_data.get("errorCode") or json_data.get("statusCode", "unknown") details = json_data.get("details", "unknown") except (ValueError, json.decoder.JSONDecodeError) as error: raise CensysJSONDecodeException( status_code=res.status_code, message=f"Response from {res.url} is not valid JSON and cannot be decoded.", body=res.text, const="badjson", ) from error censys_exception = self._get_exception_class(res) raise censys_exception( status_code=res.status_code, body=res.text, const=const, message=message, error_code=error_code, details=details, ) def _get(self, endpoint: str, args: Optional[dict] = None, **kwargs) -> dict: return self._make_call(self._session.get, endpoint, args, **kwargs) def _post( self, endpoint: str, args: Optional[dict] = None, data: Optional[dict] = None, **kwargs, ) -> dict: return self._make_call(self._session.post, endpoint, args, data, **kwargs) def _put( self, endpoint: str, args: Optional[dict] = None, data: Optional[dict] = None, **kwargs, ) -> dict: return self._make_call(self._session.put, endpoint, args, data, **kwargs) def _patch( self, endpoint: str, args: Optional[dict] = None, data: Optional[dict] = None, **kwargs, ) -> dict: return self._make_call(self._session.patch, endpoint, args, data, **kwargs) def _delete(self, endpoint: str, args: Optional[dict] = None, **kwargs) -> dict: return self._make_call(self._session.delete, endpoint, args, **kwargs) censys-censys-python-7a57a89/censys/common/config.py000066400000000000000000000054171524433232300226310ustar00rootroot00000000000000"""Interact with the config file.""" import configparser import contextlib import os from pathlib import Path DEFAULT = "DEFAULT" HOME_PATH = str(Path.home()) CENSYS_PATH = os.path.join(HOME_PATH, ".config", "censys") CONFIG_PATH = os.path.join(CENSYS_PATH, "censys.cfg") default_config = { "api_id": "", "api_secret": "", "asm_api_key": "", "color": "auto", } def get_config_path() -> str: """Returns the path to the config file. Returns: str: Path to config file. """ alt_path = os.getenv("CENSYS_CONFIG_PATH") if alt_path: return alt_path return CONFIG_PATH def _restricted_opener(path: str, flags: int) -> int: """Opener that creates files readable and writable by the owner only. Args: path (str): Path to open. flags (int): Flags passed by `open()`. Returns: int: File descriptor. """ return os.open(path, flags, 0o600) def _try_chmod(path: str, mode: int) -> None: """Best-effort permission tightening. Files are already created owner-only by `_restricted_opener`, so failing to tighten an existing path must never stop the config from being written. Args: path (str): Path to tighten. mode (int): Desired permission bits. """ with contextlib.suppress(OSError): os.chmod(path, mode) def write_config(config: configparser.ConfigParser) -> None: """Writes config to file. The config file contains API credentials, so the directory and file are created owner-only (0700/0600). Existing paths are tightened on a best-effort basis; the requested modes are still subject to the umask. Args: config (configparser.ConfigParser): Configuration to write. Raises: PermissionError: If the config file is not writable. """ config_path = get_config_path() if config_path == CONFIG_PATH: if not os.access(HOME_PATH, os.W_OK): raise PermissionError( "Cannot write to home directory. Please set the `CENSYS_CONFIG_PATH` environmental variable to a writeable location." ) elif not os.path.isdir(CENSYS_PATH): os.makedirs(CENSYS_PATH, mode=0o700) else: _try_chmod(CENSYS_PATH, 0o700) if os.path.isfile(config_path): _try_chmod(config_path, 0o600) with open(config_path, "w", opener=_restricted_opener) as configfile: config.write(configfile) def get_config() -> configparser.ConfigParser: """Reads and returns config. Returns: configparser.ConfigParser: Config for Censys. """ config = configparser.ConfigParser(defaults=default_config, default_section=DEFAULT) config_path = get_config_path() if os.path.isfile(config_path): config.read(config_path) return config censys-censys-python-7a57a89/censys/common/deprecation.py000066400000000000000000000021231524433232300236500ustar00rootroot00000000000000"""Warns on deprecated class and functions.""" import functools import warnings from typing import Callable, Optional class DeprecationDecorator: """Deprecation Decorator for classes and functions.""" def __init__(self, message: Optional[str] = None): """Inits DeprecationDecorator. Args: message (str): Optional; Message to display to user. """ self.message = message def __call__(self, func: Callable) -> Callable: """Wrapper function. Args: func (Callable): Function to wrap. Returns: Callable: Wrapped function. """ @functools.wraps(func) def new_func(*args, **kwargs): # warnings.simplefilter("always", DeprecationWarning) warnings.warn( self.message or f"Call to deprecated function {func.__name__}.", category=DeprecationWarning, stacklevel=2, ) # warnings.simplefilter("default", DeprecationWarning) return func(*args, **kwargs) return new_func censys-censys-python-7a57a89/censys/common/exceptions.py000066400000000000000000000301531524433232300235400ustar00rootroot00000000000000"""Exceptions for Censys.""" from typing import Optional class CensysException(Exception): """Base Exception for Censys.""" class CensysCLIException(CensysException): """Exception raised when the CLI is passed invalid arguments.""" class CensysAPIException(CensysException): """Base Exception for Censys APIs.""" def __init__( self, status_code: int, message: str, body: Optional[str] = None, const: Optional[str] = None, error_code: Optional[int] = None, details: Optional[str] = None, ): """Inits CensysAPIException. Args: status_code (int): HTTP status code. message (str): HTTP message. body (str): Optional; HTTP body. const (str): Optional; Constant for manual errors. error_code (int): Optional; Error code. details (str): Optional; Additional details. """ self.status_code = status_code self.message = message self.body = body self.const = const self.error_code = error_code self.details = details super().__init__(self.message) class CensysSearchException(CensysAPIException): """Base Exception for the Censys search API.""" def __repr__(self) -> str: """Representation of CensysSearchException. Returns: str: Printable representation. """ return f"{self.status_code} ({self.const}): {self.message or self.body}" __str__ = __repr__ class CensysAsmException(CensysAPIException): """Base Exception for the Censys ASM API.""" def __repr__(self) -> str: """Representation of CensysAsmException. Returns: str: Printable representation. """ return f"{self.status_code} (Error Code: {self.error_code}), {self.message}. {self.details}" __str__ = __repr__ class CensysMissingApiKeyException(CensysAsmException): """Exception raised when there is no provided ASM API key.""" class CensysRateLimitExceededException(CensysSearchException): """Exception raised when your Censys rate limit has been exceeded.""" class CensysNotFoundException(CensysSearchException): """Exception raised when the resource requested is not found.""" class CensysUnauthorizedException(CensysSearchException): """Exception raised when you doesn't have access to the requested resource.""" class CensysJSONDecodeException(CensysSearchException): """Exception raised when the resource requested is not valid JSON.""" class CensysInternalServerException(CensysSearchException): """Exception raised when the server encountered an internal error.""" class CensysInvalidRequestException(CensysAsmException): """Exception raised when the HTTP request is invalid.""" class CensysInvalidAuthTokenException(CensysAsmException): """Exception raised when the auth token is invalid.""" class CensysAsmUnauthorizedException(CensysAsmException): """Exception raised when the ASM API is unauthorized.""" class CensysInvalidAPIKeyException(CensysAsmException): """Exception raised when the ASM API key is invalid.""" class CensysTooManyRequestsException(CensysAsmException): """Exception raised when the allowed requests bandwidth is exceeded.""" class CensysAppDownForMaintenanceException(CensysAsmException): """Exception raised when the ASM API is down for maintenance.""" class CensysInvalidSeedDataException(CensysAsmException): """Exception raised when the seed data is invalid.""" class CensysAssociatedAssetsThresholdWarningException(CensysAsmException): """Exception raised when the asset count is within the warning threshold.""" class CensysTooManyInputNodesException(CensysAsmException): """Exception raised when there are too many input nodes.""" class CensysSeedNotFoundException(CensysAsmException): """Exception raised when the requested seed can not be found.""" class CensysNotASeedException(CensysAsmException): """Exception raised when the requested resource is not a seed.""" class CensysNeedConfirmationToRemoveParentSeedsException(CensysAsmException): """Exception raised when confirmation is needed to remove seeds with children.""" class CensysCannotRemoveNonExistentSeedsException(CensysAsmException): """Exception raised when trying to remove non existent seed nodes.""" class CensysCannotRemoveNonSeedsException(CensysAsmException): """Exception raised when trying to remove non seed nodes.""" class CensysInvalidSeedTypeException(CensysAsmException): """Exception raised when the seed type is invalid.""" class CensysInvalidLogbookCursorException(CensysAsmException): """Exception raised when the logbook cursor is invalid.""" class CensysTeamNotFoundException(CensysAsmException): """Exception raised when the team is not found.""" class CensysPageNumberOutOfRangeException(CensysAsmException): """Exception raised when the page number is out of range [1 - totalPages].""" class CensysInvalidPageSizeException(CensysAsmException): """Exception raised when the page size is invalid.""" class CensysHostNotFoundException(CensysAsmException): """Exception raised when the requested host is not found.""" class CensysInvalidIPv4AddressException(CensysAsmException): """Exception raised when the IPv4 address is invalid.""" class CensysAssetExcludedException(CensysAsmException): """Exception raised when the asset is excluded.""" class CensysInvalidCommentException(CensysAsmException): """Exception raised when the comment is invalid.""" class CensysCommentNotFoundException(CensysAsmException): """Exception raised when the requested comment is not found.""" class CensysSubdomainNotFoundException(CensysAsmException): """Exception raised when the requested subdomain is not found.""" class CensysInvalidColorException(CensysAsmException): """Exception raised when the specified color is invalid.""" class CensysTagHasTrailingOrLeadingWhitespaceException(CensysAsmException): """Exception raised when the specified tag has trailing or leading whitespace.""" class CensysTagIsEmptyStringException(CensysAsmException): """Exception raised when the specified tag is an empty string.""" class CensysTagLabelsDifferOnlyInCasingException(CensysAsmException): """Exception raised when the tag differs from an existing tag in only casing.""" class CensysTagLabelTooLongException(CensysAsmException): """Exception raised when the specified tag label is too long.""" class CensysTagColorTooLongException(CensysAsmException): """Exception raised when the specified tag color is too long.""" class CensysCannotCreateTagWithNewColorException(CensysAsmException): """Exception raised when the specified tag cannot be created with a new color.""" class CensysTagColorHasTrailingOrLeadingWhitespaceException(CensysAsmException): """Exception raised when the tag color has trailing or leading whitespace.""" class CensysCertificateNotFoundException(CensysAsmException): """Exception raised when the certificate is not found.""" class CensysDomainNotFoundException(CensysAsmException): """Exception raised when the domain is not found.""" class CensysInvalidCloudAssetDataException(CensysAsmException): """Exception raised when invalid cloud asset data is submitted.""" class CensysInvalidObjectStorageAssetIdentifierException(CensysAsmException): """Exception raised when object storage name is not a valid asset URL.""" class CensysInvalidObjectStorageAssetNotFoundException(CensysAsmException): """Exception raised when no object storage assets with given URL were found.""" class CensysBadJSONBodyException(CensysAsmException): """Exception raised when a bad JSON string is in the body.""" class CensysRiskNotFoundException(CensysAsmException): """Exception raised when no risks are found with given risk_id.""" class CensysInvalidDateException(CensysAsmException): """Exception raised when an invalid date is submitted.""" class CensysInvalidCloudException(CensysAsmException): """Exception raised when an invalid cloud is submitted.""" class CensysAssetNotFoundException(CensysAsmException): """Exception raised when the asset is not found.""" class CensysInvalidKeywordsInBodyException(CensysAsmException): """Exception raised when invalid keywords are in the body.""" class CensysSearchAPITimeoutException(CensysAsmException): """Exception raised when the search API times out.""" class CensysSearchAPIErrorException(CensysAsmException): """Exception raised when the search API returns an error.""" class CensysInternalServerErrorException(CensysAsmException): """Exception raised when the search API returns an error.""" class CensysInvalidCommentHTMLException(CensysAsmException): """Exception raised when the comment contains invalid HTML.""" class CensysInvalidSearchAPIResponseException(CensysAsmException): """Exception raised when the search API returns an error.""" class CensysTooSoonToResendInviteException(CensysAsmException): """Exception raised when it is too soon to resend the invite.""" class CensysExceptionMapper: """Map status code to Exception for the ASM and Search API.""" ASM_EXCEPTIONS: dict[int, type[CensysAsmException]] = { 10000: CensysMissingApiKeyException, 10001: CensysInvalidAPIKeyException, 10002: CensysInvalidAuthTokenException, 10006: CensysAsmUnauthorizedException, 10007: CensysInvalidSeedDataException, 10008: CensysInvalidRequestException, 10011: CensysCannotRemoveNonSeedsException, 10012: CensysCannotRemoveNonExistentSeedsException, 10013: CensysNeedConfirmationToRemoveParentSeedsException, 10014: CensysSeedNotFoundException, 10015: CensysNotASeedException, 10016: CensysTooManyInputNodesException, 10017: CensysAssociatedAssetsThresholdWarningException, 10018: CensysHostNotFoundException, 10019: CensysDomainNotFoundException, 10020: CensysCertificateNotFoundException, 10021: CensysInvalidIPv4AddressException, 10022: CensysAssetExcludedException, 10025: CensysTagHasTrailingOrLeadingWhitespaceException, 10026: CensysTagIsEmptyStringException, 10027: CensysTagLabelsDifferOnlyInCasingException, 10028: CensysTagLabelTooLongException, 10029: CensysAppDownForMaintenanceException, 10034: CensysTagColorTooLongException, 10035: CensysCannotCreateTagWithNewColorException, 10036: CensysTagColorHasTrailingOrLeadingWhitespaceException, 10037: CensysInvalidColorException, 10038: CensysInvalidSeedTypeException, 10039: CensysTooManyRequestsException, 10040: CensysInvalidLogbookCursorException, 10045: CensysTeamNotFoundException, 10050: CensysInvalidPageSizeException, 10051: CensysPageNumberOutOfRangeException, 10054: CensysInvalidCommentException, 10055: CensysCommentNotFoundException, 10057: CensysSubdomainNotFoundException, 10059: CensysInvalidCloudAssetDataException, 10060: CensysInvalidObjectStorageAssetIdentifierException, 10061: CensysInvalidObjectStorageAssetNotFoundException, 10067: CensysBadJSONBodyException, 10073: CensysRiskNotFoundException, 10078: CensysInvalidDateException, 10082: CensysInvalidCloudException, 10086: CensysAssetNotFoundException, 10091: CensysInvalidKeywordsInBodyException, 10096: CensysSearchAPITimeoutException, 10097: CensysSearchAPIErrorException, 10098: CensysInternalServerErrorException, 10099: CensysInvalidCommentHTMLException, 10107: CensysInvalidSearchAPIResponseException, 10120: CensysTooSoonToResendInviteException, } """Map of status code to ASM Exception.""" SEARCH_EXCEPTIONS: dict[int, type[CensysSearchException]] = { 401: CensysUnauthorizedException, 403: CensysUnauthorizedException, 404: CensysNotFoundException, 429: CensysRateLimitExceededException, 500: CensysInternalServerException, } """Map of status code to Search Exception.""" censys-censys-python-7a57a89/censys/common/types.py000066400000000000000000000002211524433232300225140ustar00rootroot00000000000000"""Common types for the Censys Python SDK.""" import datetime from typing import Union Datetime = Union[str, datetime.date, datetime.datetime] censys-censys-python-7a57a89/censys/common/utils.py000066400000000000000000000014351524433232300225200ustar00rootroot00000000000000"""Common utilities for the Censys Python SDK.""" import datetime from .types import Datetime def format_rfc3339(time: Datetime) -> str: """Formats a datetime object into an RFC3339 string. Args: time (Datetime): Datetime object to format. Returns: str: RFC3339 formatted string. """ if isinstance(time, (datetime.date, datetime.datetime)): time = time.strftime("%Y-%m-%dT%H:%M:%S.%fZ") return time def format_iso8601(time: Datetime) -> str: """Formats a datetime object into an ISO8601 string. Args: time (Datetime): Datetime object to format. Returns: str: ISO8601 formatted string. """ if isinstance(time, (datetime.date, datetime.datetime)): return time.strftime("%Y-%m-%d") return time censys-censys-python-7a57a89/censys/common/version.py000066400000000000000000000001451524433232300230420ustar00rootroot00000000000000"""Censys Version.""" import importlib.metadata __version__ = importlib.metadata.version("censys") censys-censys-python-7a57a89/censys/py.typed000066400000000000000000000000001524433232300212000ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/search/000077500000000000000000000000001524433232300207605ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/search/__init__.py000066400000000000000000000005271524433232300230750ustar00rootroot00000000000000"""An easy-to-use and lightweight API wrapper for Censys Search API (search.censys.io).""" from .client import SearchClient from .v1 import CensysData from .v2 import CensysCerts, CensysHosts __copyright__ = "Copyright 2024 Censys, Inc." __all__ = [ "SearchClient", "CensysData", "CensysCerts", "CensysHosts", ] censys-censys-python-7a57a89/censys/search/client.py000066400000000000000000000034571524433232300226210ustar00rootroot00000000000000"""Interact with all Search APIs.""" from .v1 import CensysData from .v2 import CensysCerts, CensysHosts class SearchClient: """Client for interacting with all Search APIs. All indexes are passed the args and kwargs that are provided. Examples: Inits SearchClient. >>> from censys.search import SearchClient >>> c = SearchClient() Access both v1 and v2 indexes. >>> data = c.v1.data # CensysData() >>> hosts = c.v2.hosts # CensysHosts() >>> certs = c.v2.certs # CensysCerts() """ class _V1: """Class for v1 Search APIs.""" data: CensysData def __init__(self, *args, **kwargs): """Inits V1. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ self.data = CensysData(*args, **kwargs) class _V2: """Class for v2 Search APIs.""" hosts: CensysHosts certs: CensysCerts certificates: CensysCerts def __init__(self, *args, **kwargs): """Inits V2. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ self.hosts = CensysHosts(*args, **kwargs) self.certs = CensysCerts(*args, **kwargs) self.certificates = self.certs def __init__(self, *args, **kwargs): """Inits SearchClient. Args: *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ # Backwards compatibility if len(args) == 2: kwargs["api_id"] = args[0] kwargs["api_secret"] = args[1] self.v1 = self._V1(**kwargs) self.v2 = self._V2(**kwargs) censys-censys-python-7a57a89/censys/search/v1/000077500000000000000000000000001524433232300213065ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/search/v1/__init__.py000066400000000000000000000001471524433232300234210ustar00rootroot00000000000000"""Interact with the Censys Search v1 APIs.""" from .data import CensysData __all__ = ["CensysData"] censys-censys-python-7a57a89/censys/search/v1/api.py000066400000000000000000000057501524433232300224400ustar00rootroot00000000000000"""Base for interacting with the Censys Search API.""" import os import warnings from typing import Optional from requests.models import Response from censys.common.base import CensysAPIBase from censys.common.config import DEFAULT, get_config from censys.common.exceptions import ( CensysException, CensysExceptionMapper, CensysSearchException, ) Fields = Optional[list[str]] class CensysSearchAPIv1(CensysAPIBase): """This class is the base class for all v1 API indexes.""" DEFAULT_URL: str = "https://search.censys.io/api/v1" """Default Search API base URL.""" INDEX_NAME: Optional[str] = None """Name of Censys Index.""" def __init__(self, api_id: Optional[str] = None, api_secret: Optional[str] = None, **kwargs): """Inits CensysSearchAPIv1. See CensysAPIBase for additional arguments. Args: api_id (str): Optional; The API ID provided by Censys. api_secret (str): Optional; The API secret provided by Censys. **kwargs: Arbitrary keyword arguments. Raises: CensysException: Base Exception Class for the Censys API. """ warnings.warn( "CensysSearchAPIv1 will be deprecated soon. " "Please migrate to the new Censys Platform API. " "Visit https://docs.censys.com/reference/get-started#/ for documentation " "and install the new SDK with: pip install censys-platform " "(https://pypi.org/project/censys-platform/)", category=DeprecationWarning, stacklevel=2, ) CensysAPIBase.__init__(self, kwargs.pop("url", self.DEFAULT_URL), **kwargs) # Gets config file config = get_config() # Try to get credentials self._api_id = api_id or os.getenv("CENSYS_API_ID") or config.get(DEFAULT, "api_id") self._api_secret = api_secret or os.getenv("CENSYS_API_SECRET") or config.get(DEFAULT, "api_secret") if not self._api_id or not self._api_secret: raise CensysException("No API ID or API secret configured.") self._session.auth = (self._api_id, self._api_secret) # Generate concrete paths to be called self.search_path = f"/search/{self.INDEX_NAME}" self.view_path = f"/view/{self.INDEX_NAME}/" self.report_path = f"/report/{self.INDEX_NAME}" # Confirm setup # self.account() def _get_exception_class( # type: ignore self, res: Response ) -> type[CensysSearchException]: return CensysExceptionMapper.SEARCH_EXCEPTIONS.get(res.status_code, CensysSearchException) def account(self) -> dict: """Gets the current account information. This includes email and quota. Returns: dict: Account response. """ return self._get("account") def quota(self) -> dict: """Gets the current account's query quota. Returns: dict: Quota response. """ return self.account()["quota"] censys-censys-python-7a57a89/censys/search/v1/data.py000066400000000000000000000023111524433232300225660ustar00rootroot00000000000000"""Interact with the Censys Search Data API.""" from .api import CensysSearchAPIv1 class CensysData(CensysSearchAPIv1): """Interacts with the Data index. For more details, see our documentation: https://search.censys.io/api """ _PREFIX = "/data" """Endpoint prefix.""" def get_series(self) -> dict: """Get data on the types of scans we regularly perform (series). Returns: dict: The result set returned. """ return self._get(self._PREFIX) def view_series(self, series_id: str) -> dict: """Get data on a specific series. Args: series_id (str): The ID of the series. Returns: dict: The result set returned. """ path = f"{self._PREFIX}/{series_id}" return self._get(path) def view_result(self, series_id: str, result_id: str) -> dict: """View a specific result of a specific series. Args: series_id (str): The ID of the series. result_id (str): The ID of the result. Returns: dict: The result set returned. """ path = f"{self._PREFIX}/{series_id}/{result_id}" return self._get(path) censys-censys-python-7a57a89/censys/search/v2/000077500000000000000000000000001524433232300213075ustar00rootroot00000000000000censys-censys-python-7a57a89/censys/search/v2/__init__.py000066400000000000000000000002301524433232300234130ustar00rootroot00000000000000"""Interact with the Censys Search v2 APIs.""" from .certs import CensysCerts from .hosts import CensysHosts __all__ = ["CensysCerts", "CensysHosts"] censys-censys-python-7a57a89/censys/search/v2/api.py000066400000000000000000000555461524433232300224510ustar00rootroot00000000000000"""Base for interacting with the Censys Search API.""" import os import warnings from collections.abc import Iterable, Iterator from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Optional, Union from requests.models import Response from censys.common.base import CensysAPIBase from censys.common.config import DEFAULT, get_config from censys.common.exceptions import ( CensysException, CensysExceptionMapper, CensysSearchException, ) INDEX_TO_KEY = {"hosts": "ip", "certificates": "fingerprint_sha256"} class CensysSearchAPIv2(CensysAPIBase): """This class is the base class for the Hosts index. Examples: >>> c = CensysSearchAPIv2() """ DEFAULT_URL: str = "https://search.censys.io/api" """Default Search API base URL.""" INDEX_NAME: str = "" """Name of Censys Index.""" def __init__(self, api_id: Optional[str] = None, api_secret: Optional[str] = None, **kwargs): """Inits CensysSearchAPIv2. See CensysAPIBase for additional arguments. Args: api_id (str): Optional; The API ID provided by Censys. api_secret (str): Optional; The API secret provided by Censys. **kwargs: Arbitrary keyword arguments. Raises: CensysException: Base Exception Class for the Censys API. """ warnings.warn( "CensysSearchAPIv2 will be deprecated soon. " "Please migrate to the new Censys Platform API. " "Visit https://docs.censys.com/reference/get-started#/ for documentation " "and install the new SDK with: pip install censys-platform " "(https://pypi.org/project/censys-platform/)", category=DeprecationWarning, stacklevel=2, ) CensysAPIBase.__init__(self, kwargs.pop("url", self.DEFAULT_URL), **kwargs) # Gets config file config = get_config() # Try to get credentials self._api_id = api_id or os.getenv("CENSYS_API_ID") or config.get(DEFAULT, "api_id") self._api_secret = api_secret or os.getenv("CENSYS_API_SECRET") or config.get(DEFAULT, "api_secret") if not self._api_id or not self._api_secret: raise CensysException("No API ID or API secret configured.") self._session.auth = (self._api_id, self._api_secret) # Generate concrete paths to be called self.view_path = f"/v2/{self.INDEX_NAME}/" self.search_path = f"/v2/{self.INDEX_NAME}/search" self.aggregate_path = f"/v2/{self.INDEX_NAME}/aggregate" self.tags_path = "/v2/tags" self.account_path = "/v1/account" def _get_exception_class( # type: ignore self, res: Response ) -> type[CensysSearchException]: return CensysExceptionMapper.SEARCH_EXCEPTIONS.get(res.status_code, CensysSearchException) def account(self) -> dict: """Gets the current account's query quota. Returns: dict: Quota response. """ return self._get(self.account_path) def quota(self) -> dict: """Returns metadata of a given search query. Returns: dict: The metadata of the result set returned. """ return self.account()["quota"] class Query(Iterable): """Query class that is callable and iterable. Object Searches the given index for all records that match the given query. For more details, see our documentation: https://search.censys.io/api """ # Total number of results (Set after first query) total: Optional[int] = None def __init__( self, api: "CensysSearchAPIv2", query: str, per_page: Optional[int] = None, cursor: Optional[str] = None, pages: int = 1, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs: Any, ): """Inits Query. Args: api (CensysSearchAPIv2): Parent API object. query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. pages (int): Optional; The number of pages returned. Defaults to 1. If you set this to -1, it will return all pages. fields (List[str]): Optional; The fields to be returned. Defaults to base fields. sort (Union[str, List[str]]): Optional; The fields to sort by. Defaults to None. **kwargs (Any): Optional; Additional arguments to be passed to the query. """ self.api = api self.query = query self.per_page = per_page self.cursor = cursor self.nextCursor: Optional[str] = None self.page = 1 if pages <= 0: self.pages = float("inf") else: self.pages = pages self.fields = fields self.sort = sort self.extra_args = kwargs def __call__(self, per_page: Optional[int] = None) -> list[dict]: """Search current index. Args: per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. Raises: StopIteration: Raised when pages have been already received. Returns: List[dict]: One page worth of result hits. """ if self.page > self.pages: raise StopIteration payload = self.api.raw_search( query=self.query, per_page=per_page or self.per_page or 100, cursor=self.nextCursor or self.cursor, fields=self.fields, sort=self.sort, **self.extra_args, ) self.page += 1 result = payload["result"] self.total = result["total"] self.nextCursor = result["links"].get("next") if self.total == 0 or not self.nextCursor: self.pages = 0 return result["hits"] def __next__(self) -> list[dict]: """Gets next page of search results. Returns: List[dict]: One page worth of result hits. """ return self.__call__() def __iter__(self) -> Iterator[list[dict]]: """Gets Iterator. Returns: Iterable: Returns self. """ return self def view_all(self, max_workers: int = 20) -> dict[str, dict]: """View each document returned from query. Please note that each result returned by the query will be looked up using the view method. Args: max_workers (int): The number of workers to use. Defaults to 20. Returns: Dict[str, dict]: Dictionary mapping documents to that document's result set. """ results = {} document_key = INDEX_TO_KEY.get(self.api.INDEX_NAME, "ip") with ThreadPoolExecutor(max_workers) as executor: threads = {} while self.page <= self.pages: for hit in self.__call__(): hit_key = hit[document_key] if "name" in hit and self.api.INDEX_NAME == "hosts": hit_key += "+" + hit["name"] threads[executor.submit(self.api.view, hit_key)] = hit_key for task in as_completed(threads): document_id = threads[task] try: results[document_id] = task.result() except Exception as e: results[document_id] = {"error": str(e)} return results def search( self, query: str, per_page: int = 100, cursor: Optional[str] = None, pages: int = 1, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs: Any, ) -> Query: """Search current index. Searches the given index for all records that match the given query. For more details, see our documentation: https://search.censys.io/api Args: query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. pages (int): Optional; The number of pages returned. Defaults to 1. fields (List[str]): Optional; The fields to be returned. Defaults to base fields. sort (Union[str, List[str]]): Optional; The fields to sort by. Defaults to None. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: Query: Query object that can be a callable or an iterable. """ return self.Query(self, query, per_page, cursor, pages, fields, sort, **kwargs) def search_post_raw( self, query: str, per_page: int = 100, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the given index for all records that match the given query. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): The fields to be returned. Defaults to base fields. sort (Union[str, List[str]], optional): The fields to sort by. Defaults to None. **kwargs: Arbitrary keyword arguments. Returns: dict: Search results. """ data = { "q": query, "per_page": per_page, } if cursor: data["cursor"] = cursor if fields: data["fields"] = fields if sort: data["sort"] = sort data.update(kwargs) return self._post(self.search_path, data=data) def search_post( self, query: str, per_page: int = 100, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the Certs index using the POST method. This method returns the `result` field of the raw response. If you wish to access the raw response, please use `search_post_raw` instead. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): Additional fields to return in the matched documents. Defaults to base fields. sort (Union[str, List[str]], optional): The fields to sort by. Defaults to None. **kwargs: Arbitrary keyword arguments. Returns: dict: Search results. """ return self.search_post_raw( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, )["result"] def search_get_raw( self, query: str, per_page: int = 100, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs: Any, ) -> dict: """Search current index using GET method. Args: query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. fields (List[str]): Optional; The fields to be returned. Defaults to base fields. sort (Union[str, List[str]]): Optional; The fields to sort by. Defaults to None. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The raw result set. """ args = { "q": query, "per_page": per_page, "cursor": cursor, "fields": fields, "sort": sort, } args.update(kwargs) return self._get(self.search_path, args) def search_get( self, query: str, per_page: int = 100, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs: Any, ) -> dict: """Search current index using GET method. Args: query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. fields (List[str]): Optional; The fields to be returned. Defaults to base fields. sort (Union[str, List[str]]): Optional; The fields to sort by. Defaults to None. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The raw result set. """ return self.search_get_raw( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, )["result"] def raw_search( self, query: str, per_page: int = 100, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs: Any, ) -> dict: """Search current index. Searches the given index for all records that match the given query. This method does no automatic pagination or post processing. Args: query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. fields (List[str]): Optional; The fields to be returned. Defaults to base fields. sort (Union[str, List[str]]): Optional; The fields to sort by. Defaults to None. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The raw result set. """ return self.search_post_raw( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, ) def view(self, document_id: str, **kwargs: Any) -> dict: """View document from current index. View the current structured data we have on a specific document. For more details, see our documentation: https://search.censys.io/api Args: document_id (str): The ID of the document you are requesting. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The result set returned. """ return self._get(self.view_path + document_id, args=kwargs)["result"] def bulk_view( self, document_ids: list[str], max_workers: int = 20, **kwargs: Any, ) -> dict[str, dict]: """Bulk view documents from current index. View the current structured data we have on a list of documents. For more details, see our documentation: https://search.censys.io/api Args: document_ids (List[str]): The IDs of the documents you are requesting. max_workers (int): The number of workers to use. Defaults to 20. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: Dict[str, dict]: Dictionary mapping document IDs to that document's result set. """ documents = {} with ThreadPoolExecutor(max_workers) as executor: threads = {executor.submit(self.view, document_id, **kwargs): document_id for document_id in document_ids} for task in as_completed(threads): document_id = threads[task] try: documents[document_id] = task.result() except Exception as e: documents[document_id] = {"error": str(e)} return documents def aggregate(self, query: str, field: str, num_buckets: int = 50, **kwargs: Any) -> dict: """Aggregate current index. Creates a report on the breakdown of the values of a field in a result set. For more details, see our documentation: https://search.censys.io/api Args: query (str): The query to be executed. field (str): The field you are running a breakdown on. num_buckets (int): Optional; The maximum number of values. Defaults to 50. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The result set returned. """ args = {"q": query, "field": field, "num_buckets": num_buckets, **kwargs} return self._get(self.aggregate_path, args)["result"] # Comments def get_comments(self, document_id: str) -> list[dict]: """Get comments for a document. Args: document_id (str): The ID of the document you are requesting. Returns: List[dict]: The list of comments. """ return self._get(self.view_path + document_id + "/comments")["result"]["comments"] def get_comment(self, document_id: str, comment_id: str) -> dict: """Get comment for a document. Args: document_id (str): The ID of the document you are requesting. comment_id (str): The ID of the comment you are requesting. Returns: dict: The result set returned. """ return self._get(self.view_path + document_id + "/comments/" + comment_id)["result"] def add_comment(self, document_id: str, contents: str) -> dict: """Add comment to a document. Args: document_id (str): The ID of the document you are requesting. contents (str): The contents of the comment. Returns: dict: The result set returned. """ return self._post(self.view_path + document_id + "/comments", data={"contents": contents})["result"] def delete_comment(self, document_id: str, comment_id: str) -> dict: """Delete comment from a document. Args: document_id (str): The ID of the document you are requesting. comment_id (str): The ID of the comment you are requesting. Returns: dict: The result set returned. """ return self._delete(self.view_path + document_id + "/comments/" + comment_id) def update_comment(self, document_id: str, comment_id: str, contents: str) -> dict: """Update comment from a document. Args: document_id (str): The ID of the document you are requesting. comment_id (str): The ID of the comment you are requesting. contents (str): The contents of the comment. Returns: dict: The result set returned. """ return self._put( self.view_path + document_id + "/comments/" + comment_id, data={"contents": contents}, ) # Tags def list_all_tags(self) -> list[dict]: """List all tags. Returns: List[dict]: The list of tags. """ return self._get(self.tags_path)["result"]["tags"] def create_tag(self, name: str, color: Optional[str] = None) -> dict: """Create a tag. Args: name (str): The name of the tag. color (str): Optional; The color of the tag. Returns: dict: The result set returned. """ tag_def: dict[str, Any] = {"name": name} if color: tag_def["metadata"] = {"color": color} return self._post(self.tags_path, data=tag_def)["result"] def get_tag(self, tag_id: str) -> dict: """Get a tag. Args: tag_id (str): The ID of the tag. Returns: dict: The result set returned. """ return self._get(self.tags_path + "/" + tag_id)["result"] def update_tag(self, tag_id: str, name: str, color: Optional[str] = None) -> dict: """Update a tag. Args: tag_id (str): The ID of the tag. name (str): The name of the tag. color (str): The color of the tag. Returns: dict: The result set returned. """ tag_def: dict[str, Any] = {"name": name} if color: tag_def["metadata"] = {"color": color} return self._put( self.tags_path + "/" + tag_id, data=tag_def, )["result"] def delete_tag(self, tag_id: str): """Delete a tag. Args: tag_id (str): The ID of the tag. """ self._delete(self.tags_path + "/" + tag_id) def _list_documents_with_tag(self, tag_id: str, endpoint: str, keyword: str) -> list[dict]: """List documents by tag. Args: tag_id (str): The ID of the tag. endpoint (str): The endpoint to be called. keyword (str): The keyword to be used in the endpoint. Returns: List[dict]: The list of documents. """ return self._get(self.tags_path + "/" + tag_id + "/" + endpoint)["result"][keyword] def list_tags_on_document(self, document_id: str) -> list[dict]: """List tags on a document. Args: document_id (str): The ID of the document. Returns: List[dict]: The list of tags. """ return self._get(self.view_path + document_id + "/tags")["result"]["tags"] def add_tag_to_document(self, document_id: str, tag_id: str): """Add a tag to a document. Args: document_id (str): The ID of the document. tag_id (str): The ID of the tag. """ self._put(self.view_path + document_id + "/tags/" + tag_id) def remove_tag_from_document(self, document_id: str, tag_id: str): """Remove a tag from a document. Args: document_id (str): The ID of the document. tag_id (str): The ID of the tag. """ self._delete(self.view_path + document_id + "/tags/" + tag_id) censys-censys-python-7a57a89/censys/search/v2/certs.py000066400000000000000000000335771524433232300230200ustar00rootroot00000000000000"""Interact with the Censys Search Cert API.""" import warnings from typing import Optional, Union from ...common.types import Datetime from ...common.utils import format_rfc3339 from .api import CensysSearchAPIv2 class CensysCerts(CensysSearchAPIv2): """Interacts with the Certs index. Please note that this class represents only the v2 API endpoints. Examples: Inits Censys Certs. >>> from censys.search import CensysCerts >>> c = CensysCerts() Search for hosts by sha256fp. >>> c.get_hosts_by_cert("fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426") ( [ { "ip": "string", "name": "string", "observed_at": "2021-08-02T14:56:38.711Z", "first_observed_at": "2021-08-02T14:56:38.711Z", } ], { "next": "nextCursorToken", }, ) """ INDEX_NAME = "certificates" """Name of Censys Index.""" def __init__(self, api_id: Optional[str] = None, api_secret: Optional[str] = None, **kwargs): """Inits CensysCerts. See CensysSearchAPIv2 for additional arguments. Args: api_id (Optional[str], optional): API ID. Defaults to None. api_secret (Optional[str], optional): API Secret. Defaults to None. **kwargs: Arbitrary keyword arguments. """ super().__init__(api_id=api_id, api_secret=api_secret, **kwargs) self.bulk_path = f"/v2/{self.INDEX_NAME}/bulk" def view(self, document_id: str, **kwargs) -> dict: """Fetches the certificate record for the specified SHA-256 fingerprint. Args: document_id (str): The SHA-256 fingerprint of the requested certificate. **kwargs: Arbitrary keyword arguments. Returns: dict: Certificate details. """ return self._get(self.view_path + document_id, args=kwargs)["result"] def bulk_post(self, fingerprints: list[str]) -> list[dict]: """Fetches the certificate records for the specified SHA-256 fingerprints. Using the POST method allows for a larger number of fingerprints to be queried at once. Args: fingerprints (List[str]): List of certificate SHA256 fingerprints. Returns: dict: Certificate details. """ data = {"fingerprints": fingerprints} return self._post(self.bulk_path, data=data)["result"] def bulk_get(self, fingerprints: list[str]) -> list[dict]: """Fetches the certificate records for the specified SHA-256 fingerprints. Using the GET method allows for a smaller number of fingerprints to be queried at once. Args: fingerprints (List[str]): List of certificate SHA256 fingerprints. Returns: dict: Certificate details. """ args = {"fingerprints": fingerprints} return self._get(self.bulk_path, args=args)["result"] def bulk(self, fingerprints: list[str]) -> list[dict]: """Fetches the certificate records for the specified SHA-256 fingerprints. By default, this function uses the POST method, which allows for a larger number of fingerprints to be queried at once. If you wish to use the GET method, please use `CensysCerts.bulk_get` instead. Args: fingerprints (List[str]): List of certificate SHA256 fingerprints. Returns: dict: Certificate details. """ return self.bulk_post(fingerprints) def bulk_view(self, fingerprints: list[str]) -> list[dict]: # type: ignore[override] """Fetches the certificate records for the specified SHA-256 fingerprints. By default, this function uses the POST method, which allows for a larger number of fingerprints to be queried at once. If you wish to use the GET method, please use `CensysCerts.bulk_get` instead. Args: fingerprints (List[str]): List of certificate SHA256 fingerprints. Returns: dict: Certificate details. """ return self.bulk_post(fingerprints) def search_post_raw( self, query: str, per_page: int = 50, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the Certs index using the POST method. Returns the raw response. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): Additional fields to return in the matched certificates outside of the default returned fields. sort (List[str], optional): A list of fields to sort on. By default, fields will be sorted in ascending order. **kwargs: Arbitrary keyword arguments. Returns: dict: Search results. """ return super().search_post_raw( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, ) def search_post( self, query: str, per_page: int = 50, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the Certs index using the POST method. This method returns the `result` field of the raw response. If you wish to access the raw response, please use `CensysCerts.search_post_raw` instead. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): Additional fields to return in the matched certificates outside of the default returned fields. sort (List[str], optional): A list of fields to sort on. By default, fields will be sorted in ascending order. **kwargs: Arbitrary keyword arguments. Returns: dict: Search results. """ return super().search_post( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, ) def search_get( self, query: str, per_page: int = 50, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the Certs index using the GET method. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): Additional fields to return in the matched certificates outside of the default returned fields. sort (List[str], optional): A list of fields to sort on. By default, fields will be sorted in ascending order. **kwargs: Arbitrary keyword arguments. Returns: dict: Search results. """ return super().search_get( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, ) def raw_search( self, query: str, per_page: int = 50, cursor: Optional[str] = None, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> dict: """Searches the Certs index. Searches the Certs index for all records that match the given query. This method does no automatic pagination or post processing. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. fields (List[str], optional): Additional fields to return in the matched certificates outside of the default returned fields. sort (List[str], optional): A list of fields to sort on. By default, fields will be sorted in ascending order. **kwargs: Additional keyword arguments to pass to the underlying HTTP request. Returns: dict: Search results. """ return super().raw_search( query=query, per_page=per_page, cursor=cursor, fields=fields, sort=sort, **kwargs, ) def search( self, query: str, per_page: int = 50, cursor: Optional[str] = None, pages: int = 1, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, **kwargs, ) -> CensysSearchAPIv2.Query: """Searches the Certs index. By default, this function uses the POST method, which allows for a larger number of fingerprints to be queried at once. If you wish to use the GET method, please use `CensysCerts.search_get` instead. Args: query (str): The query string to search for. per_page (int): The number of results to return per page. Defaults to 50. cursor (str, optional): Cursor token from the API response, which fetches the next page of results when added to the endpoint URL. pages (int): The number of pages to return. Defaults to 1. fields (List[str], optional): Additional fields to return in the matched certificates outside of the default returned fields. sort (List[str], optional): A list of fields to sort on. By default, fields will be sorted in ascending order. **kwargs: Additional keyword arguments to pass to the underlying HTTP request. Returns: Query: A query object that can be used to iterate over the search results. """ return super().search(query, per_page, cursor, pages, fields, sort, **kwargs) def aggregate(self, query: str, field: str, num_buckets: int = 50, **kwargs) -> dict: """Aggregates certificate records matching a specified query into buckets based on the given field. Args: query (str): The query string to search for. field (str): The field to aggregate on. num_buckets (int): The number of buckets to return. Defaults to 50. **kwargs: Additional keyword arguments to pass to the underlying HTTP request. Returns: dict: Aggregation results. """ args = {"q": query, "field": field, "num_buckets": num_buckets} args.update(kwargs) return self._get(self.aggregate_path, args=args)["result"] def get_hosts_by_cert(self, fingerprint: str, cursor: Optional[str] = None) -> dict: """Returns a list of hosts which contain services presenting this certificate, including when the certificate was first observed. Args: fingerprint (str): The SHA-256 fingerprint of the requested certificate. cursor (str): Cursor token from the API response, which fetches the next page of hosts when added to the endpoint URL. Returns: dict: A list of hosts which contain services presenting this certificate. """ warnings.warn( "This API endpoint is deprecated and scheduled for removal during a future release. Users should migrate to using the search endpoint on the Host index using the 'services.certificate: {fingerprint}' query to find any hosts currently presenting a certificate.", category=DeprecationWarning, stacklevel=2, ) args = {"cursor": cursor} return self._get(self.view_path + fingerprint + "/hosts", args)["result"] def list_certs_with_tag(self, tag_id: str) -> list[dict]: """Returns a list of certs which are tagged with the specified tag. Args: tag_id (str): The ID of the tag. Returns: List[dict]: A list of certs which are tagged with the specified tag. """ return self._list_documents_with_tag(tag_id, "certificates", "certs") def get_observations( self, fingerprint: str, per_page: int = 50, start_time: Optional[Datetime] = None, end_time: Optional[Datetime] = None, cursor: Optional[str] = None, ) -> dict: """Returns a list of observations for the specified certificate. Args: fingerprint (str): The SHA-256 fingerprint of the requested certificate. per_page (int): The number of results to return per page. Defaults to 50. start_time (str): The start time of the observations to return. end_time (str): The end time of the observations to return. cursor (str): Cursor token from the API response, which fetches the next page of observations when added to the endpoint URL. Returns: dict: A list of observations for the specified certificate. """ args = {"per_page": per_page, "cursor": cursor} if start_time: args["start_time"] = format_rfc3339(start_time) if end_time: args["end_time"] = format_rfc3339(end_time) return self._get(self.view_path + fingerprint + "/observations", args)["result"] censys-censys-python-7a57a89/censys/search/v2/hosts.py000066400000000000000000000270541524433232300230310ustar00rootroot00000000000000"""Interact with the Censys Search Host API.""" from typing import Any, Optional, Union from censys.common.types import Datetime from censys.common.utils import format_rfc3339 from .api import CensysSearchAPIv2 class CensysHosts(CensysSearchAPIv2): """Interacts with the Hosts index. Examples: Inits Censys Hosts. >>> from censys.search import CensysHosts >>> h = CensysHosts() Simple host search. >>> for page in h.search("services.service_name: HTTP"): >>> print(page) [ { 'services': [ {'service_name': 'HTTP', 'port': 80}, {'service_name': 'HTTP', 'port': 443} ], 'ip': '1.0.0.0' }, ... ] Fetch a specific host and its services >>> h.view("1.0.0.0") { 'ip': '8.8.8.8', 'services': [{}], ... } Simple host aggregate. >>> h.aggregate("services.service_name: HTTP", "services.port", num_buckets=5) { 'total_omitted': 591527370, 'buckets': [ {'count': 56104072, 'key': '80'}, {'count': 43527894, 'key': '443'}, {'count': 23070429, 'key': '7547'}, {'count': 12970769, 'key': '30005'}, {'count': 12825150, 'key': '22'} ], 'potential_deviation': 3985101, 'field': 'services.port', 'query': 'services.service_name: HTTP', 'total': 172588754 } Fetch a list of host names for the specified IP address. >>> h.view_host_names("1.1.1.1") ['one.one.one.one'] Fetch a list of events for the specified IP address. >>> h.view_host_events("1.1.1.1") [{'timestamp': '2019-01-01T00:00:00.000Z'}] """ INDEX_NAME = "hosts" """Name of Censys Index.""" def __init__(self, api_id: Optional[str] = None, api_secret: Optional[str] = None, **kwargs): """Inits CensysHosts. See CensysSearchAPIv2 for additional arguments. Args: api_id (Optional[str], optional): API ID. Defaults to None. api_secret (Optional[str], optional): API Secret. Defaults to None. **kwargs: Arbitrary keyword arguments. """ super().__init__(api_id=api_id, api_secret=api_secret, **kwargs) self.metadata_path = f"/v2/metadata/{self.INDEX_NAME}" def view( self, document_id: str, at_time: Optional[Datetime] = None, **kwargs: Any, ) -> dict: """View document from current index. View the current structured data we have on a specific document. For more details, see our documentation: https://search.censys.io/api Args: document_id (str): The ID of the document you are requesting. at_time ([str, datetime.date, datetime.datetime]): Optional; Fetches a document at a given point in time. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The result set returned. """ args = {} if at_time: args["at_time"] = format_rfc3339(at_time) return super().view(document_id, **args) def bulk_view( self, document_ids: list[str], max_workers: int = 20, at_time: Optional[Datetime] = None, **kwargs: Any, ) -> dict[str, dict]: """Bulk view documents from current index. View the current structured data we have on a list of documents. Args: document_ids (List[str]): The IDs of the documents you are requesting. max_workers (int): Optional; The number of workers to use. Defaults to 20. at_time ([str, datetime.date, datetime.datetime]): Optional; Fetches a document at a given point in time. **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: Dict[str, dict]: The result set returned. """ if at_time: kwargs["at_time"] = format_rfc3339(at_time) return super().bulk_view(document_ids, max_workers, **kwargs) def search( self, query: str, per_page: int = 100, cursor: Optional[str] = None, pages: int = 1, fields: Optional[list[str]] = None, sort: Optional[Union[str, list[str]]] = None, virtual_hosts: Optional[str] = None, **kwargs: Any, ) -> CensysSearchAPIv2.Query: """Search host index. Searches the given index for all records that match the given query. For more details, see our documentation: https://search.censys.io/api Args: query (str): The query to be executed. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. pages (int): Optional; The number of pages returned. Defaults to 1. fields (List[str]): Optional; The fields to return. Defaults to all fields. sort (str): Optional; The method used to sort results. Valid values are "RELEVANCE", "DESCENDING", and "ASCENDING". virtual_hosts (str): Optional; Whether to include virtual hosts in the results. Valid values are "EXCLUDE", "INCLUDE", and "ONLY". **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: Query: Query object that can be a callable or an iterable. """ if virtual_hosts: kwargs["virtual_hosts"] = virtual_hosts return super().search(query, per_page, cursor, pages, fields, sort, **kwargs) def aggregate( self, query: str, field: str, num_buckets: int = 50, virtual_hosts: Optional[str] = None, **kwargs: Any, ) -> dict: """Aggregate host index. Creates a report on the breakdown of the values of a field in a result set. For more details, see our documentation: https://search.censys.io/api Args: query (str): The query to be executed. field (str): The field you are running a breakdown on. num_buckets (int): Optional; The maximum number of values. Defaults to 50. virtual_hosts (str): Optional; Whether to include virtual hosts in the results. Valid values are "EXCLUDE", "INCLUDE", and "ONLY". **kwargs (Any): Optional; Additional arguments to be passed to the query. Returns: dict: The result set returned. """ if virtual_hosts: kwargs["virtual_hosts"] = virtual_hosts return super().aggregate(query, field, num_buckets, **kwargs) def metadata(self) -> dict: """Get metadata for the host index. Returns: dict: The result set returned. """ return self._get(self.metadata_path)["result"] def view_host_names(self, ip: str, per_page: Optional[int] = None, cursor: Optional[str] = None) -> list[str]: """Fetches a list of host names for the specified IP address. Args: ip (str): The IP address of the requested host. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. cursor (int): Optional; The cursor of the desired result set. Returns: List[str]: A list of host names. """ args = {"per_page": per_page, "cursor": cursor} return self._get(self.view_path + ip + "/names", args)["result"]["names"] def view_host_diff( self, ip: str, ip_b: Optional[str] = None, at_time: Optional[Datetime] = None, at_time_b: Optional[Datetime] = None, ): """Fetches a diff of the specified IP address. Args: ip (str): The IP address of the requested host. ip_b (str): Optional; The IP address of the second host. at_time (Datetime): Optional; An RFC3339 timestamp which represents the point-in-time used as the basis for Host A. at_time_b (Datetime): Optional; An RFC3339 timestamp which represents the point-in-time used as the basis for Host B. Returns: dict: A diff of the hosts. """ args: dict[str, Any] = {} if ip_b: args["ip_b"] = ip_b if at_time: args["at_time"] = format_rfc3339(at_time) if at_time_b: args["at_time_b"] = format_rfc3339(at_time_b) return self._get(self.view_path + ip + "/diff", args)["result"] def view_host_events( self, ip: str, start_time: Optional[Datetime] = None, end_time: Optional[Datetime] = None, per_page: Optional[int] = None, cursor: Optional[str] = None, reversed: Optional[bool] = None, ) -> dict: """Fetches a list of events for the specified IP address. Args: ip (str): The IP address of the requested host. start_time (Datetime): Optional; An RFC3339 timestamp which represents the beginning chronological point-in-time (inclusive) from which events are returned. end_time (Datetime): Optional; An RFC3339 timestamp which represents the ending chronological point-in-time (exclusive) from which events are returned. per_page (int): Optional; The maximum number of hits to return in each response (minimum of 1, maximum of 50). cursor (str): Optional; Cursor token from the API response. reversed (bool): Optional; Reverse the order of the return events, that is, return events in reversed chronological order. Returns: dict: A list of events. """ args = {"per_page": per_page, "cursor": cursor, "reversed": reversed} if start_time: args["start_time"] = format_rfc3339(start_time) if end_time: args["end_time"] = format_rfc3339(end_time) return self._get(f"/v2/experimental/{self.INDEX_NAME}/{ip}/events", args)["result"] def view_host_certificates( self, ip: str, per_page: int = 100, start_time: Optional[Datetime] = None, cursor: Optional[str] = None, ) -> dict: """Returns a list of certificates for the specified host. Args: ip (str): The IP address of the requested host. per_page (int): Optional; The number of results to be returned for each page. Defaults to 100. start_time (Datetime): Optional; An RFC3339 timestamp which represents the beginning chronological point-in-time (inclusive) from which events are returned. cursor (str): Optional; Cursor token from the API response. Returns: dict: A list of certificates. """ args = {"per_page": per_page, "cursor": cursor} if start_time: args["start_time"] = format_rfc3339(start_time) return self._get(f"/v2/{self.INDEX_NAME}/{ip}/certificates", args)["result"] def list_hosts_with_tag(self, tag_id: str) -> list[str]: """Returns a list of hosts which are tagged with the specified tag. Args: tag_id (str): The ID of the tag. Returns: List[str]: A list of host IP addresses. """ hosts = self._list_documents_with_tag(tag_id, "hosts", "hosts") return [host["ip"] for host in hosts] censys-censys-python-7a57a89/conftest.py000066400000000000000000000004701524433232300204070ustar00rootroot00000000000000"""Pytest Shared Fixtures. For more details see: https://docs.pytest.org/en/stable/fixture.html """ from unittest.mock import patch import pytest @pytest.fixture(scope="module", autouse=True) def _mock_settings_env_vars(): with patch.dict("os.environ", {"CENSYS_ASM_API_KEY": "testing"}): yield censys-censys-python-7a57a89/docs/000077500000000000000000000000001524433232300171375ustar00rootroot00000000000000censys-censys-python-7a57a89/docs/Makefile000066400000000000000000000014271524433232300206030ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) apidoc: cd .. && sphinx-apidoc -o docs censys --implicit-namespaces install: pip install -r requirements.txt clean: rm -rf _build .PHONY: help apidoc Makefile install clean # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) censys-censys-python-7a57a89/docs/_static/000077500000000000000000000000001524433232300205655ustar00rootroot00000000000000censys-censys-python-7a57a89/docs/_static/readthedocs.png000066400000000000000000006521471524433232300235770ustar00rootroot00000000000000PNG  IHDRDS_iCCPICC Profile(c``I,(aa``+) rwRR` \\TQk .ȬK#\WMu=.S= JI-N$(/)l") v:NՄ9l $)MzԼ` -2 ~ F @ړ!!g|J"t .GHCC*؄ 1028 1348 Screenshot Nd@IDATx]' DHPZiV:jHݩB۷ThKھw N$X!;k9Wrwl͚={gfd1F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0FC`Hգ% NxpK[0gGO6]rOz*Oq N[ Ro[q(7fqW <E8$V-:_䪯otN(̹>V,t mO[x}R5 yzx^g' {Jl?_C`x\.Pu'Wy#=]cQ²!݊VEW2>|3 'ol:%9 _y%ZǛG+!?=n{Ox ?mJqKcS9+~X?{7<oJP-#H'^W~R88$&{+Wy*KatG~Ճ8Ͽ_t -?z> X Wc38<9I׌?O?uҹv| ?~[hU-[+vGfkND71Dai@ˆWxc2Q qHi[LExlaGe|¤›4tU*ʯ6ԖՙQF*׃Q pר?qG"zY_z_zgka?";i-׀1v{'{ :='čGj-ҚR`OmGtHض ='2GT&|ze'Mel㪾q矯??}5}_P_?q5Ȗ~ʃ}N^QeWҋj.k^t \+pp [ApǨWWqejPU/o}qeT_?}\ }W|qOuxǟ@\\>W0Gk=_B ~l~nQYFCz}I~?:a[c%wYȼ<E-u^AC@+4}7R:iq)C.u[e|O|Y7]ߊqZ tsO<2nG ?9/zmO\W?z^! '8H8K.a;)\[>iooo/?[_ >o_~l]3̿ɿnm5?q01q!7'^u-znUFhti#WYe5qIj-_>~M;v1|1Ux4xt4dȐD!8tK,2i-Y9 jGXṼnB*QW=*٩ʖUyhlW>/ob g"#`0F#`0F#"pmr՟< .O"L# ቾ8F1*nD34;Jɉ> Le*QȖx(ŏ>q#Oַ??c24а#`0F#`0F &l^McK"'`m_DGdgxs%14NN)(yT8&~E|AI_:bocT =0F#`0F#`0+(1:wzCb HNxBW'~xC\'>=*+BMĥ" UlT4ҐR?#>0f؍b0F#`0F#`[bN8ahȖ7 Qqw(^중Od"}bSIa%K IKatnB%y0F#`0F#`0 Evݢ5pK~Ra?GDH-XY`mQ*'2%_%NsO22xŴb0F#`0F#`A &AڛG{zPA8CAq%HE "8!2UMQIJ CD)k1 0F#`0F#`@0 rQIpH oQJVb=G8Fd SJ[D'0(P :a(y(%6l { X0F#`0F#`80 rW- o"akGD \b?+R, ,PQ*'0#T>9Z!.F  ̏ @b0F#`0F#`L&,Pq# i?JOܥ+/*ĸL r EJ> ?/?y6<0! @wm&L{ʹUt]U=e2fK/XNuI3F#>|xXuUj# ЬfinͬO3m}k^hѢpa )Q?DpEG|({JgQ2Q8BaQňCUKmphYkIPEq'1+?E] )#%Z^VPS(~/'^] 9Y0F`BY"%=zg@j0F :4)g|F"~8OXӸqjUV0K!q-WZKOE պD3ْ|#ċsĕJgQX 9# *SU8qC\H,F˱t^n0FKg{# }' Q+3)LY; £za5a„nUX$G\#aA%H|P7Okv:ujM:mڴcOճe!\Cl W,xF)q.鐠6Q c#F*I:6j ",VҢNTn#pwD%i6kCqёй't<3gLwuWb@z뮻.œ'On53fȤ(0١,LR+LGPԵ8l0F#=54F?!@? N bBqÇD3u[Ӕ)S^^)Xf}J|)j>su"092Sɖ<9jb%(d?@#D~ҖX0(E*YʋgBOkgAG8Zhu )۪=E!<Շ8͓^cƌYic~8BHP0| H|.qXn&BDa pE~@T0_RqÛEvnE1m iHk]#MZgR>hq*0qcލ7NKC }4qRޓ;tӑ A'E{!͜131+s.k0s'9g n{t 77(/naWh.ecΞM ^Fzh>>kڜz&D{w(1E dм,e&kg^u]ϴc3sukv?HrHhHƜw^:olfWVXXQ3 )St]a#D(ŒIW&0F q2O5Eb?糖0RSvRxɃ+x˦Tb0HAHix¸\0T jf}zz:GkK"8$yͶ(w}w~oրuZQuI׼&?< E¸[?COs{z9_XlEKW]ye>6hV^r#`=mSLE=6nyn-xie%Dd(ëMiB'Ss/ćBHPPR_~K/[{\}"hi9\\]j~vQǭ{"f#B!]$Y c!k7e/ȑX.NG.M8ɭ907e*;5(Z@E ۉH8oRvW|-h_YA Qt澵i-ں(rswL#Їcuca/3z{hm#?2eJ7Q +w) ׳w\L"OM@IL[K=V`=/.!J&UH(9𫂪$aFqe*Hfc3`T%ܨ Ӡ%Q!ܥy'gtHbc[f$r\K ƌ^#{A:vXe4ѨkxZLgmB2{TًykÅV/TI<{~@<,YC̎`th=mBDlY >ɰ,M\v@=^=1vyvFxS~!}.MozS^#y}MU'VxqY{Ӕ)Sruo `mo{^o;}SQKy_ZF巧C> ӧց.|o|{=Ť##/})ug?i~~.|&̋%Uw]wޙV;GX+e|Rsᑍk9眓֏W^qEno4n7w+Ty=W_pBs=9Nu,D,+^WN]?$~_䵱H)}靁]|wlgǿ<3}re̎0 5d>M ә1J0F+d=v5K󎿘uAjMMNJi 5ƂƜK Ã!Zd2aVtpTb1*Rgtp`]]}1*Ɍ.P~xifmM7ݔOBkc.+"76J{k!9>$aÇ@ qϨ}(@-7 YǸ@.b0X Q/ D<3yy9 !3wi\; 9կ!ˑ ^zAmoz5ɬHf~/ b~KHrY4a@m;7:S{_?W@hyy &f-#Vܧi{~헃|pbF+d>:uj=K)Sҝ1s{-n#<(VN1Sir94VQʒjʆP\'ɩSf3y{ҩB7dd~[#?6Ҟz;do~3 +qԗ I-:l*!pB;mtΐH:4x#P3lxy95^ hƮ\}\Q~#`>z{AZ\#5zSxhlۛ)O?(!8BecHZ ׆$N(~D-l[%! tPD&d)iGTYU8)WƅU yŒ3fbYz-e GX\XphT'$2_̈́սu<9##GNdKӖ4{"\2)Vxpkz3Le]2GbfE}T&sK!cw #bYkWsA'藑ʁ.pYgG$d)0Yj !ʒ{ N?晢o~ҕ}KZ+BfFxјgG:u2|*_IIBRY l?;Lditw7dNߝ)- @v,a/# ϷO6:gznO.:<`G{u6 7ӌhLMKʫxxΎmz g:j (9_}J-'΍G#q4^&QQUeAe 0!*O3H4{Q5k&b@ƲZg͜u:3(gŢ U+A,ހv:BwSa/g(3E+EY3NHW&a%@,]d߲ڥ̎AT/FK㉧lE\JefW%D壆g A3"!)9{vRQa:,Hط q_! yvji :Brt$GyAK #Nd&;#$O#.(%}6˒! \Xvn|I4&Ǩ:;-+wcO,ǧœlvkP:)AFww΀Ee.tJ)+=dRbVL#v#`@o /-)e^6mC(rn #GBQyʃnyKz#A`%SGg*2S%CQ oOz5MywH|A= cgv)?ltT0#uO>=K{nv왳0횬ԯ^<,;:C-˽h´Ν_9q(=0,'m$crμ ?Rz*^/%'٦diًkwx^4uԿ}<L;/OX\س1S#;Cn;s1'v3k}R!n%]igG;}W rp| 1B:cY1eʔ'"77:gӔ˶qA"*KF,e.}Gٯ7^l::q0F/fQCrSl?BÛIE0SPBI0]8*A-o>~ L%bлZ8ix^#+ersyF1sVOLX|gmR?m?/Y_n5jt3#:[n#;/vԩmT BI nUZm67>8֣ap`3/g0[\yc+] ghmXp/M9z@(BDvD*$zAAZuE -V^5أ>1$&f`ut`A~_z39XP^PGpZ]q'GtO'퀈p^lNw9AyxKڶ4:3wt>tw+7\L`oR)& #m6h"{5r##Y=Z޾l\2[-aD]go!6R= yrnY2ޚ4qnQcqI1B?(~xED|x܀CXkxDF)D)~)8lWE».uwicṖ,͏ aa/f܌r`&-h&x`W{Ap}k/ߊ%j\Oy8`۾ 8yP*(6@-ܣF]ݭpb5Ȓ&jdOO3t'yQV-Sb6'_64'ݹn3a܆`WE\CKW!!C'Giq'?W`l[%pmY=Xx{Pږh3Dk0ˇ| #`G=.!ز-rgǦ(_2,}Ks9ڿx-1F7`wfwvl 9fި{_CUʝYbY^|d X$,r$K?\".cgb# TbSBNSa: BpaDz!@'4wM[2n5D}{G@7k#`0=fj_.X]+0٤mfׇ-KVUZ պZ?p-'!DTsѩYT¤7IG#j쌈5J(03}UNzQ.D)~nY"h:icGm\A*<7pF#` f/n0v\#`f9N<oͮR+8G[rĉ/TH\D*, Ӳ2*BDx Ga\5L~ú b#` (7dHQXHW#`@`F[u1̲.0FSxV ND|U#@#`0F#`0F@ _ 9tZڑnG($'Gت *{a\ٓMl3{ #`0F#`0F#0XX /,yĮؑO dHL, NR{"T)PyA4hpj1F#`0F#`0F`E x@Pqpĉ oGӁeD e%0@O8 Ħ*N/]lXA0F#`0F#`0 %pEj%f ND")ޑmz 5#T"r0~(7r҆t+[90F#`0F#``Nk爟ɖq7E0~XJ%@IDATIWfJ/+P'*q 'YBqtqՌRvx>v>)=\#`@Fғs匀0F#`8̝kd+wK<*G pp%g(Qb$g^]8Hv߈EÈВu"CU`.Wȃ`Cani9[0-ܲ_ϕ3F#`0F` `Bt+{K~?H'!|¸0bItE(FtEd@ZPITa\%M(N鈱#`0F#`0F#0,@r&hUbhL EZH˪a1J%] '6T4Ry "b0F#`0F#`@C>P\Pq"~ l$/J(+!^2."SyH!\Հ|H#`0F#`0F# ^P"+ śAhMJIGG /_&b1F#`0F#`0F` yĖUekD/SX SAb0#J_AȧƱt Gb0F#`0F#`A`РL!+B/d&?ٶ"bml+IJA_y!qU>#`0F#`0F#0<8Ӑ߈ oXr4Zrib*$ aI}:У t`\Ux5 4t'B>g0F#`0F#`B`bq?GG(pyGq[D ',B"0,&qJ>ق "b0F#`0F#`@KpFTuewGl^iB%#&zTA1Zڢ/62hW#`0F#`0F#0h ?/\^Q\̺hIbFa'FJCj4~qi :$x~i60F#`0F#`0=DkI@GBxu[E1>2/]*MXĦ׈Á>U":ۑm#`0F#`0F#`K@!f+TG]DcgQ! ZxrWK8i/IQ>*z #`0F#`0F#0::0 !`O%r*EZQ+fZӈe!F5SR1 k6(aQ!0F#`0F#`0E2X $?b_Hٮ"8-ŋR1J%]:Kɯ|#`0F#`0F#02:08BE<ʀk,JgWgPe?pxi?:)F] ^l+j Q#-7zմƓF7Rq0F#`0F#`$-{% BXB$ixB\r sJ$LլOU Wl{8Twx% +}\VxѠAl1y4vUҕw> CwįߛK-h:[=}-acQ tɽ۞h7oLBsܝ}j^5#ϐ:kxv^ZK 6Z;};$xp0F#`0F#` -3DE23Tg1m>v6i ;ۓW09cH{m1)-Y4]tsҦI_{.i-&_A_w=,]r+ӆ;?|҄#u[]9#`0F#`0Fg^NQ'KC+l6 J["4!+UIN⤋#4%Fg >mӁ1#oW@塧ϼ-/EGoCL[OY+=ݖnoV3zf[|:ӎn۫ee}찭,_wWzÞw sҩޛfs=33!~e^ri#.MVӂE/ϹZXNA[]7 R_>N]IÆy-lX<ƞ_G=s3QKnz oJ>R,{0F#`0F#hY2/2Q/\H!C8E!2?:#U2Kb h:Wt5TɃ*ox-=KO>i^Җ믙 vd|^Q{BC/g~1'߰]:('y!;n<.lo {LdkVGxګ cA$#lG:+SZ~2td>:NCd'q`]6HO})ڿJ`oHʄ3iҸ1/m}Wnnk޲CBe=6>~Y0F#`0F#П1n !i ?DONWGRG$jx[ Q)`Ԇ&Bb] rEjA ˾?;&Ǔc"1U(ߴ}֓')KJv!XfSnΘtq̚˪=#S:dr}g/sGQ{6X3/n/qa/"dw]K_:<2 !\ķqSUe;*օ<4Qbʼgұ2}D*Kj y1C޼Fï*դߘzSAdz-[|gvBҞ+cꇜ#`0F#`0FX~2 |"T3-LG(~Qi# WBuPO/C 2TA}*} oNUcGUBTѤ~&$CUD 3 ~t5Y]!?lud*y.HDPGqCZ ˽fR$a$9/,3@i5{} Aw]QG@rD̀{Iii|BohԱ)y8gƖ+)|| 2 .;n\;fT_0mK*wEfvy9呎\B+ro}lae<~-X+>>5?fNٰ#`0F#`0Fh,!& Ex&wDHlֳѭh(*WS1 ư*B Q6KUȃ(Ny2'Z5g~Y,M|y|X98HɝK=7LW{t1(.+e2U8FDhR*%}*&;'W@?U|LS|1;lK>dԞ@vB<~/K;6m3Pnb&A^"öIft /-Jg_p'3_MKG^d\eyӦ_{gUABeNxNY”=D-oc@2ϫ/rbvnށJ2c:mn_»=)V2F#`0F#`@C D E$ )%N2Y?<%Ry)RpGHk#$֓2~(SĨS &]'}CO8Ymx&[,G|$2tHJzvKucH_sGjKR{sQ2 _EpAғöj0F#`A`ܹ51ѱc4xF"\HHI_KVKpIeQ(qd@"9ÛtK0N:DeEA/7"Ci A PwP/Z4ϴƧcҳf)zt %/m5 #`0F#`0F`! Y#c-(wiPIFCf"4>~!S* GbW煷V-JǎL;m:.jo0F=5=xǗZ\%I%f^RvaD*-JId)dJ K0.xDg%TPGy hD,F#ȳy=7[b,=r?>`ƞ(1Qʍs=暴ۦ'*zwgΜ>>i n;:R??3[ouWuYO?я҉'V]u.pɓ'-آ63gNzvmׁfדϺn9.o_f.:o!:L-e|l5n/w4(g<O#qU#3Oǭ8-#GH;٦mA>P#-D8ErICG~ŋ q Q%d?Kc$T9 !!\Հ|H#`@CYG^NO:(~UzUz׻ޕVD+9ı>?A@Tp 饗x9*7ֈ͛7/]iڴi]ŋ~g/@w'xM3/n^p;[;yՃ[i2]o}<}ꔫz]w?\zΨ{.%'t}܋#?>o/1>_:;pzݛ.<_v{A:5$z>~,A<ߕ'%zi~pƭi%;xAqX|c%IYETUƖ< qE% zS\D+L&b0o97LOosX262Ӄ3`N;Tw{;M! wp?d=)\p9//Oo}ҷlp7N/bK7]N_x+_vqO*~kMwu|>%z=fm /xY^'_җҫ_w &lxUH13y(}7^34fU M#xyiMǧM&I? R= Geax+7M;lvU:9tx4qs^X szH1zUG<&ґmztClEa[(m0nTaרws:`֯c-9 4c$]KG"rHV)T(BERbt-ȧ򰏠ϱt X0FxnKs61n o!}[ZhSL8 &|;mH馛nJ[mUS #{r׾t9 /0|ViwޙKu [00 ѿnˍ` 39js3!zc\H:t)*ԉҎA oݱMGc?ӾVǽ.Wc^[NJGyu+Sڄ0Flk?ogpO)8]#G8lw/~3mEeJ&aPaÈ ҉CTAT AxKZA0F6-fAҢ7mNR0dq6漻GRm*B8@4mFav($ԩSW_ _BWrK()v%oۜ묳Nzߜ^4,֧zj&a!V/𨣎JƍK3fH?ҭޚXʼ맷my ; u]7s1^|Ӕ)Sұr˜+dI'E;.?ƏD~XI7OO N~j8(32QGL` APlnKC ֏oI^|iܹuܨV[p<핉.d`^l)@<2\fR7ΕO~i5,fsio<6O.K\BYӝ#O~t'fn}{_sG I*u^lf3x[ޒ_Z|Nl1f̘7)Gv?!۹j=3Ajg|έ? xљ䆴l:6lX/} G3 :}r7k־F.X^NOݰpfl2kq $\ss̫m&Z} m|wפZ-}5[3c_pNl2xPz^]7&/,H??/.HMbj+9]m?W,\$ϸnh_=ywYdtYfey8j I3#/ޗzPNPN:=/E;8Ӹ1#-Ƙh#陹/Ōy:z$,FG,GQ?#q"4o_ys(\񏑔aXODRR K^/JNXA8+Cb0K˻9lU2̚=?}Ҩb{n1̚5+ lBTy,P(-'޾O8L^<`4lB "l&I X& ~G,X>Ϸg?a(ч>L}3Iw$6 t!ԓAAέꉶq5*]veiR&ד/fGQ=ٛT#u2/~1h؃h~ꩧ2 ׿ *y p l ^̌9 1 SN-#8"! /aKsd!m_L{LP={vbCu힘#:yov‘Yߥp=R:}oCTG3ɹZk8f$#ԧޝ_Wr:[nٳ ^pw@BRxs\^pyf3כ5} ?#`z b'ٹ4,1~V-H'TUo}SJb g^us˃T<UznR.oae^N\eL/m&1"+u}}`L^rߕ˫j!EwFtF^'ln6!5t_cB:;݇2(܇ ޽fYMLխc@!2:Jaq c=$QC-T) Q%UHNtU yKƑ)޶o3>ɟ -N߉=Mzk~;^[L j˄!q /mt˩dad$   38B"P$#Fd bw*CدdgqFRJ_Wy&3ϘE@"Tb_GdDZL<-6Bou!ABt4OB=Ixʅȃ$ӟ”\"Oq C_2oy]i9ُNKox2N! WFysb|?|> o+v-3.EǏu|iN,3wE?l,k D=+HN~F 6J;s X뭣kB3b@#0o~xh5V\,$5 {j/!0ň3&nj\%l y/Ļ?n4u¨c.gcM{4M5/=ْ[c9wL}ꯎ뫣?1Ux/8fKiiF1RwQg##w0V D .Eq<@V::OG GItV=S0 D?"0~*@Xag1F^D`ؐ'^mx:guX2_Q{ي{G9BBTd'8ZPf|BRB~h G MY+pöRb Od(iܼ!Y/L~){tJXr@=Czf:ŒD3Y!֘ *Vw"CW0$3quYG3'{AhAƱd{ysbcvM&M[ 0cߎ2V!+K{vJH\kt$/[UZ 5`OIyGBKFWIy-HSr-,G:W5~CnYF<zJx,sKDX>߈ %ӱ\=a6$kx8nsQU2T?|0~p ;4zO ]4i! ;Nsno5k538tc9I?A#_Ce9JXg1F@)N{c@fo|aKBҙ5!1MazNde!YqDVm?9E\bJbmI战fND1>'aD"[OLa8Ԟg:w ALO$rc|Lyfz1d/骯tʔX@v5f~??M?iiOuL<ߪ\?][^eg}>.2N!Yh?-9{U DԼG`M;~+JvD1%RwtDQOOe::kI.DÂZ>GE3\wٻy#D z_o:=q=(lL`1F`("P?&!H^#xHҜNN< UO*[ )0QTa9̀(f0tu"4Or|#`GwC: L{"~΋ug s@#o^Vd{$Cqe.-wcl^Nluw͝nkﷵڥ&/jߢb8A#`z@\p<sQ|aoG=hI?lЕ΁‡ >9$:+}8tRsI0F`FW iZY2?ME}R.R6e"!7>~gu?!:cO>97QuQiv\g7{R򖷤7 DB6(s1ufz'cЏl>zVl>ĩ`V%mY< o8'}WFV=3ЬgNL{fW"ng͚UWl"կ~uu#mf:b ?kж"q@- q4o|xfz='Qgyf`\Z54!=O:t@s:Dy'Ho^"^XÕu{]8U }̋st(g\`\D\~(Ϗ9Hk6iKD9sҦU'oqme1!YWUZOB5^/J0- [oue=Qb!3;tZlG68|Hwi( ze۹G4P<,=.}=ǠuU K(@ns[|szp駧sE*eM4@YfmTpG 0/i#FN 6WEx۪4G.Dė^0#Ȃ^No sv6O La?ȝîEZr$D?ӫ҆D~{=)Yч©V ^7iz݋4 Ipν9s==[Gە;lVfT,so 7xڀ];cjcZA #>y|kv"J9r*ˌUIСJ J1 c :9)&mӏoqJJ;7lEΝґ|-Vlӎ`J*V)lùVEyTv?y>JPjH\^I^;\?%8}} M~2zU=4F}-\˜GTзO9f9D_Eo{NeF . ?iKqrk#&kcK3Vg1ұsKvE9%`Ee/p$Btܘb_/#0`s7pC-% IK;xؠO*]%߉&cLdAvIM=)eT)o|'xcŬT#ND=̒dd/3֛~d~/N%l=!v#lD/Q.`3O#`00{/">s1';i J^芣$ۤ BeAUў }գK ɱ?F C`SHĈO0FaڢL(2t0\?%{o0F#`lƘ8BAdIG'cTs2fq8&5gX!=ErROTՎ詬<}FXs4Y@#6\\2F8Ƙz#`0F ajkBwT.!T\" !e_wf1LrB^2Α2>?)'elXVw޴al\gu|~x~x֔ Mי6XsRxj'ƶɁQ֖Yטybᮇ祼dQKYStEԙΨz>UA\}5&DӺ5-6fVp0F#`0F#`@QCDBQ2zK^<$3Oyى٤K Beh FH:@'-՗:D8fll. O5jDxik t u 1*%teIByuB>cj1F#`0F#`hI EPG^|"e?v"CDME^9ҍ$AtҤq<SL팸 F>HzR ᩧё\'FmI(2/]@nZa;KCrߓIgʄ1aǙ򐫹P&J=ODҕ{CO.[N0F#`0F#`F`Ŋ@P ,j'x=' S9NfeQSQkDYNpSC-]fD#LuRdO. E2,/[>2lIcFQ#DW <]#L2q+fHidJY#`0F#`0F&Ԧ̋I|`T%H^eq:!*t`LކГq)#rD@8a|#iymb$ZSƅi: >M#I\S_ :D;! TSYs4RKcDܼ^ߩF#`0F#`0]"2FĨORxCj')Q# u=?be;fEDecJT&)r64(v!'{IjYX {[֝6L4~t$DW3vQ  ʆG{"-el\7TB$iQ$BG{l4:eT})%*N0F#`0F#`#tiO)zDxDԉz$)6#DCA(uA꩓.vOTSjH~>"4ם6!0atwKlfGxB2eb d& $tǞM{] cٕ~i w=-F#`0F#`0MM.PGGxBE%w$xGRGo4#DN@D&Fs˰t_6)IdԲ#ؼEa1i$ Rhw!ʴu@Ǎ>l+n43F“FNX[4mn(>t/ڮ!W7>)ӱ9Ӓˢ|U#`0F#`0 LaM Fz)Pd()DvJH򤝤!*ꨔ: v9 FyEXeY`z?C\.$d.LoW;O. c :H|rH:8F|Pܸ#cg{.V&;o0F#`0F#5@IDAT7p9Du2z'קOr e?"4 0)zq"XTJv͖\ 1)-|ImU2ҴJuF#`0F#`0]#lY7 P2V5k;Jx3Bq@y{NCǁBr1+Wа#`0F#`0F#*"PK̹E f,dEhB|"I 6ƪN/]lU(qFX0F#`0F#`nQx@Aq"liw쮽SQ6;ҿAhu )vk@ǪԮjVq5]u#`0F#`0Fa\#D8G[R$IWeq7}ڔld*' ρcr"fЎ>җv#eKΣX0F#`0F#`>#+=vH D a'Aѣ] cCRўw5#`0F#`0F#`ESA4'O<)"鋨^QP/e>s!/y)J[N6#`0F#`0F#`V C7Jq BSXYWGG'H^6b6S]>oFb0F#`0F#`p@`Ŋ%fy;׌2S$'$ IQiʣqfa#`0F#`0Fa@-BN>P"{):T6=xUB=EbN%)S#S_tG89} {C:1k1F#`0F#`0F`8!0rd!#0 9ez?jVݺ`JDb m1d =8JJ<PIHGQ^Z0F`zHg0F#` 6ިl۰0=G`2Q+(P"J??$/2fS=jNv+CQhFQaLD'dz=vS8`[}A>nXK˗/ +8.HWr02F`01b0bx4G4cj0ZuWʞ#`0F#`VUM8CBt+ N(Tu"I+f( A Al`C*mXS棽eIc_03la5'5& G #Mďbt'~<;ӋGQc'#`0F#`Vaj\!oW?ȮRtIRq(S:8:RD)}9h.}#˗<6«68saԨQ1b,7#ڊ0T'M=%0szxN|<9+1Ek0F#`0F`G(\`I?.cCzC2B*S\&)RJRNrv2"S0HN: tÖӳ&1QbW`gיAG79!ڛ˗%2:qƅuM 6^3s]WFO:#`0F#`0kW|cw*O7SHʡvR3j(QLq9e@% ͝zkYp^xf;n֚:13N*"Ti#`"EXҥKf kδ {L\cHmg0F#`0FXCqUSEj:yt.s~Q3]BUeUQz4c!58&Bב3a[8%24fJ/fScS#&Z:(u35.N-ZΝ#{ . O=TX)ʃRhQFGDWKn0F#`0GS?/)GJiFD99I^䥜r\9IJ]^&?˗>uFhԦ(U_v?~|xsj*>]w^m;?pw}F`uGR)3cҙx 3~uo0F#`0Fm As,!$DxƘx1!Ѳ:b6R0ʠ0e8NQNGѶt ۓeq36#뮻./kf h1F`x#PFw#`0F#`)"/]#H^eq:!*t`Lކ8CƁL ymOV,[6>9E}i,@ E<( <;xX0F#`0FG@7H"FE|R]$:I"v% *rT8 hPANl$m[V,_֛6)]7n\x'krN8nφ[/>?y󟟦S熝w9\veaM6 k`;Y7tuIvHxUW] _ 6ؠo{7k4y)駟ӧO/x ´iӒk^~O}wisK, kv{„ v]0<;xX0F#`0FG >Sd8DR'#ԤNh"!H|p'!XEaҤIi >$v-1 )z;&BK/ vM i:o޼ɓxSeʔ)=¡a񒗼$,X  >쓈^lN #`0F#`0F``Mg0KH.C1[ 4H񎤔cdQXC~` A?'50NF? ՓY9NNb^) vmD}3gNhR&m6fLlDD)孷r4 Q͹曇c&risԘ!t֬YaJ"[lV t <@RpejԩSÆn"S/_{@"XeשX] FWy#`0F#`|2 P;؆ZAPDiԓy:}+<=g]&uޥ-iKn-\r%avHx7Ϝ93.Lag:S-[ S>6۬nYFl3]eCJ >!N䄔-hii]S`jĦn$pL! 9ZӮH &c3jL>蒐몮+ו?Ucc;]6#"? X,>2tU=m0F#`0F` GǒXIvEFb('BeCf""}C Hig9@>/b|[2jo 7pۤh[YU Y3g#`0F#`jS PĜ/*GYeqE)ħR"Ae29ȫ~#`0F#`0.Q&N/jd?jz6%{u"# @ʆtq펋i7sQqiV(#`0F#`0F#0 8A!!҆HQqj^}QG\&z ҌP99,f=8E;)e8@UvQg1F# aΜ9jpGtw0/͞f }kW`pꩧ}+^vizy 2_E]iSN8S+@|S ˖:!|n1F@̇+ Q$aNsIO!}UF=|ک]xJiFȤˉN(zyr=l6Mb0EmtM74;̛7/\sͰΝxo&Ǥ媫 ?:u`6K, wqG"}t-}n2eʰ9ϡ|"v"σ 6 W=/,XJ?x_RspBwrf2!*0 s={=Gy$cdRHcOii0+#`7Oe?#`G%"6q.Nz)SvKN$*1S'ytpPu PJDt#`PEx뭷'|2}(r-oVKD/|Le*@gu Sm.puׅakD2~~>h޾vѓ׼5/|a,/Dm+'ï{Խ/M[ouw}W\*aZ.OuY'p`ۄhԁ)W_}u]wڴi@e ~UGa9u駟'tRZmo{[e?W=$X Uz>>OW%/~Mpy^>3F C஻jPixGu]… 1o;oVi#pq%,!y^?}"_Jܫe@݃#`@!au(/]R8[䠜ۉzE ujĀLS381GptT}eSvR1FE xKŋ(/4xy CB(a5])5v7Nj?^m)6۬|;wՔ{_ k 9',!iεT% DKM ;S‰k zخ~TxߞvXݙvN^˺9|#3Ll .NdU.Kv>$|~^>z{Me"x~鞖׿?ӏW YŽsUɺDCIV~ɗpѸ|q½ A?CzM)GSg_zw3<=t cIgM[CV`<|ukvA>g?K嚶,y F^Wa~\)er|0Fo;&pND8IK_)mCx1uSP%BeԓTnCQoR;}FX+j#`( hfxTMj^4c9&M_ e D ]>?Y=":!WU}/~ȕRM|*ɉՑ)yTCA~DtM1rRGcs~p!DژHL_,flAfkwDBڗ{_]c`yo CO* /CCrUJLckVҪ%zk^WUW]~h(cC~(q{\(5vۥ,Џ <>o}[M/H~Տ\YJ$<#e5םbl>7~dK?Y{3;,~I'53s y{Z~>!TTE 7ܐH˟)ZB%E]GuAy09NF#-LD8AK8CR"Βy+$#8M;Gfrѡ=lEtl.1+[0F(|!0>vyU|ȫ,R(!(xIoG >O6}mAq"!;d>WNyG`U$V|@nsJ؊D6۠(WN۔/`A 5}O&L!s, UT{zsO1"ذjtZ(fN˫IC4$mULdpy^Ɖci)L~󞗢|;vl DBd,2Z3)?dW.=}.5Ŵ﷾ qCZBtoߠG;?HThUb)=>x~b)0懼>wH!k{ǎ_ӟNʔk;/%|s,}αT\k3<0_V/yniiUyV:#` qYxCq6iRGiBjpM4#/gh8Hqj`rp萇#` "Ќ Iw=/Wy KSWo^d՗Msԉ$X0.SBۑ38#ia6D/OTec}4z߰(STBfΜ"dxf%mLc|bl"` ˅jfԃs'B ]nSY޿V4fE)SIzj"kfS%rsⳮ h0ʹ)KI”{#9p ´R" v\F`r?Rۦi=em˽$g:c`'΋H }K]E6(eOwʵ "xͯ'\DкD/}w}RPU`3FA   (nǕ/4iȎ?"3sGiahԼ0:DuR$^#0C˟>¼ԉ@a|Jџϖ3^sv5k^k-CVJ!--˨op*fSgC+״~ы^~xӘ<[ʩu'|}Fﳪs=ؓp#`X@ |"xF5RnSTQوUҌQ:k0tq&o u89q S!Hed/f-F#0B-B?X4'Dyה]:RN祻$Lx"r_ hq6)_! Hi<;B~>6p^xq]ur25o'm˱[/qN0VI[b]FVk1K28"h^ T!ڌ%"ai׿^؃'JܽcDi$_oǖM!!}񹼏zs~! ')z.twMWzڠI ׮p?N|GRGlCllX%tr Wv$N =!h$#` #@zNN呆yHrmE']WgSFmy[Z./묳NޭZ P(ׇle<ڴf:UcWT֕V/?ҧRCH_c|)?wZV:zVL7vgϞbJ!Dͣiȧ˫CoƖVR" u@^/\G]IZtkg ,mRHb}UWM]F7Xk(S]UY'|jROz:TO~7zi@?yudL;4m{7Fd!+s~/Gl΢ͲEq'!5 Ӕ갃"}S']П2)c+F#ӿ2KNg!ITM^s)y; By{>}z^LyەtRBC{)Dі.\b6ݑU;ćseWr|SuI"WMDZ.DTF:x9*WRcq_^c]! .NdS?!Pg6ʅi:7깷,֍e-Rz;viY= z2^O T矟6#[9_?򑏤ݿn՘ܭPeT`" *\;ͨ;^'M!X>~ɗ(j:Z/|)}6Q[_|3ixvĚ-wd(C$P@/ǐ~+2V0F4GGH&de_"RKr%YYYs*&T'"AB6)tPc*#`HLϧv<4oի^/IDiJ5\WdB|! A~x/<|k_Ky{^C߽޻~i]E;37)}C FډX/˥ "j?S!d^ XsnUi IZrW(k6\qi7$wъo7<8 AGi5V~^,?Nޯ$  ѓo8E_)Sm"@弑:X!ujkW=9?.aKUϋB'^?SP^Ce?k$y^%2?$l)Snvւ ҳghD]o _ʳ{Ŀ|/3 ~gT.[u:USYW{gO {)y:+U Uy^7TA6ӟNթ0F@}oȗ8Cq9(ĎlVF]\ez ͈va$~_Mwry*§>J `n\cD+r@(xv1gȒU3Qn}z7BdSKbq Z%WmFl_-coU%u!;Nm힟LDoہgXBbR8s먯y(GCUqMV5J?RMo mcEABE? yꩧ։.b 6 +|Aw]i;6`?t-uw(?C}3==P%J?i;?8+`V?cd(_8c%ZkDi3(^ `J AAVT\r6}v-I ѝ ص"|n`P%4̙3D4TvSg}RTj+12Q"C5KZ uLu@IDATE,}F[.--PHi&Uʹ߸/ǖ}6kv]r^F힟l&)%BGK|O }yMHw}ƸyvTNG3f?5g!wxÏ</3%g5*\ ə O3?rll, I=?"Oz ՝O֕6z\ ;JBD&Gydlg9wu$ ~%]|^ׇ' KpM0;~?ܔӮKuyJ[W|CA9\3|yчQxv}"=UIn{|-U#`EnP#yFT|c&]1RsM٣\)#*k;O9qt`L2re~gQET6bo|b 3PCVTUH$|CQ1WyQt|&DB0@t4# Kי1r${"ڀUtngJ.`AӪOr!L\>isj`+>[K#0;$_x,wNd36[0F#z"0gΜ i ]!&' [?o0FB>Q$'C#>[K)JI9r; `S10Df2f! P(u ,26Q_ h,Z0F#!pW_3gٳgի\y*a#`0F EdSǨQ5yՋ\ qEQPeT9pt՗ʣq-F#`0F#`0F FL |Ef3Ζfm{?bOc6 z:aI)O#S_tGUk#`0F#`0F#0`|o$7̹XLu=5+]TR4V Έ)uFXW9^'FJ:$_Go#`0F#`0Fe*/(~P|aoGq[ $T:DGhU.OyD(}Db[ HS}-l6-F#`0F#`0F 'jܟXQ6Y#{(-MkD=Dd8K^6IUOdDS;/kF)g~M7 ƍӘ1c.]w.FZ+E_0F#`0F#`.'O'(ZP|:qԷ?k$m̮D1qD'8HP鄔.;Lp Ya„ aҥᮻ / _^ve/\|E[foq8 ?? NgV$A?/>y[n o]6l|$ZvSO '|k֩ȣ OtqDaG<""^Qyt)s~Q<$:5NqҖH˔)00ԤYd(jPDOu D: 9"b}:"1[3f+ eo=6F#ž{ɑ)$_v@$,?τ3~?9L2%yy~q8N| d(ru58U?s̤ƴiaҤI)ϟ1 s5LulD~ԩ=yO/_—O>9믿~/-2|c߫Eq=ŋcկ=;E"/Z(\tn[lEK. Db mo{"daHopgky67F#`0F#`0@9!_h곸WcLd&%eRU4V%˹(ɖٟ*HT<쳩Ys,;yP?sSvۅD~ӟwpBw}q=:zHWfmee9?6bp9.\CC0s̴;|*dVCeg׮yo&L0EgW>?y:ۙOhiuC;Kt)Zad^oR{n.#`0F#`0FAA! q%PMASFT!}ɋdf;I#/;1t ˨ U!kDW_r,:Gg-7ߜ`&ޞ kleNm:ʺ})8h{ǏMq<98u~ĉaZ\0|0̈d졇&sMBY0F#`0F#`@ G2)7ӑpvS9N*+s"GC4(y50N(d]ʤ'JÇN<1 Gs001nV$9'?I+9$MM@s/:7M7|+{?>ή"=>'K8-Mؙ^†H3 GuT"O [\;tO׿avJ냲)wo\F*3n. w0F#`0F#`jS#B%ܗc4(U"GhD`f!8iChSNt8QF` u7Jz̞}9@ ?ImGD)Qѣw_݅q##"+[!kǎ8,XPn caSOuJѤx b+j+RhuYwV#`0F#`fZp@^kʔ)/rO| =EjAR'T48IxDM@ nz?V5 46Fsbg0Gr-wZvbu}:4 N. r(O# VP  "C&2TO>2> ,F#`0F#`0F!B Ef)n2f; QU8SbV)CjⴈP갃"}S']П2Dl^~eᚫN .б=0F#`0F#`0s&pp:hOz0 ޑ;I):}!Ju"2EjR KGeCD'2*nAI,G<0F#`0F#`fhi?ԙ o %J_Pw$)X(yǼ0%2Lzl3}^G;RS#`0F#`0F#` #:!5ӼGGqlFBb"THORhIʞlʱf~`b0F#`0F#`*{ސ8C9^m莤+")CjADU;WNI u#`0F#`0F#`Qx@Pq?b3BT,,,BI$fG6 uUէc_ ʉӦcXg1F#`0F#`0F`!P#D=T%g8EiMoD0<CjBf!U&RREzl#`0F#`0Fa\#ܠ8G򐝔I7lUY#9H}JRJI;9s$π2'eْ5>#`0F#`0F#`{,Gq+2C9*C!pQ(+y=#`0F#`0F#0MIOB񄤭 Fʼ+EiaP&-"?0B|A8ysyXU}|`wQ@$ X+5ƂƖXc4&hbX5Xc 4齗e7s]E$g̙{眃#8#8#8#8#lG&9F8E۷3O}[1?bo=9 Fd2(b$ @|b!OK9;npGpGpGpGpcc428E-MhD̆qE=*5* lƍȴ>s!Z_(mIlS8#8#8#8#846q6X:8iRG;Z;I>v͆fC?%QqqGp !PYi iy7!8~B8#8#?D-n<s6Bz~e [Iq2L[Ƴ6uU8#5Ɣ<6eKK/$sݔT˗ŋOeeeFݔ>Iw[ɪTR,^k[f8ӗZ1ө6Fu=eo^.-=wGpGp ?0/$z7'fKr{Nꌴ$o)ytpFzZ?;;9U :ԣoX5=Gp/Qׯ/zSO=UwPfԩ_Z~_J7Ͷ}r 炴hB."kjy…r뭷o-D1#sddwߔ>-2n8ʪ~+|r3YD 򪓮v #ϕD7Ѡr>Rn~Yjmۓ~--׬>q2>&4nXpGpGpqpذPH7o4~0I s>!F|ɉ h6L/}vsCGGp ʑGя~$O={.DI~wm}m?I4hڵyKNsޓ &  N:I^~e936Kt#ӦM?<ضN͑ݚ'^#pe؈!r^-iaA[IAnm yoZйi':Ņ9\ ֭l :4|˟@FXBڶm+'xpa9swa55!1g ~ٳgTرcn . =묳Rd(JIWw;} v۷oO1jKXI'nd隲 y ˓J834KErm!G3/)ןtoMGg ϳ!i-?>h7m\ȇrds#>-Lz@lH=4ө2f|Ϥgrq}қSsQwhV7'zc b\z蘒zK0~WpyjT>(7 :|S]\/-oX=S۹Œ>9S~ypGpGp6 B<G$@!(o\%mWZ;uf~FCMb%9aGǓ6IП~f BZbҪukW?]#>xק~nT!6]:6'W?0&Dz^8UWKm+?;J9ҩEdgfuPtj,5rz%iߟ8_Iv6jpD1teJ|BvjQO  7ʐ\]GpGpG` spsAeM$͍k# deMs9˓ )bi(iF [Rz!W믿C׮/䜳3uȞڄIE 8SO;4Yf̛7/Dq%'N#FG"<9/g.tOG}1WQr6360Jg`ϖ3?ߜ"A? Dup9A?=e.2M,~fVfi%N-Rz?=W*J "il"& ye\{f:|GBJ$L駅񬑊@ Q|\x59_"T9vgYoMMF$SnSoH E᯶DSF֤FI~1OؐiCd9J}Y˟\z4m\MR 9O#T44ZSJ:%~cz#8#8# \ aRh/1] 9R8 i}-SʡB$;K1`P#9IOhjkNjv %T#C 8# ]F;yԩ2L֛!HQ>Ϗ!JPNLbP =?],74ijk8L<3ÆJD]v%AI(I:nv SmKF ټyj e]”y"5Gtn-wSӥ(*e'lzfQ`tR J?2-?هK.$|~M)}&25߄9Ya:5|}sFOu55-@]̉8$fF򪒛3EN7F5;:d.P!g}IҲaԮaX.1* .#8#8#e;~UXvH8F8EI9V6.SRc&6PJ-ؔv7Ro'm0'K:E6C(_li׾}0o6 nWɁGoI&iUF}rHP5>S_kQLKYgͻի姗^/i-7,#?GF^WstXiI&(Dv&z꥛S;t DB2%-2fbe6Vb]Q"Rm#3ظq㰆(;r!a͝fΜ -{n/)1=:}ja|QGҚH}`S&J뮻j$C7 4[lڴiws/@ =}a]@ E2u c*z^ue)eTE"C'S?PǩLWWv8l5GbɑSt t6ԌQpU4UTY!I#W/8a9lvּQie CF8#8#8[f>w/%G# N;)OV1c1_%4“:N~IGKJ=Gm~h/{kOOܢLIχ|0cr%6MXtv ;*5*jܸq!*jgbT"FUi_~DQ]Ż]OGX:u$?fELovYp0sri;`ӣ*N'z|?_ D6K2e!C²&acǎa|"Y!R28YL+wIHwyGXz7u܄}Yp r9) rv} և=>ߺ}uBTj%j7Og:D7G~x˫R!rpBݩRu9ڥMCd%D{IR&A>5eXӓ/H9nd؈z; ?MιKO%]ZL_Rҩn%mɗ3FHHg>MSbmC=!G١)#mGK G8 ;y.߁~z YǓ ~~ ai{7M_ïj´?iD-":Rz :5@RɆOD="D&B5PѵP<}g]Bܤ>D [8G]j#D32oNM7!!5EF9?QBTrs;z;ɟ_/6!Lz@7yIA,?rĀfm<$O~&wkj&bzdM1eI^H @z>r˛͖5=B+=烰'grp{Nt3{^_gՇȆ5cYb DY&pGpGp ԩ{d uv?oh$?RG4}Ki3b5 @YJ-5;ӡadJ6|H-! .~Nu N~tYDig6rt]~?ם1a.?h&~HM}Y#"Eٵ|b4W;jTحyFK]%hmJ~ѡHVv`2wk[}tweGB|7F !dZE[}=sdz}Mʕ+LIosq|~ٔ>`KT?zT7( Q)- KWӺ:%+K%_w [άH7RkrtWUպfmyX>WSR7w\"!YOIGpGNeS'|pG`3 s쭦 'z_<I1))>Ŗj(9l C#VGHT~Ї!E6R'el+0ZTޫ?/O^LK#C4iNA7٘DIkSd6V9w\r ڽGy$[FX$QL9gC"z{h+kѦ+͙ nٜJ&#8[oKֶ:D7m@M@~[L6eMSfɺ4K?p VJ4s{GCzHݤ^FdlL~ظ^^ma!INVcz&,ЬYy8#8#88BHQA#.-OxE4}fGᧄSR!j10b389e")2>4tH-' z[v5:ٔhԩN<,өS6TT04 2Su&{cƄ^B#ڞ+ K4 6iz؟2eD,;3M).@wf3uLݱgyf [ΉDpG`E;oI^ JFVDhgpGpGp6 Ss+428D$dj#SH֮UUBM: gC6N=#5 ?V6DHn !]abrs Y&6>14L4A׳(هe1Fٜp>e~aGpGpG:>e:^F@g 3E%j&)Ė!K^#p&яG!jFmp RG;$ؠ(qYR9ʍZ7>k^MA.kiohKe]ӦM2AjMb^St{^vGpGpGpGp6yxD$qj<񄔍kpG D??n64Im5bmt!I%o'b@B4뒎kɓ; =Z.fLW#8#8#8#8#M"X 5ސzu28GDIf[ňU#V<"6(vNJAjw pGpGpGpG_D=tbO428DRh6xDD?Rg$m#Iݨ&N$+K_#8q#AiG̞EU'geOGpGpGpGpG`;A 2@^Z0%-?EQUG11#59n:lg}ժ`L pGpGpGpGp640 ΑzKjB˓'ɎFF3l1c6?iI[8#8#8#8#8v@(!4"5\#Yce&IcvP4#)lY?rqGpGpGpGp 340#HlĎ"6R!"ic61Ԝv9E;'m(PN)wqGpGpGpGp8BҸ-?ҁFhB|"ČشP׸*A}ņ8mv3U\GpGpGpGpG`;B &D{4JЂ-9cDS$Oͦ&4$g`HMLsv+]BPbzlR74#8#8#8#8#l5 H2# V6$HY)j:@~Ҏq8fNh63 }i >Bl}\GpGpGpGpG`A8?=='"#1$BFCf"bX(+e#8#8#8#8#lGS9#x@$OLn hI2YbU5s4r#?0B|vR3sП#YFpGpGpGpGpo##[ę'۾-ǘ؜#21R |EztL >ak8#8#8#8#811j&S4?"føH"RqzS6FdZ9pt/y[S$jwqGpGpGpGp Y߸UG,n4XYW-$K;AfCEͨ8#8#8#8#8K#niq=j#D4HR 5Y Y0}O%NWX!%WO/4_>\nfP+uѣn4CS~x Y&{ɴiRmw\:8#8#8#8#8"q iLKzݻK_`鷿M$)eH3D(0!/auG{&LQ_|1pMA#Fȫr+NoJܖ"IyZY ,C3K.֥Ihxq~1%z~^ᴩ?]5m…K r2Oڠ*U%  .-ʐZ!}[JV& U5}7_5V|?r7+hjeM۸}K/^z[^Ҥ u-$#8#lO'= M-o#)p<чrҎSQV߈.zR02A9)ͦX˛c$e[R)I~}&S%VNHP"-Yrtӵ=w(Lרϧz*LWKFLwIMehv SM<@r!! 7 ki/+{w tTxi̙NaEGv@ܫR#݄ "~Q1C) q[d'AUVEhź΅.wold){!l/gSpkQF v@qKe=7F-C}H5\IM- լrSR8I_Ӱp.KkZ} XV6<˟YĦ[0 prBxܡՑv:#8#]#@Pa,z^ Qtk&P$&l"'n|]EPR5[ƪegF N}3KFJ>?i,2cufN#zmi/<+N&GpG`{F 8y$N:O{3~OZC쉒rӧF60i8Q6s4<)5}RƶryVl 4 1Ӆ)oK,Iu޽{]F(S6m*8e#8SN .Yt^zK4.Co·~߁)}HI\GIu  eeRTwƁ$$%-WȢ5cJP2[Ht2eJȑUuwFBG2+VKYRj:R3ZU[uHK/WS UB0~Zl.'S*G#GItz~5f7Y) dzifabk}.Hއ}UZ첋\U 5 6hɭ8>!3о`/y!" #u:^mpE%8#88B'pB"C!0 ]YTVjP6uёhf5.}%?ML@֭-*ߒ#Y;mS2 KqϤNeyP Cb}}mC[u:yR2C);126>~|S %UTAO\%Esƒu ieȧ6ډN㟑!N*5P'u?O婧I'gI7aྎܣMض4(ʑyKȫcg˪5\#Ur[zҦIa8*ȈOJ>Ub1vCһCiX7W~X>X*+hFM%p>/`ju "{߄ OV2;{1}^nQ] >݄ecyWay/m^#e?rMˮ'mkȜD*1L)},"K( ٸ~H |`x.f=eWF}h|nBO$UcMfzdgʬ˼eHMJâd4`Ǿ٘泍#8#83?fɋFiف.IxFMGO6 fȤw()ueqT'8) ݤS1h+BEp#Ylx~Ui [d,i_!LISZ!M2I [HkAb3-"o~ܹwyrnr$ ##AnEpitO%ˮv\)nB* uRY:9k|&MϸEH%Rv{;*z,j(MϺ]*JtR3e{ɒgon،tcIIaENP"-ojTmR-%$DrJ❽IuQ2p@ !fAfHص4UfAnwɞJūCb9iro$$<ҧScsA x˲v5+\1/"w_s2e5r]eܴ2ծ wiUO[)k+ U[kMO?=tc*O<Qd0h`ùA! ]~ʗ_~Z#}sU z#x"Ʈ-^/5ebbN33lMUǵVA?.P, vu{@l0|S8YǴs !e+|||5gٵQytj,^9]tl#S)?omXOGpGyS3q%,u'g}̆VEQl36=VVƸިӬ#8FHTؗ8(mF%Tԑl-RF_.#rv3b > nx;y@fOf"fE]ƍ{2r(ݫ\H{=1c9/Ka](wv;t硾Nv찇L|IkeERкG|34V4uSV"m%μF@I/}^b*yΫdJF`/^ɒQ5EP|M/wIZݺ:9RR9gHʯ ] 6iɮwi'"sTk^$_y뭷hF aںuk7o9RB=z6"pĮ'[hhЗ|;yd}S eBWvMOHUxٱSKD,ǨҽWQJI:9a _X߬~ lo942UGT{֐:/>T{{cǎO8Qlmߨ(;>#կdaL6rd=)ks`,55kHvvyؓeBE"c u/ٳg<Ziti6XJJWH~.+i8"\ņ܆Ҡ_6N-|7Ѣ~oiZdʪ҅2ikXڲ3c}(,/)_/)eRih7Qf/84,c2iRFWC]!Q*{_!wdmHgL}O;$'|/l+KVM4G/9#8#}"fDƓF|RoIdVg|"mv?bͶfkHP:30) ]t9h⤒vl$uqG0"*94_|7ȮUJvt.%fHF)1 9ZDn R/ 9x`9h8뮻.3UM Юe?^9@p a0iR֋nIE }eƍiX4P{iR9q)ʔzAt+\ÔscDdEod_]eROUȌ64b|]?ٽuVved( oJi66kb3]wreU]iN_Q}ڮ;ZC _Z:uHO<1\|ŁT ׇチ$4hP 7o.W^yeL 7HVO~/eBL?[&E{ɮ]!tJ`]mr=/jdf3}Œոj`b]*;tlULꥪO1E#Wcը*6)$30q )r-ҨQ{C^tE@}Ć]vYxb)..N]#8"؜6mZ,6iD>hC… õ\ۤϣѵR"4oC \D G'{#t(kydeKcf,^ eyҤ~ٹ2$70A).h-KW͔z-)IEMwVMҿy2bMǥCӽ>WRvi rySe ]OY͖W+^r+ykt,=(4>:ۨ3e귤㎅2Pn܋W~[6YyGpGpW{,}AB>z@ՖKS+O4߭=`;Y=k)4nԒ!͎H*t^ȮfY&YP_Z>SV.#+&eRHG:iYZuBEteD'ϘvO?*uӤ)Vu,R#L*3 ֳS۵hժ<#!H3fLNDBJCzю܆`_~ d:=Xs=at"nhv9C I۬Y|0RHH}/!c ѼUè!=d̅);nsf^ՠ(W=̚T>4/ݡXuHMh@_5}.:&ǫ-O%չhѢ{m6]_Gu4lPN99G?Q셴1bD 4!tb}Z9㎁('3 v/p-O;4>|xG2lOQ}Okf*-xұ^BJK*e[+oCfJ5[:,N|(n{B Co$ / >-]1W~ur{SGJۆ{qvzL}w+)d>9> p׈v8Y&N_V~Q?6ʮS?\S'΍ ?o8#8#}A 2lC;VEX*4“G01#OqӱfNNJ);y.#8[~$DѦNT|%<[b%GKhњ"D.S!h>PեKy?q ,쥗^ cDDDAk12!iT&w8Y:kP)v% );wsZZiJɍrsKJFF2wH6˲ѝT+ܨ4;FY||oC"I5jv+tR)k(٪ZThwk߾}a%" 뗴˴n.3 ۴Y4)'|O4)q]NJ;+N [o!CS.@=ujӤRn)ԼO&TlvErA9'I躢8k`3T.?g|D%]Om9Gk}:$5!%|.LԔ7ۤW6i\ m"~'Gyy y[/C'v)y~h.AGӥnvDti|>gYҪ[y3}Xn@1d WcΚ2*TD܎`4] 6_ ekueKdRj4SC]t,6lz> vxY^S;.U減eno.Gzkׯх(oҵA 23x4և_޴kh"\GpG Dj?5R4zS<~PU!j)yIH+RCNjpG`k#`?ғ Fk+utOom"Afl3OhZކ uz11/Lout@PʄwJ벲I{[w_#ސ|h`0C)SIC/#+7-mV}-벣SM7J;̷T+7+stIhnWKω:dnaMByX?SaJ;O>]>pˮyPp0UR 21Ҕ3;I}B&Şь50dIѣ!5}NuAQӥV.#8[#,coL*~Gn((?W+YZD)Ҳ:@&m%!fؘ6XGh~:`}臝)HſP\;e<"RO[I;"{PV=uZF} v44yOt ¿HVqy͊R6o4:"[f~d$νSd$}zah/b? C!iұJ_ ][?"tލeRXaW۵ ]B뭷-sBf?`MJT"DI'8 D2%P&,!tlQWM3M.!DkN6?<nOk᥸:PecNJvJgq Syb92gDzV_w|7oXׯ-͜K0kUypGp#c7OA ~}1ޑ~'I_Ej< Ҍ>۵a]JykPURZ.94[֓'ꜵA]孏&3uc+}$X7jѰ@9 )hBPڳ}?7f\)ًٌ40Pٱ-9:`+3,ILT.e5}.w6՛n2|6}ٰ뇲+c26BaQCjDR.%\>,mgB(g1뮔P/um$`ϟ5sse^jސwJNg޽V_24r~hԹ_}V=w-~ _'ET ѡG}O+9~Mrd[}˯}M⩇y5n3:ede?dtӓ^<@?kk |rqGpG@! 7ȣ|bxDҔ#GE#Nԧ&tEZJ fg~I9%5"G)m; 4zpm]u?7ֿ;*7C7nrg []9c+eL%Y!JԌ؍r;ltj">e_M7n&)l=>wL'kFfom}•ܼyB6lc+O##WmDyAn,i\T^8v'ynV|`'+w+,9oFT>fhBYBRW:Y8{,] RncYr4k%ku͏+*M`kɼ2-2]B=U]#a*5k%ō+RO\乵h+ T@钯e2ri\,\:SMؕ>8c[#8@puqxusgjvIG.{=QA;Q|UM/yi-5"1RS9Ua kG_v2ipGBHRQ)|蚊ᇻ~sS5_ȴUQUg@7u*t/hpd|Hz[D^Eb#z*d3w wM;6̓zP]45u4Hs%0#EP `E-N AFG@tWl%_9$ Ŗ.Y'뚦iP 7YX{AA\im Iu'e&\f<&eu%{Ii^W]K1Ѹ5̕o,ע%UUpmU`t½[?TFPy[}=R`3؏ӪT, M?6]Ðv!)c^6S=q]z{D4vM @蛰'iae0̖O<]rSHfn} !թD*^z]zYYcI9H(;WgJfdxi,[8w٪D~T,Yyu|OXɩkK#D0>X}hC8za %]I6 sAܱ$4ZSGpGp7)Z%F(ɯՃvT5~Yh*țfKm(lp38eR Ff-%E!ρ6=s\\Gp6|M# Ua3%|J`t5(AІL(STta6\zS:Cֽt鲨~Mn&~VTD]ԏ\$YN=>T܋hKՌ-iuRZ?"ʹqv d蚅YK>FK:xyqWS@W DFSH?p={vi!2$kB_9kb -`1I  m|<ƧjįlEb)mY_^5FS/ƠQ;G}l="[UKnu ĚUIeD%#lKhW@RO'0Pg %ކ_;Hb|?T0PRjs#o)f'*2F# J\)jJ5CEoh 6^/؈k~3B-A-0f#8#|?_MyNy2GACK&u6jQ:@f a58H;idž9B>Yb#8VDi'MJbG$|A -Wu~X9c]g %E`laoiW uba/:w f8ǐ]WވaЀI.61t45 Uzj˜!ĔfhE DhDkٔ)iҾCf3gẩo%`Ə Jq7]+rgK*D&N^FFxvۍ?Dc27Dj5~nXwqGpG{@7 O\p<'ZE͆zE'N_zSAy=156aRNɛC䭌#8w/b%e9lЩr죕n|zjlz->dv|Ө_D>P W]~Q9[~q3[*yꂞ~D1B&yƋJUeړi17Skg|%Vfɧy =%Ӡ4:?bx7;=8#8 $wHI<џF-B8cl`t`X͆zR1a`l#k})B#8wAF}Ct⦸meS6FP o7%Qon:ZYuw$b YlDdٵIeuX0=V]#6Zl -=[ UڒzQg_2@j_@#AjI帽:ʑڇrHÇ˔)SRMU>69)6OmnVh3>2/5 .g†eS`HS{X]ljvmCGpGpoӟ<{K)/~K0.G%}8Caa i|IO4ʖ]G.8:.:#8#8#}Fw>_ܙ /4<Id޸Hꌳ4=8H[S8IMT$  Ҟ$CHNNJEy?ڌML7iSlsevͲ9!GpGpGpGpG:"8A-n<"u[HmZ#8#8#8#86@!j| \ j`m?YxIm fl9aӇ6ĈO#9ѵ͆rrmf7Wl dCJ%''GN*~XꮰC8Bk/iڬ,\P9l;jT𙶃 ȢE_Q^.'K߾RXXvp~dW_U;zi72w֮}{+; 89O  9 @m$XqP(N -Т EkK5Cp$H.yo޾fk ~i{9gN=lGݒf|b&`&`&`&`&`&`&`&`N@!J(T2>)Sm4JU6FKJF#7Pz%QNʒNyG5H5Mb?umȪ^>%y9lЧON+Ea!?'x㍰ɦe]6MZuz矇=;KaE ovKuI*?z~:-7pCc!C)l&`&`&`&`&`&`&`&`&01@VN(Q2Lb6:/y7#wyC蝉X͹*>٬oMS>$~au _~eXm GsWګ6kq L kcX^s 1r΁J㔖IU8Brq@IDATITt*.I-MDžUqj1149$?fHc`ĒKg9x꫇^8qm{T WO@9S,~8657٤8nKt({0jԨŠ+Ș`B KG?,COVe&sM!?`9g1,7a$njNm&`&`&`&`&`&`&`&`&0@[+ZNYA')Js$P>DOjG}"VM%d4ҡ)Ki0T<-ўDgy&Eȭn+GUҸCOWv E* Leڡ4X%rj-P$j돔EE錪~ryTԨ!)4OAlj@Bf } |Ncテm300000000(̘FDHH4C鋜+?ɧ(qU~TD5uL7N>45xEX'o5Dc_foFఙ $PECȉg(&K9C!5rCc4$+:c0O#"hK(q% SyBKOq6000000000XN2'KJCD+lo1uJǹUXAJ=;ISYM=HES`Tg|sk&`&`&`&`&`&`&`&`& { P E돔S1iGZPtI&QR չ*hjLRAeR:yc bLLLLLLLLL(LFȔyBDB+jDMDK H$ʠ1PYy>u&eU7Fm&`&`&`&`&`&`&`&`&`]@6;MPz!!:B釔kI=G- 1I2P\"fFUg;Iqڡya300000000ƎEL&(R'" :ԗ6-L2\fةKGʊDLLLLLLLLLk(Lb+^( =GږF S'!Iʨ -)-xSf&`&`&`&`&`&`&`&`]`b倉(--   tQt@Pi?Jl * T%9MSi_eiC"(N4 @"PD=J%W4EiTT_bdT2UQ1S"_աcRW^Mc&`&`&`&`&`&`&`&`&% 5 Js$9#Meu.\S$_y>弢A%'4Ai1|!ujK\1Pf&`&`&`&`&`&`&`&`&uH#l/\H¤Ȉ jRU9C>i1LL'&7ߤL5T?H&裏Mƿ6 @%)d~sB1Iz"8e+PeZX-A4HG퐯2((C] 6c OA`ȑN jԨGiߍvm~ˆ޽{?<[>vcIZ{챀x#kVZ)pݕlVK` )_1͑ ᗿez/31v-w1lIXs5>^yv{G0 HD#(y~ݰ5#u>u9ZXXt=IJSC>#Kѓ|Lm+.pWs gޓO9%Pr)lKuW_f!s*B (bbKK" guVt' @Gá^7bĈзo$vmo}R6:z$ʰ LGu]2nC6k%[@G:ᇃ%:n׊ɽ[oMg4ֿ_'ł3[ww$߿d:>^{݅j=ZMLړo1rpsPzdG*_erbdQ5Hɹ W> m: F=G.Θ..O>Ik- ⋧AHSF9×_~/my87:ҭJE%Dȧ|رziL oJEf&0[xKJ=${DQ1=kG&wyi7_x7bQG.Tv( MV駟>YO>E% ASDx2.K?Oz꩓דO>z;Si)NTë~zs}|յ^!yI,FXF؅Ɏ;^Z7B]jGaD?tLW-7/8=:3&X׹G1\s7Ur1u8b:B9c'f^z"1el=Y(/Bnkvibsp@CSSS1H_'|r!2_bA1q"W{}x) _~7?3wqL/O3?dUym:Yng.#xޓg$L1^w>kV5S~t/ B?~=^ش1rg!Cke,¸Zk+sow`/({%ߧpu$^z Zi|x:V]uպFor11k{O6rt/V\q䡬> w~2}3>8gtV|&2ltO޹a%ȿ5Zmh1˟n 9ul&`&!2oL&x#KGX a[{S0hЁ0Ȋ!NbrxCmPHEAk?b<"LHd8/!"t`<"b )u1c#O=Q=6nɭZ;y"E"^ExR=jqWzxp+c3|>S^d`X~:?Ӈ%=ȏ?#bc6BOp>? cmcޓ ID{́q`,Bw}wZ+Xm | H>ELfn)w;c*^ﯦ&u";=?Pzƽ fx3l,Q]<!2K@ FӼw|Qc嵫Ws+?axM7yo}nᵬe'gKy3y.~ v?5|C!35<00đK'̿ =넔Hz7]^so7,a:WżC8(F |s@@ zjx)!:N|aZ5.f}VY'z<5=~ΧLǚGLJb!<rnzRc;q@ؽG(;~ag(|e6q-X0X> EѺm_Oƒ _X2ma3 pĴ]5!(p`x!.G3F Mܿ0<hXC, ' %!FpϪU75Z叼X?bxैg,bd<#؍CT8p`W"S`ifATI?d>^; , KrC 5BdbB?k]g#5 r>2x~  CO0Dz.$B$^ׁ^F8?B(b a׋kc-`1/""i[k i>3f !> Á}K/^sÈP8Ih׆%?!&B1?~GϺVΌ #9[Yy|6RU_!C9xsꩧ&Fn|W׍8F5/ܣAee􇠬הFN+`^gU~?(Si|xYgJ;kz zwڨ>&W}Ozպ0+?((}YQ]^|g L J_DiQi9nHSiKBw&2C:FUPm,.uv#i+ǵ@â}4_&DY/4}_B(VlϿ~k+uGES‚ʶ% _?X(2l@5uWp—^u}X»ax` _|؈wuh&paP[~4YF#7pro<{ ix<4EDM3]SvgyX ݧJ۷ooB֋F( cwg1Aj9W]Gy;K,Db>a\&?mK嘿⠾QW>0ē;%CM `z#g`%!!e#/V7G102#`V2L+9ѵ!vX z-v=N}⦲9gyi–%0I6 y0gKw)5U#4B#^|z~ߥs"JCd;  Zj^EhܠARig@u*x nA+D>F|rU[4/k^ޏԧ]|Nymd0PW $'~`*AV}.*/uTOw׾Do׫uۚdX{ y߳ x#rQJ{#\ GSÑ$J_Hȗu0SFG呮(#-r%@IrЈ,Έ3( 9'8E cG>_|SE18>]ƴx5 <"Оѻ*IijR5c-*llʒsTm6x: x%xH`RvgZo$bwć{Xg6!z"<V"E*?&`60xXY%:XkjǃS^ ‰ސxo 1ܫǟzuxU3bLEյ!r4bq(7kF^_ZE#ԎzTZX$,Jo$w%,#h$iRpw5=9IߌC?)Y3ȁκ}\xslhFm䳜ޣm5Ty‹zWN?>Mh-|Vꫭ f `e(oڹ>>ڌgr1Seaa{ >ƣeYxM)sV~A3ޣ|6\g#jo]>f LTeB@@ h*1&ras\Z%yW*4>YѪ B'ԨLqtL鴭:AC7UbM9qExv ) xRൄ a0.4x'eVވl_ӹtdeW;|$z N,0O37:oa$a\~xxtTL&I<(3}{"5 xzqA@dE N<ιq?SF?3),+m Nk;խed浖.ֱ6o155v- rZsp+3z/N&W/5D8jc@=(8GAth<6 Qcdy},h6_{^#きp% K=!s>缧G,EOw  y#Ze45Zm#rn3)鬇{Cdkߓy?yڽ:Syh4ޣxrw?$%uLL`&P.FeL*N)-TIIiUD I! | Lu8M*'e[8%47<E<"ŗ;g'W/ɹ׽'ZP`)vXc5 _gqk'72=gǘZ|\ZE?T{}{n"C,XWH3>`x: >se]6y=:G}FV=A;i?pc8q\[Bcau}' 4(}.yZt>|&yszcꕩw5zyy/ob~ g;c ־'ֽ|_q/-!B^)ed徎x|qOh.3ج600M-C$ISIW=鏄Q )KVMUs24'*SH>}|Rj_'Lb(]K ,qyp#D"\Ur*æFGo6I//V|Y* 5!\hQEy!_.n_B'><4=,uv Ň@@@zy`m&`&DhYӓuG ^7<#~k͎PXJ+%Ȱӻv2V]PuFkCaan<TZ⨣J݈XÚ{w-S;Ń8m2 }WnvSEAPJV-0k:m×u ӓ͌cРAEp8Ccx*nnL}Q#88Y V^zM+OTp~h=hwDxvE{4~0x#zY#`juyD>$cDD6M+2< R~e$2"h[/x|+O^Əg}٧Dꫯ&oJMفBfR1*+Q-Q!OVvuȽyyݹN^[2|.Ǘ(${#yAhFD^Ǐ>K05\Byd^^µqO);=h >0D0̦Pt10 xh,yA/7WPGhs`8eꡦREA2.CETC45%rO,#'s 4mR4/TGxE"-_iF|t=/DϏYqjc(>ǃLmbHW7=)E^x[?3*լd׺p/Dh̗@۪u?uFϥex)ϡ .[ysaZ{Z+5V{϶ lZ"2~.fkFiS-1ƷjDJs|]ÄgTY>> /l 4K< 8e)s}Q:#e\$NN?Rf%j%1)^<5hBLEh Tf`Մ٘ekS◊;6557{(nXj30$Lc2e-p`Y[?!7vkZv` 2LFa 6`c9 X/V<`x8ֿdMQ'|eUy 5Y#f&`&` ~Ћ)!/*.P#Z"#"y;etFiU2*z DAR#@W5dG5l&`&0O证g&`&`&`&` ZK'ήAOq6 jލ(#!'Z#!"&!&mRihIWrN>FhQj\ G=Bͼ AxZ; ֢L"iW,D00000000h\cDSd,kƘj$&JȤSLBҨˀ t2O@tADx~cL"'|:Xc5v4 mVniZ~iԩVO6^cݺIocb&`&`&`&`&`&`&`& HDCDHv(}Q!Z:j-GaLLb%$.S!OlJIO~6aGydR<}pݵ׆'x"l_E]4L5^ vZ*J}L?|ᖛoW\~yjg\s Z*3[/<_~a˘7|cdž}4\paW_ضa0)q}Qjo|0~ǝv ~t]w^`ծ,ѽGk}7]5W_xtgq1>t]00000000ACyGC^Hr#%r.R&u)*&RLHAȗiKcăs _G ߦjҊ+&[o x:YubgZb-çуO/BVz0L3YX~ 30C.W[}0l/8 .vTvѲ.8#aM7-nִ{WY%6lX^}0[lkYf zhe;p~&Yf;vk\D8N=598Fvmkp?=:6qy<\?w)rbs']>[xG_ ! DF vtDc4 H%VCѓaI9')ҶʐF='!?Z2揷!8N1aؕW&{M^x1~I '/"fD/^y啰+.4L9Q|4zlyLї1z)ӯw;uUzw&P oDݻ'q j0}:4qDgśUW=OwH qs'ֱ A!"u)FObtB鏤a%A>Ft&}J1I19Ơd4hN0 嫞ER)zS ;(l^QbhnᆰG3Ƙ1a7 F!i}Y D@g)ES( {)b8+Sg.{! ط~M(7ҔN 6ztna&M?_ X]^zpdž/2-p_Z=Hs00000000I Ƥ5(5{f hS98W:a-r. r- *JN i@%nRV'_鄴asΙĽ t4zdyQ{8c7n\< uNκQ }-Nmya57ψ>(\Sc紆&mXF`͹vU>Ph?2Nۿ2R>x0[\;%g00000000I@CTz4F1 eSO+0=卹Gvpah\oTq(6IU/z~̵^;la̘1ꫮ /FZt~Eٔ ѓkaRlaZn>F}.600000000I|C+ Q$a.Jsɩ\~VI'MOX~9@IDATVj :ky[LLLLLLLLLhZ;G ,[P&"bbTDOB= D'(Ԧz t!ơ&C7DZ\GlH;j+RSKyoJTCjN$JU>PW"΋LLLLLLLLL(: ÎAXM K*KDĒM¦\] Iũ󴯲! 'OR\cZdM,%vGJejե%%l,z0C[ Lp :GIrd,IS$NFG)ݑ%&5$1Hͣc3 3$t9S) T9Qx a= oVt8sL1m̘1w 7tSRɧ>pA)B Î8Baر7\}{"g1ta9 _uqu L8-JD/]H'\_-c4鋴wW9J J:IhA(TH.!eQJ\U*GfFk/b=kCxeC~ ,_|n=wߝʥ  -h}=¨Q“O zglix*c{wk:Imْ]\ϔk$z_dͶ&!﯄i&`&`&`&`&`&`&`&`&Pz4F hJF4DJ6ERS~Y[M qi|9+_=,iRqcXxʫr0,&GxC""*[kJ+f;Oy%(nk> 'w\xÆm_{YƎN3|3;n=խU^nLGѣGl__~p)^ƍkE]$wzwB{,| }ـ;qC*x 0 XMhLLLLLLLL1Ĉ!/b)ǁȹNΥ]JsTڤ.ZX5A4w@ԡ *K9kМ˔ysz);(lzѫ*NXfe&HE77|ޏSן$*^ʦJ٨IR1D[,XZ'Jf#?$6Z>~?+,0YnaƙfJy;0L=6=(LLLLLLLLL 1rpCB&HG*JUF"1ZRX5AT )"'Ft*189FYVҨh<`$ 93M?4w!< H6Ejᭉ)'/ %vmቸR=y#0555)zŵCYӔ600000000h >nf8: V(49LjKLb<#KߌR@Y\Ii4h1&N' uqQtʫT3y^&GlҢ[s9PR6Fo~.ny]nףFq]sL~^Y> i>ޟ+e0lذ80[q#*lrKZ4oq00000000('ZֈN(Cs$]"aj RXꔐ1 T&e5]k!ƆEK)Ço!q&A>LՌqc[T^85:Esǝv lR2Δ/bߑ5zN kX7.+R#F´|tq 60^i q7碀j300000000z 1MDOQq鏈q9j#m#<8Qu(J:U9>yK(0ELbS/ML%;̓mX&|mn=*5㢋. 3O裏:?a7n!uZ?x_>}+ᗿeJiO٘~LLLLL:5 s#0\)/ԣ,:NBi2Sg1RIyy ⪆y6%q)Ku9mqª MLL`"!ЧOp7Ɉ0<̰{WC?<uQa #FhK/t^<8gO:tD[ڜo&`&`&`&`&PKLN CU])[ NiyP .550us 6TF"iL*Iec4VLL:@Ϟ=^^T%X"/ )x6TS姝gj_3K7 믿~wG}4~r/|۵o߾Ŵ =2lذp a oL^[n(>cg .ӖYk{K\LLLLL`'".H?Z-bfKTH9G'+D DM%ҡ:eT)MI"4 NqЎLL퉷!ZhRg}BM0=fu$})ԙzKjD[[`JPҙB⋫H cfIbdᣏ>JK<@#km{c&4)R SOcfVa7Aȑ#*lfuf&`&`&`&`&0(,b苊K$u88ۉ4JEr^bՄH 3iC:a,h|6N?&`&`C`g,NwGtjXL{*56}!>z .oV`*V[m6pN蟥.iYX;mV6tiY.5z K`ܶfPW^yeS7dH1~W$V lA 1=z6ݺ!&fH#kQ& Q$:tDWHU*RXM!UDq圐PtΕ%]Sg2o t.<Zc/G-C3i͆O.(W_)DDcj>y=#J;_}.~t r!2Oص1P6,("DԔѸ!s!O>iw>9d7<_c@fltm?Tb8|hx lO X߉&`&`&`&`&`.'tGB<J_\#F9LZ$J蠒iyq:#"0ڦ iSMBUc(Xl&`&5 !⁚b5\'%\ -~mL/iO1kC|ŞZs x[Dr1sc^;M)yyȺo}:(;\s%W.+ٺJYagN$^Ռ)Ji6խVtN8D 5'ح՞LLLLL&,Qixsv(F5ATB%&F%i #XQ.2OnHf&`]So$~2;7=scZunxg2]3HS핇egvA/~i𖔵^{4\4(yy񲒱&Z; X+IeզKdpܳSASvH#_tѩL:lELL`b$z:!Q"ه~X$xb-B\tEtrbL?%:4,BtG3]ӶYd*n%N30Ce=Pv/78Jz^/v+KFw㏗$m喉9뭷^6lX17 Wl&`&`&`&`&`L/\\!|Gi#唇юH%_JO5A*(y(b`E|PрoL(޽{|j8FCL'1^6bĈe[8ãiZ+o9}Xǒi6''^Ӛ_|H[U'oALn9wqEoRav}]E L$ S%^&=0P:#qKk":SFuFLjj:,h49TdNW{1j30K.>*9gxFv GWY_N_V^y9Bog_,S_j[kޫ0jʅJץv$ܡ O sA/0*ݐt:&)QҤ'J{$D ʐc$T(J$sEbvIW.ۑڱ>S G|$6Q?jªlꩧV4_Or/UeS /0mn{MGYsDV:ojj* ,%ꫯv]=8%W )U 5Lǻ% r/џa)+޺600000@؀)1S"!%j&Q#i&#6&" 0$s s4ڡJ'Meitq:wh&`&0 hjj*JuQ M/X8EAXϒu3˭|9昣XM0[G}4SY/} ŁԈwܑJ06 e1iլgϞ,1P݋6l0Y FJj300002GKDC\Gy*$rh iC#!j2uKLJ 0ӨDM҈0$z 9W:->f&`&0)y,Rd˳;}nN8묳džH7tSI{.'j#'ވ|xNX[]) \NLLLLL`&sL4qs%i؏kiR!]ʪ<:&#mQ'/O Hu{()AT^i\${555ugKm?sw= ׏l뭿~Xr%{#m>0<|ƚv<uta0ȱkwZwN~\xG0=cy&`]vuMp}m0e|V(븿>!H!^8s?>H#͚,L۷oKd'㞌+}V N?_,Z}2Æ Kk_2Vvl衇B~bXk3 M2k)'Y38cXz饓8rTn*Z~*"0N;,4U^P@$n.˓)o(ܞx 5^gy&8`յtiR)hTl]5M5Qј&Q&o,11cX{AA J{s?3÷?_%b:ẋ8vqaLD@D@D@v/#ޱwwp +ɇJZRm2R|T#)q×|6 ,D(gS:"XrA@F;}b2y0ʩ3ie[ Zq#_}n:2).nGq˚ +\6f*bs1>C+L6և| πceLD@bwIJiXlۥ^Vrk׮Xbo>/~ _lrK\KMGD k%dE"۵Ѭ38?Il;|l܍{mwy2Qd\Bd7c7X+MxoMiĉ4[]Nc+jQhZҢo׮]*UV֫WG!r!N:kSAD@D@D@D@D`' `!:"u?顋nhA14TG%<>c,(ek7p 0 #a]ķ+XiVdWM=ᇳM)_V+\ ݳW/WFщ~::~$9K<%vDM]Q%@N?oT:FUWBߏmJB.;* 0X:k֬t\ 3yokjc++.YHߒjY*:VQr*1R~#̞ZZ)+t)wQVjT"R2 М|I)ae,M4Bl>DRB[D_ė4,(G,*BLGt O&HLz&-&I?h啤K[.{Ƿ{fPFdOe!lQZ_Ȫ}ݶ} lб[5GR߮WK%?Ӎ}^ȣJ+8 '6ߟ䘀Vb.v}w;Gη0V0o>D@D 8򷨢/]l!,fW:'FECyćհ|mm;w VfWn۷cOD@D@D@D@D !.ɣQ2yȇޘ>tK&1Ub?ڋ˲@;cIx6L$&O;ŗ<囃7U->|xJ+cW_G&q>!||=jC? Ir .xoeS>."Nג}+\9z?׎w=3,UC]lEeU~*WVT_/J!<*bTIVVbh6v菥 "``7LʴaYDONN `eꢍ|[ұ K#Vo-,6_l"r&+Dw3:v`o f{n:wEј-9_-.@ܼ/~痖`D4e~v}XzAժ}E֟t7])`]Lg SBLh/F=y4H2@gQ< @ fЎL6|ivrGLVq6aoYd"^41gs3~(yh| }~b?z@ᆎʟmhZ""ih8e:mYԋ-+df[L4q3XQeSL2P7R%*ƍg\qZ;m2ٞjI/[fz*݊|aɧR*sn*3s:XioU.,$ Վ;|p>oTD@D@D@D@D@D@D@D@I=042CS 1R4 ÐzG,bI_, JOC8Gp& @;eebP ׫ W!Rbxʫ~&6h綋_'` |k{l "_5%bK}.hJ}׶Ɩg}#o g~yҐ瞳F-6gmoߘ[NJ:0s1#ltu*M #!:1hhyBB'ch|Ĥ/~, 1HNJϘ8#u };ye"UW^iV{eX&}}"5Cٹ[nc_W,m7ZV{?N/P˾Jc~{+/Vv7g]~K=F0.cB<܋~="X?g^s=s{睗bv 6z󈱕p4 !#xB$ =2[oOl-S eYdP1'x#R:BL%ևFlr+ /^ĿJDի.]ZLnM#m>+z %o<>g(5vK`Q5q%K k"4joz#! I=!nHfi#FVhIg ,S` !rz6 F$%~'|<["͙_!ݒ?ẹ8pxBk,z4G Cl1qvtD#j RFcY"% 7g r'&)OV8LD@D@D@D@D@D@D@D@D`Ƕ[聡1p-Yq4BOx?z,>T:3:&$G=Oh#2*D-n|7fB/Y8eJ4eL&K&'~ /l% C$_AJTǑ|2Ha[?Fq||ht=&h[u ~+Vn-~JE@D@D@D@D@D@D@D@D@Fq:!c菡zUne"L 1u~X@1I,C[ x—|$zc;aA>E@D@D@D@D@D@D@D@D@S\r&N&Z! nZ#i菞-n4lh!?`VX >XPGzӖI" " " " " " " " "PE j&#z!ꏡuFڣ.h,)!Zˤ)W%v|2(cr)D@D@D@D@D@D@D@D@D@FC psGH8,)>DHgci()%@!Pr`24CtC` m2t ѲXB-^~|Fcg X J9UF9dLD@D@D@D@D@D@D@D@DX6ɍYpMݸ`)lKGZd.L Q(' !'D'_>ї ?yV&" " " " " " " " "PT^X34¨a4-ZcVY L Tl&>?b fZn[R6mga]ԩck֬O&M{o裏|[f-[vو#^{Usα/&o5w~wc:uc&" " " " " " " "5_>a  0C  zKFݑȓll#Ё'f C=qXlʳ}>&qh#ƢRGzﵗ;_:t˯p ~a٩-˗ۂ 6Wƒc"N<٦NRJ%;C0(1Tbg,KV #Xcg"3|ixٺ gejղ|K#<֤iS[jUa[ǿKVZ5+l[v֨Q#DefYnʪΒc2_ʚ7M9_LՋd4'tCD7A m0tB4C;#)'ՈEc^U x +R2h D1qSHb?-[ٳb(̞=;=ׯo'rӯ5lЦOnO>4ۯ.\hvٺuVz>3s;7W]s͚5?_[]v_/!)n3RF]z '_D[JvM7`}}̚.2f&N`spv衇ѣC5k%K~voӧO3ɏ=s̴6Syh!'"(CD_ >Y?'?zbZ%;>a`,gbLExi%hg* ><>~Ba$oO/8ԥsε[$~߰.]Qlb {Wl+B4ib'~k6f̘$0.[4!B)$~㓈I{ƍ^zj5\$&u*qy|:{Sn]kTӎpP|׿UMіG" " " " " " " " [N 'v` 3tǂhϬNB%esRC4)SE;m! ?^6<[9( QW^iuOjzoH_|4E7x#}iu%VXK_4tP;cqF);u"?o)ߢEf?&Nh7xcr{ۋtcְaN(].oBE{|_J[wմ2`h-bKչzȣER)SgS{,D RO%0I dz6$<"vRmԩ)>[燽fcYs)l'd=*.p!nb|>k}n+.]Bȭblg}7-ws=ȡKD@D@D@D@D@D@D@D@@h#R!fB1r4%ǒew)2ԵF0`1),i6NL6T/yRH#^˖-dW^u!}I[kשcF\6WH>1җd^U1VvŎu⥑<ַYGuTr"'Vvw$?듕%×˚6B=lЃ/D@D@D@D@D@D@D@D@D 'bJ¢?ʥ 1IyqQriv o.2:e!jƠ1P/fɇ1p yB)z +[J~V~v&[ȹ[\iՉq|Y/pzg3ST@0pziVY? }tgjJcr<㱲4ml[yMEh}ǓPQ+"xd'Ƅc΢)6crg֯ߐY4뮼 >Fa苑eSGD||B$18>هz_L/b5 ?ϦrmMٚN4r;gfn(~~&(1-uqpS)_}eYCՠ9_;(vSӦMƛo^znT&"PQJWs ,_8{cLl߬A{szMnxM]`<۷k 벱^=ݮg]yR_;olS<1;Smu~N֫C3^֩UAv5Mzy㔷xֵMv@EL2 }6529>hC/ l>Hd)dcfY#03+LA_W%v gAș 6G %}|C_\ ]bvGXϞ=;m$>+Fe" "PQ&^b6fڮy};_{U mi6l,uqtrYN$R֮Q|n}:5~.N^5=p⮷Ƚ9Gu <ꘙvϥoq,m@n !cPHCŖU_hԇ1OȉI9&$"Kl&Fm<2R/JoбcGC:tG D@D"_zq{ڷ./?~u;vͽ؝ &kGFSDUk[Z| U\Ξo[Sy u᳦ 5kaJ׊} 2r`|H0G0|Hz| M2M()0"`&Oh Rfb>hxQ5 uogkD@Ǒy#F8W}n2$Ct#S ( ۟Ю>eBC?g?<\u3ڪ6p6va] cD/ÿb,9ܷ[,]eMs"muͶ}E'oZں߼a]w?kw &;vt22IbɦYjo-~sl [HcЧ"{/D@D@D@D@D@D "79WQHaVgeiޔCäX@L|X JJ-3PLvN<2mc=f'%k즛nӧo zQ08ӜYb2?ӓU|,5K~qp ֽ]}e\VXd|W&1uu|vMOn6Y/Xšԫmv],ݻssѾuAsaGa O~ ػ:jV%OzR Vȏ+NEP-GYC|+D10}b!|ȉo 1—/}q96A`6x¹kֲeK4hPa]y2E( "rsng\`yDif#E6|I쉷'Z=[}KNB*?/- ~4},Zi+VڹNY:nʋ@&= ?&I$"#KLD@%=zΝ;R~SأMs; cq1_¼!"*ϗ}+}liز]>

Kg/\8ղ>װ"3e]vOѶI~ng~D@D@D@D@D@D`;#[&N&?zꏡ5͍Yt$H68|Q/)g}:8+d" " #186C(Eck}%"vjԇ@"оE}czNS5~E*#"T}?yoL΋meٯN׸GĎ߿,زO_ïruѥzXɉ{Y:56?W.R~.ɟBupt6_N-\1b|)2,M/_G`ɒ%UF;$5jX ?xV(F]H&)F}釮?iN<~Ō@Xѯ4& rDLYrvD@D@0:~zl?>$mcG ߳uFRmڠvv|b(lϊ1 Ub($d" " " " " Uڵ^V7j+I D$>1Blq'D҉̔|IIŇϦe_8GO=+|߷KՕmz}E@D@D@D@D@Dj^90z F#hhzUC l Ƭ,/NDGLʡQ h'ʑO5]I&" " " " " "P5 ED3 ݑ: C_7zU:"I#Vj(eYL"9v&SHy P_z6M 16b-mV@![!"avgz膡RhW 1> 18&F#"eR!zpOKӗ| u 2֭Kz&"!z`蒡?V".|[Cdr1Ieb1C$.+&-UCD@D@D@D@D@D@D@D@Djذ!IYmr膼pYOh`z4H8Ig4Pϳd#SvhA $Pv/e'{mWD@D@D@D@D@D@D@D@D@ܖL-1(W[>A4DMD20 !Lo<F'&׳cժU쳵WvzQ9/" " " " " " " " " hqhV!~?e|)˷e>C%e2% ЗG[P'Ʃ龕j7|5nܸpU+W)S޲MW9sm5vWtJײeKk׮1RQpK`Zd艱:34DRCODGNԅ#uMF=-)O"pL.M&F](? P)RarQt…6O͛[޽O%5XpJ{{ za At{ԼE@D@D@D@D@D@D@D` 2Lb1%a<ԇN)%HJI)G|0|(S QG#&BEJu.^k׶ d{キyQihag4\QF ˨)c[7őX p,-!PBsB 0†&!b(i !莤!FW|hx | L IYZ>cUiLҫZq-?َ9$""6wTOy7uDO8{/~rֹs$`.[Bl#9&շ|<`+V0b߆n رc?o<յצ 6N:٘1c'8ڴicgͲo92 ۥK7/b-uݖ/_n>硇γZjUve2Ru~`J_7X5FS 뼝/j8a뽝8h/O2BJD@D@D@D@D@D` ~ Mr)F> 0~Q#aԇ`ZRc>Y/F  %6VqF7&Gn#x3&Um6m]vz%q|ǦMjI<3m6>ow\)GEFfwqCK9fΜi7x}vG~r[s;w=#֫W/R }|U8I~ٳ? &>?ТE΋Xo~o?lo|øjQa$Ҿ+6!lZ>g_v>c'^lrKg؀?n4!חӆOY6f,>"مna!x2IĉXe#F[!bQGh:bf7&E,OCA%}k:+2>i~lg$%I#9_^SOفI˱|W>V91ŋ/^{5o~2E u%rnlOE!{=)[ڵooG &Mk^yo)ZY|١CU]}R>D@D`s|2k]vۛ6k k׼nVv {yۓ}*ﺤWm@nhh:’#bhY?#?ze;f[c')(r:&q_KJ |Q'Ym˖.ŋY&M67m d<+7[jewгJuI9)*В>9a*N~̙vJek E>վ?vc0~ * fz#z=C:ޤ2K bȁ! I,4I& 1S*/1C~cY3U@>LPdg`1hLr(jN['~lyvq/R?Z;ɇg[>A1x1HrL_O|L4b_L<9VGC zieRfl=֭[D?{{MC?v39<5j8m6d?~|Z-eF\DUַϷ>-_< d'o|_]7x#:vaÆ:ÏX>=4>ˎ66e^e^"b-aKLD`!c 7ܐ^E]tQmSL(zSm+z5ïZ5ً~41Du;)^d{#y:\=n~țaa.6W,Zay~=~֡E6蟯۝~<@&;ow " " " " " "iGDC гc.sy'`GiqM1) E_~9 sSu.|rY2՞a?^lTD? <;ղeK4hPa]y23/On--VYnM̺idp9WjVzo 4;9#g+~fy'4m>(S>#e:َW/* >xդ!Ȇ镗_F]6ۚ]dK2?ml^%c}D@D@D@D@D@D`"^X92? +CߒLcxs, Q!"EdИ(XV0!F>їrMh,ԉqgymi |WQ)ʎqaS^yMG;ҷ~~ܦϱgߝjGi7ٟ>٦[ϬQnV]gێܻo}a/3ܣ՛ՠ2Z7g{g.#/ ִAm}:sIo$GkeʋD=042CS 1R4 G}E{A? t#0hC>ڣgL0T\w-'/+A߶ٳf;O" "Pԩczj:+yy›jjcwQ|?3|xj|UػsڜU?g"_]%7du|'6gJ ~>R8T_|A4!;c@?L4;iaL6i7#l[瑉M#4-1_vm!s6֭ec7QާβyK1Qd幑+fϾ3FyZλغ Asʵv![V mU[Lb][3A9!ز}E,F+G׹y_լ~/J:_{;;c>_?}:w`{Oo*eU^D@D@D@D@D@H!&vC9it hywKqeD# "rb!nR0G{Aސ9XE" " Um]?@$mշ?gs^'kʌsBOozn\ڦtlO_?{)ƭ5^ZWtI{GY:/NǦvkQ߅]h]cLֻC3kCo/SCD@D@D@D@D@v9} M#z!Z#y񥎘ɇ@%'"'1 D)c%NԓP^VvlFOqߢ嫭vI.\I,k,3׫]ebtִ~maIs퇷nWO7=5~!oE~vh>6" " " " "1{bM(M49CnEdgAiQ1C 13ɲ PXi_%QOfizR:@%h+^*й1Ky:e<%MK҅JSTnաuYQUe" " " " " "-2 =04F~p!l h#z")}L>)>5KHZe )za苡!!҆O=F74J_1'Ftʘ@!d?LvRc*>!FW%_ tLd" "  n~l5Mb [ bh!x‘#eBg$Z%mE\t2eY1|SP[0QlCCE,&gyLD@D@D@D@D@D@D@D@D\q!:b菡z x,+^f01:"j$OecbF=ΰ]" " " " " " " " " )  C+D pKIClqacF;AXe@Oe">֛'HF w(Z`V[5 ET 3=tEcQMQԒ_&M9P\* OE#OMY&" " " " " " " " "P_ 0 h`h?7G)e1H'v oij1YQԫS[\)抋LD@D@D@D@D@D@D@D@DQ90! $+C 1XY0r3Db^ep^{E!x2Bjc5(TyNj2B`$7fu5wゥ-ch0'F}ї ?yV&" " " " " " " " "PT^X34¨a4-ZcVY L Tlֶym}l֥KhY1C E7l#CH;y, J|hs6 NKH~ K>;U-w}en*5yՕWڄ 25mٲkFk׮m3=;ɻ#CD@v,ז@vYMm`66ib{;Zոݼ&*" " " " " N k!3 nE;#~dGїEˊ5O-٩D NA4RIpSI'&D|[)mGX%ڿkb#o˸s1=0; Qɓ'{㩃G`eehMo5|=9|}8}6r[CLz3! |CkmW l|O'FP:`f+#m14|ʱs686כu@fw8za!|RϭDUhgBO =b{PYH q%L:bPRmBT6NRD$B#:*=բO5kw][ZO>{o;Co߾V뮺b[;P=z|!F=M7ْ%K~v9X>}RI&ٌzX`D@D c 7ܐF>蠃좋.6mTL2}]g]czK7/*Zofv=Į{h]2K5[.J/[鿞gLcr7駷?>۟UE[I|Ivjޙׯ)mg|֖\cϸOk&E@D@D@D@D@D`%꜡'"z")'#F'BG &iԔ'FG RG9&C8aԅ/qO4,^.[-E =|>2igy-\6lh|01{[5lQo~ʩW_ypg[֭k\`]j}]֥K ';nʫ3:+ 7愇 ʈD`6QkfĖy{رڧ.-^hNo'̱/. 0]X}ih||$>Z$GkהEmޒ3:5_u%U@nH6M#~N<֪U!3ϣ97jd/BN(G]gOhrM7ib\t2dHݺ;KR,_XM,[+;2UN;S젞ډw- #~r,ZFZߖNZ>|=?m-M;_5= ?d4{}©-ujuTn<b#}>+h|%[鯸$"sE5@j8ʏ-ZN2莤DDCCtPk[Яm7uA YC<눮giuVjbuj|]rBt.i} e5W\}ugi+<¹Y[b]v0c'#6+/" " " " " ;<h8C -F!F}>?d&2%>BЌ_1E`4y|Ÿ\Иh^:o޺6r%7lVw}=8˟T˞Jv*G^zktR{/~S." "uԩSN=T:thZzTQ}'ۻSsgyr#7\g(l֠NGOVu^s6sr3e +I1<`=_4Rc_wqӧ~jyÂ`*oخ9_0@,t膈!v Od;ҏz .#i$N{ 9:dPb@&m1#f6nvp`Q}ʞ]o~>v~ss֨qtgMvR/J(elɹuwé! lSNM.LD@Gyl]2K'>0DP=9ӓ?^iGOO&~MK{JًVY}8UHƗߛ4׾g vR횤mϿ7==?(xeZ|b%iċL=O:oM6Ћn~ͦ]?x8cd" " " " " "ȭED'zb!zU)}+K$NVbiv̶Ib h&q_KJPqy ~Ctj\IGiql̘1~l]@?ߓCzݣ˕9=7)t{OPl6xĔ62l Z " " " " " ;( r`H!vR$=,G)1O# 4ec`RF#8 OVy ѓ4ʴ/qӾ^3{g8kРA6^YrK=cq<[7swP_z?6mnt!^[$n NV'0rԨ>fU̦Eաe$q.gK5_*mG,N>wq.<:Wxh+_>lu_f1lF#=Ij#&K aK.7|"6Z8t>:2P #~ȇ(~# o+D_W<2߱cGc̙3aÒX&D@6VnY?SSlocgK~;ߪ_tp]DD@D@D@D@*@[=@e@jԨQv(g vy,UFMIC %σIiohx!`fH;8 HT%\ģe.١^\/+" HCyd" " " " " " ,Dt0tUba芤<8 qz,;FAM3+dfBԌAcHcc!p &<})sm2M=042CS 1R4 tG}#ȼh4(kgBf 0ї?^<,*r+DC_ΡXm'n2,!b`I]\B8>x1Ňz'no} y@"PZ!0mc2%G2IEgǃlЏ6|_A]CD@D@D@D@D@D@D@D@uyb@IDATBL`hA4: Q@,A#0~Ƥ!>!;b!z]$d" "PE }K!_TW{\]%f," " " " " D s#p<!:b菡zUn x,+^f5cX'Ox%1INygXЮOlkQ鱩}Ԥeo/?[d'_7Ďœvն7SD@D@D@D@D`-AD'D PnZ#i菞-nܘEGd1q,^O!F]PGtȋ2}+|f\h" " " " " "Prg Ē>~RqI*9J|!Y^_ rXoL6l7yi\[7s`3/n殉"~}I+z#:!ih')Hrh^U(/fQ FИ@Lŗ6ڈ>1)*l#^%ؖX͛g/[+}zϽ;?saiڶvQݭ^sڤ{ulfl\7k̔.N 3=7Om3}i>d>nZZ5Nk @ȃB3ax A7 M2N?Rڒ)}^,|(#B&eR'r^UXO>ڳ}rUmk_ԤI>mr|if)[D'ߝ|?OYm%cw߾6mT={vR sJT(˗ۤIŜ0aBry q"ʚ<'Y[]W}kdgӷ?;v}mM_h-9VڊkFi|V V_pto~GE.`ֶFn2r>vfW: ث4x=:a]loGxEU/@y,~|HJ? C3 ݑr:"I#Vj(OI^(ky>RL"^6b2vn3D[je~ѭ[: OK/h}$멶s][nsO5s͝;7wWЃ>2qGgeژ>6w.2g)^[l;|ꢪLD@*@=sׯ_awvO\H,ˢMzi] .^ {Է?>Sƾ;߶qSXO? YC<눮giml>cS+ιYCH-jVf' {;\ޅsGe" " " " " " '[!"'+C?&yHC?/yR~vRBHl2|ÿDc b1-XL*W&}1R&Q+Dwn?rUV+'Nh㏷E3O?ms̱>gmo=pQ7md {$vwb&v.bT$:u󀳻(BB iBB #DRQAE@Q)"Ei/J^CC !B7לs9{fɆp=];Sڇc=6aҤI1X!ڴ+Yώnf8b~<6ubۡwzX=^5)?yfxI'+I1}{\a=?X}xD:㶏[ۆ*΍{vI}^5l{~8*|eLEKU\d300000h:FCsL*!J4SiO}iSOjDy@!0FYU` ц%!|~z嗇{﹧4Bv1 y$4Ȭe*1 ^- lA?ْb2-סZ;5oϲqUPmDP{r'G{Vi_DMDu n47?:6"Fz?E ᠟ڷm:whʾv[{aL.R.jb*JRS=Lg^iu(uOM)eWOq swGl% &`&`&`&`&`& #PԚ2smP(:"vG#~y?J$Ɵ/UQK줎:2( .FY(|IGm~K pniECD\ &'(i&㪫‰q<+Jpާb7^rIb7O?=\oj>sDwXqkgx~h4e+hmyie(<;/7vlةSpq׵WG{t\ F%_\}u;&`&At.#qLOl`x<~t+Nn}Rig{:vps6\|ǫx(g"ZnٷG8aM}JlEq~nSf *0լZZ |KLz`D@*)cC[G#y|G1bUj Jg ot6: N_i"1TqVy-c}I~%У{$0N6-ka˭ â8L`ٺ~i}5.];.̉: [|S|&nEv~FM'O\g3k.l&Idx B(`5ׄ?꫇Gy$\za ?#|W$AuO:(}970գc;taefEաmܫK˩KϚ}ڕ9_tҴ9i+3|\/a:[!6LLLLLLV v+ =Sb4DR]D::&mQIcoⳖ "0&C8aW=uP&L/rM~o',~*kꛒ&nύ˙>昼jy:Lgvkٹ?a`%ꗎ?>d8qbre)[*UGY 5?k̙I]=](U췢P-M0h&n;w3M.r₡L.[mlK'nC6zyHmy7z`eJj(~h0WTɑJ]#U<ԏx-bl炠'6c꜑x_ o1q _/(LLLLLLLLL`YK ݐ4C}yd51X31rŧMƟ:RLRm&`&` g O`a=€Ͼ.0qԶn}7 nT>!!w*|a> /wdD8nWuY?>F7%n}$"7=6<6O/I8zύonz!Ly  N#ߟo. |DƇT:bK xI+D= y.I)SɄJlPG?'!uCաM~M=/ #ͫSԤm :LLLLLLE?A4D4C4EV(U=ZG0|Ўߤ/ZI1$P.) 6 yj 1yb]4A\tI>~L:E B`֬YaĉaԩSNw͞#OoV[N bWx"b۶}\.mouOmۣ~ FN X7<{e͌`Zm/aN[65|a]睲ŷ&ЫG;ovvڬ(Jxqaڬgl:ιUa 'L9460'(z߿ʱ.o&`&`&`&`&`+…%mRZc"͑z4\D7O}(PV=)4LL"% /ӠU@NGnw¤isBP<+3E \u=wAdXxÙ ǚ@q@a$GtF\xQO\ď:&юI}b4ş~j#^ 600B`ذa;,5xp 7K>vJxiİۀu϶mZ?' lWK^pNabrțÅQH<)Rn7ܼ~}':sY>h;f⌰v;EARFWv 7`p^csn w>Lx%^J4qTM\'~#bY&|X5?'|*̊Ͽ|a&`&`&`&`&`g,bXIabkBLSEAj&]@cg)eL2>Đ&FK" <fΜFY6ÇZo%׵v(mc١7=>2zP;mfJtnai"-6>}(n/ V!xi],-((dU+m^e!+کku7[6gƕSx)y7+H>>^SwR&q[<6" yJT | ,X I0i x4B-Dv*iSRZL:jbSPM?|Iʚ\l.A_ D|Ҭ~RMV9">EGY\vj8t1(6D۶n:d˴=WYTY*d[ҍ{IQ!?8b_nxilx26mޛij?_ >2"1 +6OC f5(bWm%⌽;Gf&`&`&`&`&`w٥J !ztGRSJ$&%N")y{?k J$VƔ`y@5Ab'ԇ21UXLL`!о}pdž?L4) pK=l? aYVL"rmof%3RW"_ͶhͰV/IgtrQSQ/.p/?]$_m'<7b|)e;g~琭cb;Ug ktY-tƜpiDGZ"!,H>oW?D*ŗ /%&(TT]Pb5Ml |o\Cл[va$*(7l-g(7oWrh-~\I.?\fia@М1{~8r†>>'|oO$rM ʙl`c7Rnm "/UԾM~|ve+=g^tǫH7kyiQ4mLS0000&0mڴ~A'@6mBΝw/<:>y37>hFIi$~bN?tEIK;y4JYO@&j”;5 %lIQ0AMzZM600Oͯz?j͋sA+%Xt+iBwOO7ӽeRmjTDU ˄TH9i(cFI9")Y-!N&yhR1[R^ĘiD)3acLL[vG^Ϛ։r;LLLLL6֭bT=rvDi*4Di:iM*Zc JuDLJ 2v՞Oeʗ*f&`&PEGo_w200000&(!f(ݑ:Oi)]zcJVRIX&w΃΄4)/o̦I!*xH6000000000:#P\!*M0ߙ.P!e2FTQ~>j A#k2XLJbh̖&(R^xh`300000000:$`A3KJD+\Dz2/Ai ix>i)}04i?C]S?LLLLLLLLLEID̵A ySYGZ%>X^'1b`%UV)DU0 J_50@U_O{~J yh\D(P?VKh)! 3 2azB՟>䉉ƬLLLLLLLLLdJ+~_sGL?o`L"(y$⯲D<F`;U8500000000- b%CJC\#Al1x5CID B0+GC/D+:^ @X0>(P"iCwDC$v(; )m5<m600:&pᢰ >Mw'}0~ʬvY)K-2\>4VJ '@ Q١KJ\\UZ11DNT@R_O>g30Sg {{tέw>l[0rBχc4euZ{蛉/ G_p8伻LLLLLVv &C7 Qza#J{$XKAјI D&vơ&E;/- Km&`&`I֧퇛Y/̷;O?;\k&`&`&`&`&`+ Q Dvؒ#cUC *btQ'#N/_bh8mzF@ @=`-Ov\5lҫKx1aҴ9~7000000" >(P[4EK?*tGزX%f"f2I2~i@cjmʏ8Y D`֬YaĉaԩSNw͞#q`뇄[Ne1oztanZ]W+_Ȉpouv5K .p⾛7^3\vkŷ& Sg^=:o|v@yR7yVcꄩC|p|(n]7èqSCvm zDiNsj+{:!; LJXcQh _n% ^>k^|N]7 ۡw:gnxgyam_x?VpЮm]d֩}8Ρu]bn͟{ @"EsޘQ/2fH +3W3u25MF;a@NSDG~]l&`&`%[n%uQO]tQ3fL* r7eR`w/'d5pf#Jud0q$z3Džw |% ?I'x2ypxÑKm?]ύ?e[4?yfYi*\-gGqv B-QŖi>~?q~vInL[׻t40FgI#"L֗C:Mֽ=CFF;OWLJ s3/{,LLLLLL` H#A˪?=VVk(^ Ĥ4Q&Vv҆_/Fx<*S)LL>bÆ wygikv83KuMp!K&=XO |u~mKq)[{QVm:?/<=w|YӒ%nWv [ mZ~<*;_ C%N/ kv)Fua{^ wX5qu(vP1ރ1#?)3RY%HqS= ՛Yjv?{sq~jۓRYo&֩}e$Μ3?{a(@c[/t;K{oa&`&`&`&`&`KC@;bt@*J_TEH_L 8/>ZI^-Q< DH11F= ЮN>%ɐ*b* @`̙aȑeS>|xYgckw}޹]Joz|dXѺ]Jb([b#·007SmR 7'עȉ͘chR1t).cLlF+J?[ % ##2[ۯ:4=O~9W3[Y|fvᡴ}P\q{P\ڪmV=at21g)uMo+IܱOZ /ULw^<ҠޛL\60000Xj>s ᏮHȓǡ/;UecP.Y.d*cҠH)2&J=&P* ,16Q_mSn ||D~R)3禕_yB+7m՟*<оkw}p=QzIHxhq¥O?jukEG쬃,[IYˣ%Gr>X|jV[N80ku5g>tqgVg rlV5񈁫{U۴ l^~'D].∃:U600000XF#!?>T)O?L*U/qU1O YK *0hC^*KdIōْ?y JH}c 4iR@ =z\.Yg՜aYg6xDq6m-m[~vj߭_M.ZvqBUNMLLLLL9G:1YO#e مF^1i/a3D @kŧ&$TGP_k)z850Xo\CDVxseq9/݀ttNpEG.8♝whx3dnq$&݈KLXAn_;M"*i.w1I LSz8O5=9zpr Qd+<@;kWnc8u$! qU{ƛW&;Da1Gca'\.thx~6wbd'G.`+ob÷0PNMLLLLLXd MHG:JWH|̦ydQdPDN&CYyǪKl#>F"FLL><o{faܔYYɸO>"ߦb\Hy.}KBhuWOO1.Ez⾛KF!>^<65_fc|V]5$vQۆx5m,Q-5 5=R1ow.:.w]Ob% _u߰x!TsMOl?C}/O)ޔs oMBJReO=4 U.h(_ 4@QCDCD3YzXH/y1Gz|#&ytG7cvq &?UNH"'1  1G;W`zR~)n,J[;/MsEbfG]y&AI愃ϻ+Y˸iZkeVmNMLLLV iӦ< ݺu9Mh<ȪO hԓ#vv=bq!HV3:Ka ` JƗEcuj%K hLLLLLLLLL@6ȁ Y FiZY)Ѳ2̖f봗_Q@TU*qbf&`&`&`&`&`&`&`&`&P/Orc+,ɖU$4Gi]Jk 0äd(0J'_>~1k300000000'[kJ#TQˢ? ZcU>>*c HM%G׃O7KeťO+_ hhhZ8)TecFjU3!11$I%Kl^:)sLLLLLLLL@q4ECvKn(|F#m(QQ I󚌔\b1?)/M_mB4R @X hh%?$yQ Q.3ɠ$y|e'el٤G'ML/|V-*400000000:#hQ(smtCXGz-$H Jd%UV)DӠLeW})LJ>yE(f&`&`&`&`&`&`&`&`&PO[#r)ZC×[-AT&L&d$dbZʄ1ꑯV?}_Y @=v J/D?D'V(hC$#DP:H _ey &$vR)yG[LLLLLLLLL%VJDCDD'\V&cPT/!44QN=/>G|He%"pj&`&`&`&`&`&`&`&`&P[y.n@IDATJVJ/\vabD&/<(+&_9f&`&`&`&`&`&`&`&`&P?./RH# D/ɟ<0-eeM|^Omm&`&`&`&`&`&`&`&`&`uDe7B&)=1 6E\oD˱4%n"<fb^~8ye|xl&`&`&`&`&`&`&`&`&`M=2uoo-Ƙk`S_ !A1J s?!|C!Oܶ @@(1TtEx>!O S i^(O5*2Շv&@K^gҖMLLLLLLLLLA@4DZz 5X%n&)S,W *O\^blꧺB @=XT[\:bK DG̔ȉZ)"J$]yR4RxeƢLLLLLLLLL^?A4DtEC,¤^cr0Y^X^*-DN8h$~  I'H%%:!m[!˭1A.ɑC_O:Q|l300000000hsHCjQ2QOG,EI)/>c?qF m&`&`&`&`&`&`&`&`&`uDU$C!0 ycꚫ?jWƩ*9XX%RgAqdc6Hia+J ?LLLLLLLLL,X 2y.(RqbDf*ASI57EɨK2%/T 460x3fL馛L#뮻nzW ,Ç gFmT*;c&`&`&`&`++R%(P"(tHh|GK"LաU;]bډj %2 d`M5P̦ؤyU(LLV,Yfكnhw_iѧ~:L64Gꪫy桇 ٿZM)߶mгgϰzۗaĈeu*t)*6H4n=†nV_}}]"0jԨoA-5000-EhhhSM"~KS^HQ_ҿ1O0AIUJ_la  $'SOM"ԩS%;"~+ sy煽ګT3hL}wepi 6 U?/]J_?\{re㮹O x`ΊJ+/s3f( @ަ͒~*WʹD̪LLLL>3D sCD/7JFc6RH˸LR2VTc*T|!W\+S&ætO xg>s7/HMvۅLLLL>.~4A 1Ai+Q:%LiYe,P/16&pI&TMF H$GmSԅ6 GDm<رc˶oVa7Nm,mVˋ+M{m0LVuرAc_ץ6{-LeˁS1VG8®7+ʛoy\-ظl:thIE8C/Y-Vv:RSbv޽LLLL#lGZ"B l123C^'m天}00IPԠ< %4 fcLL`E`gbc~?p!6V&V 懘xk78sVٳg{/СC-%O0! Ν}ђ; 6 80J7R+N9ɓ'|0vaaM6 lkmm]VꦌowMy}7].]ꗔᘁ'3`۵k$>eʔ0s̪grqG,q\uYg_^z5y%M9n:}o7=~(&ѣC>VT 4FuTH* mr'SiER.ZBMѤ)?Fʉb*N00Cz)*~%W.fXjKNVY"պ}{I+-yV DH=cԢߟ0?.CB}뭷t#֡ZD}7\`SN9[om rLUW]Iaw7eSOc|Zk_Z@w@dcko}[AP;Rc6c {W^ye/|uQOq/_ ٿ.b"bg-]ٞlM8묳J{w‹/XG/Kz=!sG/sT%.䒰>$J;馛z0w_ \Ԕס~=ܳ_Jc_q}b60000hM!hJG4Di:iR$J 9D (I LTeR)=Kӧ/ULV,V}qOcp'?%믿jQD,4VFߟgyY;C03Y9طo߼X*[6۬$,9ljq`mݖ7MӧO+Oxr/_3Vp ImuF l/Bl[¤I`E]|Kg5c>NHq9\o s)u6TtOUb)h g ^xaָK=P+&g}@=h wh ݑ:Oi)} ]Q=58U3MB'N;R`)/o̦X9?.f&`&Pi[Y[+/AC+3ŸǬg[9+ec !|rMy]Ks1 ?\Ƒܹsi*lQB$b(niiE$lW)0@R|GU1w͍y]qI ev|T?B/¯՝\ePss@KS?h1JRv~u.cm֕ !B?+iu[[ּC9S8+m60000h QtBB|gtC@Yj6U_ꨏZgi^G^KAИ-MP<4}ɷ Hf&`uBqlF*ԪM qs=[vsYT/S"n햄\W6wIhCl{饗Ҋ>.Qmvʋ-YA?4.Gh)Si&<3.8E/?Ӷga;4}HV!2 +e++olWgI>}>缤>mT= n3000d~D/0*ݐz:&)Q'J{$E$>+v̖"(T+C iP҇6/Ǒrf&`&P*WjoZªM+QUKYBmۮ*?Y\ A8_]8[|'feVRe9\zՕ^d_~&Kj+Vgy#Z=͙6|6(/X;ҭ ELMjK2]b6{@NMLLLL d=%fJC$^z":DM#JN$mcoⳖ "0&C8aW=u%)r*;50۷o[)ò%*eKT_.L$x≥ʜaR7s[xnj^L۲sD69n`-L}t9+bwy$G?C.>|x@T=/cwk R̟!>}~̍-_}ރ7k_|Gk=*Yg$ڒ~g 7|sK{C /r@4E8\xG裏w;vl] 0 ɵ^[jG~y{wzO28sҥ^G7J6oLmryy-'n„ ޓX G]Nű 1K100X{r `&^_Wǡ F+d@G#yyHcQ=DR''(OJ#R&Ov|łh0@= I_!v"Iҗa[s16+D^z .$҆'Q-?2shI?lРAL'g]hf.:ʼ?7m㈭l;mҹB^62T Gַ<$̩qQq\Пg%!_H_~7l6 !AXrǎKb#b,:F v} ?я ={&ru'ezi'QTU !^{VtMesA2dH8~!7X"p {)[8ccV_࿋@F0zkVuG):K:r>s!fr"gZlDV4"ղZV_jS{X%AJfsNPO9眴>oGDELfhYXAտDf>&qU2V~_~J)sSj(fS}I_8V`}Mi>Oi1£>VE[?A8p(;_q X+nc1kKX+{bXKju&`&`&`&`&P@w^t@\[%Gi x٘΄HCªuKૼĪҊP|Q~<~=hqLVvU#J= e.ImKOc8ϓ|/)Hde.Y٘ U Ӥ1xVRݒ KVj"(#² ھ}c2b6?>hjl 8nf3z $]㻼S|Xj$ˣ-yuhQɣERσިQd̖KhI(@;xP"g&]b)}i+ȯLLLLLLLLLH#\^c.汥?6@טI\̓H$ a'O?U3}0y: /t@isP:!).G-J=*BJP fb^J+A2yȫ?LLLLLLLLL G#!eyhe)Bf53aPLR3\ŏz|dDG})s(6000000000&(Qb4EJsT}C^z"#"鋩Pj(U3Y%d! JC䵥7QUfb8%j|:ڙX+F̦~+/ʛq E-Qť#_-AzLI,bLڕW=/O)gQvj,LLLLLLLLL@QCDCDWD;^"L/.O1-7 嵋NҒJ䤞396L"^@/A;G<Y @XY0tBTQҶD2y=呏ĩL׋K8Lj6000000000z!P\!&(};:T6(t):]I!R?C=aWm[bf&`&`&`&`&`&`&`&`&PGZJ2$I s7vk#Ռ%X2)uƟWM6fS^6|FB_LLLLLLLL*.(}Pz(*Ff,As *<&uJlB MT) @.UiQtXU\@|Wfx7H#&~LS@)ZxK^1IUOll3,LLLLLLLLLcJ`Qaw8 :!BdA˪?JkYUrE8v $HH/zğ:^L/z- 2 LLLLLLLL@ Q\[5 U֩Tc ԸfqRj˗IS)5V%v|Q9Gr9Ql6000000000:"p!b2B 1AiKL6000000000:$0>d24A= Uɘmh8T3Kdry0&1MSꋏQ/RY* y^ա !.O c!vJ$e!DM"Zi|R`300000000#pa. &}P^V 0E҆HJ=%QFw$Gywg300000000# &C7 Qza#.H<^X M DQ3}5)yxiQ\r o300000000[Rb-AT*,,AK"f!M-u-V_ĐʋӦgXg300000000#EA-}Pڣ$A-9H>MS#ˌjP535!U&RV"x75LLLLLLLLL. 5 Js$ITzc&_1RkMO)W5ft@vǏGzS.9 Q1_%H*_oM4\j'_  @}(nWŶhͰՆ=؉3Ón~tL8cPЮM;7KۇwD1>O.>u{xrIdpAgjN%Z&ʮe`eý oյlZi< (( f}>s8qy^^ӳ|<];n5rWpKޱ6Kd&`&`&`&`&`D*O1r%!Rz$Qո|9R5#5DESP 21Ė}ztëLLC#7Kz]fqrHNqs´+'Qig?8qV{iԳKglSw&`&`&`&`&`&`h|CS\VG/H/uRvI1H]f֚I#"1-B Ӈ)A$C?ĠdIM @_!dɒ7o^,\0 ǏqjWx}liI<ᕫFqY3F.];p1Y]W iI 'Kb&C$^sgN"~jQrħ3yĽ,bl7vD5cr>玴x^[Y?>$~C5wO/\cSN;r9svc̨!qC1.ò "+5O:a+IД3!uͥ͡*?/ `#HIeZ ^YHI0N[ԉI q1n30cM{lxqiŜ9sz!K/}&ơi%;E1ʁEkk`M=|MM-Z_pw?h\/]13vi|׶w9-=혍s?}z՘W]uU=:N:j_W+3{6=/^qyn q֥GIY-3ο[JCsQ/X~4DA&8&))iS'ں ЏjJ0 @!x5kVM>3f?B^مƟy0{N_y㹮xͬ,DaAg=0 [G19pP׍5^6qٝ~I>x "sįƦyGk.eݙ7r MB0;7Np͍;k\500000uN` IY+( /a_9Ư]?lKd~5NO6f(I0$t2OA(J~bk1Pf&`}ɓcҤI1sjZSLֻZyv,:#<&/o1ggǣvd'aƱ/.=~M/\xoͨ3=?xP]/Sk 63Rɯ(Y-d~Ln+u@X9iLLLLLz@q ;R Je)p' qDHJ9^|+õݚ ,ʄ,V`e@ 1h1͡ML|U16a30;w\\r%1@ =#_nx(axS:#5L+xڽF&`&`&`&`&`&[*; №F5z9&M?ƥ)үyFė *rqZ-@vʇէ6*˅ ?2NŸ&`&Ї~SC;'Pazw޺C}ic7}N|t!tHѩ=(N9TN#@c߆˿%t4>|XЂ8R,t^wr%;\qfwO< zcr=MLLLLLT!nX#zt?Ж0ٗ6F)SZ&%V+w97>P1ZE%rRMO8=PJ2Um&`&`BsgC?_,m*͏dq/^ؤƩ͌RMz^ڵw׻Q%JI~yj#n g+;d=2R۠~=7HvE<6N|rti&`&`&`&`&`ʕ/V_tX%JOD7DSHɥqJb--3uUS5b* 9$i+ßy|V O}S^ܹj30Mo :nт^aIMVƢcdzwhg'yebaz?t|{cr=MLLL^-zq& Ç?(}$]| Z.DLE a?)1J|g"e:%uj Q)HaU´;CS5/LI, *a呆l&`&`&n ltSwm`:~}+dx{[&`&`&`&`&`&[ڐkܑUHY9hc̗FI"iLJ\$#JcZq_ԍ)000000000!п?`6iݡĵ#ޘPs)g$P,ԙGIe ӅO975m|ULLLLLLLLZ@hh肔+5Dik?3QT"QHqI$IЇ|S51ylKGo300000000h1y2p])kh6DN"hbe_(cRj)1ZH%J妙K{w& 600000000hA+V f}=PG?i{?E XZÌ$hU-TM%"I3MSn,wLLLLLLLLLZ%AR-ݐ8#~J #NS)!ԉO6jT.Dô(u0Ij.m0Vm&`&`&`&`&`&`&`&`&`DȼtD4BSDK~vo)|5D%j"ZJ$aIi;+ c%_*|P'&6000000000V"P<&(PZCz?e2zCldA%1%b1HuWIS鲙 6lh+%z!|iphdꗐIre0&yL"v_sQ;!$DN`6(K/JDʾ4l30hEϿg\pO\{3K-n:|pxCew<_qkvϞm9"~CE-HG2n8s0iN@?MLZO.O,nٰ,Lnးs8·/?~hd07?ydAZg~|OS.wSֹ+t֥7cû4N&`&`&`&`&`&` Tv JEO(%2g^#8X2SR +ܑ>HLq4O|)!Ol m+K)8rx'FJm!Im{cܦCsXw~kfŻ z<[u 6w  TbY; N KO2JbW=.87ru0YYR"4ؠtQʏ_LL%Kļyb…1lذ?~|ӻ91뉅qWlan֡;&Gn24MOpI}/]ۍ}<~T{4a:^qizW>]C7Ex1dP\;9w޸{ʙy|ۮqν3NxN1qgbFJ\}8c b +qF{8j^I`IdHxcm300000>BMQ&%vR_.ƹuR_M?#~;ˏӤ hPIj6I$mO%DG~rH.600icO<1N;3]$NN3{6O}lwoۊUKgĜy;"{NB@! Gi )I)Q Qc +='nL KTӶ O~5*F'tR+/n[$n}Gϸ:O/\cMMLLLLLE$(/,|Q_VE4D}W?#ħ\LjGB\B0E2,i1 q_}ğ&`&`}㎋<0vyxGqDyPd<%:|y?SnoX  ?"eɂĔ-s@I>Gv|1|߀՗+|˳ )~xpe8g)h>h%qq~ |X̯ɘWy<]kb-''ҳC+o7HoI[no9xR>9f?}lֿqSt')ۛョ_98s$STʞ+-zNw*oڼWCyLzw8'mRyLLLLL^Lڵ?4AC4F 6F)=Q#}.jU^cQdQ)1HL6/C1V/Vf&`&BvjdS; Oe1GtX选#v1v 4DSQ|yNI84t{v[Dq ??|;}F#w8y(mJkk;s{Dy(}}a&`&`&`&`&`/&>nf(CaRj^vI1Ǘ97S"RN-&?'$,ʋQs~J.6usMU m~{N%ݳņݖ}E࢑ݡ{o%/=Nt^+VC:/brAk3000x)XhK9}n5Fx<]h\"ȮP hS#q|\|.y$2NR[RRM?$LL@žI4dX߳9B4{NjO600000XW*;DJcDDؔx.=Q׫MfDS|: j~DHTZHe G</=h300000000hEe.FJtR+LSi(92HF%Ii2um܈Mܯd %}UJ(l&`&`&`&`&`&`&`&`&zVVuAh\h F$jGcF4(.`'2)PeB(s$[}R"a300000000$Pq !ڣG鄩#1?ldlЉIb (%WBX|+&n4l300000000xXt8 :!BdAk?JkPr,E̴vc : K?TүG3$ @C-MGBtĵuB6 Y]cZ%i6,2/>\jn. 8Mf&`&`&`&`&`&`&`&`&BVDB̆V!:!>(#qQ'N"z ^5UүJ1A?屁rM- @090!%0JԆ+-k:SCl5O#jǧO¤ݠ%2Wub뤦LLLLLLLLLUe Wdk?_HŮ8%"zrL;<%2G")Ǘ: 5/Um&`&`&`&`&`&`&`&`&`DfMi0*=qmG!Ck,JRgMgjTʲMy$^Ơ.|ʹYm+.sP%LLLLLLLLLT!&`f );\1hnd)1iW'uL-]bsS'TͱxL^1QLLLLLLLLLTvJSD?t Җhc]'꣙ 41e]H%P)iRLLLLLLLLZYDsD+D.)$T0# ,$s;;i7Ukfc$_&L7}6)Ti&`&`&`&`&`&`&`&`-F`ժ, "P mܱUڌIiBKQy$JRkRӢ$Ü2v[J궙 #9L-Qڽ?Lh)!IFB&%$1W|TCjnLLLLLLLLLZ@t8: BCtBiN#MjGá_B&ɕH1%ة GJD.MLLLLLLLLZ@ynݡ !HliZk,;%xR(A&Q~Օ|4_1tCB?>Ç*% 600000000h=+W`fQZ$ch3\JmtGJ|T`rl CbJHqXLLLLLLLLLZ@hzwhoR],;Pl&"bbũ> I}U<%"?|O1g300000000h!+VfC7!Qza#vG$bc//΄HhOңZDQs\ܴ(Ю:P Qt@PiRb3AT*,,A"f{5 Z>:O|C"(7ΘUR\SLLLLLLLLLTQ@Aiْ;4E_c 42M ,WxDG2qo @K@kDH6#T;jKcטvnh,Ș8sHlςe6'mR5ˁ96000000000! GzQ#eL$P)*A#fb&ôuƨLLLLLLLLLZ@yPR/NHQ3Klj{jkɈO$LbJP7<%N\e. @k@,5F4E[ĝcKxLHdQLJ|)G?>2Ob(\+㩰 0@iC)JWTY1Ő\LjGB\B0E,᫹NQJU鷙 s.( ޸^G-V4%pJe*}*X+Fyk_Ub300000000hڵ?D=-.q]5DG̔ȉZ)dC`R4)yZO%c\LLLLLLLLLUT?A4DtECl¤_bo)lI]-v/R"'$NrcDR݀nq.5x6000000000V#r%`6DD'd:DYL.rG2qh3GuuN5ҰLLLLLLLLLUTv J_!խjLzbOGmdFFF?%bJO%))S+5\|Ǹ9 jx+@LLLLLLLLLZ@~YCz#%arTSZ`#ca $Sׅ dS5(akC2 @XBZerJ/\Q6ĈHȔ*AS)'EMr3%/>T 460xi3gNr-;cկ~ϋlqqřg-YxwC9ύCϨy2ގK8TJ) hDt'Vù!`VT'H(1kTs)5G7/)а _K,kNj>]]$>*˅ ?2NŸ&`& </~+~78.\b""藾x衇7n\|ӟ޻rޥѣc޼y,ׯ[;[_rX1GЇ5y{ƨQaf&`&`&`&`/ew9AD/\.zFS5(EJZ&%V+w97>P1ZE%rRMO8=[XUgLU KV[m~򗿌G}"mv~|9Kc?>wG5]z|9jŋiC g?xm}8묳bJ?T|;:uϨ;sύo|zLLLL4 ?脔ȗ54Ei\d2SWu 5L2*%e21䣤RWux_f&`&b6lXu'c=V({o}N vF~7ƩZ)SG?po{. a{g5sWwmn S8ppvV_|q >?'mY /z*|q]wԩSkgX/pMyyLLLLJxPbC !CLz]tF|kRd47)5ʂZ]܀X:uJD*%\̈́4d30X؉E뮫D_WQGxt^ >ڂݘ<]KĎ|;%xh}>*o|AV<6]C |GVmٲe9رcq̘1/)2 |vi[ɇ?;hw[򖦏W ~x.:thVߡn-bg0000&?YԇGJhh\8Ye~FiX3!R¦LRRaqceb>QULa&`&~p'C>E8|qh&qщy\]tQ~]+f쵯}m11ӹM.?5IJⓟd'g? o{ۢ?v?;,} )xG~,u|I'wW\qElM?S׾+{U;̱53|{o}[|UwI>;w7 p*^x_{oV1Dw,yj?;<@4-P00000^ PL! #;OL4ItFWɘUU\6D () LRm)O]\VX|8l&`&~ s;0ՙ!|JL! 1}ݢ?kBC1C:3i!"~fvWwD"#tMo~3&LNxUot̙5>`~W(7e_p" /#x֯A m)b-Gxm@i3}?yX7bm}̲MlO?s2!2f*񂃬mvgwruf8vPg=f&`&`&`&`kKMf(ݑ>YtGW~HJ,ȔD9 8 ia%7ocLZbk)6rgv~9^A K.y&@Q(7ÉtrB }*- Aw]/vq%z/ݖ~k|J1vyخy5 db*F.T5{jWﯻYL\zag;y '/|_.RǍ{l+tgI"lgB%Oݖ~qXS^ؕ#6?2g2i890I?'g>a\잖?wψ;:wCPw @/AJ)n4}i! K+^k5z;O>dMgϮyTm߸뮻Vʝ}JqrWgo6S.@ۈ.7eg/#!rFUVT{Mn4 !wryŠX>umɼL-Z_W{+\^+:)ylnSv Kg1OYoj}&`&`&`&`&Л 2WR)͑1 odRO%f2&1qFhHs^̧T2Vm&`&`Fs7X7J G=>L(%שּׁ[K!yzfCم(ѷu zD_pXzeܮ2agYGblMĥտN+w G1BDJՖHc"9/9ڭ LbL9F$nf6Db* JuKU @+pk%̙3X4D0aBU-w{2VoE?Y}=yn]kTxh_ZVϰ>a,wT ԍihg]#vەͦzkw"00000^ P|7B/0*ݐ~:&)Q'J{D$>+v֚"MJ %UgQL.tCTGbkI600#Px5{W"< Aգ .믿>PԈ\xOc{2D<>]OK$87N_=|ĉUA]U=&#_җxK2&!CԸ4ڽZPiߡFg&`&`&`&`EWU =Sb4DJ'#j}?'m1ҷ "0 G[#~O||ڔ2ݜ.ML L8.٭y)pދA3<3LPD-F`sOޡzmb>IO8~ƌ%bȘIr|i{q~'>Xݙ;25eKjb1ccꪫrW\qEuQ{svɸ}sϫ+>lM[}7pC:.W=;;3#?o ߡ LLLLz@w:AV.ǚ@ld2KŒMJV7|nŷ8m5gĉ߽H @#[ ώ.]<2@PVDa<_:F˖-inz n\_~yuYymS;3_${oc!q7B L_D{G?яywr/߹sX{馛.,GgG,a|ϡrKw-ψ5ƎڭG|-b%O{}țwq8 Ox`cݙ9,K ue؋[ ;d'\K?'#"ܣANȂ*xgǻ;Ü5fc}mYGJ#3b:rh=̢_\-{TB>癝ͿCĶ -K̜ t4kAK=Q#e@*S%><^S#u.MvȩvY*)&i"X'r<5 .uƹH 'QLL^<,<:W!}_޺f1ؕʎeub1Ox;#Gⳟlrc;;wi\7|&vm75s?Dbce5Rq:kذ$(5]]~z@μ2/ڝqüQ|dkQɟ~sLL0Cރ B(o?oo7|g|#QWl3~>tɇ>-cQͿCij @o+: e-ҷ.Gin3c(Cj}Mૺ椮P|9Qy<~[:uUO͝60nK<;v"Pv1rm aw;q:If7g@ټ[Q|5AOcih]lfkf(ϛf+;kg?[lxѣf~{P5g&`&`&  9}:0Õa6Lh!bYчȅ/L}U+rr@Ę@?D$aSJ4+H1%Kɘ|Cm,& 60 /40000 `A%~DDMMtFtAJhEυިQdVKh(8sP"gfc\b)s]*J_|,b300000000h!{K,E2:# V ed!Lԙ qaFlӧ16000000000"Pb PR/NHɦK3Q3K^lR5E~'DžIS ꦴT1h3sLLLLLLLLLZzd1򔹬XꏊYS"d62& b-UK?!zCPg潢LLLLLLLLLZz4F+,Śu菘bHd.V@R_ .!Ss"\R|U4l300000000'! ]P"-qZ7!hJU'AՉU* RWT޿f&`&`&`&`&`&`&`&`& VkhzZ\:;k&ҏ)R")"Ji\usSJƸV/VMM @hhh لISZ0YۻZ_*-DNI00\jk Um&`&`&`&`&`&`&`&`&`F`Jdl #҉FNXo)du&8eu]咏ef%ka @EDșC[՘Ğڔz~&JĔ`IJRRWjЏqsVjT ~ ɇFJtR+ҵNCɑF,IJ ?Uɦj׍Q2Ø>߇*e 000000000V#bʬ JD#B#^64Jm&KGhZ|bh>PHD%b!~HLLLLLLLLL *qw4F(Q:alHLīR,$2Q?PJv.RWLJ۠i400000000xXt8 :!BdAk?JkPr,E̴vc $HH7~ğ>nL7~ ?2!LLLLLLLLZ@hm=#(SzipuR/IӖ)5ueq|RGusi8Ql6000000000"r%b6B5 1Ai=KL:q:cꗨ~%^P〈E5q- kjLLLLLLLLLZȁ ׅ(Qڤ6t\hYәRf>vxaT;>%x&Um-A\'5m&`&`&`&`&`&`&`&`&`B-ˍnro$[[E*vYT/aѓd)a9AOI?1|4yj300000000h%W7kJ#TQk? ZcU?>k:SRṃ$2uuSMj[qχ*f&`&`&`&`&`&`&`&`GQD4AK4CmNH)Q)\G#p#ӮOPHL:cRn)?jcwf&`&`&`&`&`&`&`&`GCT"!OKn(|5F( MPbDO4DAtµ/m2U;L2I F$)QN]k.>G|(eU"pi&`&`&`&`&`&`&`&`&*s3^( 7GbKLZcF)D B0,Z>T)a @X>(P7 "CwDCqRh;R:e+',.e[P`OӜTuWBu%_bf&`&`&`&`&`&`&`&`&B*E;ԻC{KJRfفb3AC.NIXH)x4? @ X3! i K;#qxya}t&D@7%~*"@%juEvՁLLLLLLLLLuTv"J;\4 Rad X1۫YVJWy˗Aqtl&`&`&`&`&`&`&`&`&`-D"J{$͖1Ĥ)RgNWGŔciTgaDML%ĸԵSW :ɏ-}?LLLLLLLLLZZ#ڠ4Gꈝ)7jU[#ȸƴvCcFO EĔDfcd.qұ~< ++YOE<_f< NLLLLL>(PJ"tWd3!J62ۿ+IR':k>JXK1基l&`&` -θ'g[t8`#v 5 +Vے9yhP_uqwke1{Ɵy0.󱘺Ǹؿ.9gcȆbܦCsOj ? |PhuGJ8ƴRb2K]"J@(TĨ+TuŠM ںRf&`&*|rQs}0 74)618λ[E)泓]i>\ývmдsྟ| SGQàLLLLL^* K> _\c闰JLWoZk 2 D`LChMb>O JJԴ K,y… cذa1~nvsb űخ=ݬ{<q}qݏS. N݇T}s#S=be;Nϸ`+ʫo>,q߃W400000E`eS1iՎԏHMR\OY~)- `##0HLR2W6 ) ik:1)Hq @#0mڴ8cON;-̙ 񰩻¯nq2~xz'zm30k{H}82 o=xREr`V}$!vkf1atY[<`q|m9~w@ft @ P!*=- |k*4FSxYc,XJB 3GIJȉT rseN/l&`&`}ϯtUWѣ㤓NuƵ۱vQ"tc_6!I9~;nQ"IG=sq̧Ml8" /ƹ%p~Ð&8~ƍl^ӇS+IJ LŋǬYj1cFM+6ky$3{yeI~;icAv[]UBbh\}kCW6xȠwn?vD,M;MW.v +z<zyύ-R~#NT XMz".4B(=b' 42MO)t2.g!2&}ܱDT)i/H}600ɓcҤI5YL2ݕv-t=-zIg=/uv|qGzߺ$K@/}2>&`&`&`&`&`&'*'ͣ#JN0)uU$Gbe#CTLVrS>ĠSǏG)а ;..䒘?~ qN^yy:t}vKzѴ̱? OĴ T])aS[bU謢ga&ݚؐ K:fԐǻMʵ|;u>д$LLLLLLD`Uw#-Q[FC<}GqUFά\\` VuHP}Jƹ?ML?=8u6hP@3>w-88 ޫy:<˧[(}g9m]yLẄ-G 'nڙ{ñ8j&zΉٗxWN_]Y4=Y;loح/haEπ$4?x-ް{wCsԷ<)ξzVzYeǪƵK\~&T: {ZdY<$,?6ɇ\700000>I`JY[TQ%鋝6[F7DSH%Q:2KU0^c,ȴ UXH}!!-H[qCT O}S^GSf&`}gs{$eYm3x} `%c$L64yXNGldKHk"=,F=>87 ,j}߿"^=?n4000-Z7sV&M Ç=.qq`"&ڢQJ0%3]J2N:~5$j2I 7@%AT SJ0\HC600V#0"ޙeTC:*wuv΍;_vJkIGθ*^,N{Y|ksMLLLLL`hkC":sGC_T]#e)zJD_Kcm(iZ$kMI&ur)T*+1?c̯O6 +O]60008r,ƇKuz*^ `300000bt=Pi SW6i$%t2XΡΘU75W[3A 0J@Z@mJjkڪ3ULL:\600000xCP#%} /ָԕIՇ&-R@ 42%QN.3NBZ6FM`Ƈ>䛪9)Xl'G0b300000000h)'ӥJ+-_(?/OG3AT#hbeu%EIRCS)iRLLLLLLLLZYDSD?D.)G)jkȼMS)y%^&-R4e Y @kXM@YjhVVAb:c*%"8jTm7jTrT.DL2-L(K:>)cUn @+<2/Ô(ݛc _kQ2Id$dRbJsuW |P'&6000000000V"P<&(PZCz?e2zCldA%1%b1HuWIS鲙 6lh+%z!|iphdꗐIre0&yL"v_sQP1˸ԧ9U40h5C6Gmb ;z8[ߕ3ʻ_|z]׋ @WTv ;'J#Sk?SQ&cJca#}hJRC*.7AWiB곙 f;¯o3.'o>,جqpy.LLLLL7 >!JI[G4G%1G{,L2NG9LJ)0m-DhӏiA颔7+T %K,y… cذa1~^MoŷMwe <\nȂxanxk&c?:[ qg+ݛ?('&3q͈y~=x{ݿgF51>ڽ&zuo/W tBoRH[!1)MTaEDOډǥrV)LLx≸k*;bqiU6VfpLZ9g,l.]_:a8$z:np*K?9=޸~]OՑMq!2'N+(C1Sg-+W %1у776aX*{Sx5Oy]y5o٭S-tF$TuĨj]zw<8 ǣZ&HcIZBY7ncLgxoQMD{o^yLLLLL:#LMP"(zʤS6kQfQ/HJ$ՅO+[ 3)F0gPb5YiÇH&bLL3XtiL>j*SL*o˂S˫-ÿ[5i_I|&;o=(%:/=j RVtxW}6bURY)wyNE(CĊ%=KZ՜Cb(mۥ՜ؒiE)[1_s^H+Cm7t<[LLLLLL`3#^X*#}aC3F?i8IuڨNJcjʯsgiP \^y ,16Q_UT L0!ƏӦMLeĉ<:c~~$|-JJDKVR^ò YY|GzAU<" 8jaҫ,j~yf:}lkKy\Kͬ?VW%ݷbJ+E?҆%!/P&`&`&`&`&`&y JcKQ@%JoQÝWO=U=ArVp&2yL 4AOf&`& +N<ĸcz1lԙr09@ɞV.ZzdyMJ1cVp6yVLqQU3&yaԖq@+Sr,\2 Vtoc:c}@gj}Zӳ.?~/G^aIS_խ-Q{ trC1|AKA:)K$6I_ZY{hCqj ŏSפ)TGTg600NE`ҤI1lnZkϡNB&hbΞ;OSg^>/&x9J+B|ǞqԞ[e? /3f/R= 7<|ZI- e쿦MPr '3uWeqdKox2TrO)XOd jqpФ뿿?H7=|%o3$d!Ǵ ǤV"@nqڱf_wG?\b2cVK|x.Ym!O/N?ٍ4ֵXN0ՓcĠ>1{$< F$P WRM#uJ_Jy񥎘FTSS &?DNz2*/>D)c%Is>{vT @%CuڹujhtpwuG>8{1v㐥W{W.7ny䅸7WS&vcc3gN_L}LLLL:36AL!f 퐋bYW ',v|S_|A.ڋR&Gg)x3LAI͒Ak`|cSfEUOJ< T@Ni__f] qb;2vѿeڒ&`&`&`&`&`@y@ih/KOD0%JwlO%DLMчRo _Ő//}hS\f&`&y l]!j}{GLLLLL:-ahI /JC$EC FJL=ATA-52 e&E;)eZPէχ*f&`&I v3LLLLLLhlD̆VH QO39RO:#yiQکS\t6mNMa@>DQd1%҇\S,&}R"a300000000.Hpw^eh )xUV/ 5c#b,nKTǺ;R_MLLLLLLLL6SKO  J+D^FR)[m4h"?`Q ^?醔R|dov @W"PC--rh腈?JT*1n6564 |4e R\SUNi.E͑'bS t!MMH U@+D'H\4H6'-v R5b \(ܦCZ>6600000000r@rSE1TF-J,T嬔LSaT+>%xJ VRJ_CqT t Yn,ꆫK /QZbWQKEbR2\KDPR/y eW t%ȁ٤B¨ 5ZcQT}N%|VUJZ,)/ .|}SRV\P%LLLLLLLLL(!&`f I;*1kmn˴Sb&>1&U1)RwMQ|S61yl` @"?0VGZ} ̩c8 @ WJSD?t 毓mQm?JLa;58bEVmW%]k\4ӯ+D t1͘uk}KgUS^X}z!}+_s'kJ3&`&`&`&`&`&`Y_Q @ʤ$+[L1RO?-LS#1铥R=AT` HZ\IY)[Y`MW>mXSK4j&`]}㸃cϾ?rY0S q{bR=?+'jkb`:gLLLLLLsVΒ BfQ *SB?,z=)%VGK5=Ae%8ueP\.&IQ'EM2 tOe\rY}GT7)^ZUϾc8=n,|LLLLLLʏKz`tF*Kkm㋞HQTUQ÷tU2#U&qZHGk t&˖-yŢE_~1f̘>Wx"|xe1bpxہıde=vYx٪h|t^qmS+7<83 -'wᘽ`YL~h1w9 āi*Vb3ON1*`s!QvQ׶[ @ JInH=uMRGIOHH |hW씭6 յ"MR,3)Ӡĥ^m!n b600NF+;/CSO=5FQg_ߝowLyĖ19;$`(x;Įc3_FÓx4~͂9sAm_ECv1C׻Ò:(6000000'P')1S")Չ:6IXѷT=AT D`&HeM2FqzKS&Tvj&`&&O5\SwÇN;R9O0{FD8׳JsQN*\;6x;d?^X ~~kتƦS cn>w۲:nx\000000#P~dΈhhR?/*EO-Q#)q;RF*kO:i 5W^21n6m:&`&`ҥKcU32eJUyC S ?wU2VpGD:?.ߒwolGNcm7r@,YQ?y阡;$f&`&`&`&`&`&a`*P /b(#Ah/2#)>ʓMJः:*N]K`+>+!ILL0aB?>MVĉ+U5O?vQW;xˆGf_|^wsqɧS8/3q %=o3000000=D?%beԡbK1I}'3-^$hJ4| /&*_ڥP]j tzO<1nƘ?~ s1uj<~}b @@y(ڠPDAq(>E??bk+v,ӤH1֤rEzab~JKJ uk{R4 fA/koiQI I+Bw.Dܜ'+YC?oaєDMM0[UwEC !GgSxŒb^:19~4#(u :( |YC!J|i:QJL~/qRu)gZ;'YO ȓ!z!-KJE9C\BW}#bf tAiܶWD`-y31M 5)Lv** WTWOLLLLLLLLL%-QOKGc+ Kdr eP)1+z`xJiZ]X5m&`&`&`&`&`&`&`&`&`]@YCDCDWD;^"L/nL16 յb'JLc?qF& 6000000000.D&ːHo$E7,j1u?tӦHc[,:O^~ &=mOFRLLLLLLLLFiY>FȅF(pCGi&Qu](Z0EPƏUŘ!G"* 4PDf&`V6Ēzx-]S^X\N1=[|^>Ű/4~߳1{ꆵ4qLLLLLLZ(D#'ͣ#JN (_$f_+2LMΚ򤈝&TGL(LL.+<w>b̜8{~Ԁxk*Wqɿ^waYd|pܘ0fPu篌FK>uX+hO_87ڪ </wL{\ڬJ[7eq}FM 600000xu(/XA+KZ"Z!ƷC6i.Mzӗ-R|W)oe,[5=5:.T)\XSqW00.D_1OV1t@J'^o\~ctUIDٽqj~է#tTV*}$cIX}??s.Mک200000W@yQ@tâ?pU[P0ٗ2F*SZ&)2Vs.xCQI *n_ rXg$&ILL X_ݽY E<V) ̫F_|K+CWj^I\f]Ci>?=7mg LLLLL6 &Y+TK脤'MQ#)I髲TULWY{(T8Y|i㢍ѤRUxj_e30L-[͋EE~b̘1<z>mT[ЧGabsKo|*M{yşxC3sV\YGեEVۨ@tOn>pTrYIzn\Z/U>*N>fBt_ŵ6Qi?RHq7?B1cv}=qX+镏ħ=|xdQW?]{z cՋX<>[m/KB<`&`&`&`&`&`@]]E 4ŕ!" !]E8Er\(HIVԠ IbnBJĸ*DRf&`&I\qqyr!qꩧѣiv: $dv/YU­Ҋ{%qql;' _ 4@_MU}({~( X/_ZOƱ=2~IĜɓ_?ʯBCY|-gF|OG^7!/}<$~tߛGM7}iQQV],& #3\|ѿw ʼn?IB_HB8{m8纸g Ⱥx8;]ޙzY_^DmXNMLLLLL3E&͐ GNL4ItFWJUU'HF)J%UTzj/^eSCa30C`ҥ1} M2ܑ[Dq.BccG˒M+ANբissޭ:K"ē3GԋWʙץyYCJ}y񊼺}]ۊaLű9~Q)sҞ15.J s!qSZӷ @g$PCP#)u /RV{KI$U=ATcHM1 ݬI[` t&LǴi*8qb%N[ʮ\{c?{TO߸X× ٖڢ%+T7;&kmr|-}H9hyP+6w:җ^^5qYdKa-VVurLLLLL:! Q4D Eێ)O?򷡊h*RO'!5eя:ʶL10FZ#&%Q[I,>B4Q @'"ЫW8w9N:8cytps"5g?)Ee|V[Ca1b?SV"JvNK#E1ɁRԯW1OB bg9n˱89i]OT+G>/12ŵ @g%ИΉH+-.) P_c+14m0# |k$ɗQoVM~1ynBe`á600E`ҤI!wg\ Ό zCmՑlo#ݷOho˷:-qP90ct{λjrpX{~^~J{37N/>[| {I;|^禇ϧϙGUBɱ,ͣmZoyl gkK7ӳueCc|x.?B00000Hn!PA 8050}5QVljVV"}85O] ;_[wIioRl-z@нGs!{ũo5Щ @'%P~d^%Z!z"tF*Kkm HQTU7FS]!2}M2 &8,ˡ}vל9s_LL3xࡇ:6YlOly#<;=~QU搣{TVI۸>Y*9/%$.\؈V4խ1˩ @"P__ߵnw%PWW?$x U>ό2:6&W(N$jC?ȫV[{+DՁŀ-jc AK\|41 (7f&`C wy|c+/;bB eܵk=Ef&`&`&`&`&`iYkDD3x)zhDtDIz[iC[z40a@LOR!(E?Tvj&`&`H-cdzdf&`&`&`&`&`]@yn }Q))o -R$cTY{+D;%x2QL&kQO}%ڼq5600xk-c|ݵoLLLLLSI@IDATD( fQ0h&HI2菤(Oʊ @.Ͷ@C=qlγURkXLLLLLLLLLnݐ^M KZ15VgIIYT')IDN4xI |ڛGj l?R(#H*xy`I$VoJT =jA$JU;W6000000000CBTA؞ *te¦*H/_bHiӵZk t!eA-}Pڣ$A-قH>S#L<#j"fjBL^=Uv"٢on @$ֈ6(͑A򊑲J͸LLLLLLLLL%-QOKGc+| #fJDHdC`Ԯ)~O)m\ LLLLLLLLL(khh YIō?&aY^*-DN8h$ts~ t5MMȂ٤JG?ն&A.#\Q8q(GyuNcf @W!P^!&(}3:T6?jQ&2eQ"KtP&pJ}J1nßj#^Hy @"PSeH^7BTiSr-c` LJ' ?UM\# ѯ߇*e ~100000000FQZerJ/Qⶉ2uDђ,? :PHD%b!zHLLLLLLLLL (q<!:G鄩rGbJ$^b$[:"j$1UX|+&ꉍukwv lVGD'DV,(pCGi3۔"fEVH7zğ:nL7z5ϐb&`&`&`&`&`&`&`&`]@yQm=#n(S\ӜZeҔ%lJqMUY8_|T.Ԝ(6e @"Ԅ PCtB }P%&F8Llc𪗨zMCMEQTת覹LLLLLLLLLC̆f.)G &4rUeUe*H4ձSZ)0)*k5(e UqRf&`&`&`&`&`&`&`&`&U44d*,6T$4Gi]I%pV*KEbR20J'_>~)k300000000Jjk+5B¨  U>>*S HM%-ӏc_>žX)+.}j|hl&`&`&`&`&`&`&`&`&EKDDD3I鄤nµ6eZ)1ӪN[R榨O)cb6xQLLLLLLLLL(~ȓ=J+,_QH;?/wK{GĤ󚌔\b1?)7M_u^!(LLLLLLLLL hlz&#Z!ztIhEM<~QKQʯf$AA5I\ŕ囲UmL8i`̗ͧLLLLLLcɒ%bJ=zD߾}c;m0WYD,j2%?⧫?WbLHZU闭=Ae%8ueЖ/Ie"oqRфhk(CIY y󒸷E 2$rsi@X\_%fDfZ(?2/2iFɫ,2F?E MQ}㢽_%m%kOUP xdmk9N?L8#/em&`&`&`&`&`&`&`"з_\>ojpZME]24 O&aT')!:/MIOHH |hW씭6 յ͊MR,'@ J\|n*a"LLLLLLLjQţBRY5nMshh艈3!R/=Q(:iQ}K5-^qhT@LL,}%p2I]Aizts*;5000000[CCd1GR))o -R$cTY{+DQW0R&JIԤ^yM\>TcS>T 600000000r`^ QZ$mhN_ReGR|'meŎFf[ 8LL}R61mcf300000000B{@rcR[ꏔ>۬,O*%YDd\tQFДxe#%.ʚ* @W"؈ ݐ4CiEQ#)&8EVP XGծID@HH5&%Ԕ2AݘKV*LLLLLLL`Lyxoo:2.XfT.^8N֮W_uU߯c~MMMqۭƗXVL#:4IEP:!iGG|Z-ɥ4xsMu20#Д4q\*Კ l駟[n_]5:^?q."ҽ{_xQ=`|K_+yZf& G5FGVyʊm?1GŬJ22:3(/3y|00ꕧI M<}ԗrwڗ-[K.MY 0p`/y_pk?s5ϯ8E~ag}-vW㞴( >7_L@C)JWTZO郡?b!-KJE9C\BW}#bf &O&?z~׿]M<9>mo}K_Bghhh^S>zr,\0o7O=d|sWGYAڲQ9*hq^)L`%7$i܉WD`-21M 5I)Lv** WTW//f&`&`&`&`&`&`&al9$կ#<?v=hCO?-vm࢟797wc)_:ǘ3i=91 {cС1bĈ8?3rq]wK.u\~IbZ)~>0͞@y xJ#R(pc)t'R/1!L2b&ISF?6ՅUSf&`&`&`&`&`&`&ve;??#m٥Uf&`&`&`&`&`&`&oz2~Ƙ1c[NG,~3}Ayx饗ҁLTꋙyN7tTyq=wW E4gƎ;TuL`'P]P y. AT\2hϊJ->$`jRmUŘADTI`e\Of&`&`&`&`&`&`&k鐥Jz^!o}Yʩ7ScU>;?>8~}m֬Y1~q۽SL`} 4ofWK%,)ӦŕJc]q!V+Cl՜Ҧ˟&:RL)}7\ @th҆Pb<8>kҥl4hpKtCv_0M2-^@҇ǔ;0Ri.IՎOɹP*&&MY\"'y(OQe9,꫱: +Qw5꫇>VV5bKOD7D_"'yCeMQjQPj59'~1Pm&`&`&`&`&`&`&`&`&`]@]Ff] FtGԯUD˪B2Tբ431 "SFTF奎n38nώɏ=ޓNZ{Ĉ1qGfi6 N0끤Z 'ʩPQ#ڴ yO&% Oh'DP_>~)nN;ۏ;.w/k=m̼^{5Wrǽn%71rcۡyM>&LQGWoX)oHx=H?赁lȼLLLLLLLLA904BUH!Xָ6AvDK&$uwUc]|-/&=5n .(FU#e˖EiwpB3w6lX(~+>@ho{`\qEviŭ 0!S100008KwUtW9߾ tnm<KbhD76!iNM^gQƏDSMgmI0`@<Oj~<#$g#A(yg?D˗.&?= }lMI@|i~ZZ] /8[ߊ7QSS?>mZ;IXla8+_+7qkty΃ ^zo,}wzo[|ⓟ$q8 {G?QP^1Xm=CyˁmO>D"<2z83㼟,N|{|Q^O?t$HcO/1rW|m[An@Ilos6o~Zm30000h@}XFV)R @%SPE% J7LJ;"~(OF1kxY J @dhK8툾RM:xYcdGN$T~S ț#[lE,^8u$a'$_]8=IqvZ)WZ[Z)]Ky<% m$B謴BUƸ`Ziz;×DߝJcs| 2$͛kM=TIx{a]Y=:婧gHw;n㎸>XŘI{ޑU|Ш8qD<{#Ѱ1o 1gsݠGĜ̈W{{x1ccЁ#_Z%c3bQcQۻ6guj4ԯ|װ=L]/^5=j['n\ު456Ecb3"nH!1ŪJgLLLLL༌p;O QL:'SEMPztFA.jdH?tD Q}*ƕ:"j0CDT/7?Zr1m8T8;&c1",Ԟג c͇+΁ y&4=V0hyw< S^Gw!H59K- ,$Q[9xsD!fXK/jK;N+LςMcc˞ϧпS9{|JE 6 tF . tACDD0)}PbhfPZQcDK詾Tl&c *K>I$_}S6R_[SmMg;z=9Do\<*Mz?i^Iiդ*Nx׻ޙ{W;8==mo9s]]pyUImHPcv=c5eʔgsOqg5)hthvƩi5.{>0j:& e):oҪ%KĪ+/3Ŋx|^}Vx/Z':1Q?yn,Zĝ[mx隧c x{t'KjһHap w쎝&(NQ~B4gMLLLLLL6%IHP鏤#!)==Ue-xo$XPh(Nj zR^m3]m䩗ߘ8_z)UuvM!Zg{R/})hԞOGYXܻ՟̫"[V²ړ~1x">{vvԘ/Z^uGCf?R%^{o0m9&J?[xu:t#Ҿ>I'i)|*6Эp4=N?{0V=C?s@h]@8]K%]95M{ᦱ6?:11Jiy̌>1堦)*= KVkICӦ'(LLLLLL w6 Xh9( BBԅ f(Ae95evb~LUtm+Dr&ՍT$>CN_uT %<1E僣ֵWUkDmӚ⸭XVv=zl4&9e"_Cq> L_Ew~_r<LLLLLLLs(,D7RARH?EAΘ9itX) L2Q_"PbY1!5q;Xi3xq7m[mig\uO]InkKKM|~J5ċt"+a^!JP&`&`&`&`&` Qt% :  p`OD:Q>KCʔM*Wҵő`7 5ҢpJ?Mz!m&`P̪ҶUEkܢNiZ.x]c&`&`&`&`&`&`&Ic2^+e+b'u mzBLrbїE-3;|Ú(0A%rҏvk"R?Ԯ6V>ev\LLLLLLLLL`s%>X;)k1TVIik]J5IU_3 ͱT'ejUPVSLLLLLLLLL @#>H^z#uUX;Um1<9MD"&fR$v"K )^qr-ff~100000000.E &^nHz z"ZDBICԢԮ( 1镥RW[Tb&/e.tAz.ݬV^dA4 @"lٲE閊 wvS2BJ:-T ik&"\bH.S:( &h#bm-o300000000*V^ .|1ݏה(1PZH=JLي<Tm6S$4 S(O72ٔ:\l&`&`&`&`&`&`&`&`&`]3<3%tID;D#Ĥ?"J~}G&2|$jjLPyMTjH(n0eI ~E#jj6ro'&`&`&`&`&`&`&`&`&`Zs=""MQGi'1w^Gڥ\e2IW;ieK'Tm+f̘qkizRS000000000͜ٳ;߹=ƲtJwDo\UG#eEEk;H%)Q4DR&KmԑJ%u؂9s\*Q. KMMMq饗^j*N/jrTڣF4ǢO6Ŕ%r4YKy#N)}j7/OD[be={(  lC9Losι6mH $yE?H#mQGKy߮C{ i5>$Ӏ#!2*/SeԢG{I1m}wS4 D1n '4Bɭ BGJ$v$fL@:5i$RG΍uOeXǎ{СCOY8>@ ؘerLL?A)g300000MOm&9 hl̿g6fe~nϛ{uuuu9Ň*C_K0iL|o_Q{_῿Jŋ+ =o?20}7P hߟ2V{IKR/?_? |,_>]K,X´iӦ36tC|!f(F ÇdL(Ӈ<!i釷9>ԩ>e[t-5)I; Jb?>C; QGm}Ӆ@J>?uK]QxMf]*/>kΤ?epaJ@;S<1(ǧNcRUCW_=D;JSz??W&On>mYkR/iR"^T`"ߔg.uB4yڵʓHO118>(lŇvYP&SEdW4S_@DMV;Ю<}ɓ]qG>G?SYI1R=eLk ?|Ɵ?|b6,)U o q*2S>Go _ ?;_\6翿著IkUG?-(c*>}1cރ$j||d!?)uJv^NsfRAuCĕ?)ISCZ4|"O\ͅL-/EWWVO\Ť %៿~WHV{Ϙ;l5?1Oxǟ?O3xo3?7Xg_˿4G0VO*?-7KÇbT'ėn}뒞8/0mD n2I*('n8k,(?U徤\S|ʌ#+E6LJ~\jSQ4R4Gm|b1<~Ok^#&zOgz'5ǟO? 𹰦w.|)Ӧ/Ii^iі9yiJeB$%Yr޲%9w`ǹڤuMSm2a-E7Dx [פu!4N'8Xy Pk]_ު=_Hwz<Ƿsbmtѡv? ?`:şo[QUksO ݿDן".]lsM@r^߽sgO9[OUGgG漢??w\ڣ{'telxv挾_ ?X[}um՝s-Ѕ1{Igu>MC _8w8c 9Dɛ:}(#)/E[/T.zaÇhǘ|ǠEoz/n/oGWgwnP_ߗCyDWOdݿZݿy9?sc?z}c ɇk_%|4_&OL:Q`rZ|: b]ӿaș˘>cr.ځibrzӮ:+P_S5ʿ}U?ʧGʨoPrë_K/iq~ Z1__ç-iäL(B|yZ.ƅ o2ZlϿ w>/ϏC_ЁG;\b?JB]?GV%@M3Mrsp@./Ǿ\,9 Hc.s0FxuD:gD]\X2x^X<rM{tD }\S7T)ʿF:uFdYyp̀LOSkw1sͻy>̥J1.5Л}5k=x؆g1D]>a}ͭ1eWgʓ>XKA_ C?<檋 ;eu~M]?M on7FzޫΟqQ9ˎc(?Nz)T,ܕgOTxA8S뼿WϞyvǷB cq~ 뗙öj >.D,ER=Ϙ2o.z >'}|ApݎӺ-9zg?mj>/Ӈ9sw~7._of1?h?'? p/+ʿo[ݙG?_T~}] _9o2zC؅X:1^‘d .E!i/6h 0/0O]mkO=ZdirB׏c__oBԔ՟߻G/ce+ɣܳ}9+?~)ٹ9!g>oDO\c|x3 O|O ,ź0sA]KA.-ƴ s }txM}*s.g\p+\IDAT{/g>t~[qUޜkG{̡-ȿ%_`|ս`_z[31-4i @N|Zgm:ş⯙b?ơzghTW{GV 4F™R<`Ș:wZj|GهZ)˹HfP /{#%i%}Y 2}O?ؑCy:WCus3W)Q)V캪Ͻ'uQE'7_W:J=g2<̿cC{{#s;BC3DY/B e\(/.sY?6nϐю w1,ΗK3}Zսh6 ]1Wm2ÿ~7}w>}Пa캜}/i >ou% 1˜Aco[QE8kg5Zhtg~/{u$˘bߚxkz!9?}C9Y# }߬͢X|}.GW}^>zs k/kKzEBŽs->>5|iA#{q\)N/ڢbYUUV5~?z߼Y;ެO>~x᏾au/cşogǎXbE(<"6߳_Jǩ/NܨA8GϸhZ8K-o RquvcdAp?' ,bAdqJ/<s#W}i%m8c9̝ iu?MbG|*i5s/s.QUUVm,՟՟96 ZU O>s/λ_?j<۬?{AƟ/5[}ؒ_p=1+>- tOvyȵ?}ξrBNp_҇<7<_;rZgo(n S$T fYs;r+-4˿F 7_g̝}垯qO?pT~o}{dG?' ]ċ?]?}?.o8Ft>O{zV?w-3I-]|gIm47pi- -;XI¨{Ήqז=S-˿ꏽ_ AbBU,8¨.[LWׯcEYr#>{W7ch>OI2#^%؇O`?% ޷ogs|dW3p^1ʿ?󬻷g@xCC{W/M0xY+( b]΍e9s rd~ lη[[K߱c[˧ߩot?K) "ώ=OES)5O1fMD?ώx|;!?˚XoF3ι)7O~εU/A }1,/d #X|mu3G^ og.viKt_񇈸c18kDVOjd ɧ>DT ?17зr|=x|~ $g8GX<<}Bvl~j= 2?|]/cT6M:{N4?SKR= r(TY_voǓ_ݿub'q2/g^\~,w/* tPH0wa\M-ꏽ?ʿRߨ?qRYm@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|AE(IENDB`censys-censys-python-7a57a89/docs/advanced-usage.rst000066400000000000000000000007341524433232300225440ustar00rootroot00000000000000Advanced Usage ============== Proxies ------- If you need to use a proxy, you can configure resource indexes with the proxies argument: .. code:: python from censys.search import CensysHosts proxies = { "https": "http://10.10.1.10:1080", } c = CensysHosts(proxies=proxies) c.account() .. note:: HTTP proxies will be ignored in favor of HTTPS proxies. See Requests :ref:`requests:proxies` for more information on the format of proxies. censys-censys-python-7a57a89/docs/censys.asm.rst000066400000000000000000000003731524433232300217570ustar00rootroot00000000000000censys.asm package ================== .. automodule:: censys.asm :members: :undoc-members: :show-inheritance: censys.asm.api module --------------------- .. automodule:: censys.asm.api :members: :undoc-members: :show-inheritance: censys-censys-python-7a57a89/docs/censys.common.rst000066400000000000000000000020171524433232300224640ustar00rootroot00000000000000censys.common package ===================== .. automodule:: censys.common :members: :undoc-members: :show-inheritance: censys.common.base module ------------------------- .. automodule:: censys.common.base :members: :undoc-members: :show-inheritance: censys.common.config module --------------------------- .. automodule:: censys.common.config :members: :undoc-members: :show-inheritance: censys.common.types module -------------------------- .. automodule:: censys.common.types :members: :undoc-members: :show-inheritance: censys.common.utils module -------------------------- .. automodule:: censys.common.utils :members: :undoc-members: :show-inheritance: censys.common.exceptions module ------------------------------- .. automodule:: censys.common.exceptions :members: :undoc-members: :show-inheritance: censys.common.deprecation module -------------------------------- .. automodule:: censys.common.deprecation :members: :undoc-members: :show-inheritance: censys-censys-python-7a57a89/docs/censys.rst000066400000000000000000000001531524433232300211740ustar00rootroot00000000000000API Reference ============= .. toctree:: :maxdepth: 4 censys.asm censys.common censys.search censys-censys-python-7a57a89/docs/censys.search.rst000066400000000000000000000003261524433232300224420ustar00rootroot00000000000000censys.search package ===================== .. toctree:: :maxdepth: 4 censys.search.v1 censys.search.v2 .. automodule:: censys.search :members: :undoc-members: :show-inheritance: :noindex: censys-censys-python-7a57a89/docs/censys.search.v1.rst000066400000000000000000000004401524433232300227640ustar00rootroot00000000000000censys.search.v1 package ======================== .. automodule:: censys.search.v1 :members: :undoc-members: :show-inheritance: censys.search.v1.api module --------------------------- .. automodule:: censys.search.v1.api :members: :undoc-members: :show-inheritance: censys-censys-python-7a57a89/docs/censys.search.v2.rst000066400000000000000000000004401524433232300227650ustar00rootroot00000000000000censys.search.v2 package ======================== .. automodule:: censys.search.v2 :members: :undoc-members: :show-inheritance: censys.search.v2.api module --------------------------- .. automodule:: censys.search.v2.api :members: :undoc-members: :show-inheritance: censys-censys-python-7a57a89/docs/cli.html000066400000000000000000000002361524433232300205750ustar00rootroot00000000000000

censys-censys-python-7a57a89/docs/cli.rst000066400000000000000000000001301524433232300204320ustar00rootroot00000000000000CLI Reference ============= .. autoprogram:: censys.cli:get_parser() :prog: censys censys-censys-python-7a57a89/docs/conf.py000066400000000000000000000067411524433232300204460ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys from datetime import datetime import sphinx_rtd_theme # noqa # -- Path setup -------------------------------------------------------------- sys.path.insert(0, os.path.abspath("..")) from censys.common import __version__ # -- Project information ----------------------------------------------------- project = "Censys Python" author = "Censys, Inc." copyright = f"{datetime.now().year or 2021}, {author}" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags release = __version__ # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.coverage", "sphinx.ext.viewcode", "sphinx.ext.intersphinx", "sphinx.ext.autosectionlabel", "sphinx.ext.napoleon", "sphinx_rtd_theme", "sphinx-prompt", "sphinx_tabs.tabs", "sphinx_copybutton", "sphinxcontrib.autoprogram", ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**tests**"] suppress_warnings = ["autosectionlabel.usage-cli"] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Theme Options html_theme_options = { "analytics_id": "UA-73424227-8", "style_external_links": False, "collapse_navigation": True, } # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # -- Extension configuration ------------------------------------------------- # Sort members by type # autodoc_member_order = "groupwise" # Add mappings intersphinx_mapping = { "requests": ("https://requests.readthedocs.io/en/latest/", None), "python": ("https://docs.python.org/3/", None), } # Prefix document path to section labels, otherwise autogenerated labels would look # like 'heading' rather than 'path/to/file:heading' autosectionlabel_prefix_document = True # Define the prompt text that you’d like removed from copied text in your code blocks copybutton_prompt_text = "$" # Napoleon napoleon_include_init_with_doc = False censys-censys-python-7a57a89/docs/contributing.rst000066400000000000000000000005271524433232300224040ustar00rootroot00000000000000Contributing ============ All contributions (no matter how small) are always welcome. Working on your first Pull Request? ----------------------------------- You can learn how from this *free* series `How to Contribute to an Open Source Project on GitHub `__ censys-censys-python-7a57a89/docs/development.rst000066400000000000000000000010611524433232300222110ustar00rootroot00000000000000Development =========== Clone the repository: .. tabs:: .. tab:: SSH .. prompt:: bash $ git clone git@github.com:censys/censys-python.git .. tab:: HTTPS .. prompt:: bash $ git clone https://github.com/censys/censys-python.git .. tab:: GitHub CLI .. prompt:: bash $ gh repo clone censys/censys-python Install dependencies via ``pip``: .. prompt:: bash $ cd censys-python/ uv install Run the test suite with ``pytest``. More information about testing is available at :ref:`testing:Testing`. censys-censys-python-7a57a89/docs/index.rst000066400000000000000000000024051524433232300210010ustar00rootroot00000000000000.. Censys Python documentation master file, created by sphinx-quickstart on Fri Sep 11 10:57:18 2020. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to Censys Python's documentation! ========================================= Release v\ |version|. (:ref:`quick-start:Quick Start`) .. image:: https://img.shields.io/pypi/v/censys?color=orange&logo=pypi&logoColor=orange :target: https://pypi.org/project/censys/ .. image:: https://img.shields.io/badge/python-3.8%2B-blue?logo=python :target: https://www.python.org/downloads/ .. image:: https://img.shields.io/github/license/censys/censys-python?logo=apache :target: https://github.com/censys/censys-python/blob/main/LICENSE **Censys Python** is an easy-to-use and lightweight API wrapper for Censys APIs. ------------------- The User Guide -------------- .. toctree:: :maxdepth: 1 quick-start usage-cli usage-v2 usage-v1 usage-asm advanced-usage The API Documentation --------------------- .. toctree:: :maxdepth: 1 censys cli The Contributor Guide --------------------- .. toctree:: :maxdepth: 1 contributing development testing GitHub Repo censys-censys-python-7a57a89/docs/quick-start.rst000066400000000000000000000022261524433232300221420ustar00rootroot00000000000000Quick Start =========== Assuming you have Python already, install the package: .. prompt:: bash pip install censys If you do not have pip installed, get it `here `_. Configure your credentials: .. tabs:: .. tab:: Search API .. prompt:: bash censys config Or you can set the environment variables: .. prompt:: bash export CENSYS_API_ID= export CENSYS_API_SECRET= Find your credentials on the `Account page `_. .. tab:: ASM API .. prompt:: bash censys asm config Or you can set the environment variables: .. prompt:: bash export CENSYS_ASM_API_KEY= Find your credentials on the `Integrations page `_. Optionally, you can enable tab completion for the CLI by adding this line to your `~/.bashrc`, `~/.zshrc`, or equivalent. .. prompt:: bash eval "$(register-python-argcomplete censys)" .. note:: Please note that autocomplete is supported for field names in the `search` command. censys-censys-python-7a57a89/docs/testing.rst000066400000000000000000000006671524433232300213570ustar00rootroot00000000000000Testing ======= Testing is done using `pytest `__. To run the full test suite against your changes, install :ref:`dev dependencies ` and simply run ``pytest``. Which should return without any errors. .. prompt:: bash pytest .. note:: Tests currently require credentials to be setup. More information about credentials is available at :ref:`quick-start:Quick Start`. censys-censys-python-7a57a89/docs/usage-asm.rst000066400000000000000000000261721524433232300215630ustar00rootroot00000000000000ASM Usage ========= The Censys ASM API provides functionality for interacting with Censys ASM endpoints such as Seeds, Assets, Logbook Events, Risks, Inventory Search, and Saved Queries. The following API clients provided are: - :attr:`seeds ` - Provides programmatic management of seeds in the ASM platform. - :attr:`assets ` - Returns asset data for hosts, certificates, and domains. This option also allows the user to manage tags and comments on assets. - :attr:`logbook ` - Returns logbook events. Can be used to execute targeted searches for events based on start id or date, and event type filters. - :attr:`risks ` - Returns risk data for hosts, certificates, and domains. This option also allows the user to get more information about a specific risk. - :attr:`inventory ` - Returns inventory data for hosts, certificates, and domains. This option also allows the user to Search for assets based on a variety of criteria. - :attr:`web entities ` - Returns web entities instances. This option also allows the user to manage tags and comments on web entities. - :attr:`saved queries ` - Returns saved queries. This option also allows the user to manage saved queries. More details about each option can be found in the `Censys ASM API documentation `__. Users can also test example requests from the API documentation page. Python class objects can be used individually, but must be initialized for each resource type (Seeds, Assets, Events, Risks, Inventory, Clouds, SavedQueries). - :attr:`Seeds ` - :attr:`Assets ` - :attr:`CertificatesAssets ` - :attr:`DomainsAssets ` - :attr:`HostsAssets ` - :attr:`ObjectStoragesAssets ` - :attr:`SubdomainsAssets ` - :attr:`WebEntitiesAssets ` - :attr:`Logbook ` - :attr:`Risks ` - :attr:`InventorySearch ` - :attr:`SavedQueries ` Alternatively, all class objects can be used together by initializing an AsmClient object. This client wraps the APIs under one object for ease of use. - :attr:`AsmClient ` ``Seeds`` ---------- Below we show examples for **listing seeds** from the Censys ASM platform. .. code:: python3 from censys.asm import Seeds s = Seeds() # Get all seeds seeds = s.get_seeds() print(seeds) # Get a specific type of seed. Optional seed types are ["IP_ADDRESS", "DOMAIN_NAME", "CIDR", "ASN"] # Here we get IP address seeds. seeds = s.get_seeds("IP_ADDRESS") print(seeds) # Get a single seed by its ID (here we get seed with ID=3) seeds = s.get_seeds(3) print(seeds) Below we show examples for **adding seeds** to the Censys ASM platform. .. code:: python3 from censys.asm import Seeds s = Seeds() # Add a list of seeds. To add a single seed, just pass a list containing one seed. # Here, we add two ASN seeds. seed_list = [ {"type": "ASN", "value": 99998, "label": "seed-test-label"}, {"type": "ASN", "value": 99999, "label": "seed-test-label"}, ] s.add_seeds(seed_list) # Add a list of seeds, replacing existing seeds with a specified label # Here, all seeds with label="seed-test-label" will be removed and then # Seeds 99996 and 99997 will be added. seed_list = [{"type": "ASN", "value": 99996}, {"type": "ASN", "value": 99997}] s.replace_seeds_by_label("seed-test-label", seed_list) Below we show examples for **deleting seeds** from the Censys ASM platform. .. code:: python3 from censys.asm import Seeds s = Seeds() # Delete all seeds with a specified label # Here we delete all seeds with label="seed-test-label" s.delete_seeds_by_label("seed-test-label") # Delete a seed by its ID # Here, a seed with ID=224 will be deleted. s.delete_seed_by_id(224) ``Assets`` ---------- There are four types of assets (Hosts, Certificates, Domains, and Subdomains). Each asset type shares the same API interface so we will use a mixture of asset types in the following examples. Below we show examples for **viewing assets** on the Censys ASM platform. .. code:: python from censys.asm import HostsAssets h = HostsAssets() # Get a generator that returns hosts hosts = h.get_assets() print(next(hosts)) # Get a single host by ID (here we get host with ID="0.0.0.0") host = h.get_asset_by_id("0.0.0.0") print(host) Below we show examples for **managing asset comments** via the ASM API. .. code:: python from censys.asm import DomainsAssets d = DomainsAssets() # Get a generator that returns all comments on a specific domain asset # Here we get all comments on the domain with ID="my_domain.com" comments = d.get_comments("my_domain.com") print(next(comments)) # Get a single comment on a specific domain by comment ID # Here we look at domain with ID="my_domain.com" and get comment with ID=3 comment = d.get_comment_by_id("my_domain.com", 3) print(comment) # Add a comment to a domain asset # Here we add comment "hello world" to domain with ID="my_domain.com" d.add_comment("my_domain.com", "hello world") Below we show examples for **managing asset tags** via the ASM API. .. code:: python from censys.asm import CertificatesAssets c = CertificatesAssets() cert_sha = "0006afc1ddc8431aa57c812adf028ab4f168b25bf5f06e94af86edbafa88dfe0" # Add a tag to a certificate asset # Here we add tag "New" to certificate with ID=cert_sha c.add_tag(cert_sha, "New") # We can optionally give the tag a hexadecimal color where the default=#ffffff (white) # Here we add a blue tag "New-2" to certificate with ID=cert_sha c.add_tag(cert_sha, "New-2", color="#0011ff") # Delete a tag by tag name # Here we delete tag name="New" from certificate with ID=cert_sha c.delete_tag(cert_sha, "New") Below we show examples for **subdomain asset tags** via the ASM API. .. code:: python from censys.asm import AsmClient client = AsmClient() sub = client.get_subdomains("my_domain.com") # Add a tag to a subdomain under my_domain.com sub.add_tag("sub.my_domain.com", "New") ``Logbook`` ----------- .. note:: Note that all timestamp fields in logbook operations use **ISO-8601** format. This is the full list of event types that can be used as filters: - ``CERT`` - ``CERT_RISK`` - ``DOMAIN`` - ``DOMAIN_EXPIRATION_DATE`` - ``DOMAIN_MAIL_EXCHANGE_SERVER`` - ``DOMAIN_NAME_SERVER`` - ``DOMAIN_REGISTRAR`` - ``DOMAIN_RISK`` - ``DOMAIN_SUBDOMAIN`` - ``HOST`` - ``HOST_CERT`` - ``HOST_PORT`` - ``HOST_PROTOCOL`` - ``HOST_RISK`` - ``HOST_SOFTWARE`` - ``HOST_VULNERABILITY`` Below we show examples for **creating a logbook cursor** for retrieving filtered events. .. code:: python from censys.asm import Logbook l = Logbook() # Get a logbook cursor beginning at timestamp "2020-04-22T06:55:01.000Z" cursor = l.get_cursor("2020-04-22T06:55:01.000Z") print(cursor) # Get a logbook cursor beginning at event ID=10 cursor = l.get_cursor(10) print(cursor) # Get a logbook cursor that filters on events of type "CERT" and "CERT_RISK" cursor = l.get_cursor(filters=["CERT", "CERT_RISK"]) print(cursor) # Get a logbook cursor combining previous start ID and filters cursor = l.get_cursor(10, filters=["CERT", "CERT_RISK"]) print(cursor) Below we show examples for **getting logbook events.** .. code:: python from censys.asm import Logbook l = Logbook() # Get a generator that returns all events events = l.get_events() print(next(events)) # Get events based off cursor specifications events = l.get_events(cursor) print(next(events)) ``Risks`` --------- Below we show an example of **getting risk instances**. .. code:: python from censys.asm import Risks r = Risks() # Get risk events risk_events = r.get_risk_events() print(risk_events) # Get a dict that returns all risk instances risk_instances = r.get_risk_instances() print(risk_instances) # Get a single risk instance by ID risk_instance = r.get_risk_instance(1) print(risk_instance) # Get risk types risk_types = r.get_risk_types() print(risk_types) # Get a single risk type by ID risk_type = r.get_risk_type("missing-common-security-headers") print(risk_type) ``InventorySearch`` ------------------- Below we show an example of **searching for assets**. .. code:: python from censys.asm import InventorySearch i = InventorySearch() # Get a dict that contains a list of hits for a search query with pagination assets = i.search(workspaces=["my_workspace"], query="host.services.http.response.body: /.*test.*/") print(assets) # Aggregate search results by a field aggregation = i.aggregate(workspaces=["my_workspace"], query="host.location.country_code: 'US'", aggregation={"term": {"field": "host.autonomous_system.bgp_prefix", "numberOfBuckets": 50}}) print(aggregation) # Get list of all available fields fields = i.fields() print(fields) ``SavedQueries`` ---------------- Below we show an example of **managing saved queries**. .. code:: python from censys.asm import SavedQueries s = SavedQueries() # Get a dict that contains a list of saved queries saved_queries = s.get_saved_queries() print(saved_queries) # Get a single saved query by ID saved_query = s.get_saved_query_by_id("query_id") print(saved_query) # Add a saved query saved_query = s.add_saved_query("my_saved_query", "host.services.http.response.body: /.*test.*/") print(saved_query) # Update a saved query saved_query = s.edit_saved_query_by_id("query_id", "my_updated_saved_query", "host.services.http.response.body: /.*test.*/") print(saved_query) # Delete a saved query s.delete_saved_query_by_id("query_id") ``AsmClient`` ------------- The Censys AsmClient wraps the Seeds, Assets, and Events classes into a single object. It can be used as a single point of interaction for all three APIs. Below we show how to initialize the AsmClient class object as well as a couple examples of its use. Note that with the AsmClient object, all Seeds, Assets, and Event operations can be accessed in a similar way as the individual APIs above. .. code:: python from censys.asm import AsmClient client = AsmClient() # Get all seeds seeds = client.seeds.get_seeds() print(seeds) # Get all domain assets domains = client.domains.get_assets() print(next(domains)) # Get all logbook events logbook_events = client.logbook.get_events() print(next(logbook_events)) ``Exceptions`` -------------- .. TODO: Add exceptions censys-censys-python-7a57a89/docs/usage-cli.rst000066400000000000000000000235111524433232300215440ustar00rootroot00000000000000CLI Usage ========= .. raw:: html :file: cli.html Before continuing please ensure you have successfully configured your credentials. .. prompt:: bash censys config The configuration file by default is written to ``~/.config/censys/censys.cfg``, but you can change this by setting the ``CENSYS_CONFIG_PATH`` environment variable. .. prompt:: bash export CENSYS_CONFIG_PATH=/path/to/config/file Optionally, you can enable tab completion for the CLI by adding this line to your `~/.bashrc`, `~/.zshrc`, or equivalent. .. prompt:: bash eval "$(register-python-argcomplete censys)" .. note:: Please note that autocomplete is supported for field names in the `search` command. ``search`` ---------- Below we show an example of searching hosts from the CLI. .. prompt:: bash censys search 'services.http.response.html_title: "Dashboard"' By combining the ``search`` command with ``jq`` we can easily manipulate the output to get the desired fields. .. prompt:: bash censys search 'services.service_name: ELASTICSEARCH' | jq -c '.[] | {ip: .ip}' By setting the ``--pages`` flag to ``-1`` we can get all pages of results. .. prompt:: bash censys search 'ip: 8.8.8.0/16' --pages -1 | jq -c '[.[] | .ip]' By settings the ``--index-type`` flag we can search other indexes such as ``certificates``. .. prompt:: bash censys search 'parsed.subject_dn: "censys.io"' --index-type certificates For the ``certificates`` index we can also add the ``--fields`` flag to specify which fields we want returned. .. prompt:: bash censys search 'parsed.subject.country: AU' --index-type certificates --fields parsed.issuer.organization ``view`` -------- Below we show an example of viewing a host from the CLI. .. prompt:: bash censys view 8.8.8.8 Below we show an example of viewing a certificate from the CLI. .. prompt:: bash censys view 9b267decc8d23586dc4c56dd0789574cab0f28581ef354ff2fcec8ca6d992fc2 --index-type certificates You can save results to a file using the ``-o`` argument. .. prompt:: bash censys view 8.8.8.8 -o google.json We can then parse this json with something like ``jq``. .. prompt:: bash cat google.json | jq '[.services[] | {port: .port, protocol: .service_name}]' If you have access to historical ``hosts`` data you can view the data at a specific point in time using the ``--at-time`` argument. .. prompt:: bash censys view 1.1.1.1 --at-time 2023-01-01 .. note:: The ``--at-time`` argument is only available for the ``hosts`` index. ``subdomains`` -------------- Below we show an example of subdomain enumeration from the CLI. .. prompt:: bash censys subdomains censys.io You can limit the number of results by setting the ``--max-records`` flag. .. prompt:: bash censys subdomains censys.io --max-records 10 We can then output the results in JSON format using the ``--json`` flag. .. prompt:: bash censys subdomains censys.io --json ``account`` ----------- Below we show an example of viewing your account information from the CLI. .. prompt:: bash censys account You can also request the JSON version of your account information. .. prompt:: bash censys account --json ``asm`` ------- ``add-seeds`` ^^^^^^^^^^^^^ See CLI command :ref:`asm add-seeds` for detail documentation of parameters. Below we show an example of adding seeds from the CLI. .. prompt:: bash censys asm add-seeds -j '["1.1.1.1"]' You can add seeds from JSON or CSV files. JSON is assumed unless ``--csv`` is specified. The CSV file option is shown here. .. prompt:: bash censys asm add-seeds --csv -i 'good_seeds.csv' .. list-table:: CSV File Format :header-rows: 1 * - type - value - label * - IP_ADDRESS - 1.1.1.1 - Example Label * - DOMAIN_NAME - one.one.one.one - Example Label You can also add seeds from STDIN using the ``-i -`` argument. In the example below we are adding IPs from a Censys Search. .. prompt:: bash censys search 'services.tls.certificates.leaf_data.issuer.common_name: "Roomba CA"' | jq '[.[] | .ip]' | censys asm add-seeds -i - You can also add seeds from an nmap XML file using the ``--nmap-xml`` argument. In the example below we are adding IPs from a nmap scan on ``censys.io``. .. prompt:: bash nmap censys.io -oX censys.xml censys asm add-seeds --nmap-xml censys.xml ``delete-seeds`` ^^^^^^^^^^^^^^^^ See CLI command :ref:`asm delete-seeds` for detail documentation of parameters. Below we show an example of deleting seeds from the CLI. .. prompt:: bash censys asm delete-seeds -j '["1.1.1.1"]' You can delete seeds using file input as well, including CSV files. .. prompt:: bash censys asm delete-seeds --csv -i 'bad_seeds.csv' .. list-table:: CSV File Format :header-rows: 1 * - id - type - value - label * - 1 - IP_ADDRESS - 1.1.1.1 - Example Label * - 2 - DOMAIN_NAME - one.one.one.one - Example Label ``delete-all-seeds`` ^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm delete-all-seeds` for detail documentation of parameters. Below we show an example of deleting all seeds from the CLI. You will be prompted to confirm. .. prompt:: bash censys asm delete-all-seeds If you want to delete all seeds without a prompt, you can use the ``--force`` parameter. .. prompt:: bash censys asm delete-all-seeds --force ``delete-labeled-seeds`` ^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm delete-labeled-seeds` for detail documentation of parameters. Below we show an example of deleting all seeds with a given label from the CLI. .. prompt:: bash censys asm delete-labeled-seeds -l "Some Label" ``replace-labeled-seeds`` ^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm replace-labeled-seeds` for detail documentation of parameters. Below we show an example of replacing labeled seeds from the CLI, which will replace all existing seeds that have the specified label with the provided seeds, which will also have that label applied. .. prompt:: bash censys asm replace-labeled-seeds -l "Some Label" -j '["1.1.1.1"]' You can also use a variety of methods to specific the new seeds, including providing them in a CSV file. .. prompt:: bash censys asm replace-labeled-seeds -l "Some Label" --csv -i 'new_seeds.csv' ``list-seeds`` ^^^^^^^^^^^^^^ See CLI command :ref:`asm list-seeds` for detail documentation of parameters. Below we show an example of listing all seeds in CSV file format and appending it to a file. .. prompt:: bash censys asm list-seeds --csv >> seeds.csv You can also filter the seeds by type (``-t``) and/or label (``-l``). .. prompt:: bash censys asm list-seeds -t 'IP_ADDRESS' -l 'Some Label' >> filtered_seeds.json ``list-saved-queries`` ^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm list-saved-queries` for detail documentation of parameters. Below we show an example of listing all saved queries in CSV file format and appending it to a file. .. prompt:: bash censys asm list-saved-queries --csv >> saved_queries.csv You can also filter the saved queries by query name prefix (``--query-name-prefix``) and/or filter term (``--filter-term``). .. prompt:: bash censys asm list-saved-queries --query-name-prefix 'Some Prefix' --filter-term 'Some Term' >> filtered_saved_queries.json ``add-saved-query`` ^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm add-saved-query` for detail documentation of parameters. Below we show an example of adding a saved query from the CLI. .. prompt:: bash censys asm add-saved-query --query-name 'Some Query' --query 'services.http.response.html_title: "Dashboard"' ``get-saved-query-by-id`` ^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm get-saved-query-by-id` for detail documentation of parameters. Below we show an example of getting a saved query by ID from the CLI. .. prompt:: bash censys asm get-saved-query-by-id --query-id 'Some ID' ``edit-saved-query-by-id`` ^^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm edit-saved-query-by-id` for detail documentation of parameters. Below we show an example of editing a saved query by ID from the CLI. .. prompt:: bash censys asm edit-saved-query-by-id --query-id 'Some ID' --query-name 'Some Query' --query 'services.http.response.html_title: "Dashboard"' ``delete-saved-query-by-id`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm delete-saved-query-by-id` for detail documentation of parameters. Below we show an example of deleting a saved query by ID from the CLI. .. prompt:: bash censys asm delete-saved-query-by-id --query-id 'Some ID' ``execute-saved-query-by-name`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm execute-saved-query-by-name` for detail documentation of parameters. Below we show an example of executing a saved query by name from the CLI. .. prompt:: bash censys asm execute-saved-query-by-name --query-name 'Some query name' ``execute-saved-query-by-id`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See CLI command :ref:`asm execute-saved-query-by-id` for detail documentation of parameters. Below we show an example of executing a saved query by ID from the CLI. .. prompt:: bash censys asm execute-saved-query-by-id --query-id 'Some query ID' ``search`` ^^^^^^^^^^ See CLI command :ref:`asm search` for detail documentation of parameters. Below we show an example of executing an inventory search query from the CLI. .. prompt:: bash censys asm search --query 'Some query' censys-censys-python-7a57a89/docs/usage-v1.rst000066400000000000000000000027411524433232300213250ustar00rootroot00000000000000Usage v1 ======== The Censys Search API provides functionality for interacting with Censys resources such as Data, and for viewing Account information such as query quota. There is one API options that this library provides access to: - :attr:`data ` - Returns collections of scan series whose metadata includes a description of the data collected in the series and links to the individual scan results. More details about each option can be found in the `Censys API documentation `__. A list of index fields can be found in the `Censys API definitions page `__. .. note:: Please note that the Censys Search Certificates v1 API is being deprecated. Please use the :ref:`CensysCerts (v2) index ` for this functionality. ``data`` -------- Below we show an example using the :attr:`CensysData ` index. .. code:: python from censys.search import CensysData c = CensysData() # View a specific result from a specific series result = c.view_result("ipv4_2018", "20200818") print(result) ``account`` ----------- Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts c = CensysHosts() # Gets account data account = c.account() print(account) # Gets account quota quota = c.quota() print(quota) censys-censys-python-7a57a89/docs/usage-v2.rst000066400000000000000000000206111524433232300213220ustar00rootroot00000000000000Usage v2 ======== The Censys Search API provides functionality for interacting with Censys resources such as Hosts. There are three main API endpoints that this library provides access to: - :attr:`search ` - Allows searches against the Hosts index using the same search syntax as the `web app `__. - :attr:`view ` - Returns the structured data we have about a specific Host, given the resource's natural ID. - :attr:`aggregate ` - Allows you to view resources as a spectrum based on attributes of the resource, similar to the `Report Builder page `__ on the web app. More details about each option can be found in the `Censys API documentation `__. A list of index fields can be found in the `Censys API definitions page `__. Python class objects must be initialized for each resource index (Hosts). - :attr:`CensysHosts ` - :attr:`CensysCerts ` ``search`` ---------- Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/search_hosts.py :literal: ``view`` -------- Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/view_host.py :literal: ``bulk_view`` ------------- Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/bulk_view_hosts.py :literal: ``aggregate`` ------------- Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/aggregate_hosts.py :literal: ``metadata`` ------------- **Please note this method is only available only for the CensysHosts index.** Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/metadata_hosts.py :literal: ``view_host_names`` ------------------- **Please note this method is only available only for the CensysHosts index.** Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/view_host_names.py :literal: ``view_host_events`` -------------------- **Please note this method is only available only for the CensysHosts index.** Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/view_host_events.py :literal: ``view_host_diff`` ------------------ **Please note this method is only available only for the CensysHosts index.** Below we show an example using the :attr:`CensysHosts ` index. .. include:: ../examples/search/view_host_diff.py :literal: ``get_hosts_by_cert`` --------------------- **Please note this method is only available only for the CensysCerts index** Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Fetch a list of events for the specified IP address. hosts, links = c.get_hosts_by_cert( "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426" ) print(hosts) Comments -------- ``get_comments`` ^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Fetch a list of comments for the specified certificate. comments = c.get_comments( "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426" ) print(comments) ``add_comment`` ^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Add a comment to a host. comment = h.add_comment("1.1.1.1", "This is a test comment") print(comment) ``update_comment`` ^^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Update a comment to a host. comment = h.update_comment("1.1.1.1", 101, "This is an updated test comment") ``delete_comment`` ^^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Delete a comment for a certificate. c.delete_comment( "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426", 102 ) Tags ---- ``list_all_tags`` ^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Fetch a list of all tags. tags = h.list_all_tags() print(tags) ``create_tag`` ^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Create a new tag. tag = c.create_tag("test-tag") print(tag) # Optionally you can specify a color for the tag. tag = c.create_tag("test-tag", color="#00FF00") print(tag) ``get_tag`` ^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Fetch a tag. tag = h.get_tag("123") print(tag) ``update_tag`` ^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Update a tag. tag = c.update_tag("123", "test-tag") print(tag) # Optionally you can specify a color for the tag. tag = c.update_tag("123", "test-tag", color="#00FF00") print(tag) ``delete_tag`` ^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Delete a tag. h.delete_tag("123") ``list_tags_on_document`` ^^^^^^^^^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Fetch a list of tags for a document. tags = c.list_tags_on_document( "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426" ) print(tags) ``add_tag_to_document`` ^^^^^^^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Add a tag to a document. h.add_tag_to_document("123") ``remove_tag_from_document`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Remove a tag from a document. c.remove_tag_from_document( "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426" ) ``list_certs_with_tag`` ^^^^^^^^^^^^^^^^^^^^^^^ **Please note this method is only available only for the CensysCerts index** Below we show an example using the :attr:`CensysCerts ` index. .. code:: python from censys.search import CensysCerts c = CensysCerts() # Fetch a list of certs with the specified tag. certs = c.list_certs_with_tag("123") print(certs) ``list_hosts_with_tag`` ^^^^^^^^^^^^^^^^^^^^^^^ **Please note this method is only available only for the CensysHosts index.** Below we show an example using the :attr:`CensysHosts ` index. .. code:: python from censys.search import CensysHosts h = CensysHosts() # Fetch a list of hosts with the specified tag. hosts = h.list_hosts_with_tag("123") print(hosts) censys-censys-python-7a57a89/examples/000077500000000000000000000000001524433232300200255ustar00rootroot00000000000000censys-censys-python-7a57a89/examples/README.md000066400000000000000000000023651524433232300213120ustar00rootroot00000000000000# Examples Examples for using the Python Library. ## Imports For our package import statements are structured like this. ### Search ```python # Access all indexes from censys.search import SearchClient # Access only the hosts index from censys.search import CensysHosts # Access only the certificates index from censys.search import CensysCerts ``` ### ASM ```python from censys.asm import AsmClient ``` ## Available Examples ### Search Examples - [Using `SearchClient`](search/search_client.py) #### Hosts Index - [View Host](search/view_host.py) - [Search Hosts](search/search_hosts.py) - [Aggregate Hosts](search/aggregate_hosts.py) - [Bulk View Hosts](search/bulk_view_hosts.py) - [Hosts Metadata](search/metadata_hosts.py) - [View Host Events](search/view_host_events.py) - [View Host Names](search/view_host_names.py) - [View Host Diff](search/view_host_diff.py) #### Certificates Index - [View Certificate](search/view_cert.py) - [Search Certificates](search/search_certs.py) - [Report Certificates](search/report_certs.py) ### ASM Examples - [Get Cloud Host Counts](asm/cloud_host_count.py) - [Get Host Ricks](asm/get_host_risks.py) - [Get Domains and Subdomains](asm/get_subdomains.py) - [Add Seeds in Bulk (from CSV)](asm/add_seeds_bulk.py) censys-censys-python-7a57a89/examples/asm/000077500000000000000000000000001524433232300206055ustar00rootroot00000000000000censys-censys-python-7a57a89/examples/asm/add_saved_queries.py000066400000000000000000000005421524433232300246270ustar00rootroot00000000000000"""Add a saved query.""" from censys.asm import SavedQueries # Create an instance of the SavedQueries class saved_queries = SavedQueries() # Add a saved query query = "host.services.http.response.body: /.*test.*/" query_name = "Test Query" res = saved_queries.add_saved_query(query, query_name) # Print the added saved query print(res) censys-censys-python-7a57a89/examples/asm/add_seeds_bulk.py000066400000000000000000000010551524433232300241100ustar00rootroot00000000000000"""Add seeds in bulk from CSV file.""" import csv from censys.asm import Seeds s = Seeds() seeds_to_add = [] # Read CSV file # Example CSV file (headers are required): # type,value,label # IP_ADDRESS,1.1.1.1,"Optional label" # DOMAIN_NAME,example.com, with open("seeds.csv") as csv_file: reader = csv.DictReader(csv_file) for row in reader: seed = { "type": row.get("type"), "value": row.get("value"), "label": row.get("label"), } seeds_to_add.append(seed) s.add_seeds(seeds_to_add) censys-censys-python-7a57a89/examples/asm/cloud_host_count.py000066400000000000000000000003031524433232300245260ustar00rootroot00000000000000"""Retrieve host counts by cloud since the first day of 2021.""" from datetime import date from censys.asm import Clouds c = Clouds() count = c.get_host_counts(date(2021, 1, 1)) print(count) censys-censys-python-7a57a89/examples/asm/delete_saved_query_by_id.py000066400000000000000000000004071524433232300261770ustar00rootroot00000000000000"""Delete a saved query by ID.""" from censys.asm import SavedQueries # Create an instance of the SavedQueries class saved_queries = SavedQueries() # Delete the saved query by ID query_id = "query_id" saved_queries.delete_saved_query_by_id(query_id) censys-censys-python-7a57a89/examples/asm/edit_saved_query_by_id.py000066400000000000000000000006361524433232300256660ustar00rootroot00000000000000"""Edit a saved query by ID.""" from censys.asm import SavedQueries # Create an instance of the SavedQueries class saved_queries = SavedQueries() # Edit the saved query by ID query_id = "query_id" query = "host.services.http.response.body: /.*test2.*/" query_name = "Test Query 2" res = saved_queries.edit_saved_query_by_id(query_id, query, query_name) # Print the edited saved query print(res) censys-censys-python-7a57a89/examples/asm/get_host_risks.py000066400000000000000000000010621524433232300242050ustar00rootroot00000000000000"""Retrieve host risks and subsequent host info for each host risk.""" from censys.asm import HostsAssets, Logbook logbook = Logbook() hosts = HostsAssets() cursor = logbook.get_cursor(filters=["HOST_RISK"]) events = logbook.get_events(cursor) for event in events: # only show logbook events with the 'add' tag if event["operation"] == "ADD": print(event) # enrich the data of this host_risk with more data from the host itself host = hosts.get_asset_by_id(event["entity"]["ipAddress"]) print(host) censys-censys-python-7a57a89/examples/asm/get_saved_queries.py000066400000000000000000000004601524433232300246550ustar00rootroot00000000000000"""Get the saved queries.""" from censys.asm import SavedQueries # Create an instance of the SavedQueries class saved_queries = SavedQueries() # Get the saved queries queries = saved_queries.get_saved_queries() # Print the saved queries for query in queries["results"]: print(query) censys-censys-python-7a57a89/examples/asm/get_saved_query_by_id.py000066400000000000000000000004571524433232300255210ustar00rootroot00000000000000"""Get a saved query by ID.""" from censys.asm import SavedQueries # Create an instance of the SavedQueries class saved_queries = SavedQueries() # Get the saved query by ID query_id = "query_id" query = saved_queries.get_saved_query_by_id(query_id) # Print the saved query print(query) censys-censys-python-7a57a89/examples/asm/get_subdomains.py000066400000000000000000000005311524433232300241610ustar00rootroot00000000000000"""List all domains and subdomains in ASM.""" from censys.asm import DomainsAssets d = DomainsAssets() domains = [] for domain in d.get_assets(): domain_str = domain["data"]["domain"] domains.append(domain_str) subdomains = [sub["subdomain"] for sub in d.get_subdomains(domain_str)] domains.extend(subdomains) print(domains) censys-censys-python-7a57a89/examples/asm/search.py000066400000000000000000000004421524433232300224240ustar00rootroot00000000000000"""Execute a saved query by ID.""" from censys.asm import InventorySearch # Create an instance of the SavedQueries class inventory_search = InventorySearch() # Delete the saved query by ID query = "host.services.service_name: HTTP" res = inventory_search.search(query=query) print(res) censys-censys-python-7a57a89/examples/search/000077500000000000000000000000001524433232300212725ustar00rootroot00000000000000censys-censys-python-7a57a89/examples/search/aggregate_certs.py000066400000000000000000000002701524433232300247710ustar00rootroot00000000000000"""View specific certificate.""" from censys.search import CensysCerts c = CensysCerts() res = c.aggregate("parsed.subject.country: AU", "parsed.issuer.organization", 5) print(res) censys-censys-python-7a57a89/examples/search/aggregate_hosts.py000066400000000000000000000017401524433232300250140ustar00rootroot00000000000000"""Aggregate hosts data set.""" from censys.search import SearchClient c = SearchClient() # The aggregate method constructs a report using a query, an aggregation field, and the # number of buckets to bin. report = c.v2.hosts.aggregate( "services.service_name: HTTP", "services.port", num_buckets=5, ) print(report) # { # "total": 987342156, # "total_omitted": 836949090, # "potential_deviation": 3965103, # "buckets": [ # {"key": "80", "count": 58727150}, # {"key": "443", "count": 46716751}, # {"key": "7547", "count": 19185117}, # {"key": "22", "count": 13276559}, # {"key": "30005", "count": 12487489}, # ], # "query": "services.service_name: HTTP", # "field": "services.port", # } # You can also specify whether to include virtual hosts in the report. report = c.v2.hosts.aggregate( "services.service_name: HTTP", "services.port", num_buckets=5, virtual_hosts="INCLUDE", ) print(report) censys-censys-python-7a57a89/examples/search/bulk_view_certs.py000066400000000000000000000005541524433232300250370ustar00rootroot00000000000000"""View specific certificate.""" from censys.search import CensysCerts c = CensysCerts() certificates = c.bulk_view( [ "82689890be745aef821ee4a988a0b81331f714d7301b288976fab36219b1f493", "9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", ] ) for certificate in certificates: print(certificate["fingerprint_sha256"]) censys-censys-python-7a57a89/examples/search/bulk_view_hosts.py000066400000000000000000000004071524433232300250540ustar00rootroot00000000000000"""Bulk IP Lookup Example.""" from censys.search import CensysHosts h = CensysHosts() IPS = [ "1.1.1.1", "1.1.1.2", "1.1.1.3", ] hosts = h.bulk_view(IPS) print(hosts) # { # "1.1.1.1": {...}, # "1.1.1.2": {...}, # "1.1.1.3": {...}, # } censys-censys-python-7a57a89/examples/search/metadata_hosts.py000066400000000000000000000004671524433232300246530ustar00rootroot00000000000000"""Metadata on the hosts index.""" from censys.search import CensysHosts h = CensysHosts() # Fetch metadata about hosts. meta = h.metadata() print(meta) # { # "services": [ # "DNS", # "FTP", # "HTTP", # "POP3", # "SMTP", # "SSH", # ... # ] # } censys-censys-python-7a57a89/examples/search/search_certs.py000066400000000000000000000022031524433232300243060ustar00rootroot00000000000000"""Search certificate data set.""" from censys.search import CensysCerts c = CensysCerts() # Single page of search results query = c.search( "parsed.subject.country: AU", fields=[ "names", "fingerprint_sha256", "parsed.issuer.organization", "parsed.subject.postal_code", ], sort=["parsed.issuer.organization", "parsed.subject.postal_code"], ) hits = query() print(hits) # Multiple pages of search results query = c.search( "parsed.subject.country: AU", fields=[ "names", "fingerprint_sha256", "parsed.issuer.organization", "parsed.subject.postal_code", ], sort=["parsed.issuer.organization", "parsed.subject.postal_code"], pages=2, per_page=5, ) for hits in query: for hit in hits: print(hit) # View all results query = c.search( "parsed.subject.country: AU", fields=[ "names", "fingerprint_sha256", "parsed.issuer.organization", "parsed.subject.postal_code", ], sort=["parsed.issuer.organization", "parsed.subject.postal_code"], pages=2, per_page=5, ) print(query.view_all()) censys-censys-python-7a57a89/examples/search/search_client.py000066400000000000000000000004321524433232300244460ustar00rootroot00000000000000"""Example using SearchClient.""" from censys.search import SearchClient c = SearchClient() # v2 indexes hosts = c.v2.hosts # Same as hosts = CensysHosts() certs = c.v2.certificates # Same as certs = CensysCerts() # v1 indexes data = c.v1.data # Same as data = CensysData() censys-censys-python-7a57a89/examples/search/search_hosts.py000066400000000000000000000015231524433232300243320ustar00rootroot00000000000000"""Search hosts data set.""" from censys.search import CensysHosts h = CensysHosts() # Single page of search results query = h.search("services.service_name: HTTP", per_page=5) print(query()) # Multiple pages of search results query = h.search("services.service_name: HTTP", per_page=5, pages=2) for page in query: for host in page: print(host) # View all results (this will do a lookup for each host returned by the search) query = h.search("services.service_name: HTTP", per_page=5, pages=2) print(query.view_all()) # Search for virtual hosts query = h.search("not services.service_name: HTTP", per_page=5, virtual_hosts="ONLY") print(query()) # Search including specific fields query = h.search( "not services.service_name: HTTP", per_page=5, fields=["ip", "services.port", "services.service_name"], ) print(query()) censys-censys-python-7a57a89/examples/search/view_all_hosts.py000066400000000000000000000004231524433232300246650ustar00rootroot00000000000000"""View all hosts for a given query.""" from censys.search import CensysHosts h = CensysHosts() # Search for hosts with a specific service query = h.search("services.service_name: FTP", per_page=1, pages=2) # View all results all_hosts = query.view_all() print(all_hosts) censys-censys-python-7a57a89/examples/search/view_cert.py000066400000000000000000000002701524433232300236320ustar00rootroot00000000000000"""View specific certificate.""" from censys.search import CensysCerts c = CensysCerts() res = c.view("82689890be745aef821ee4a988a0b81331f714d7301b288976fab36219b1f493") print(res) censys-censys-python-7a57a89/examples/search/view_host.py000066400000000000000000000035611524433232300236600ustar00rootroot00000000000000"""View specific host.""" from censys.search import CensysHosts h = CensysHosts() # Fetch a specific host and its services host = h.view("8.8.8.8") print(host) # You can optionally pass in a RFC3339 timestamp to # fetch a host at the given point in time. # Please note historical API access is required. host = h.view("8.8.8.8", at_time="2021-03-01T17:49:05Z") print(host) # You can also pass in a date or datetime object. from datetime import date host = h.view("8.8.8.8", at_time=date(2021, 3, 1)) print(host) # { # "ip": "8.8.8.8", # "services": [ # { # "dns": { # "server_type": "FORWARDING", # "resolves_correctly": False, # "r_code": "UNKNOWN_CODE", # }, # "extended_service_name": "DNS", # "observed_at": "2021-02-28T23:58:55.705035895Z", # "perspective_id": "PERSPECTIVE_TELIA", # "port": 53, # "service_name": "DNS", # "source_ip": "74.120.14.37", # "transport_protocol": "UDP", # "truncated": False, # }, # ..., # ], # "location": { # "continent": "North America", # "country": "United States", # "country_code": "US", # "postal_code": "", # "timezone": "America/Chicago", # "coordinates": {"latitude": 37.751, "longitude": -97.822}, # "registered_country": "United States", # "registered_country_code": "US", # }, # "location_updated_at": "2022-01-20T16:08:47.237933Z", # "autonomous_system": { # "asn": 15169, # "description": "GOOGLE", # "bgp_prefix": "8.8.8.0/24", # "name": "GOOGLE", # "country_code": "US", # }, # "autonomous_system_updated_at": "2022-01-20T16:08:47.237933Z", # "dns": {}, # "last_updated_at": "2021-02-28T23:58:55.765Z", # } censys-censys-python-7a57a89/examples/search/view_host_certificates.py000066400000000000000000000016741524433232300264100ustar00rootroot00000000000000"""View host certificates.""" from censys.search import CensysHosts h = CensysHosts() # Fetch a list of certificates for the specified IP address. certificates = h.view_host_certificates("1.1.1.1") # print(certificates) # You can also pass in a date or datetime objects. from datetime import date certificates = h.view_host_certificates("1.1.1.1", per_page=1, start_time=date(2023, 1, 1)) print(certificates) # { # "ip": "1.1.1.1", # "certificates": [ # { # "fingerprint": "string", # "port": 0, # "service_name": "string", # "transport_protocol": "TCP", # "first_observed_at": "2023-10-02T13:32:26.202Z", # "last_observed_at": "2023-10-02T13:32:26.202Z", # "first_updated_at": "2023-10-02T13:32:26.202Z", # "last_updated_at": "2023-10-02T13:32:26.202Z", # }, # ... # ], # "links": {"next": "nextCursorToken"}, # } censys-censys-python-7a57a89/examples/search/view_host_diff.py000066400000000000000000000012331524433232300246420ustar00rootroot00000000000000"""View Host Diff.""" from datetime import date from censys.search import CensysHosts h = CensysHosts() # Compare a single host between two timestamps diff = h.view_host_diff("1.1.1.1", at_time=date(2022, 1, 1), at_time_b=date(2022, 1, 2)) print(diff) # Compare a single host between its current timestamp and a timestamp diff = h.view_host_diff("1.1.1.2", at_time=date(2022, 1, 2)) print(diff) # Compare two hosts diff = h.view_host_diff("1.1.1.1", ip_b="1.1.1.2") print(diff) # Compare two hosts between two timestamps diff = h.view_host_diff( ip="1.1.1.1", ip_b="1.1.1.2", at_time=date(2022, 1, 1), at_time_b=date(2022, 1, 2), ) print(diff) censys-censys-python-7a57a89/examples/search/view_host_events.py000066400000000000000000000024061524433232300252410ustar00rootroot00000000000000"""View host events.""" from censys.search import CensysHosts h = CensysHosts() # Fetch a list of events for the specified IP address. events = h.view_host_events("1.1.1.1") print(events) # You can also pass in a date or datetime objects. from datetime import date events = h.view_host_events("1.1.1.1", per_page=1, start_time=date(2022, 1, 1), end_time=date(2022, 1, 31)) print(events) # { # 'ip': '1.1.1.1', # 'events': [ # { # 'timestamp': '2022-01-01T00:00:01.713Z', # 'service_observed': { # 'id': {'port': 80, 'service_name': 'HTTP', 'transport_protocol': 'TCP'}, # 'observed_at': '2021-12-31T23:59:39.910804158Z', # 'perspective_id': 'PERSPECTIVE_NTT', # 'changed_fields': [{'field_name': 'http.request.uri'}, {'field_name': 'http.response.headers.Cf-Ray.headers'}, {'field_name': 'http.response.headers.Location.headers'}, {'field_name': 'banner'}, {'field_name': 'banner_hashes'}] # }, # '_event': 'service_observed' # } # ], # 'links': { # 'next': 'AS-RtkcKDRPshfT6ojz5ubSuyen_J_J2s9VLmJf9WCg7_jGt0KdU2JvoYW9QXof1Cskvm-b41QyRiR38kWADJuUA_w8rAA5ZNv9llYarhmPv22nIf88JFGGhH0h6dRZ7kDy5RfsiUxNFXeMQQXz0BWYrcQ==' # } # } censys-censys-python-7a57a89/examples/search/view_host_names.py000066400000000000000000000003521524433232300250360ustar00rootroot00000000000000"""View host names.""" from censys.search import CensysHosts h = CensysHosts() # Fetch a list of host names for the specified IP address. names = h.view_host_names("1.1.1.1") print(names) # [ # "one.one.one.one", # ... # ] censys-censys-python-7a57a89/pyproject.toml000066400000000000000000000076631524433232300211370ustar00rootroot00000000000000[project] name = "censys" version = "2.3.0" description = "An easy-to-use and lightweight API wrapper for Censys APIs (censys.io)." authors = [{ name = "Censys, Inc.", email = "support@censys.io" }] license = "Apache-2.0" readme = "README.md" keywords = ["censys", "api", "search", "attack surface management"] classifiers = [ "Typing :: Typed", "Topic :: Internet", "Topic :: Security", "Topic :: Documentation :: Sphinx", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP :: Indexing/Search", "Topic :: Scientific/Engineering :: Information Analysis", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Networking", "Topic :: Utilities", "Environment :: Console", "Natural Language :: English", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: Science/Research", "Intended Audience :: System Administrators", "Operating System :: OS Independent", "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: PyPy", ] requires-python = ">=3.9" dependencies = [ "requests>=2.32.0", "backoff>=2.0.0,<3.0.0", "rich>=10.16.2", "argcomplete>=2.0.0,<4.0.0", ] [project.urls] "Censys Homepage" = "https://censys.io/" "Censys Search" = "https://search.censys.io/" "Discussions" = "https://github.com/censys/censys-python/discussions" "Documentation" = "https://censys-python.rtfd.io" "Changelog" = "https://github.com/censys/censys-python/releases" "Tracker" = "https://github.com/censys/censys-python/issues" "Source" = "https://github.com/censys/censys-python" [project.scripts] censys = "censys.cli:main" [dependency-groups] dev = [ "ruff>=0.9.0", "pytest>=7.3,<10", "pytest-cov>=4", "pytest-mock>=3.10.0,<4", "responses>=0.23.1", "mypy>=1.5.1,<3", "types-requests>=2.29.0.0,<3", ] docs = [ "sphinx>=7.2.6", "sphinxcontrib-autoprogram>=0.1.9", "sphinx-rtd-theme>=3.0.2", "sphinx-prompt>=1.8.0", "sphinx-tabs>=3.4.1", "sphinx-copybutton>=0.5.2", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["censys"] [tool.ruff] line-length = 127 target-version = "py39" [tool.ruff.lint] select = [ "E", "W", # pycodestyle "F", # pyflakes "I", # isort "N", # pep8-naming "UP", # pyupgrade "C4", # flake8-comprehensions "PT", # flake8-pytest-style "SIM", # flake8-simplify ] ignore = [ "E501", # line too long — ruff format is the source of truth; remaining are unfixable literals "N818", # exception naming — renaming CensysException* would be a breaking API change "PT028", # test default args — false positive for test utility helpers, not fixtures ] [tool.ruff.lint.per-file-ignores] "examples/**" = ["E402"] [tool.ruff.lint.isort] known-first-party = ["censys"] [tool.mypy] python_version = "3.9" files = ["censys"] namespace_packages = true explicit_package_bases = true warn_unused_configs = true warn_redundant_casts = true warn_unused_ignores = true strict_equality = true extra_checks = true no_implicit_reexport = true check_untyped_defs = true disallow_untyped_decorators = true [[tool.mypy.overrides]] module = ["rich", "attr"] ignore_missing_imports = true [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--cov -rs" filterwarnings = ["ignore:.*:DeprecationWarning:.*"] python_classes = ["Test*", "*Tests", "*Test"] censys-censys-python-7a57a89/scripts/000077500000000000000000000000001524433232300176765ustar00rootroot00000000000000censys-censys-python-7a57a89/scripts/update_autocomplete.sh000077500000000000000000000007731524433232300243070ustar00rootroot00000000000000#!/usr/bin/env bash USER_AGENT="censys-python/$(uv version | cut -d ' ' -f 2)" update_autocomplete() { echo "Updating autocomplete data for $1..." # Write a temporary file to avoid corrupting the original if the download fails tmp_file=$(mktemp) wget "https://search.censys.io/static/data/autocomplete-$1.json" -U "$USER_AGENT" -O "$tmp_file" && echo "" >> "$tmp_file" mv "$tmp_file" "censys/cli/data/$1_autocomplete.json" } update_autocomplete hosts update_autocomplete certificates censys-censys-python-7a57a89/tests/000077500000000000000000000000001524433232300173515ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/__init__.py000066400000000000000000000000001524433232300214500ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/asm/000077500000000000000000000000001524433232300201315ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/asm/__init__.py000066400000000000000000000000001524433232300222300ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/asm/test_api.py000066400000000000000000000066341524433232300223240ustar00rootroot00000000000000import json import unittest import pytest import responses from pytest_mock import MockerFixture from requests.models import Response from censys.asm.api import CensysAsmAPI from censys.common.exceptions import ( CensysAsmException, CensysException, CensysExceptionMapper, ) from ..utils import CensysTestCase class CensysAPIBaseTestsNoAsmEnv(unittest.TestCase): @pytest.fixture(autouse=True) def __inject_fixtures(self, mocker: MockerFixture): self.mocker = mocker def setUp(self): self.responses = responses.RequestsMock() self.responses.start() self.mocker.patch.dict("os.environ", {"CENSYS_ASM_API_KEY": ""}) self.mock_open = self.mocker.patch("builtins.open", new_callable=self.mocker.mock_open, read_data="[DEFAULT]") self.addCleanup(self.responses.stop) self.addCleanup(self.responses.reset) def test_no_env(self): self.mocker.patch("builtins.open", new_callable=self.mock_open, read_data="[DEFAULT]") with pytest.raises(CensysException, match="No ASM API key configured."): CensysAsmAPI() AsmExceptionParams = [(code, exception) for code, exception in CensysExceptionMapper.ASM_EXCEPTIONS.items()] class CensysAsmAPITests(CensysTestCase): def setup_method(self): self.setUpApi(CensysAsmAPI(self.api_id)) @pytest.mark.parametrize(("status_code", "exception"), AsmExceptionParams) def test_get_exception_class(self, status_code, exception): # Mock mock_response = self.mocker.patch("requests.models.Response.json") response = Response() mock_response.return_value = {"errorCode": status_code} # Actual call/assertion assert CensysAsmAPI()._get_exception_class(response) == exception def test_exception_repr(self): # Actual call exception = CensysAsmException( 404, "Unable to Find Seed", error_code=10014, details="[{id: 999}]", # noqa: FS003 ) # Assertion assert ( repr(exception) == "404 (Error Code: 10014), Unable to Find Seed. [{id: 999}]" # noqa: FS003 ) @pytest.mark.parametrize("keyword", ["assets"]) def test_page_keywords(self, keyword): # Mock page_json = { "pageNumber": 1, "totalPages": 2, keyword: ["test1", "test2", "test3"], } second_page = ["test4", "test5"] def keyword_callback(request): temp_json = page_json.copy() page_number = int(request.params.get("pageNumber")) temp_json["pageNumber"] = page_number if page_number > 1: temp_json[keyword] = second_page return (200, {}, json.dumps(temp_json)) # Actual call self.responses.add_callback( responses.GET, f"{self.base_url}/{keyword}?pageNumber=", callback=keyword_callback, ) res = list(self.api._get_page(f"/{keyword}")) # Assertions assert res == page_json[keyword] + second_page def test_get_workspace_id(self): self.responses.add( responses.GET, f"{self.base_url}/integrations/v1/account", status=200, json={"workspaceId": "test-workspace-id"}, ) # Actual call res = self.api.get_workspace_id() # Assertions assert res == "test-workspace-id" censys-censys-python-7a57a89/tests/asm/test_assets.py000066400000000000000000000266601524433232300230560ustar00rootroot00000000000000from urllib.parse import quote import pytest from pytest_mock import MockerFixture from censys.asm.client import AsmClient from censys.common.exceptions import CensysInvalidColorException from .utils import ( BETA_URL, RESOURCE_PAGING_RESULTS, TEST_SUCCESS_CODE, TEST_TIMEOUT, V1_URL, MockResponse, ) ASSETS_URL = f"{V1_URL}/assets" BETA_ASSETS_URL = f"{BETA_URL}/assets" ASSET_TYPE = "assets" SUBDOMAIN_ASSET_TYPE = "subdomains" COMMENT_TYPE = "comments" TEST_PAGE_NUMBER = 2 TEST_PAGE_SIZE = 2 TEST_COMMENT_ID = 3 TEST_COMMENT_TEXT = "This is a test comment" TEST_TAG_NAME = "asset-test-tag" TEST_TAG_COLOR = "#4287f5" TEST_INVALID_TAG_COLOR = "4287f5" ASSET_PARAMS = [ ("hosts", "3.12.122.3"), ( "certificates", "0006afc1ddc8431aa57c812adf028ab4f168b25bf5f06e94af86edbafa88dfe0", ), ("domains", "amazonaws.com"), ("subdomains", "s3.amazonaws.com"), ("web_entities", "www.amazon.com:443"), ("object_storages", "https://censys-python.s3.us-east-2.amazonaws.com/"), ] class AssetsUnitTest: """Unit tests for Host, Certificate, and Domain APIs.""" @pytest.fixture(autouse=True, params=ASSET_PARAMS, ids=[p[0] for p in ASSET_PARAMS]) def _setup(self, request, mocker: MockerFixture): self.asset_type, self.test_asset_id = request.param self.mocker = mocker mocker.patch("time.sleep", return_value=None) self.client = AsmClient() self.resource_type = ASSET_TYPE if self.asset_type != "subdomains" else SUBDOMAIN_ASSET_TYPE return def get_asset_accessor(self): return getattr(self.client, self.asset_type) def asset_type_url(self): if self.asset_type == "subdomains": return f"{ASSETS_URL}/subdomains" if self.asset_type == "web_entities": return f"{ASSETS_URL}/web-entities" if self.asset_type == "object_storages": return f"{BETA_ASSETS_URL}/object-storages" return f"{ASSETS_URL}/{self.asset_type}" def asset_id_url(self): if self.asset_type == "object_storages": return f"{self.asset_type_url()}/{quote(self.test_asset_id, safe='')}" return f"{self.asset_type_url()}/{self.test_asset_id}" def test_get_assets(self): if self.asset_type == "web_entities": with pytest.raises(NotImplementedError): self.get_asset_accessor().get_assets() else: # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call assets = self.get_asset_accessor().get_assets() res = list(assets) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_called_with( self.asset_type_url(), params={"pageNumber": 3, "pageSize": 500}, timeout=TEST_TIMEOUT, ) def test_get_assets_by_tag(self): if self.asset_type == "web_entities": pytest.skip("Web entities do not support listing") return # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call assets = self.get_asset_accessor().get_assets( tag=[TEST_TAG_NAME], tag_operator="is", source=["Seed"], discovery_trail=True, ) res = list(assets) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_called_with( self.asset_type_url(), params={ "pageNumber": 3, "pageSize": 500, "tag": [TEST_TAG_NAME], "tagOperator": "is", "source": ["Seed"], "discoveryTrail": True, }, timeout=TEST_TIMEOUT, ) def test_get_assets_by_page(self): if self.asset_type == "web_entities": pytest.skip("Web entities do not support listing") return # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type, TEST_PAGE_NUMBER) # Actual call assets = self.get_asset_accessor().get_assets(page_number=TEST_PAGE_NUMBER, page_size=TEST_PAGE_SIZE) res = list(assets) # Assertions assert RESOURCE_PAGING_RESULTS[:6] == res assert mock_request.call_args_list[0][1]["params"]["pageNumber"] != 1 mock_request.assert_called_with( self.asset_type_url(), params={"pageNumber": 3, "pageSize": 2}, timeout=TEST_TIMEOUT, ) def test_get_asset_by_asset_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call self.get_asset_accessor().get_asset_by_id(self.test_asset_id) # Assertions mock_request.assert_called_with( self.asset_id_url(), params={}, timeout=TEST_TIMEOUT, ) def test_get_asset_comments(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, COMMENT_TYPE) # Actual call comments = self.get_asset_accessor().get_comments(self.test_asset_id) res = list(comments) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_called_with( f"{self.asset_id_url()}/{COMMENT_TYPE}", params={"pageNumber": 3, "pageSize": 500}, timeout=TEST_TIMEOUT, ) def test_get_asset_comments_by_page(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, COMMENT_TYPE, TEST_PAGE_NUMBER) # Actual call comments = self.get_asset_accessor().get_comments(self.test_asset_id, page_number=2, page_size=2) res = list(comments) # Assertions assert RESOURCE_PAGING_RESULTS[:6] == res assert mock_request.call_args_list[0][1]["params"]["pageNumber"] != 1 mock_request.assert_called_with( f"{self.asset_id_url()}/{COMMENT_TYPE}", params={"pageNumber": 3, "pageSize": 2}, timeout=TEST_TIMEOUT, ) def test_get_comment_by_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, COMMENT_TYPE) # Actual call self.get_asset_accessor().get_comment_by_id(self.test_asset_id, TEST_COMMENT_ID) # Assertions mock_request.assert_called_with( (f"{self.asset_id_url()}/{COMMENT_TYPE}/{TEST_COMMENT_ID}"), params={}, timeout=TEST_TIMEOUT, ) def test_add_comment(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, COMMENT_TYPE) # Actual call self.get_asset_accessor().add_comment(self.test_asset_id, TEST_COMMENT_TEXT) # Assertions mock_request.assert_called_with( f"{self.asset_id_url()}/{COMMENT_TYPE}", params={}, timeout=TEST_TIMEOUT, json={"markdown": TEST_COMMENT_TEXT}, ) def test_delete_comment(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.delete") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call self.get_asset_accessor().delete_comment(self.test_asset_id, TEST_COMMENT_ID) # Assertions mock_request.assert_called_with( (f"{self.asset_id_url()}/comments/{TEST_COMMENT_ID}"), params={}, timeout=TEST_TIMEOUT, ) def test_add_tag_with_color(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call self.get_asset_accessor().add_tag(self.test_asset_id, TEST_TAG_NAME, TEST_TAG_COLOR) # Assertions mock_request.assert_called_with( f"{self.asset_id_url()}/tags", params={}, timeout=TEST_TIMEOUT, json={"name": TEST_TAG_NAME, "color": TEST_TAG_COLOR}, ) def test_add_tag_with_invalid_color(self): # Actual call/error raising with pytest.raises(CensysInvalidColorException): self.get_asset_accessor().add_tag(self.test_asset_id, TEST_TAG_NAME, TEST_INVALID_TAG_COLOR) def test_add_tag_without_color(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call self.get_asset_accessor().add_tag(self.test_asset_id, TEST_TAG_NAME) # Assertions mock_request.assert_called_with( f"{self.asset_id_url()}/tags", params={}, timeout=TEST_TIMEOUT, json={"name": TEST_TAG_NAME}, ) def test_delete_tag(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.delete") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, self.resource_type) # Actual call self.get_asset_accessor().delete_tag(self.test_asset_id, TEST_TAG_NAME) # Assertions mock_request.assert_called_with( (f"{self.asset_id_url()}/tags/{TEST_TAG_NAME}"), params={}, timeout=TEST_TIMEOUT, ) def test_get_subdomains(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") if self.asset_type != "domains": pytest.skip("Only applicable to domains assets") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, "subdomains") # Actual call subdomains = self.client.domains.get_subdomains(self.test_asset_id) res = list(subdomains) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_called_with( f"{self.asset_id_url()}/subdomains", params={"pageNumber": 3, "pageSize": 500}, timeout=TEST_TIMEOUT, ) def test_get_web_entity_instances(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") if self.asset_type != "web_entities": pytest.skip("Only applicable to web entities assets") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, "instances") # Actual call instances = self.client.web_entities.get_instances(self.test_asset_id) res = list(instances) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_called_with( f"{self.asset_id_url()}/instances", params={"cursor": "test", "pageSize": None}, timeout=TEST_TIMEOUT, ) censys-censys-python-7a57a89/tests/asm/test_beta.py000066400000000000000000000101331524433232300224530ustar00rootroot00000000000000import responses from censys.asm import Beta from ..utils import CensysTestCase from .utils import BETA_URL TEST_LOGBOOK_DATA = { "nextWindowCursor": "string", "results": [ { "type": "HOST", "logSubType": "ASSOCIATE", "ip": "string", "id": 0, "timestamp": "string", "logData": {}, } ], } TEST_CLOUD_ASSETS = { "addedAssets": ["string"], "removedAssets": ["string"], "updatedAssets": ["string"], } TEST_INPUT_ASSETS = { "pageNumber": 0, "pageSize": 0, "totalPages": 0, "totalItems": 0, "assets": [{"type": "ASN", "value": "string", "source": "API", "label": "string"}], } TEST_ASSET_COUNTS = { "environment": "ALL", "newAssetsSince": "string", "requestCompleteTime": "string", "totalCount": 0, "totalNewCount": 0, "totalCountsBySubEnvironment": [{"environment": "string", "totalCount": 0, "totalNewCount": 0}], } TEST_HOST_COUNTS_BY_COUNTRY = { "environment": "ALL", "newAssetsSince": "string", "requestCompleteTime": "string", "totalCount": 0, "totalNewCount": 0, "totalCountsBySubEnvironment": [ { "environment": "string", "totalCountsByCountry": [ { "country": "string", "countryCode": "string", "totalCount": 0, "totalNewCount": 0, } ], } ], } TEST_USER_WORKSPACES = [{"workspace": "string", "roles": ["string"]}] class BetaUnitTest(CensysTestCase): def setup_method(self): self.client = Beta(self.api_key) def test_get_logbook_data(self): # Setup response self.responses.add( responses.POST, BETA_URL + "/logbook/getLogbookData", status=200, json=TEST_LOGBOOK_DATA, ) # Actual call res = self.client.get_logbook_data(filters={"key": "value"}, cursor="cursor") # Assertions assert res == TEST_LOGBOOK_DATA def test_add_cloud_assets(self): # Setup response self.responses.add( responses.POST, BETA_URL + "/cloudConnector/addCloudAssets", status=200, json=TEST_CLOUD_ASSETS, ) # Actual call res = self.client.add_cloud_assets(cloud_connector_uid="uid", cloud_assets=[{"key": "value"}]) # Assertions assert res == TEST_CLOUD_ASSETS def test_get_input_assets(self): # Setup response self.responses.add( responses.GET, BETA_URL + "/assets/inputAssets", status=200, json=TEST_INPUT_ASSETS, ) # Actual call res = self.client.get_input_assets(page_number=1, page_size=10) # Assertions assert res == TEST_INPUT_ASSETS def test_get_asset_counts(self): # Setup response self.responses.add( responses.GET, BETA_URL + "/assets/counts", status=200, json=TEST_ASSET_COUNTS, ) # Actual call res = self.client.get_asset_counts(since="2021-01-01T00:00:00Z", environment="env", asset_type="type") # Assertions assert res == TEST_ASSET_COUNTS def test_get_host_counts_by_country(self): # Setup response self.responses.add( responses.GET, BETA_URL + "/assets/hostCountsByCountry", status=200, json=TEST_HOST_COUNTS_BY_COUNTRY, ) # Actual call res = self.client.get_host_counts_by_country(since="2021-01-01T00:00:00Z", environment="env") # Assertions assert res == TEST_HOST_COUNTS_BY_COUNTRY def test_get_user_workspaces(self): # Setup response self.responses.add( responses.GET, BETA_URL + "/users/user_uuid/workspaces", status=200, json=TEST_USER_WORKSPACES, ) # Actual call res = self.client.get_user_workspaces(user_uuid="user_uuid") # Assertions assert res == TEST_USER_WORKSPACES censys-censys-python-7a57a89/tests/asm/test_clouds.py000066400000000000000000000045621524433232300230420ustar00rootroot00000000000000import responses from censys.asm.client import AsmClient from ..utils import CensysTestCase from .utils import V1_URL TEST_COUNT_JSON = { "totalAssetCount": 0, "totalNewAssetCount": 0, "totalCloudAssetCount": 0, "totalCloudNewAssetCount": 0, "assetCountByProvider": [{"cloudProvider": "string", "assetCount": 0, "newAssetCount": 0}], } class CloudsUnitTest(CensysTestCase): def setup_method(self): self.client = AsmClient(self.api_key) def test_get_host_counts(self): # Setup response self.responses.add( responses.GET, V1_URL + "/clouds/hostCounts/2021-01-01", status=200, json=TEST_COUNT_JSON, ) # Actual call res = self.client.clouds.get_host_counts("2021-01-01") # Assertions assert res == TEST_COUNT_JSON def test_get_domain_counts(self): # Setup response self.responses.add( responses.GET, V1_URL + "/clouds/domainCounts/2021-01-01", status=200, json=TEST_COUNT_JSON, ) # Actual call res = self.client.clouds.get_domain_counts("2021-01-01") # Assertions assert res == TEST_COUNT_JSON def test_get_object_store_counts(self): # Setup response self.responses.add( responses.GET, V1_URL + "/clouds/objectStoreCounts/2021-01-01", status=200, json=TEST_COUNT_JSON, ) # Actual call res = self.client.clouds.get_object_store_counts("2021-01-01") # Assertions assert res == TEST_COUNT_JSON def test_get_subdomain_counts(self): # Setup response self.responses.add( responses.GET, V1_URL + "/clouds/subdomainCounts/2021-01-01", status=200, json=TEST_COUNT_JSON, ) # Actual call res = self.client.clouds.get_subdomain_counts("2021-01-01") # Assertions assert res == TEST_COUNT_JSON def test_get_unknown_counts(self): # Setup response self.responses.add( responses.GET, V1_URL + "/clouds/unknownCounts", status=200, json=TEST_COUNT_JSON, ) # Actual call res = self.client.clouds.get_unknown_counts() # Assertions assert res == TEST_COUNT_JSON censys-censys-python-7a57a89/tests/asm/test_events.py000066400000000000000000000125761524433232300230610ustar00rootroot00000000000000import unittest import pytest from pytest_mock import MockerFixture from censys.asm.client import AsmClient from censys.asm.logbook import Filters from .utils import ( RESOURCE_PAGING_RESULTS, TEST_SUCCESS_CODE, TEST_TIMEOUT, V1_URL, MockResponse, ) EVENTS_URL = f"{V1_URL}/logbook" EVENTS_CURSOR_URL = f"{V1_URL}/logbook-cursor" EVENTS_RESOURCE_TYPE = "events" TEST_CURSOR = "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MH0" TEST_NEXT_CURSOR = "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ9" TEST_START_DATE = "2020-10-29T19:26:34.371Z" TEST_START_ID = 20712 class EventsUnitTests(unittest.TestCase): """Unit tests for Events API.""" @pytest.fixture(autouse=True) def __inject_fixtures(self, mocker: MockerFixture): """Injects fixtures into the test case. Args: mocker (MockerFixture): pytest-mock fixture. """ # Inject mocker fixture self.mocker = mocker """Unit tests for Host, Certificate, and Domain APIs.""" def setUp(self): self.client = AsmClient() def test_get_logbook_cursor_no_args(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) # Actual call self.client.events.get_cursor() # Assertions mock_request.assert_called_with(EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT) def test_get_logbook_cursor_with_start_date(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) # Actual call self.client.events.get_cursor(start=TEST_START_DATE) # Assertions mock_request.assert_called_with( EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT, json={"dateFrom": TEST_START_DATE}, ) def test_get_logbook_cursor_with_start_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) # Actual call self.client.events.get_cursor(start=TEST_START_ID) # Assertions mock_request.assert_called_with( EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT, json={"idFrom": TEST_START_ID}, ) def test_get_logbook_cursor_with_filters(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) filters = [Filters.HOST, Filters.DOMAIN] # Actual call self.client.events.get_cursor(filters=filters) # Assertions mock_request.assert_called_with( EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT, json={"filter": {"type": filters}}, ) def test_get_logbook_cursor_with_filters_and_start_date(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) filters = [Filters.HOST, Filters.HOST_CERT] # Actual call self.client.events.get_cursor(start=TEST_START_DATE, filters=filters) # Assertion mock_request.assert_called_with( EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT, json={"filter": {"type": filters}, "dateFrom": TEST_START_DATE}, ) def test_get_logbook_cursor_with_filters_and_start_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, None) filters = [Filters.HOST, Filters.HOST_CERT] # Actual call self.client.events.get_cursor(start=TEST_START_ID, filters=filters) # Assertions mock_request.assert_called_with( EVENTS_CURSOR_URL, params={}, timeout=TEST_TIMEOUT, json={"filter": {"type": filters}, "idFrom": TEST_START_ID}, ) def test_get_all_events(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, EVENTS_RESOURCE_TYPE) # Actual call events = self.client.events.get_events() res = list(events) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_any_call(EVENTS_URL, params={"cursor": None}, timeout=TEST_TIMEOUT) mock_request.assert_any_call(EVENTS_URL, params={"cursor": TEST_NEXT_CURSOR}, timeout=TEST_TIMEOUT) def test_get_events_with_cursor(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, EVENTS_RESOURCE_TYPE) # Actual call events = self.client.events.get_events(TEST_CURSOR) res = list(events) # Assertions assert res == RESOURCE_PAGING_RESULTS mock_request.assert_any_call(EVENTS_URL, params={"cursor": TEST_CURSOR}, timeout=TEST_TIMEOUT) mock_request.assert_any_call(EVENTS_URL, params={"cursor": TEST_NEXT_CURSOR}, timeout=TEST_TIMEOUT) censys-censys-python-7a57a89/tests/asm/test_inventory.py000066400000000000000000000074551524433232300236120ustar00rootroot00000000000000import pytest import responses from censys.asm.inventory import InventorySearch from ..utils import CensysTestCase from .utils import BASE_URL, WORKSPACE_ID INVENTORY_BASE_PATH = f"{BASE_URL}/inventory/v1" INVENTORY_SEARCH_PATH = INVENTORY_BASE_PATH INVENTORY_AGGREGATE_PATH = f"{INVENTORY_BASE_PATH}/aggregate" INVENTORY_FIELDS_PATH = f"{INVENTORY_BASE_PATH}/fields" TEST_INVENTORY_SEARCH_JSON = { "totalHits": 0, "nextCursor": "", "previousCursor": "string", "queryDurationMillis": 0, "hits": [{}], } TEST_INVENTORY_AGGREGATE_JSON = { "workspaces": ["string"], "query": "string", "aggregation": { "term": {"field": "string", "numberOfBuckets": 0}, "rareTerm": {"field": "string", "maxCount": 0}, "nested": {"path": "string"}, "reverseNested": {"path": "string"}, "filter": {"query": "string"}, "cardinality": {"field": "string"}, }, } TEST_INVENTORY_FIELDS_JSON = {"fields": [{"path": "string", "type": "string", "description": "string", "repeated": True}]} class InventoryTests(CensysTestCase): api: InventorySearch def setup_method(self): self.setUpApi(InventorySearch(self.api_key)) @pytest.mark.parametrize( ("kwargs", "params"), [ ( { "query": "test", "page_size": 50, "cursor": "test", "sort": ["test"], "fields": ["test"], }, "?workspaces=test-workspace-id&query=test&pageSize=50&cursor=test&sort=test&fields=test", ), ( { "workspaces": ["1", "2"], "query": "test", }, "?workspaces=1&workspaces=2&query=test&pageSize=50", ), ], ) def test_search(self, kwargs, params): mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID # Setup response self.responses.add( responses.GET, INVENTORY_SEARCH_PATH + params, status=200, json=TEST_INVENTORY_SEARCH_JSON, ) # Actual call res = self.api.search(**kwargs) # Assertions assert res == TEST_INVENTORY_SEARCH_JSON @pytest.mark.parametrize( "kwargs", [ { "workspaces": ["1", "2"], "query": "test", "aggregation": { "field": "test", "size": 50, "sort": "test", "order": "test", }, }, ], ) def test_aggregate(self, kwargs): # Setup response self.responses.add( responses.POST, INVENTORY_AGGREGATE_PATH, status=200, json=TEST_INVENTORY_AGGREGATE_JSON, match=[responses.matchers.json_params_matcher(kwargs)], ) # Actual call res = self.api.aggregate(**kwargs) # Assertions assert res == TEST_INVENTORY_AGGREGATE_JSON @pytest.mark.parametrize( ("kwargs", "params"), [ ( { "fields": ["host.services.name", "host.services.port"], }, "?fields=host.services.name&fields=host.services.port", ), ], ) def test_fields(self, kwargs, params): # Setup response self.responses.add( responses.GET, INVENTORY_FIELDS_PATH + params, status=200, json=TEST_INVENTORY_FIELDS_JSON, ) # Actual call res = self.api.fields(**kwargs) # Assertions assert res == TEST_INVENTORY_FIELDS_JSON censys-censys-python-7a57a89/tests/asm/test_risks.py000066400000000000000000000207501524433232300227010ustar00rootroot00000000000000import urllib.parse import pytest import responses from responses import matchers from censys.asm.risks import Risks from ..utils import CensysTestCase from .utils import V2_URL TEST_EVENT_JSON = { "delta": "string", "id": 0, "op": "string", "reason": "string", "src": "string", "srcID": "string", "ts": "2022-02-15T20:22:30.262Z", } TEST_RISK_EVENTS_JSON = { "total": 1, "next": "eyJhZnRlcklEIjo3NzQwLCJsaW1pdCI6MTAwfQ==", "events": [TEST_EVENT_JSON], "endOfEvents": True, } TEST_RISK_INSTANCE_JSON = { "id": 0, "events": [TEST_EVENT_JSON], "metadata": [{"src": "system"}], } TEST_RISK_INSTANCES_JSON = { "risks": [TEST_RISK_INSTANCE_JSON], } TEST_PATCH_RISK_INSTANCE_JSON = { "categoriesChanges": 0, "changes": 0, "metadataAdded": 0, "metadataRemoved": 0, "severityChanges": 0, "skips": 0, "userStatusChanges": 0, } TEST_RISK_TYPE = "service.authentication.http_weak_auth/after-slash" ESCAPED_TEST_RISK_TYPE = urllib.parse.quote(TEST_RISK_TYPE, safe="") TEST_RISK_TYPE_JSON = { "config": "string", "contextType": "string", "defaultCategories": [["string"]], "defaultSeverity": "low", "description": "string", "enabled": True, "events": [TEST_EVENT_JSON], "lastDisableReason": "string", "lastSeverityChangeReason": "string", "lastUpdatedAt": "2022-02-15T20:22:30.262Z", "name": "string", "recommendedSeverity": "low", "remediations": ["string"], "riskCount": 0, "subjectType": "string", "type": "string", } TEST_RISK_TYPES_JSON = { "type": [TEST_RISK_TYPE_JSON], } TEST_PATCH_RISK_TYPE_JSON = { "changes": 0, "configChanges": 0, "defaultCategoriesChanges": 0, "defaultSeverityChanges": 0, "enabledChanges": 0, } class RisksTests(CensysTestCase): api: Risks def setup_method(self): self.setUpApi(Risks(self.api_key)) @pytest.mark.parametrize( ("kwargs", "params"), [ ({}, ""), ( {"start": "2023-12-14T03:27:00Z", "end": "2022-03-18T09:18:54Z"}, "?start=2023-12-14T03:27:00Z&end=2022-03-18T09:18:54Z", ), ({"after_id": 489, "limit": 100}, "?afterID=489&limit=100"), ( {"cursor": "eyJhZnRlcklEIjo3NzQwLCJsaW1pdCI6MTAwfQ=="}, "?cursor=eyJhZnRlcklEIjo3NzQwLCJsaW1pdCI6MTAwfQ==", ), ], ) def test_get_risk_events(self, kwargs, params): # Setup response self.responses.add( responses.GET, V2_URL + f"/risk-events{params}", status=200, json=TEST_RISK_EVENTS_JSON, ) # Actual call res = self.api.get_risk_events(**kwargs) # Assertions assert res == TEST_RISK_EVENTS_JSON @pytest.mark.parametrize( ("kwargs", "params"), [ ({}, ""), ({"include_events": True}, "?includeEvents=True"), ({"include_events": False}, "?includeEvents=False"), ], ) def test_get_risk_instances(self, kwargs, params): # Setup response self.responses.add( responses.GET, V2_URL + f"/risk-instances{params}", status=200, json=TEST_RISK_INSTANCES_JSON, ) # Actual call res = self.api.get_risk_instances(**kwargs) # Assertions assert res == TEST_RISK_INSTANCES_JSON def test_patch_risk_instances(self): # Mock mock_patch = self.mocker.patch.dict( { "id": 0, "categories": ["test-patch"], } ) self.responses.add( responses.PATCH, V2_URL + "/risk-instances", status=200, json=TEST_PATCH_RISK_INSTANCE_JSON, match=[matchers.json_params_matcher(mock_patch)], ) # Actual call res = self.api.patch_risk_instances(mock_patch) # Assertions assert res == TEST_PATCH_RISK_INSTANCE_JSON def test_search_risk_instances(self): # Mock mock_search = self.mocker.patch.dict( { "fields": ["id", "context", "type", "metadata", "events"], "limit": 1000, "page": 1, "query": { "field": "firstComputedAt", "operator": "=", "value": "2022-02-15T17:46:10.328Z", }, "sort": ["severity", {"context.type": "asc"}], } ) self.responses.add( responses.POST, V2_URL + "/risk-instances/search", status=200, json=TEST_RISK_TYPE_JSON, match=[matchers.json_params_matcher(mock_search)], ) # Actual call res = self.api.search_risk_instances(mock_search) # Assertions assert res == TEST_RISK_TYPE_JSON @pytest.mark.parametrize( ("kwargs", "params"), [ ({"risk_instance_id": 0}, "0"), ({"risk_instance_id": 1, "include_events": True}, "1?includeEvents=True"), ({"risk_instance_id": 2, "include_events": False}, "2?includeEvents=False"), ], ) def test_get_risk_instance(self, kwargs, params): # Setup response self.responses.add( responses.GET, V2_URL + f"/risk-instances/{params}", status=200, json=TEST_RISK_INSTANCE_JSON, ) # Actual call res = self.api.get_risk_instance(**kwargs) # Assertions assert res == TEST_RISK_INSTANCE_JSON def test_patch_risk_instance(self): # Mock mock_risk_id = 0 mock_patch = self.mocker.patch.dict( { "id": mock_risk_id, "categories": ["mock-patch"], } ) self.responses.add( responses.PATCH, V2_URL + f"/risk-instances/{mock_risk_id}", status=200, json=TEST_PATCH_RISK_INSTANCE_JSON, match=[matchers.json_params_matcher(mock_patch)], ) # Actual call res = self.api.patch_risk_instance(mock_risk_id, mock_patch) # Assertions assert res == TEST_PATCH_RISK_INSTANCE_JSON @pytest.mark.parametrize( ("kwargs", "params"), [ ({}, ""), ({"include_events": True}, "?includeEvents=True"), ({"include_events": False}, "?includeEvents=False"), ({"sort": ["severity", "type:asc"]}, "?sort=severity&sort=type:asc"), ({"page": 1, "limit": 10000}, "?page=1&limit=10000"), ({"page": 10}, "?page=10"), ], ) def test_get_risk_types(self, kwargs, params): # Setup response self.responses.add( responses.GET, V2_URL + f"/risk-types{params}", status=200, json=TEST_RISK_TYPES_JSON, ) # Actual call res = self.api.get_risk_types(**kwargs) # Assertions assert res == TEST_RISK_TYPES_JSON @pytest.mark.parametrize( ("kwargs", "params"), [ ({"risk_type": TEST_RISK_TYPE}, ESCAPED_TEST_RISK_TYPE), ( {"risk_type": TEST_RISK_TYPE, "include_events": True}, ESCAPED_TEST_RISK_TYPE + "?includeEvents=True", ), ( {"risk_type": TEST_RISK_TYPE, "include_events": False}, ESCAPED_TEST_RISK_TYPE + "?includeEvents=False", ), ], ) def test_get_risk_type(self, kwargs, params): # Setup respnonse self.responses.add( responses.GET, V2_URL + f"/risk-types/{params}", status=200, json=TEST_RISK_TYPE_JSON, ) # Actual call res = self.api.get_risk_type(**kwargs) # Assertions assert res == TEST_RISK_TYPE_JSON def test_patch_risk_type(self): # Mock mock_patch = self.mocker.patch.dict( { "type": TEST_RISK_TYPE, "categories": ["mock-patch"], } ) self.responses.add( responses.PATCH, V2_URL + f"/risk-types/{ESCAPED_TEST_RISK_TYPE}", status=200, json=TEST_PATCH_RISK_TYPE_JSON, match=[matchers.json_params_matcher(mock_patch)], ) # Actual call res = self.api.patch_risk_type(TEST_RISK_TYPE, mock_patch) # Assertions assert res == TEST_PATCH_RISK_TYPE_JSON censys-censys-python-7a57a89/tests/asm/test_saved_queries.py000066400000000000000000000141231524433232300244020ustar00rootroot00000000000000import pytest import responses from censys.asm.saved_queries import SavedQueries from ..utils import CensysTestCase from .utils import BASE_URL SAVED_QUERIES_BASE_PATH = f"{BASE_URL}/inventory/v1/saved-query" TEST_GET_SAVED_QUERIES_JSON = { "results": [ { "createdAt": "2024-01-01T01:00:00.000Z", "query": "string", "queryId": "string", "queryName": "string", } ], "totalResults": 0, } TEST_ADD_SAVED_QUERY_JSON = { "result": { "createdAt": "2024-01-23T04:54:41.679Z", "query": "string", "queryId": "string", "queryName": "string", } } TEST_GET_SAVED_QUERY_BY_ID_JSON = { "result": { "createdAt": "2024-01-23T04:55:10.320Z", "query": "string", "queryId": "string", "queryName": "string", } } TEST_EDIT_SAVED_QUERY_BY_ID_JSON = { "result": { "createdAt": "2024-01-23T04:55:25.070Z", "query": "string", "queryId": "string", "queryName": "string", } } TEST_DELETE_SAVED_QUERY_BY_ID_JSON = {} class SavedQueriesTests(CensysTestCase): api: SavedQueries def setup_method(self): self.setUpApi(SavedQueries(self.api_key)) @pytest.mark.parametrize( ("kwargs", "params"), [ ( { "query_name_prefix": "test-query-name-prefix-1", "page_size": 1, "page": 2, "filter_term": "test-filter-term-1", }, "?queryNamePrefix=test-query-name-prefix-1&pageSize=1&page=2&filterTerm=test-filter-term-1", ), ( { "query_name_prefix": "test-query-name-prefix-2", "page_size": 2, "page": 3, }, "?queryNamePrefix=test-query-name-prefix-2&pageSize=2&page=3", ), ( { "query_name_prefix": "test-query-name-prefix-3", "page_size": 1, "filter_term": "test-filter-term-3", }, "?queryNamePrefix=test-query-name-prefix-3&pageSize=1&page=1&filterTerm=test-filter-term-3", ), ( { "query_name_prefix": "test-query-name-prefix-4", "page": 2, "filter_term": "test-filter-term-4", }, "?queryNamePrefix=test-query-name-prefix-4&pageSize=50&page=2&filterTerm=test-filter-term-4", ), ( {"page_size": 1, "page": 2, "filter_term": "test-filter-term-5"}, "?pageSize=1&page=2&filterTerm=test-filter-term-5", ), ( {"query_name_prefix": "test-query-name-prefix-6"}, "?queryNamePrefix=test-query-name-prefix-6&pageSize=50&page=1", ), ({}, "?pageSize=50&page=1"), ], ) def test_get_saved_queries(self, kwargs, params): # Setup response self.responses.add( responses.GET, SAVED_QUERIES_BASE_PATH + params, json=TEST_GET_SAVED_QUERIES_JSON, status=200, ) # Actual call res = self.api.get_saved_queries(**kwargs) # Assertions assert res == TEST_GET_SAVED_QUERIES_JSON @pytest.mark.parametrize( ("kwargs", "body"), [ ( {"query": "test-query-1", "query_name": "test-query-name-1"}, {"query": "test-query-1", "queryName": "test-query-name-1"}, ), ], ) def test_add_saved_query(self, kwargs, body): # Setup response self.responses.add( responses.POST, SAVED_QUERIES_BASE_PATH, status=200, json=TEST_ADD_SAVED_QUERY_JSON, match=[responses.json_params_matcher(body)], ) # Actual call res = self.api.add_saved_query(**kwargs) # Assertions assert res == TEST_ADD_SAVED_QUERY_JSON @pytest.mark.parametrize( ("kwargs", "path"), [ ({"query_id": "test-query-id-1"}, "/test-query-id-1"), ], ) def test_get_saved_query_by_id(self, kwargs, path): # Setup response self.responses.add( responses.GET, SAVED_QUERIES_BASE_PATH + path, status=200, json=TEST_GET_SAVED_QUERY_BY_ID_JSON, ) # Actual call res = self.api.get_saved_query_by_id(**kwargs) # Assertions assert res == TEST_GET_SAVED_QUERY_BY_ID_JSON @pytest.mark.parametrize( ("kwargs", "body", "path"), [ ( { "query_id": "test-query-id-1", "query": "test-query-1", "query_name": "test-query-name-1", }, {"query": "test-query-1", "queryName": "test-query-name-1"}, "/test-query-id-1", ), ], ) def test_edit_saved_query_by_id(self, kwargs, body, path): # Setup response self.responses.add( responses.PUT, SAVED_QUERIES_BASE_PATH + path, status=200, json=TEST_EDIT_SAVED_QUERY_BY_ID_JSON, match=[responses.json_params_matcher(body)], ) # Actual call res = self.api.edit_saved_query_by_id(**kwargs) # Assertions assert res == TEST_EDIT_SAVED_QUERY_BY_ID_JSON @pytest.mark.parametrize( ("kwargs", "path"), [ ({"query_id": "test-query-id-1"}, "/test-query-id-1"), ], ) def test_delete_saved_query_by_id(self, kwargs, path): # Setup response self.responses.add( responses.DELETE, SAVED_QUERIES_BASE_PATH + path, status=200, json=TEST_DELETE_SAVED_QUERY_BY_ID_JSON, ) # Actual call res = self.api.delete_saved_query_by_id(**kwargs) # Assertions assert res == TEST_DELETE_SAVED_QUERY_BY_ID_JSON censys-censys-python-7a57a89/tests/asm/test_seeds.py000066400000000000000000000150051524433232300226460ustar00rootroot00000000000000import unittest import pytest from pytest_mock import MockerFixture from censys.asm.client import AsmClient from .utils import TEST_SUCCESS_CODE, TEST_TIMEOUT, V1_URL, MockResponse SEEDS_URL = f"{V1_URL}/seeds" SEED_RESOURCE_TYPE = "seeds" TEST_SEED_ID = 6 TEST_SEED_LABEL = "seed-test-label" TEST_SEED_TYPE = "DOMAIN_NAME" TEST_SEED = {"type": "ASN", "value": 90000, "label": "seed-test-label"} TEST_SEED_NO_LABEL = { "type": "ASN", "value": 90001, } TEST_SEED_LIST = [ {"type": "ASN", "value": 90002, "label": "seed-test-label"}, {"type": "ASN", "value": 90003, "label": "seed-test-label"}, {"type": "ASN", "value": 90004, "label": "seed-test-label"}, ] TEST_SEED_LIST_NO_LABEL = [ {"type": "ASN", "value": 90002}, {"type": "ASN", "value": 90003}, {"type": "ASN", "value": 90004}, ] class SeedsUnitTests(unittest.TestCase): """Unit tests for Seeds API.""" @pytest.fixture(autouse=True) def __inject_fixtures(self, mocker: MockerFixture): """Injects fixtures into the test case. Args: mocker (MockerFixture): pytest-mock fixture. """ # Inject mocker fixture self.mocker = mocker """Unit tests for Host, Certificate, and Domain APIs.""" def setUp(self): self.client = AsmClient() def test_get_seeds(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.get_seeds() # Assertions mock_request.assert_called_with(SEEDS_URL, params={}, timeout=TEST_TIMEOUT) def test_get_seeds_with_label(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.get_seeds(seed_type="ASN", label=TEST_SEED_LABEL) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"type": "ASN", "label": TEST_SEED_LABEL}, timeout=TEST_TIMEOUT, ) def test_get_seeds_by_type(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.get_seeds(seed_type=TEST_SEED_TYPE) # Assertions mock_request.assert_called_with(SEEDS_URL, params={"type": TEST_SEED_TYPE}, timeout=TEST_TIMEOUT) def test_get_seed_by_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.get") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.get_seed_by_id(TEST_SEED_ID) # Assertions mock_request.assert_called_with(f"{SEEDS_URL}/{TEST_SEED_ID}", params={}, timeout=TEST_TIMEOUT) def test_add_seed(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.add_seeds([TEST_SEED]) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"force": None}, timeout=TEST_TIMEOUT, json={"seeds": [TEST_SEED]}, ) def test_add_seeds(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.add_seeds(TEST_SEED_LIST) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"force": None}, timeout=TEST_TIMEOUT, json={"seeds": TEST_SEED_LIST}, ) def test_add_seeds_forced(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.post") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.add_seeds(TEST_SEED_LIST, force=True) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"force": True}, timeout=TEST_TIMEOUT, json={"seeds": TEST_SEED_LIST}, ) def test_replace_seeds_by_label(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.put") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.replace_seeds_by_label(TEST_SEED_LABEL, [TEST_SEED_NO_LABEL]) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"label": "seed-test-label", "force": None}, timeout=TEST_TIMEOUT, json={"seeds": [TEST_SEED_NO_LABEL]}, ) def test_replace_seeds_by_label_forced(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.put") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.replace_seeds_by_label(TEST_SEED_LABEL, TEST_SEED_LIST_NO_LABEL, force=True) # Assertions mock_request.assert_called_with( SEEDS_URL, params={"label": "seed-test-label", "force": True}, timeout=TEST_TIMEOUT, json={"seeds": TEST_SEED_LIST_NO_LABEL}, ) def test_delete_seeds_by_label(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.delete") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.delete_seeds_by_label(TEST_SEED_LABEL) # Assertions mock_request.assert_called_with(SEEDS_URL, params={"label": TEST_SEED_LABEL}, timeout=TEST_TIMEOUT) def test_delete_seed_by_id(self): # Mock mock_request = self.mocker.patch("censys.common.base.requests.Session.delete") mock_request.return_value = MockResponse(TEST_SUCCESS_CODE, SEED_RESOURCE_TYPE) # Actual call self.client.seeds.delete_seed_by_id(TEST_SEED_ID) # Assertions mock_request.assert_called_with(f"{SEEDS_URL}/{TEST_SEED_ID}", params={}, timeout=TEST_TIMEOUT) censys-censys-python-7a57a89/tests/asm/utils.py000066400000000000000000000042121524433232300216420ustar00rootroot00000000000000RESOURCE_PAGING_RESULTS = ["a", "b", "c", "a", "b", "c", "a", "b", "c"] TEST_TIMEOUT = 30 TEST_SUCCESS_CODE = 200 BASE_URL = "https://app.censys.io/api" V1_URL = f"{BASE_URL}/v1" V2_URL = f"{BASE_URL}/v2" BETA_URL = f"{BASE_URL}/beta" INVENTORY_URL = f"{BASE_URL}/inventory" WORKSPACE_ID = "test-workspace-id" class MockResponse: # Dummy resource list to iterate over for testing paging RESOURCES = ["a", "b", "c"] # Dummy end of events list to simulate 3 pages of results END_OF_EVENTS = [False, False, True] def __init__(self, status_code, resource_type, page_number=1): self.number_generator = self.get_page_number() self.end_of_events_generator = self.get_end_of_events() self.resource_type = resource_type self.page_number = page_number self.status_code = status_code self.json_data = { resource_type: self.get_resource(), # Return dummy resource generator "pageNumber": 1, "totalPages": 3, # Default to 3 pages of resources "nextCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ9", "cursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MH0", "endOfEvents": False, } @property def ok(self): return 200 <= self.status_code <= 400 def json(self): if self.resource_type != "instances": self.json_data["endOfEvents"] = next(self.end_of_events_generator) self.json_data["pageNumber"] = next(self.number_generator) else: self.json_data["cursor"] = "test" if not next(self.end_of_events_generator) else None if "pageNumber" in self.json_data: del self.json_data["pageNumber"] self.json_data[self.resource_type] = self.get_resource() return self.json_data # Generate dummy resources for pagination def get_resource(self): yield from self.RESOURCES # Generate next page number for pagination; max 10 pages def get_page_number(self): for i in range(0, 10): yield i + self.page_number # Generate endOfEvents boolean def get_end_of_events(self): yield from self.END_OF_EVENTS censys-censys-python-7a57a89/tests/cli/000077500000000000000000000000001524433232300201205ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/cli/__init__.py000066400000000000000000000000001524433232300222170ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/cli/test.xml000066400000000000000000000247371524433232300216360ustar00rootroot00000000000000
censys-censys-python-7a57a89/tests/cli/test_account.py000066400000000000000000000034331524433232300231700ustar00rootroot00000000000000import contextlib import json from io import StringIO import pytest import responses from censys.cli import main as cli_main from tests.search.v1.test_api import ACCOUNT_JSON from tests.utils import V1_URL, CensysTestCase class CensysCliAccountTest(CensysTestCase): def test_table(self): # Mock self.patch_args( [ "censys", "account", ], search_auth=True, ) self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) temp_stdout = StringIO() # Actual call with ( contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit, match="0"), ): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert ACCOUNT_JSON["email"] in cli_response assert ACCOUNT_JSON["login"] in cli_response quota = ACCOUNT_JSON["quota"] assert f"{quota['used']} / {quota['allowance']}" in cli_response def test_json(self): # Mock self.patch_args( [ "censys", "account", "--json", ], search_auth=True, ) self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) temp_stdout = StringIO() # Actual call with ( contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit, match="0"), ): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert json.loads(cli_response) == ACCOUNT_JSON censys-censys-python-7a57a89/tests/cli/test_asm.py000066400000000000000000002065271524433232300223250ustar00rootroot00000000000000import contextlib import json import os.path from io import StringIO import pytest import responses from responses import matchers from responses.matchers import json_params_matcher from censys.cli import main as cli_main from censys.cli.commands.asm import get_seeds_from_xml from tests.asm.utils import INVENTORY_URL, V1_URL, WORKSPACE_ID from tests.cli.test_config import TEST_CONFIG_PATH from tests.utils import CensysTestCase SEEDS_JSON = [ {"value": 0, "type": "ASN"}, {"value": 0, "type": "IP_ADDRESS"}, {"value": 0, "type": "DOMAIN_NAME"}, {"value": 0, "type": "CIDR"}, ] XML_SEEDS = [ {"value": "45.33.32.156", "type": "IP_ADDRESS"}, {"value": "scanme.nmap.org", "type": "DOMAIN_NAME"}, ] ADD_SEEDS_JSON = { "addedSeeds": [ { "source": "API", "createdOn": "string", "id": 0, "type": "ASN", "value": 0, "label": "string", }, { "source": "API", "createdOn": "string", "id": 0, "type": "IP_ADDRESS", "value": "string", "label": "string", }, { "source": "API", "createdOn": "string", "id": 0, "type": "DOMAIN_NAME", "value": "string", "label": "string", }, { "source": "API", "createdOn": "string", "id": 0, "type": "CIDR", "value": "string", "label": "string", }, ], "skippedReservedSeeds": ["string", 0], } GET_SEEDS_JSON = { "seeds": [ { "id": 1, "type": "ASN", "value": 0, "label": "Test", "source": "API", "createdOn": "2022-11-01T12:34:23.111142Z", }, { "id": 2, "type": "IP_ADDRESS", "value": "1.2.3.4", "label": "Test", "source": "API", "createdOn": "2022-11-01T12:34:23.111142Z", }, { "id": 3, "type": "DOMAIN_NAME", "value": "foo.com", "label": "Test", "source": "API", "createdOn": "2022-11-01T12:34:23.111142Z", }, { "id": 4, "type": "CIDR", "value": "200.200.200.0/24", "label": "Test", "source": "API", "createdOn": "2022-11-01T12:34:23.111142Z", }, { "id": 5, "type": "IP_ADDRESS", "value": "5.6.7.8", "label": "Test 2", "source": "API", "createdOn": "2022-11-01T12:34:23.111142Z", }, ] } GET_SAVED_QUERIES_JSON = { "results": [ { "queryId": "1", "queryName": "foo domain", "query": "domain: foo.com", "createdAt": "2024-01-01T01:00:00.000Z", }, { "queryId": "2", "queryName": "bar domain", "query": "domain: bar.com", "createdAt": "2024-01-01T01:00:00.000Z", }, ], "totalResults": 2, } ADD_SAVED_QUERY_JSON = { "result": { "createdAt": "2024-01-01T01:00:00.000Z", "query": "domain: foo.com", "queryName": "Test", "queryId": "100", }, } SAVED_QUERY_JSON = { "result": { "queryId": "100", "queryName": "foo domain", "query": "domain: foo.com", "createdAt": "2024-01-01T01:00:00.000Z", }, "totalResults": 1, } SEARCH_JSON = { "totalHits": 3, "queryDurationMillis": 50, "hits": [ {"_details": {}, "domain": {"name": "foo.com"}, "type": "DOMAIN"}, { "_details": {}, "domain": {"name": "bar.foo.com"}, "type": "DOMAIN", }, { "_details": {}, "domain": {"name": "b.foo.com"}, "type": "DOMAIN", }, ], } SEARCH_JSON_PAGE_1 = { "totalHits": 5, "nextCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ9", "queryDurationMillis": 50, "hits": [ {"_details": {}, "domain": {"name": "foo.com"}, "type": "DOMAIN"}, { "_details": {}, "domain": {"name": "bar.foo.com"}, "type": "DOMAIN", }, ], } SEARCH_JSON_PAGE_2 = { "totalHits": 5, "nextCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ8", "previousCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ9", "queryDurationMillis": 50, "hits": [ { "_details": {}, "domain": {"name": "b.foo.com"}, "type": "DOMAIN", }, { "_details": {}, "domain": {"name": "a.foo.com"}, "type": "DOMAIN", }, ], } SEARCH_JSON_PAGE_3 = { "totalHits": 5, "previousCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ8", "queryDurationMillis": 50, "hits": [ { "_details": {}, "domain": {"name": "r.foo.com"}, "type": "DOMAIN", }, ], } SEARCH_JSON_PAGINATED = { "totalHits": 5, "previousCursor": "eyJmaWx0ZXIiOnt9LCJzdGFydCI6MjA3MTJ8", "queryDurationMillis": 50, "hits": [ {"_details": {}, "domain": {"name": "foo.com"}, "type": "DOMAIN"}, { "_details": {}, "domain": {"name": "bar.foo.com"}, "type": "DOMAIN", }, { "_details": {}, "domain": {"name": "b.foo.com"}, "type": "DOMAIN", }, { "_details": {}, "domain": {"name": "a.foo.com"}, "type": "DOMAIN", }, { "_details": {}, "domain": {"name": "r.foo.com"}, "type": "DOMAIN", }, ], } SAVED_QUERY_ID = "12345" SAVED_QUERY_NAME = "Test query" TEST_XML_PATH = os.path.join(os.path.dirname(__file__), "test.xml") class CensysASMCliTest(CensysTestCase): def test_add_seeds(self): # Mock self.patch_args(["censys", "asm", "add-seeds", "-j", json.dumps(SEEDS_JSON)], asm_auth=True) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert "Added 4 seeds" in temp_stdout.getvalue() def test_add_seeds_no_type(self): # Mock self.patch_args(["censys", "asm", "add-seeds", "-j", json.dumps(["1.1.1.1"])], asm_auth=True) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[json_params_matcher({"seeds": [{"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}]})], ) # Actual call cli_main() def test_add_seeds_multi_type(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "-j", json.dumps( [ {"value": "1.1.1.1"}, {"value": "192.168. 0.15/24", "type": "CIDR"}, ] ), ], asm_auth=True, ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ {"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}, {"value": "192.168. 0.15/24", "type": "CIDR", "label": ""}, ] } ) ], ) # Actual call cli_main() def test_add_seeds_from_stdin(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "-i", "-", ], asm_auth=True, ) self.mocker.patch( "sys.stdin", StringIO( json.dumps( [ {"value": "1.1.1.1"}, {"value": "192.168. 0.15/24", "type": "CIDR"}, ] ) ), ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ {"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}, {"value": "192.168. 0.15/24", "type": "CIDR", "label": ""}, ] } ) ], ) # Actual call cli_main() def test_add_seeds_from_file_json(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "-i", "test.json", ], asm_auth=True, ) self.mocker.patch( "builtins.open", new_callable=self.mocker.mock_open, read_data=json.dumps( [ {"value": "1.1.1.1"}, {"value": "192.168.0.15/24", "type": "CIDR"}, ] ), ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ {"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}, {"value": "192.168.0.15/24", "type": "CIDR", "label": ""}, ] } ) ], ) # Actual call cli_main() def test_add_seeds_from_file_csv(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "--csv", "-i", "seeds.csv", ], asm_auth=True, ) # Just to make sure we're reading from the fake file self.mocker.patch("censys.common.config.CONFIG_PATH", TEST_CONFIG_PATH) self.mocker.patch( "builtins.open", new_callable=self.mocker.mock_open, read_data="\n".join(["type,value", "IP_ADDRESS,1.1.1.1", "CIDR,192.168.0.15/24"]), ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ {"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}, {"value": "192.168.0.15/24", "type": "CIDR", "label": ""}, ] } ) ], ) # Actual call cli_main() def test_add_seeds_from_file_csv_without_flag(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "-i", "seeds.csv", ], asm_auth=True, ) # Just to make sure we're reading from the fake file self.mocker.patch("censys.common.config.CONFIG_PATH", TEST_CONFIG_PATH) self.mocker.patch( "builtins.open", new_callable=self.mocker.mock_open, read_data="\n".join(["type,value", "IP_ADDRESS,1.1.1.1", "CIDR,192.168.0.15/24"]), ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ {"value": "1.1.1.1", "type": "IP_ADDRESS", "label": ""}, {"value": "192.168.0.15/24", "type": "CIDR", "label": ""}, ] } ) ], ) # Actual call cli_main() def test_add_seeds_invalid_json(self): # Mock self.patch_args( ["censys", "asm", "add-seeds", "-j", "
not json
"], asm_auth=True, ) # Actual call/error raising with pytest.raises(SystemExit, match="1"): cli_main() def test_add_seeds_bad_json(self): # Mock self.patch_args(["censys", "asm", "add-seeds", "-j", json.dumps([12345])], asm_auth=True) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "Invalid seed" in temp_stdout.getvalue() def test_add_seeds_partial(self): # Mock self.patch_args(["censys", "asm", "add-seeds", "-j", json.dumps(SEEDS_JSON)], asm_auth=True) partial_json = ADD_SEEDS_JSON.copy() partial_json["addedSeeds"] = partial_json["addedSeeds"][1:] self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=partial_json, ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert "Added 3 seeds" in temp_stdout.getvalue() assert "Seeds not added: 1" in temp_stdout.getvalue() def test_add_seeds_none(self): # Mock self.patch_args(["censys", "asm", "add-seeds", "-j", json.dumps(SEEDS_JSON)], asm_auth=True) partial_json = ADD_SEEDS_JSON.copy() partial_json["addedSeeds"] = [] self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=partial_json, ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "No seeds were added. (Run with -v to get more info)" in temp_stdout.getvalue() def test_get_seeds_from_xml(self): # Actual call seeds = get_seeds_from_xml(str(TEST_XML_PATH)) # Assertions assert seeds == XML_SEEDS def test_add_seeds_from_xml_file(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "--nmap-xml", str(TEST_XML_PATH), ], asm_auth=True, ) self.responses.add( responses.POST, V1_URL + "/seeds", status=200, json=ADD_SEEDS_JSON, match=[ json_params_matcher( { "seeds": [ { "value": "45.33.32.156", "type": "IP_ADDRESS", "label": "", }, { "value": "scanme.nmap.org", "type": "DOMAIN_NAME", "label": "", }, ] } ) ], ) # Actual call cli_main() def test_add_seeds_from_invalid_xml_file(self): # Mock self.patch_args( [ "censys", "asm", "add-seeds", "--nmap-xml", str(TEST_XML_PATH), ], asm_auth=True, ) self.mocker.patch( "xml.etree.ElementTree.open", new_callable=self.mocker.mock_open, read_data="
not xml
", ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_delete_all_seeds_force_one_seed(self): # Mock self.patch_args( [ "censys", "asm", "delete-all-seeds", "--force", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json={"seeds": [item for item in GET_SEEDS_JSON["seeds"] if item["value"] == "1.2.3.4"]}, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 2 # make sure both requests were seen assert "Deleted 1 of 1 total seeds." in temp_stdout.getvalue() def test_delete_all_seeds_force(self): # Mock # # The idea is that delete-all-seeds will call GET to get seeds, we'll give it back the # two IP_ADDRESS seeds (just to cut down the list to test), and then we should see it call # DELETE on each of the seed IDs we gave it back, and then it will report on the number of # seeds deleted # self.patch_args( [ "censys", "asm", "delete-all-seeds", "--force", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json={"seeds": [item for item in GET_SEEDS_JSON["seeds"] if item["type"] == "IP_ADDRESS"]}, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/5", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 of 2 total seeds." in temp_stdout.getvalue() def test_delete_all_seeds_yes(self): # Mock # # The idea is that delete-all-seeds will call GET to get seeds, we'll give it back the # two IP_ADDRESS seeds (just to cut down the list to test), and then we should see it call # DELETE on each of the seed IDs we gave it back, and then it will report on the number of # seeds deleted # self.patch_args( [ "censys", "asm", "delete-all-seeds", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json={"seeds": [item for item in GET_SEEDS_JSON["seeds"] if item["type"] == "IP_ADDRESS"]}, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/5", status=200, match=[matchers.query_param_matcher({})], ) self.mocker.patch("builtins.input", side_effect=["y"]) # answer 'y' to are you sure # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 of 2 total seeds." in temp_stdout.getvalue() def test_delete_all_seeds_no(self): # Mock self.patch_args( [ "censys", "asm", "delete-all-seeds", ], asm_auth=True, ) self.mocker.patch("builtins.input", side_effect=["n"]) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_delete_seeds_by_ip(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "-j", json.dumps(["1.2.3.4", "5.6.7.8", "9.9.9.9"]), ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/5", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 seeds.\nUnable to delete 1 seeds because they were not present.\n" in temp_stdout.getvalue() def test_delete_seed_by_id(self): # Mock self.patch_args( ["censys", "asm", "delete-seeds", "-j", json.dumps([{"id": 2}])], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 2 # make sure all three requests were seen assert "Deleted 1 seeds." in temp_stdout.getvalue() def test_delete_seeds_by_id(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "-j", json.dumps([{"id": 2}, {"id": 5}]), ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/5", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 seeds." in temp_stdout.getvalue() def test_delete_seeds_by_csv(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "--csv", "-i", "seeds.csv", ], asm_auth=True, ) # Just to make sure we're reading from the fake file self.mocker.patch("censys.common.config.CONFIG_PATH", TEST_CONFIG_PATH) self.mocker.patch( "builtins.open", new_callable=self.mocker.mock_open, read_data="\n".join(["type,value", "IP_ADDRESS,1.2.3.4", "CIDR,200.200.200.0/24"]), ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/4", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 seeds." in temp_stdout.getvalue() def test_delete_seeds_by_asm_csv(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "--csv", "-i", "seeds.csv", ], asm_auth=True, ) # Just to make sure we're reading from the fake file self.mocker.patch("censys.common.config.CONFIG_PATH", TEST_CONFIG_PATH) self.mocker.patch( "builtins.open", new_callable=self.mocker.mock_open, read_data="\n".join(["Type,Value", "IP_ADDRESS,1.2.3.4", "CIDR,200.200.200.0/24"]), ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/4", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 2 seeds." in temp_stdout.getvalue() def test_delete_seeds_nonexistent_id(self): # Mock self.patch_args( ["censys", "asm", "delete-seeds", "-j", json.dumps([{"id": 100}])], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/100", status=404, match=[matchers.query_param_matcher({})], body=json.dumps( { "message": "Unable to Find Seed", "errorCode": 10014, "details": [{"id": 100}], } ), ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 2 # make sure all three requests were seen assert "Deleted 0 seeds.\nUnable to delete 1 seeds because they were not present.\n" in temp_stdout.getvalue() def test_delete_seeds_multiple_nonexistent_id(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "-j", json.dumps([{"id": 100}, {"id": 101}]), ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/100", status=404, match=[matchers.query_param_matcher({})], body=json.dumps( { "message": "Unable to Find Seed", "errorCode": 10014, "details": [{"id": 100}], } ), ) self.responses.add( responses.DELETE, V1_URL + "/seeds/101", status=404, match=[matchers.query_param_matcher({})], body=json.dumps( { "message": "Unable to Find Seed", "errorCode": 10014, "details": [{"id": 101}], } ), ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert len(self.responses.calls) == 3 # make sure all three requests were seen assert "Deleted 0 seeds.\nUnable to delete 2 seeds because they were not present.\n" in temp_stdout.getvalue() def test_delete_seeds_no_id_or_ip(self): # Mock self.patch_args( ["censys", "asm", "delete-seeds", "-j", json.dumps([{"foo": 100}])], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="0"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "Error, no seed id or value for seed.\nNo seeds to delete.\n" in temp_stdout.getvalue() def test_delete_seeds_id_and_value(self): # Mock self.patch_args( [ "censys", "asm", "delete-seeds", "-j", json.dumps([{"id": 1, "value": "1.2.3.4"}]), ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) self.responses.add( responses.DELETE, V1_URL + "/seeds/2", status=200, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert "Deleted 1 seeds.\n" in temp_stdout.getvalue() def test_delete_labeled_seeds(self): # Mock self.patch_args(["censys", "asm", "delete-labeled-seeds", "--label", "Test"], asm_auth=True) self.responses.add( responses.DELETE, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({"label": "Test"})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert 'Deleted seeds with label "Test".\n' in temp_stdout.getvalue() def test_delete_labeled_seeds_without_label(self): # Mock self.patch_args( [ "censys", "asm", "delete-labeled-seeds", ], asm_auth=True, ) # Actual call with pytest.raises(SystemExit, match="2"): cli_main() def test_replace_labeled_seeds(self): # Mock self.patch_args( [ "censys", "asm", "replace-labeled-seeds", "--label", "Test", "-j", json.dumps( [ {"value": "1.1.1.1"}, { "value": "192.168. 0.15/24", "type": "CIDR", "label": "Test Label", }, ] ), ], asm_auth=True, ) self.responses.add( responses.PUT, V1_URL + "/seeds", status=200, json={ "removedSeeds": [], "skippedReservedSeeds": [], "addedSeeds": [ { "id": 100, "value": "1.1.1.1", "type": "IP_ADDRESS", }, { "id": 101, "value": "192.168. 0.15/24", "type": "CIDR", }, ], }, match=[matchers.query_param_matcher({"label": "Test", "force": True})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert "Removed 0 seeds. Added 2 seeds. Skipped 0 reserved seeds." in temp_stdout.getvalue() def test_replace_labeled_seeds_without_label(self): # Mock self.patch_args( [ "censys", "asm", "replace-labeled-seeds", ], asm_auth=True, ) # Actual call with pytest.raises(SystemExit, match="2"): cli_main() def test_list_seeds_json(self): # Mock self.patch_args( [ "censys", "asm", "list-seeds", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == GET_SEEDS_JSON["seeds"] def test_list_seeds_csv(self): # Mock self.patch_args( [ "censys", "asm", "list-seeds", "--csv", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json=GET_SEEDS_JSON, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "id,type,value,label,source,createdOn", "1,ASN,0,Test,API,2022-11-01T12:34:23.111142Z", "2,IP_ADDRESS,1.2.3.4,Test,API,2022-11-01T12:34:23.111142Z", "3,DOMAIN_NAME,foo.com,Test,API,2022-11-01T12:34:23.111142Z", "4,CIDR,200.200.200.0/24,Test,API,2022-11-01T12:34:23.111142Z", "5,IP_ADDRESS,5.6.7.8,Test 2,API,2022-11-01T12:34:23.111142Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_seeds_type_ip(self): # Mock self.patch_args( [ "censys", "asm", "list-seeds", "--type", "IP_ADDRESS", "--csv", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json={"seeds": [item for item in GET_SEEDS_JSON["seeds"] if item["type"] == "IP_ADDRESS"]}, match=[matchers.query_param_matcher({"type": "IP_ADDRESS"})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "id,type,value,label,source,createdOn", "2,IP_ADDRESS,1.2.3.4,Test,API,2022-11-01T12:34:23.111142Z", "5,IP_ADDRESS,5.6.7.8,Test 2,API,2022-11-01T12:34:23.111142Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_seeds_label_test(self): # Mock self.patch_args( [ "censys", "asm", "list-seeds", "--label", "Test", "--csv", ], asm_auth=True, ) self.responses.add( responses.GET, V1_URL + "/seeds", status=200, json={"seeds": [item for item in GET_SEEDS_JSON["seeds"] if item["label"] == "Test"]}, match=[matchers.query_param_matcher({"label": "Test"})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "id,type,value,label,source,createdOn", "1,ASN,0,Test,API,2022-11-01T12:34:23.111142Z", "2,IP_ADDRESS,1.2.3.4,Test,API,2022-11-01T12:34:23.111142Z", "3,DOMAIN_NAME,foo.com,Test,API,2022-11-01T12:34:23.111142Z", "4,CIDR,200.200.200.0/24,Test,API,2022-11-01T12:34:23.111142Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_json(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json=GET_SAVED_QUERIES_JSON, match=[matchers.query_param_matcher({"pageSize": 50, "page": 1})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == GET_SAVED_QUERIES_JSON def test_list_saved_queries_csv(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json=GET_SAVED_QUERIES_JSON, match=[matchers.query_param_matcher({"pageSize": 50, "page": 1})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "queryId,queryName,query,createdAt", "1,foo domain,domain: foo.com,2024-01-01T01:00:00.000Z", "2,bar domain,domain: bar.com,2024-01-01T01:00:00.000Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_page_2(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", "--page", "2", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json=GET_SAVED_QUERIES_JSON, match=[matchers.query_param_matcher({"pageSize": 50, "page": 2})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "queryId,queryName,query,createdAt", "1,foo domain,domain: foo.com,2024-01-01T01:00:00.000Z", "2,bar domain,domain: bar.com,2024-01-01T01:00:00.000Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_page_size_1(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", "--page-size", "1", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json={ "totalResults": "1", "results": [GET_SAVED_QUERIES_JSON["results"][0]], }, match=[matchers.query_param_matcher({"pageSize": 1, "page": 1})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "queryId,queryName,query,createdAt", "1,foo domain,domain: foo.com,2024-01-01T01:00:00.000Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_query_name_prefix(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", "--query-name-prefix", "foo", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json={ "totalResults": 2, "results": [query for query in GET_SAVED_QUERIES_JSON["results"] if "foo" in query["queryName"]], }, match=[matchers.query_param_matcher({"pageSize": 50, "page": 1, "queryNamePrefix": "foo"})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "queryId,queryName,query,createdAt", "1,foo domain,domain: foo.com,2024-01-01T01:00:00.000Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_filter_term(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", "--filter-term", "domain", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json=GET_SAVED_QUERIES_JSON, match=[matchers.query_param_matcher({"pageSize": 50, "page": 1, "filterTerm": "domain"})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions expected_output = ( "\n".join( [ "queryId,queryName,query,createdAt", "1,foo domain,domain: foo.com,2024-01-01T01:00:00.000Z", "2,bar domain,domain: bar.com,2024-01-01T01:00:00.000Z", ] ) + "\n" ) assert expected_output in temp_stdout.getvalue() def test_list_saved_queries_keyerror(self): # Mock self.patch_args( [ "censys", "asm", "list-saved-queries", "--csv", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query", status=200, json={}, match=[matchers.query_param_matcher({"pageSize": 50, "page": 1})], ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_add_saved_query(self): # Mock self.patch_args( [ "censys", "asm", "add-saved-query", "--query-name", "Test", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.POST, INVENTORY_URL + "/v1/saved-query", status=200, json=ADD_SAVED_QUERY_JSON, match=[ json_params_matcher( { "queryName": "Test", "query": "domain: foo.com", } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert ( "Added saved query.\nQuery name: Test\nQuery: domain: foo.com\nQuery ID: 100\nCreated at: 2024-01-01T01:00:00.000Z\n" in temp_stdout.getvalue() ) def test_add_saved_query_failed(self): # Mock self.patch_args( [ "censys", "asm", "add-saved-query", "--query-name", "Test", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.POST, INVENTORY_URL + "/v1/saved-query", status=0, json={ "details": [{"details": "go here"}], "error": "string", "statusCode": 0, }, match=[ json_params_matcher( { "queryName": "Test", "query": "domain: foo.com", } ) ], ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_get_saved_query_by_id(self): # Mock self.patch_args( ["censys", "asm", "get-saved-query-by-id", "--query-id", "100"], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query/100", status=200, json=SAVED_QUERY_JSON, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert ( "Query name: foo domain\nQuery: domain: foo.com\nQuery ID: 100\nCreated at: 2024-01-01T01:00:00.000Z\n" in temp_stdout.getvalue() ) def test_get_saved_query_by_id_failed(self): # Mock self.patch_args( ["censys", "asm", "get-saved-query-by-id", "--query-id", "100"], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1/saved-query/100", status=0, json={ "details": [{"details": "go here"}], "error": "string", "statusCode": 0, }, match=[matchers.query_param_matcher({})], ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_edit_saved_query_by_id(self): # Mock self.patch_args( [ "censys", "asm", "edit-saved-query-by-id", "--query-id", "100", "--query-name", "foo domain", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.PUT, INVENTORY_URL + "/v1/saved-query/100", status=200, json=SAVED_QUERY_JSON, match=[ json_params_matcher( { "queryName": "foo domain", "query": "domain: foo.com", } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert ( "Edited saved query.\nQuery name: foo domain\nQuery: domain: foo.com\nQuery ID: 100\nCreated at: 2024-01-01T01:00:00.000Z\n" in temp_stdout.getvalue() ) def test_edit_saved_query_by_id_failed(self): # Mock self.patch_args( [ "censys", "asm", "edit-saved-query-by-id", "--query-id", "100", "--query-name", "foo domain", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.PUT, INVENTORY_URL + "/v1/saved-query/100", status=0, json={ "details": [{"details": "go here"}], "error": "string", "statusCode": 0, }, match=[ json_params_matcher( { "queryName": "foo domain", "query": "domain: foo.com", } ) ], ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_delete_saved_query_by_id(self): # Mock self.patch_args( ["censys", "asm", "delete-saved-query-by-id", "--query-id", "100"], asm_auth=True, ) self.responses.add( responses.DELETE, INVENTORY_URL + "/v1/saved-query/100", status=200, json={}, match=[matchers.query_param_matcher({})], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions assert "Deleted saved query with ID 100." in temp_stdout.getvalue() def test_delete_saved_query_by_id_failed(self): # Mock self.patch_args( ["censys", "asm", "delete-saved-query-by-id", "--query-id", "100"], asm_auth=True, ) self.responses.add( responses.DELETE, INVENTORY_URL + "/v1/saved-query/100", status=0, json={ "details": [{"details": "go here"}], "error": "string", "statusCode": 0, }, match=[matchers.query_param_matcher({})], ) # Actual call with pytest.raises(SystemExit, match="1"): cli_main() def test_execute_saved_query_by_name(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_queries") mock_query.return_value = { "results": [ { "queryId": "1", "queryName": "foo domain", "query": "domain: foo.com", "createdAt": "2024-01-01T01:00:00.000Z", } ] } self.patch_args( [ "censys", "asm", "execute-saved-query-by-name", "--query-name", "foo domain", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == SEARCH_JSON def test_execute_saved_query_by_name_not_found(self): # Mock mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_queries") mock_query.return_value = {"results": []} self.patch_args( [ "censys", "asm", "execute-saved-query-by-name", "--query-name", "nonexistent query name", ], asm_auth=True, ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "No saved query found with that name." in temp_stdout.getvalue() def test_execute_saved_query_by_name_failed(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_queries") mock_query.return_value = { "results": [ { "queryId": "1", "queryName": "foo domain", "query": "domain: foo.com", "createdAt": "2024-01-01T01:00:00.000Z", } ] } self.patch_args( [ "censys", "asm", "execute-saved-query-by-name", "--query-name", "foo domain", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=500, json={}, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "Failed to execute saved query." in temp_stdout.getvalue() def test_execute_saved_query_by_name_invalid_page_size(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_queries") mock_query.return_value = { "results": [ { "queryId": "1", "queryName": "foo domain", "query": "domain: foo.com", "createdAt": "2024-01-01T01:00:00.000Z", } ] } self.patch_args( [ "censys", "asm", "execute-saved-query-by-name", "--query-name", "foo domain", "--page-size", "1001", ], asm_auth=True, ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "page size must be within [0,1000]" in temp_stdout.getvalue() def test_execute_saved_query_by_id(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_query_by_id") mock_query.return_value = SAVED_QUERY_JSON self.patch_args( [ "censys", "asm", "execute-saved-query-by-id", "--query-id", "100", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == SEARCH_JSON def test_execute_saved_query_by_id_not_found(self): # Mock mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_query_by_id") mock_query.return_value = { "status_code": 404, "error": "No query found with ID: 500", } self.patch_args( [ "censys", "asm", "execute-saved-query-by-id", "--query-id", "500", ], asm_auth=True, ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "No saved query found with that ID." in temp_stdout.getvalue() def test_execute_saved_query_by_id_failed(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_query_by_id") mock_query.return_value = SAVED_QUERY_JSON self.patch_args( [ "censys", "asm", "execute-saved-query-by-id", "--query-id", "100", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=500, json={}, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "Failed to execute saved query." in temp_stdout.getvalue() def test_execute_saved_query_by_id_invalid_page_size(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID mock_query = self.mocker.patch("censys.asm.SavedQueries.get_saved_queries") mock_query.return_value = SAVED_QUERY_JSON self.patch_args( [ "censys", "asm", "execute-saved-query-by-id", "--query-id", "100", "--page-size", "1001", ], asm_auth=True, ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "page size must be within [0,1000]" in temp_stdout.getvalue() def test_search(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID self.patch_args( [ "censys", "asm", "search", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == SEARCH_JSON def test_search_failed(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID self.patch_args( [ "censys", "asm", "search", "--query", "domain: foo.com", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=500, json={}, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 50, } ) ], ) # Actual call temp_stdout = StringIO() with ( pytest.raises(SystemExit, match="1"), contextlib.redirect_stdout(temp_stdout), ): cli_main() # Assertions assert "Failed to execute query." in temp_stdout.getvalue() def test_search_paginated_all(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID self.patch_args( [ "censys", "asm", "search", "--query", "domain: foo.com", "--page-size", "2", "--pages", "-1", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON_PAGE_1, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 2, } ) ], ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON_PAGE_2, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "cursor": SEARCH_JSON_PAGE_1["nextCursor"], "query": "domain: foo.com", "pageSize": 2, } ) ], ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON_PAGE_3, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "cursor": SEARCH_JSON_PAGE_2["nextCursor"], "query": "domain: foo.com", "pageSize": 2, } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == SEARCH_JSON_PAGINATED def test_search_paginated_partial(self): # Mock mock_request = self.mocker.patch("censys.asm.api.CensysAsmAPI.get_workspace_id") mock_request.return_value = WORKSPACE_ID self.patch_args( [ "censys", "asm", "search", "--query", "domain: foo.com", "--page-size", "2", ], asm_auth=True, ) self.responses.add( responses.GET, INVENTORY_URL + "/v1", status=200, json=SEARCH_JSON_PAGE_1, match=[ matchers.query_param_matcher( { "workspaces": WORKSPACE_ID, "query": "domain: foo.com", "pageSize": 2, } ) ], ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions actual_json = json.loads(temp_stdout.getvalue()) assert actual_json == SEARCH_JSON_PAGE_1 censys-censys-python-7a57a89/tests/cli/test_cli.py000066400000000000000000000025711524433232300223050ustar00rootroot00000000000000import contextlib from io import StringIO import pytest from censys.cli import main as cli_main from censys.cli.commands import __all__ as cli_commands from censys.common import __version__ from tests.utils import CensysTestCase class CensysCliTest(CensysTestCase): def test_default_help(self): # Mock self.patch_args(["censys"]) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit): cli_main() # Assertions assert temp_stdout.getvalue().strip().startswith("usage: censys") def test_help(self): # Mock self.patch_args(["censys", "--help"]) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit): cli_main() stdout = temp_stdout.getvalue().strip() # Assertions assert stdout.startswith("usage: censys") assert "{" + ",".join(sorted(cli_commands)) + "}" in stdout assert "-v, --version" in stdout def test_version(self): # Mock self.patch_args(["censys", "-v"]) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit): cli_main() # Assertion assert __version__ in temp_stdout.getvalue() censys-censys-python-7a57a89/tests/cli/test_config.py000066400000000000000000000165411524433232300230050ustar00rootroot00000000000000import os import stat from unittest.mock import patch import pytest import responses from pytest_mock import MockerFixture from censys.cli import main as cli_main from censys.common.config import ( CENSYS_PATH, CONFIG_PATH, DEFAULT, _restricted_opener, default_config, get_config, write_config, ) from tests.search.v1.test_api import ACCOUNT_JSON from tests.utils import V1_URL, CensysTestCase TEST_CONFIG_PATH = CONFIG_PATH + ".test" def prompt_side_effect(arg, **kwargs): if arg == "Censys API ID": return CensysTestCase.api_id if arg == "Censys API Secret": return CensysTestCase.api_secret if arg == "Censys API ID [cyan](****************************aaaa)[/cyan]": return "" if arg == "Censys API Secret [cyan********************************bbbb)[/cyan]": return "" raise NotImplementedError(f"No prompt handler for {arg}") def confirm_side_effect(arg, **kwargs): return arg == "Do you want color output?" class CensysConfigCliTest(CensysTestCase): @pytest.fixture(autouse=True) def _config_setup(self, mocker: MockerFixture): mocker.patch("censys.common.config.CONFIG_PATH", TEST_CONFIG_PATH) self.mock_open = mocker.patch( "builtins.open", new_callable=mocker.mock_open, read_data="[DEFAULT]\napi_id =\napi_secret =\nasm_api_key =", ) mocker.patch("rich.prompt.Prompt.ask", side_effect=prompt_side_effect) mocker.patch("rich.prompt.Confirm.ask", side_effect=confirm_side_effect) self.mock_chmod = mocker.patch("censys.common.config._try_chmod") def test_search_config(self): # Mock self.patch_args( [ "censys", "config", ] ) self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) with pytest.raises(SystemExit, match="0"): cli_main() # Assert that the config file was read from the right place self.mock_open.assert_called_with(TEST_CONFIG_PATH, "w", opener=_restricted_opener) def test_search_config_failed(self): # Mock self.patch_args( [ "censys", "config", ] ) self.responses.add( responses.GET, V1_URL + "/account", status=401, json={"error": "Unauthorized"}, ) # Actual call/error raising with pytest.raises(SystemExit, match="1"): cli_main() def test_search_config_makedirs(self): self.patch_args( [ "censys", "config", ] ) self.mocker.patch("censys.common.config.os.path.isdir", return_value=False) mock_makedirs = self.mocker.patch("censys.common.config.os.makedirs") self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) with pytest.raises(SystemExit, match="0"): cli_main() mock_makedirs.assert_called_with(CENSYS_PATH, mode=0o700) def test_config_default(self): mock_isfile = self.mocker.patch("censys.common.config.os.path.isfile", return_value=True) config = get_config() mock_isfile.return_value = False mock_isfile.assert_called_with(TEST_CONFIG_PATH) self.mock_open.assert_called_once() for key, value in default_config.items(): assert value == config.get(DEFAULT, key) def test_search_config_custom_config(self): self.patch_args( [ "censys", "config", ] ) self.mocker.patch.dict("censys.common.config.os.environ", {"CENSYS_CONFIG_PATH": "censys.cfg"}) self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) with pytest.raises(SystemExit, match="0"): cli_main() # Assert that the config file was read from the right place self.mock_open.assert_called_with("censys.cfg", "w", opener=_restricted_opener) def test_search_config_perm_error(self): self.patch_args( [ "censys", "config", ] ) self.mocker.patch("censys.common.config.os.access", return_value=False) self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) with pytest.raises(SystemExit, match="1"): cli_main() @pytest.fixture def home_config(tmp_path, mocker, monkeypatch): """Points the default config location at a throwaway home directory.""" monkeypatch.delenv("CENSYS_CONFIG_PATH", raising=False) censys_path = tmp_path / ".config" / "censys" config_path = censys_path / "censys.cfg" mocker.patch("censys.common.config.HOME_PATH", str(tmp_path)) mocker.patch("censys.common.config.CENSYS_PATH", str(censys_path)) mocker.patch("censys.common.config.CONFIG_PATH", str(config_path)) return censys_path, config_path def test_write_config_creates_and_rewrites(home_config): censys_path, config_path = home_config # First write creates the directory and the file write_config(get_config()) assert censys_path.is_dir() assert config_path.is_file() # Second write takes the existing-directory and existing-file branches write_config(get_config()) assert get_config().get(DEFAULT, "color") == "auto" def test_write_config_survives_unchmodable_path(home_config): # A path we may not chmod must not stop the config from being written: # root-owned config dirs in containers, a non-owned CENSYS_CONFIG_PATH, and # mounts that reject chmod outright (NFS, CIFS, WSL DrvFs without metadata). _, config_path = home_config write_config(get_config()) with patch( "censys.common.config.os.chmod", side_effect=PermissionError(1, "Operation not permitted"), ): write_config(get_config()) assert config_path.is_file() @pytest.mark.skipif(os.name != "posix", reason="POSIX file permissions only") def test_write_config_restricts_permissions(home_config): censys_path, config_path = home_config old_umask = os.umask(0o022) try: write_config(get_config()) assert stat.S_IMODE(os.stat(censys_path).st_mode) & 0o077 == 0 assert stat.S_IMODE(os.stat(config_path).st_mode) & 0o077 == 0 # Pre-existing loose permissions are tightened on rewrite os.chmod(censys_path, 0o755) os.chmod(config_path, 0o644) write_config(get_config()) assert stat.S_IMODE(os.stat(censys_path).st_mode) & 0o077 == 0 assert stat.S_IMODE(os.stat(config_path).st_mode) & 0o077 == 0 finally: os.umask(old_umask) @pytest.mark.skipif(os.name != "posix", reason="POSIX file permissions only") def test_write_config_keeps_permissions_when_chmod_fails(home_config): _, config_path = home_config write_config(get_config()) with patch( "censys.common.config.os.chmod", side_effect=PermissionError(1, "Operation not permitted"), ): write_config(get_config()) assert stat.S_IMODE(os.stat(config_path).st_mode) & 0o077 == 0 censys-censys-python-7a57a89/tests/cli/test_hnri.py000066400000000000000000000104011524433232300224650ustar00rootroot00000000000000import contextlib from io import StringIO import pytest import responses from censys.cli import main as cli_main from censys.cli.commands.hnri import CensysHNRI from censys.common.exceptions import CensysCLIException from tests.search.v2.test_hosts import VIEW_HOST_JSON from tests.utils import V2_URL, CensysTestCase class CensysCliHNRITest(CensysTestCase): IPIFY_URL = "https://api.ipify.org?format=json" IP_ADDRESS = "8.8.8.8" def setup_method(self): self.api = CensysHNRI(self.api_id, self.api_secret) def test_hnri_medium(self): # Mock self.patch_args(["censys", "hnri"], search_auth=True) mock_ip = self.mocker.patch( "censys.cli.commands.hnri.CensysHNRI.get_current_ip", return_value=self.IP_ADDRESS, ) # Setup response response = VIEW_HOST_JSON.copy() response["result"]["services"] = [ {"port": 443, "service_name": "HTTPS"}, {"port": 53, "service_name": "DNS"}, ] self.responses.add( responses.GET, f"{V2_URL}/hosts/{self.IP_ADDRESS}", status=200, json=response, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() stdout = temp_stdout.getvalue().strip() # Assertions assert mock_ip.return_value in stdout assert "Medium Risks Found" in stdout assert "HTTPS" in stdout assert "443" in stdout assert "DNS" in stdout assert "53" in stdout def test_hnri_no_medium(self): self.patch_args(["censys", "hnri"], search_auth=True) # Mock mock_ip = self.mocker.patch( "censys.cli.commands.hnri.CensysHNRI.get_current_ip", return_value=self.IP_ADDRESS, ) # Setup response response = VIEW_HOST_JSON.copy() response["result"]["services"] = [{"port": 23, "service_name": "VNC"}] self.responses.add( responses.GET, f"{V2_URL}/hosts/{self.IP_ADDRESS}", status=200, json=response, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() stdout = temp_stdout.getvalue().strip() # Assertions assert mock_ip.return_value in stdout assert "High Risks Found" in stdout assert "VNC" in stdout assert "23" in stdout assert "You don't have any Medium Risks in your network" in stdout def test_hnri_not_found(self): self.patch_args(["censys", "hnri"], search_auth=True) # Mock mock_ip = self.mocker.patch( "censys.cli.commands.hnri.CensysHNRI.get_current_ip", return_value=self.IP_ADDRESS, ) # Setup response response = VIEW_HOST_JSON.copy() response["result"]["services"] = [] self.responses.add( responses.GET, f"{V2_URL}/hosts/{self.IP_ADDRESS}", status=200, json=response, ) temp_stdout = StringIO() # Actual Call with contextlib.redirect_stdout(temp_stdout): cli_main() stdout = temp_stdout.getvalue().strip() # Assertions assert mock_ip.return_value in stdout assert "No Risks were found on your network" in stdout def test_get_current_ip(self): # Setup response self.responses.add( responses.GET, self.IPIFY_URL, status=200, json={"ip": self.IP_ADDRESS}, ) # Actual call ip_address = self.api.get_current_ip() # Assertions assert ip_address == self.IP_ADDRESS def test_no_risks(self): # Actual call/error raising with pytest.raises(CensysCLIException): self.api.risks_to_string([], []) def test_open(self): self.patch_args(["censys", "hnri", "--open"]) # Mock mock_open = self.mocker.patch("censys.cli.commands.view.webbrowser.open") # Actual call/error raising with pytest.raises(SystemExit, match="0"): cli_main() # Assertions mock_open.assert_called_with("https://search.censys.io/me") censys-censys-python-7a57a89/tests/cli/test_search.py000066400000000000000000000337171524433232300230110ustar00rootroot00000000000000import argparse import contextlib import json import os from io import StringIO from pathlib import Path from typing import Optional from urllib.parse import urlencode import pytest import responses from requests import PreparedRequest from responses import matchers from censys.cli import main as cli_main from censys.cli.commands.search import ( CERTIFICATES_AUTOCOMPLETE, HOSTS_AUTOCOMPLETE, fields_completer, ) from censys.common.exceptions import CensysCLIException, CensysException from tests.search.v2.test_certs import SEARCH_CERTS_JSON from tests.search.v2.test_hosts import ( SEARCH_HOSTS_JSON, SERVER_ERROR_JSON, TOO_MANY_REQUESTS_ERROR_JSON, ) from tests.utils import V2_URL, CensysTestCase WROTE_PREFIX = "Wrote results to file" def search_callback(request: PreparedRequest) -> tuple[int, dict[str, str], str]: payload = json.loads(request.body) # type: ignore resp_body = { "result": { "query": payload["q"], "hits": [{"parsed.names": ["censys.io"], "parsed.issuer.country": "US"}], }, "links": { "prev": None, "next": "test-next-cursor", }, } return (200, {}, json.dumps(resp_body)) class CensysCliSearchTest(CensysTestCase): def test_search_help(self): # Mock self.patch_args(["censys", "search", "--help"]) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit): cli_main() # Assertions assert temp_stdout.getvalue().strip().startswith("usage: censys search") def test_no_creds(self): # Mock self.patch_args(["censys", "search", "test"]) self.mocker.patch("builtins.open", new_callable=self.mocker.mock_open, read_data="[DEFAULT]") self.mocker.patch.dict("os.environ", {"CENSYS_API_ID": "", "CENSYS_API_SECRET": ""}) # Actual Call/Assertion with pytest.raises(CensysException, match="No API ID or API secret configured."): cli_main() def test_invalid_timeout(self): # mock self.patch_args( [ "censys", "search", "parsed.names: censys.io", "--index-type", "certificates", "--timeout", "-1", ], search_auth=True, ) # Actual call with pytest.raises( CensysCLIException, match="Timeout must be greater than 0.", ): cli_main() def test_write_invalid_output_path(self): # Setup response self.responses.add( responses.POST, V2_URL + "/certificates/search", json=SEARCH_CERTS_JSON, ) # mock self.patch_args( [ "censys", "search", "parsed.names: censys.io", "--index-type", "certificates", "--fields", "parsed.issuer.country", "--output", "censys-certs.html", ], search_auth=True, ) # Actual call with pytest.raises( CensysCLIException, match="JSON is the only valid file format for Search 2.0 responses.", ): cli_main() def test_write_screen(self): # Setup response self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "RELEVANCE", "virtual_hosts": "EXCLUDE", } ) ], ) # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "1", ], search_auth=True, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() json_response = json.loads(temp_stdout.getvalue().strip()) # Assertions assert json_response == SEARCH_HOSTS_JSON["result"]["hits"] def test_search_virtual_hosts(self): # Setup response self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "RELEVANCE", "virtual_hosts": "ONLY", } ) ], ) # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "1", "--virtual-hosts", "ONLY", ], search_auth=True, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() json_response = json.loads(temp_stdout.getvalue().strip()) # Assertions assert json_response == SEARCH_HOSTS_JSON["result"]["hits"] def test_search_sort_order(self): # Setup response self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "DESCENDING", "virtual_hosts": "EXCLUDE", } ) ], ) # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "1", "--sort-order", "DESCENDING", ], search_auth=True, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() json_response = json.loads(temp_stdout.getvalue().strip()) # Assertions assert json_response == SEARCH_HOSTS_JSON["result"]["hits"] def test_write_json(self): # Setup response self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "RELEVANCE", "virtual_hosts": "EXCLUDE", } ) ], ) # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "1", "--output", "censys-hosts.json", ], search_auth=True, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert cli_response.startswith(WROTE_PREFIX) json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-hosts." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert len(json_response) >= 1 assert json_response == SEARCH_HOSTS_JSON["result"]["hits"] # Cleanup os.remove(json_path) def test_write_csv_fail(self): # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "1", "--output", "censys-hosts.csv", ], search_auth=True, ) # Actual call/error raising with pytest.raises( CensysCLIException, match="JSON is the only valid file format for Search 2.0 responses.", ): cli_main() @pytest.mark.parametrize( ("status_code", "json_response"), [ (429, TOO_MANY_REQUESTS_ERROR_JSON), (500, SERVER_ERROR_JSON), ], ) def test_midway_fail(self, status_code: int, json_response: dict): # Setup response next_cursor = SEARCH_HOSTS_JSON["result"]["links"]["next"] self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "RELEVANCE", "virtual_hosts": "EXCLUDE", } ) ], ) self.responses.add( responses.POST, V2_URL + "/hosts/search", status=status_code, json=json_response, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "sort": "RELEVANCE", "virtual_hosts": "EXCLUDE", "cursor": next_cursor, } ) ], ) # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--pages", "2", "--output", "censys-hosts.json", ], search_auth=True, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert cli_response.startswith(WROTE_PREFIX) json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-hosts." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert len(json_response) >= 1 assert json_response == SEARCH_HOSTS_JSON["result"]["hits"] # Cleanup os.remove(json_path) def test_open_certificates(self): # Mock self.patch_args( [ "censys", "search", "domain: censys.io AND ports: 443", "--index-type", "certificates", "--open", ], ) mock_open = self.mocker.patch("censys.cli.commands.search.webbrowser.open") # Actual call/error raising with pytest.raises(SystemExit, match="0"): cli_main() query_str = urlencode({"q": "domain: censys.io AND ports: 443", "resource": "certificates"}) # Assertions mock_open.assert_called_with( f"https://search.censys.io/search?{query_str}" # noqa: E231 ) def test_open_hosts(self): # Mock self.patch_args( [ "censys", "search", "services.service_name: HTTP", "--index-type", "hosts", "--open", ], ) mock_open = self.mocker.patch("censys.cli.commands.search.webbrowser.open") # Actual call/error raising with pytest.raises(SystemExit, match="0"): cli_main() query_str = urlencode({"q": "services.service_name: HTTP", "resource": "hosts"}) # Assertions mock_open.assert_called_with( f"https://search.censys.io/search?{query_str}" # noqa: E231 ) @pytest.mark.parametrize( ("index_type", "autocomplete_file", "prefix"), [ ("hosts", HOSTS_AUTOCOMPLETE, ""), ("hosts", HOSTS_AUTOCOMPLETE, "service"), ("certificates", CERTIFICATES_AUTOCOMPLETE, ""), ("invalid", None, ""), ], ) def test_fields_completer( self, index_type: str, autocomplete_file: Optional[Path], prefix: str, ): parsed_args = argparse.Namespace(index_type=index_type) if autocomplete_file is None: expected_fields = [] else: expected_fields = json.load(autocomplete_file.open())["data"] expected_fields = [ field_value for field in expected_fields if not (field_value := field["value"]).endswith(".type") ] if prefix == "": expected_fields = expected_fields[:20] assert fields_completer(prefix=prefix, parsed_args=parsed_args) == expected_fields if __name__ == "__main__": from unittest import main main() censys-censys-python-7a57a89/tests/cli/test_subdomains.py000066400000000000000000000037271524433232300237060ustar00rootroot00000000000000import contextlib from io import StringIO import pytest import responses from censys.cli import main as cli_main from censys.cli.commands import subdomains from tests.utils import V2_URL, CensysTestCase TEST_DOMAINS = { "help.censys.io", "lp.censys.io", "about.censys.io", "fast.github.com", } CERT_SEARCH_RESPONSE = { "result": { "total": len(TEST_DOMAINS), "hits": [{"names": [name]} for name in TEST_DOMAINS], "links": {}, }, } class CensysCliSubdomainsTest(CensysTestCase): @pytest.mark.parametrize( "test_json_bool", [True, False], ) def test_print_subdomains( self, test_json_bool: bool, test_subdomains: set[str] = TEST_DOMAINS, ): # Mock mock_print_json = self.mocker.patch("censys.cli.utils.console.print_json") mock_print = self.mocker.patch("censys.cli.utils.console.print") # Actual call subdomains.print_subdomains(test_subdomains, test_json_bool) # Assertions if test_json_bool: mock_print_json.assert_called_once() else: for subdomain in test_subdomains: mock_print.assert_any_call(f" - {subdomain}") # noqa: E221 def test_search_subdomains(self): # Mock self.responses.add( responses.POST, V2_URL + "/certificates/search", json=CERT_SEARCH_RESPONSE, match=[responses.matchers.json_params_matcher({"per_page": 100, "q": "names: censys.io"})], ) self.mocker.patch( "argparse._sys.argv", ["censys", "subdomains", "censys.io"] + CensysTestCase.cli_args, ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions mock_cli_response = temp_stdout.getvalue().strip() for line in mock_cli_response.split("\n"): assert "censys.io" in line censys-censys-python-7a57a89/tests/cli/test_utils.py000066400000000000000000000015661524433232300227010ustar00rootroot00000000000000import argparse from datetime import datetime import pytest from censys.cli.utils import valid_datetime_type from tests.utils import CensysTestCase class CensysCliUtilsTest(CensysTestCase): @pytest.mark.parametrize( ("string", "expected"), [ ("2021-05-20", datetime(2021, 5, 20)), ("2021-05-20 12:00", datetime(2021, 5, 20, 12, 00)), ], ) def test_valid_datetime(self, string, expected): # Actual call actual = valid_datetime_type(string) # Assertions assert actual == expected @pytest.mark.parametrize( "string", [ "2021/05/20", "2021/05/20 12:00", ], ) def test_invalid_datetime(self, string): # Actuall call/error raising with pytest.raises(argparse.ArgumentTypeError): valid_datetime_type(string) censys-censys-python-7a57a89/tests/cli/test_view.py000066400000000000000000000227111524433232300225060ustar00rootroot00000000000000import contextlib import json import os from io import StringIO from unittest.mock import mock_open import pytest import responses from censys.cli import main as cli_main from censys.common.exceptions import CensysCLIException, CensysException from tests.cli.test_search import WROTE_PREFIX from tests.search.v2.test_certs import VIEW_CERT_JSON from tests.search.v2.test_hosts import VIEW_HOST_JSON from tests.utils import V2_URL, CensysTestCase class CensysCliViewTest(CensysTestCase): def test_search_help(self): # Mock self.patch_args(["censys", "view", "--help"]) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout), pytest.raises(SystemExit): cli_main() # Assertions assert temp_stdout.getvalue().strip().startswith("usage: censys view") def test_no_creds(self): # Mock self.patch_args(["censys", "view", "test"]) self.mocker.patch("builtins.open", new_callable=mock_open, read_data="[DEFAULT]") self.mocker.patch.dict("os.environ", {"CENSYS_API_ID": "", "CENSYS_API_SECRET": ""}) # Actual call with pytest.raises(CensysException, match="No API ID or API secret configured."): cli_main() def test_write_json(self): # Mock self.patch_args( [ "censys", "view", "8.8.8.8", "--index-type", "hosts", "-o", "censys-view-8.8.8.8.json", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/hosts/8.8.8.8", status=200, json=VIEW_HOST_JSON, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert cli_response.startswith(WROTE_PREFIX) json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-view-8.8.8.8." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert VIEW_HOST_JSON["result"] == json_response # Cleanup os.remove(json_path) def test_write_output_path(self): # Mock self.patch_args( [ "censys", "view", "8.8.8.8", "--output", "censys-google-dns.json", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/hosts/8.8.8.8", status=200, json=VIEW_HOST_JSON, ) output_path = "censys-google-dns.json" # Actual call cli_main() # Assertions assert os.path.isfile(output_path) with open(output_path) as json_file: json_response = json.load(json_file) assert VIEW_HOST_JSON["result"] == json_response # Cleanup os.remove(output_path) def test_write_screen(self): # Mock self.patch_args( [ "censys", "view", "8.8.8.8", "--index-type", "hosts", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/hosts/8.8.8.8", status=200, json=VIEW_HOST_JSON, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() json_response = json.loads(temp_stdout.getvalue().strip()) # Assertions assert VIEW_HOST_JSON["result"] == json_response def test_at_time(self): # Mock self.patch_args( [ "censys", "view", "8.8.8.8", "--index-type", "hosts", "-o", "censys-view-8.8.8.8.json", "--at-time", "2021-05-20", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/hosts/8.8.8.8?at_time=2021-05-20T00%3A00%3A00.000000Z", status=200, json=VIEW_HOST_JSON, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() cli_response = temp_stdout.getvalue().strip() # Assertions assert cli_response.startswith(WROTE_PREFIX) json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-view-8.8.8.8." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert VIEW_HOST_JSON["result"] == json_response # Cleanup os.remove(json_path) def test_ip_plus_name(self): # Mock self.patch_args( [ "censys", "view", "1.1.1.1+one.one.one.one", "--index-type", "hosts", "-o", "censys-view-1.1.1.1.json", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/hosts/1.1.1.1+one.one.one.one", status=200, json=VIEW_HOST_JSON, ) # Actual call temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions cli_response = temp_stdout.getvalue().strip() assert cli_response.startswith(WROTE_PREFIX) json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-view-1.1.1.1." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert VIEW_HOST_JSON["result"] == json_response # Cleanup os.remove(json_path) def test_incorrect_index_type_certs(self): # Mock self.patch_args( [ "censys", "view", "9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", "--index-type", "hosts", "-o", "censys-view-cert.json", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/certificates/9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", status=200, json=VIEW_CERT_JSON, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions cli_response = temp_stdout.getvalue().strip() assert WROTE_PREFIX in cli_response json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-view-cert." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert VIEW_CERT_JSON["result"] == json_response # Cleanup os.remove(json_path) def test_has_at_time_for_certs(self): # Mock self.patch_args( [ "censys", "view", "9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", "--index-type", "certificates", "--at-time", "2021-05-20", "-o", "censys-view-cert.json", ], search_auth=True, ) self.responses.add( responses.GET, V2_URL + "/certificates/9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", status=200, json=VIEW_CERT_JSON, ) temp_stdout = StringIO() # Actual call with contextlib.redirect_stdout(temp_stdout): cli_main() # Assertions cli_response = temp_stdout.getvalue().strip() assert WROTE_PREFIX in cli_response json_path = cli_response.replace(WROTE_PREFIX, "").strip() assert json_path.endswith(".json") assert "censys-view-cert." in json_path with open(json_path) as json_file: json_response = json.load(json_file) assert VIEW_CERT_JSON["result"] == json_response # Cleanup os.remove(json_path) def test_incorrect_ip_address(self): # Mock self.patch_args( [ "censys", "view", "abc:123", "--index-type", "hosts", ], search_auth=True, ) # Actual call with pytest.raises(CensysCLIException, match="Invalid IP address: abc:123"): cli_main() def test_open(self): # Mock self.patch_args(["censys", "view", "8.8.8.8", "--open"], search_auth=True) mock_open = self.mocker.patch("censys.cli.commands.view.webbrowser.open") # Actual call with pytest.raises(SystemExit, match="0"): cli_main() # Assertions mock_open.assert_called_with("https://search.censys.io/hosts/8.8.8.8") censys-censys-python-7a57a89/tests/search/000077500000000000000000000000001524433232300206165ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/search/v1/000077500000000000000000000000001524433232300211445ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/search/v1/__init__.py000066400000000000000000000000001524433232300232430ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/search/v1/test_api.py000066400000000000000000000042241524433232300233300ustar00rootroot00000000000000import unittest from unittest.mock import mock_open, patch import pytest import responses from requests.models import Response from censys.common.exceptions import ( CensysException, CensysExceptionMapper, CensysSearchException, ) from censys.search.v1.api import CensysSearchAPIv1 from tests.utils import V1_URL, CensysTestCase ACCOUNT_JSON = { "login": "test@censys.io", "first_login": "2021-01-01 01:00:00", "last_login": "2021-01-01 01:00:00.000000", "email": "test@censys.io", "quota": {"used": 1, "resets_at": "2021-01-01 01:00:00", "allowance": 100}, } SearchExceptionParams = [(code, exception) for code, exception in CensysExceptionMapper.SEARCH_EXCEPTIONS.items()] class CensysSearchAPITests(CensysTestCase): def setup_method(self): self.setUpApi(CensysSearchAPIv1(self.api_id, self.api_secret)) def test_account(self): self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) res = self.api.account() assert res == ACCOUNT_JSON def test_quota(self): self.responses.add( responses.GET, V1_URL + "/account", status=200, json=ACCOUNT_JSON, ) res = self.api.quota() assert res == ACCOUNT_JSON["quota"] @pytest.mark.parametrize(("status_code", "exception"), SearchExceptionParams) def test_get_exception_class(self, status_code, exception): response = Response() response.status_code = status_code assert self.api._get_exception_class(response) == exception def test_exception_repr(self): exception = CensysSearchException(404, "Not Found", const="notfound") assert repr(exception) == "404 (notfound): Not Found" @patch.dict("os.environ", {"CENSYS_API_ID": "", "CENSYS_API_SECRET": ""}) class CensysAPIBaseTestsNoSearchEnv(unittest.TestCase): @patch("builtins.open", new_callable=mock_open, read_data="[DEFAULT]") def test_no_env(self, mock_file): with pytest.raises(CensysException, match="No API ID or API secret configured."): CensysSearchAPIv1() censys-censys-python-7a57a89/tests/search/v1/test_data.py000066400000000000000000000026571524433232300235000ustar00rootroot00000000000000import responses from censys.search import SearchClient from tests.utils import V1_URL, CensysTestCase SERIES_JSON = { "primary_series": "", "raw_series": "", } VIEW_JSON = { "description": ( "The Censys IPv4 dataset provides data about the services " "(e.g., HTTP, SMTP, MySQL) running on all publicly-accessible IPv4 hosts." ), "results": {"historical": []}, } RESULT_JSON = {"files": {"filename": {"file_type": "data"}}} SERIES = "ipv4_2018" RESULT = "20200818" class CensysDataTest(CensysTestCase): def setup_method(self): self.setUpApi(SearchClient(self.api_id, self.api_secret).v1.data) def test_get_series(self): self.responses.add( responses.GET, f"{V1_URL}/data", status=200, json=SERIES_JSON, ) series = self.api.get_series() assert series == SERIES_JSON def test_view_series(self): self.responses.add( responses.GET, f"{V1_URL}/data/{SERIES}", status=200, json=VIEW_JSON, ) res = self.api.view_series(SERIES) assert res == VIEW_JSON def test_view_result(self): self.responses.add( responses.GET, f"{V1_URL}/data/{SERIES}/{RESULT}", status=200, json=RESULT_JSON, ) res = self.api.view_result(SERIES, RESULT) assert res == RESULT_JSON censys-censys-python-7a57a89/tests/search/v2/000077500000000000000000000000001524433232300211455ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/search/v2/__init__.py000066400000000000000000000000001524433232300232440ustar00rootroot00000000000000censys-censys-python-7a57a89/tests/search/v2/test_api.py000066400000000000000000000031621524433232300233310ustar00rootroot00000000000000import unittest from unittest.mock import mock_open, patch import pytest import responses from requests.models import Response from censys.common.exceptions import CensysException, CensysExceptionMapper from censys.search.v2.api import CensysSearchAPIv2 from tests.search.v1.test_api import ACCOUNT_JSON from tests.utils import V1_URL, CensysTestCase SearchExceptionParams = [(code, exception) for code, exception in CensysExceptionMapper.SEARCH_EXCEPTIONS.items()] class CensysSearchAPITests(CensysTestCase): api: CensysSearchAPIv2 def setup_method(self): self.setUpApi(CensysSearchAPIv2(self.api_id, self.api_secret)) @pytest.mark.parametrize(("status_code", "exception"), SearchExceptionParams) def test_get_exception_class(self, status_code, exception): response = Response() response.status_code = status_code assert self.api._get_exception_class(response) == exception def test_account_and_quota(self): self.responses.add( responses.GET, f"{V1_URL}/account", status=200, json=ACCOUNT_JSON, ) results = self.api.account() assert results == ACCOUNT_JSON results = self.api.quota() assert results == ACCOUNT_JSON["quota"] @patch.dict("os.environ", {"CENSYS_API_ID": "", "CENSYS_API_SECRET": ""}) class CensysAPIBaseTestsNoSearchEnv(unittest.TestCase): @patch("builtins.open", new_callable=mock_open, read_data="[DEFAULT]") def test_no_env(self, mock_file): with pytest.raises(CensysException, match="No API ID or API secret configured."): CensysSearchAPIv2() censys-censys-python-7a57a89/tests/search/v2/test_certs.py000066400000000000000000000430211524433232300236760ustar00rootroot00000000000000from datetime import datetime from typing import Any, Optional import pytest import responses from responses import matchers from censys.search import SearchClient from tests.utils import V2_URL, CensysTestCase TEST_CERT = "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426" ALTERNATE_CERT = "9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041" TEST_SEARCH_QUERY = "parsed.subject.country: AU" TEST_AGGREGATION_FIELD = "parsed.issuer.organization" EXAMPLE_CERT_JSON = { "fingerprint_sha256": "9b00121b4e85d50667ded1a8aa39855771bdb67ceca6f18726b49374b41f0041", "fingerprint_sha1": "d508e7f8163fb67434f84091dc7c2ca8afd5234d", "fingerprint_md5": "3818d99263b47ab28f7de5b293ee1418", "tbs_fingerprint_sha256": "4b098b6bd9459340fb0f3cfb80f0bc3283370c455d57ca20da40e7eecce341d5", "tbs_no_ct_fingerprint_sha256": "5c095a40e76c245323086d26d1fa428d3b443b42fb58c7dbb19b32dfe516b749", "spki_fingerprint_sha256": "cc9b074ebf41b484a56923d5585594967bda7a7f8b5be187ef0e7ae1ec90003c", "parent_spki_fingerprint_sha256": "390bc358202771a65e7be7a87924d7f2a079de04feb5ffd4163fae4fbf9b11e9", "parsed": { "version": 3, "serial_number": "311703586789118042424998420179537559397550", "issuer_dn": "C=US, O=Let's Encrypt, CN=R3", "issuer": { "common_name": ["R3"], "country": ["US"], "organization": ["Let's Encrypt"], }, "subject_dn": "CN=www.kgcontracting.co", "subject": {"common_name": ["www.kgcontracting.co"]}, "subject_key_info": { "key_algorithm": {"name": "RSA", "oid": "1.2.840.113549.1.1.1"}, "rsa": { "exponent": 65537, "_encoding": {"modulus": "DISPLAY_HEX"}, "modulus": "c0b3b7d595e250fcbc54c6f9e81113c12da29fe35350a02b8ab769a5e43f07df7ac8ff72c482b3e838d64e97cb3fa15e415acbcfe6758a4e7ac401b4a5294ecca6ad1b583ec2136a408524eeadca55ba4a8af490cb9c764efbdecbe59a4ca160905e5972548018f55194e7ac94b2153d97bd5f055d58ad3abe0d1daa3b3c97fed490f1bb58fc5c819618891d05c32d68aeaabada321736e417f0fa58d7093d352c1800191645d1b820f5f7c93301ab7ca78393e953b82719741b735a67ce6a63faec0ac9d2d917f03b9fc0a0ab8012f4763c50118663af897294e85ba6e11c5ca0fd749645c2a58ddf14ad62717b40b5e7b620d256be28789e1ebd62e8046acd", "length": 2048, }, "_encoding": {"fingerprint_sha256": "DISPLAY_HEX"}, "fingerprint_sha256": "754cb1e2e2088214a5970662bb5a60aec8a2e29b94f53529aa608abee6682c60", "_key": "rsa", }, "validity_period": { "not_before": "2022-12-31T11:37:55Z", "not_after": "2023-03-31T11:37:54Z", "length_seconds": 7775999, }, "signature": { "signature_algorithm": { "name": "SHA256-RSA", "oid": "1.2.840.113549.1.1.11", }, "_encoding": {"value": "DISPLAY_HEX"}, "value": "95347a7bd89da6a6bd9adcc509669d933b5e2a29b414317d72b02c583bfadbf616f5b6977824ee80484fe4ae0c38dbb1013d896b5a9db12fdc62296fbefbba84ed089a6e62c148cc9d1e60bfd9cf6e7d1c8f5e0f315872618a6805e5bf48432ce2d0aac07d4ab845a7f68c1a61c3dd37c0e40c26b386b1f37ff2fc50e6eacdc305ecd13da1c43bb1209cb13c5a6387cffb57fad81d01496b3c6499817ed1cf1a0ef809b6b408035b5de72423c6c7f84c3a9e9513c89fa8fc49bf6f90746561ebd1b4ea22e7557d3e505b5ecd289dbb8ce71c4d3acc2e67a27bbb12dd242c8fa512d19dc8b7bd6b88bcc6a6484bb39c894f7b756630a10525a9ab3359030aa2ad", "valid": True, "self_signed": False, }, # Removed extensions for brevity "serial_number_hex": "039403b7283199171fd9c1af1c8210f5a4ae", "redacted": False, }, "names": ["kgcontracting.co", "www.kgcontracting.co"], "validation_level": "DV", "validation": { "nss": { "ever_valid": True, "had_trusted_path": True, "chains": [ { "_encoding": {"sha256fp": "DISPLAY_HEX"}, "sha256fp": [ "0ac730f6b3a98bab6aa97c9c4c71b34dd5599f4933630e6d24a26751bd12ebac", "96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6", ], }, { "_encoding": {"sha256fp": "DISPLAY_HEX"}, "sha256fp": [ "67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd", "96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6", ], }, ], "_encoding": {"parents": "DISPLAY_HEX"}, "parents": [ "0ac730f6b3a98bab6aa97c9c4c71b34dd5599f4933630e6d24a26751bd12ebac", "67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd", ], "type": "LEAF", "is_valid": False, "has_trusted_path": False, "in_revocation_set": False, }, # Removed additional validation data for brevity }, "ever_seen_in_scan": True, "raw": "MIIFPzCCBCegAwIBAgISA5QDtygxmRcf2cGvHIIQ9aSuMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJSMzAeFw0yMjEyMzExMTM3NTVaFw0yMzAzMzExMTM3NTRaMB8xHTAbBgNVBAMTFHd3dy5rZ2NvbnRyYWN0aW5nLmNvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLO31ZXiUPy8VMb56BETwS2in+NTUKArirdppeQ/B996yP9yxIKz6DjWTpfLP6FeQVrLz+Z1ik56xAG0pSlOzKatG1g+whNqQIUk7q3KVbpKivSQy5x2Tvvey+WaTKFgkF5ZclSAGPVRlOeslLIVPZe9XwVdWK06vg0dqjs8l/7UkPG7WPxcgZYYiR0Fwy1orqq62jIXNuQX8PpY1wk9NSwYABkWRdG4IPX3yTMBq3yng5PpU7gnGXQbc1pnzmpj+uwKydLZF/A7n8Cgq4AS9HY8UBGGY6+JcpToW6bhHFyg/XSWRcKljd8UrWJxe0C157Yg0la+KHieHr1i6ARqzQIDAQABo4ICYDCCAlwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSmobmsnQiGs7WPX6up9C90HZ7ynTAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAxBgNVHREEKjAoghBrZ2NvbnRyYWN0aW5nLmNvghR3d3cua2djb250cmFjdGluZy5jbzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQMGCisGAQQB1nkCBAIEgfQEgfEA7wB1ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhWgwUWYAAAQDAEYwRAIgPnPJ0efxcIewdyN8cVA54dXzbNdWNd9EAXdnInNU3X0CIBfPtneRMMSLSWhR1a65cMO0Pg+h8x1rwD8zONe4cWlHAHYA6D7Q2j71BjUy51covIlryQPTy9ERa+zraeF3fW0GvW4AAAGFaDBTWAAABAMARzBFAiEAnHKNpDyb9HALbXPDsxVbVHPWKdW48GxzNYlM5h+/OvACIC5nfWT45zaOOXabpFgS+j0KLjzOdh2OiYJ2OS1ZK1R1MA0GCSqGSIb3DQEBCwUAA4IBAQCVNHp72J2mpr2a3MUJZp2TO14qKbQUMX1ysCxYO/rb9hb1tpd4JO6ASE/krgw427EBPYlrWp2xL9xiKW+++7qE7QiabmLBSMydHmC/2c9ufRyPXg8xWHJhimgF5b9IQyzi0KrAfUq4Raf2jBphw903wOQMJrOGsfN/8vxQ5urNwwXs0T2hxDuxIJyxPFpjh8/7V/rYHQFJazxkmYF+0c8aDvgJtrQIA1td5yQjxsf4TDqelRPIn6j8Sb9vkHRlYevRtOoi51V9PlBbXs0onbuM5xxNOswuZ6J7uxLdJCyPpRLRnci3vWuIvMamSEuznIlPe3VmMKEFJamrM1kDCqKt", "added_at": "2023-01-06T12:46:27Z", "modified_at": "2023-04-01T16:18:53Z", "validated_at": "2023-04-01T16:18:53Z", "parse_status": "CERTIFICATE_PARSE_STATUS_SUCCESS", "zlint": { "version": 3, "timestamp": "2023-03-14T22:25:59Z", "notices_present": True, "failed_lints": ["n_subject_common_name_included"], "warnings_present": False, "errors_present": False, "fatals_present": False, }, "precert": False, "revoked": False, "labels": [ "untrusted", "ever-trusted", "ct", "leaf", "dv", "was-trusted", "expired", ], } VIEW_CERT_JSON = { "code": 200, "status": "OK", "result": EXAMPLE_CERT_JSON, } BULK_VIEW_CERTS_JSON = { "code": 200, "status": "OK", "result": [EXAMPLE_CERT_JSON], } SEARCH_CERTS_JSON = { "code": 200, "status": "OK", "result": { "query": TEST_SEARCH_QUERY, "total": 50000, "duration_ms": 356, "hits": [EXAMPLE_CERT_JSON], "links": {"prev": "prevCursorToken", "next": "nextCursorToken"}, }, } AGGREGATE_CERTS_JSON = { "code": 200, "status": "OK", "result": { "query": TEST_SEARCH_QUERY, "field": TEST_AGGREGATION_FIELD, "total": 58906, "duration_ms": 690, "buckets": [ {"key": "Internet Widgits Pty Ltd", "count": 23748}, {"key": "DigiCert Inc", "count": 10885}, {"key": "QuoVadis Limited", "count": 1883}, {"key": "Entrust, Inc.", "count": 1475}, {"key": "Webmin", "count": 1071}, ], }, } VIEW_HOSTS_BY_CERT_JSON = { "code": 200, "status": "OK", "result": { "fp": TEST_CERT, "hosts": [ { "ip": "string", "name": "string", "observed_at": "2021-08-02T14:56:38.711Z", "first_observed_at": "2021-08-02T14:56:38.711Z", } ], "links": {"next": "nextCursorToken"}, }, } OBSERVATIONS_CERT_JSON = { "code": 200, "status": "OK", "result": { "fingerprint": "string", "observations": [ { "ip": "string", "name": "string", "port": 0, "service_name": "string", "transport_protocol": "TCP", "first_observed_at": "2024-10-17T18:13:23.554Z", "last_observed_at": "2024-10-17T18:13:23.554Z", "first_updated_at": "2024-10-17T18:13:23.554Z", "last_updated_at": "2024-10-17T18:13:23.554Z", } ], "links": {"next": "nextCursorToken"}, }, } class TestCerts(CensysTestCase): def setup_method(self): self.setUpApi(SearchClient(self.api_id, self.api_secret).v2.certs) def test_view(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}", status=200, json=VIEW_CERT_JSON, ) result = self.api.view(TEST_CERT) assert result == VIEW_CERT_JSON["result"] @pytest.mark.parametrize("method_name", ["bulk_post", "bulk", "bulk_view"]) def test_bulk_post(self, method_name: str): self.responses.add( responses.POST, f"{V2_URL}/certificates/bulk", status=200, json=BULK_VIEW_CERTS_JSON, ) method = getattr(self.api, method_name) result = method([TEST_CERT, ALTERNATE_CERT]) assert result == BULK_VIEW_CERTS_JSON["result"] def test_bulk_get(self): certs = [TEST_CERT, ALTERNATE_CERT] params = {"fingerprints": certs} self.responses.add( responses.GET, f"{V2_URL}/certificates/bulk", status=200, json=BULK_VIEW_CERTS_JSON, match=[matchers.query_param_matcher(params)], ) result = self.api.bulk_get(certs) assert result == BULK_VIEW_CERTS_JSON["result"] def test_bulk_get_single(self): params = {"fingerprints": TEST_CERT} self.responses.add( responses.GET, f"{V2_URL}/certificates/bulk", status=200, json=BULK_VIEW_CERTS_JSON, match=[matchers.query_param_matcher(params)], ) result = self.api.bulk_get(TEST_CERT) assert result == BULK_VIEW_CERTS_JSON["result"] @pytest.mark.parametrize( ("method_name", "raw"), [ ("search_post_raw", True), ("raw_search", True), ("search_post", False), ], ) def test_search_post(self, method_name: str, raw: bool): self.responses.add( responses.POST, f"{V2_URL}/certificates/search", status=200, json=SEARCH_CERTS_JSON, ) method = getattr(self.api, method_name) result = method(TEST_SEARCH_QUERY) if raw: assert result == SEARCH_CERTS_JSON else: assert result == SEARCH_CERTS_JSON["result"] @pytest.mark.parametrize( ("fields", "sort", "cursor"), [ (None, None, None), (["names", "fingerprint_sha256"], None, None), (None, ["parsed.issuer.organization", "parsed.subject.country"], None), ( ["names", "fingerprint_sha256"], ["parsed.issuer.organization", "parsed.subject.country"], None, ), (None, None, "nextCursorToken"), ], ) def test_search( self, fields: Optional[list[str]], sort: Optional[list[str]], cursor: Optional[str], ): self.responses.add( responses.POST, f"{V2_URL}/certificates/search", status=200, json=SEARCH_CERTS_JSON, ) query = self.api.search(TEST_SEARCH_QUERY, fields=fields, sort=sort, cursor=cursor) assert next(query) == SEARCH_CERTS_JSON["result"]["hits"] @pytest.mark.parametrize( ("params", "expected_params"), [ ({}, {"q": TEST_SEARCH_QUERY, "per_page": 50}), ({"per_page": 1}, {"q": TEST_SEARCH_QUERY, "per_page": 1}), ( {"cursor": "nextCursorToken"}, {"q": TEST_SEARCH_QUERY, "cursor": "nextCursorToken", "per_page": 50}, ), ( {"fields": ["names"]}, {"q": TEST_SEARCH_QUERY, "fields": "names", "per_page": 50}, ), ( { "fields": [ "names", "fingerprint_sha256", "parsed.issuer.organization", "parsed.subject.postal_code", ] }, { "q": TEST_SEARCH_QUERY, "fields": [ "names", "fingerprint_sha256", "parsed.issuer.organization", "parsed.subject.postal_code", ], "per_page": 50, }, ), ( {"sort": "parsed.subject.postal_code"}, { "q": TEST_SEARCH_QUERY, "sort": "parsed.subject.postal_code", "per_page": 50, }, ), ( {"sort": ["parsed.subject.postal_code", "parsed.subject.country"]}, { "q": TEST_SEARCH_QUERY, "sort": ["parsed.subject.postal_code", "parsed.subject.country"], "per_page": 50, }, ), ], ) def test_search_get(self, params: dict[str, Any], expected_params: dict[str, Any]): self.responses.add( responses.GET, f"{V2_URL}/certificates/search", status=200, json=SEARCH_CERTS_JSON, match=[matchers.query_param_matcher(expected_params)], ) result = self.api.search_get(TEST_SEARCH_QUERY, **params) assert result == SEARCH_CERTS_JSON["result"] def test_aggregate(self): params = { "q": TEST_SEARCH_QUERY, "field": TEST_AGGREGATION_FIELD, "num_buckets": 50, } self.responses.add( responses.GET, f"{V2_URL}/certificates/aggregate", status=200, json=AGGREGATE_CERTS_JSON, match=[matchers.query_param_matcher(params)], ) result = self.api.aggregate(TEST_SEARCH_QUERY, TEST_AGGREGATION_FIELD) assert result == AGGREGATE_CERTS_JSON["result"] def test_get_hosts_by_cert(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}/hosts", status=200, json=VIEW_HOSTS_BY_CERT_JSON, ) result = self.api.get_hosts_by_cert(TEST_CERT) assert result == VIEW_HOSTS_BY_CERT_JSON["result"] def test_get_hosts_by_cert_with_cursor(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}/hosts?cursor=nextCursorToken", status=200, json=VIEW_HOSTS_BY_CERT_JSON, ) results = self.api.get_hosts_by_cert(TEST_CERT, cursor="nextCursorToken") assert results == VIEW_HOSTS_BY_CERT_JSON["result"] def test_get_observations_by_cert(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}/observations", status=200, json=OBSERVATIONS_CERT_JSON, ) result = self.api.get_observations(TEST_CERT) assert result == OBSERVATIONS_CERT_JSON["result"] def test_get_observations_by_cert_with_cursor(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}/observations?per_page=50&cursor=nextCursorToken", status=200, json=OBSERVATIONS_CERT_JSON, ) results = self.api.get_observations(TEST_CERT, cursor="nextCursorToken") assert results == OBSERVATIONS_CERT_JSON["result"] def test_get_observations_with_rfc3339_timestampts(self): self.responses.add( responses.GET, f"{V2_URL}/certificates/{TEST_CERT}/observations?per_page=50&start_time=2024-10-14T18%3A13%3A23.554000Z&end_time=2024-10-17T18%3A13%3A23.554000Z", status=200, json=OBSERVATIONS_CERT_JSON, ) result = self.api.get_observations( TEST_CERT, start_time=datetime(2024, 10, 14, 18, 13, 23, 554000), end_time=datetime(2024, 10, 17, 18, 13, 23, 554000), ) assert result == OBSERVATIONS_CERT_JSON["result"] censys-censys-python-7a57a89/tests/search/v2/test_comments.py000066400000000000000000000072751524433232300244160ustar00rootroot00000000000000import pytest import responses from censys.search.v2 import CensysCerts, CensysHosts from censys.search.v2.api import CensysSearchAPIv2 from tests.utils import V2_URL, CensysTestCase TEST_COMMENT = "**This is a comment.**" GET_COMMENTS_RESPONSE = { "code": 200, "status": "OK", "result": { "ip": "8.8.8.8", "comments": [ { "id": "comment-id", "ip": "1.1.1.1", "author_id": "string", "contents": TEST_COMMENT, "created_at": "2016-01-01T00:00:00Z", } ], }, } ADD_COMMENTS_RESPONSE = { "code": 200, "status": "OK", "result": { "id": "comment-id", "ip": "1.1.1.1", "author_id": "string", "contents": TEST_COMMENT, "created_at": "2016-01-01T00:00:00Z", }, } GET_COMMENT_RESPONSE = { "code": 200, "status": "OK", "result": { "id": "comment-id", "ip": "1.1.1.1", "author_id": "string", "contents": "**This is a comment.**", "created_at": "2016-01-01T00:00:00Z", }, } INDEX_PARAMS = [ {"index": "hosts", "index_cls": CensysHosts, "document_id": "1.0.0.0"}, { "index": "certificates", "index_cls": CensysCerts, "document_id": "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426", }, ] class CensysCommentsTests(CensysTestCase): index: str index_cls: CensysSearchAPIv2 document_id: str api: CensysSearchAPIv2 @pytest.fixture(autouse=True, params=INDEX_PARAMS, ids=["hosts", "certificates"]) def _index_config(self, request): self.index = request.param["index"] self.index_cls = request.param["index_cls"] self.document_id = request.param["document_id"] self.setUpApi(self.index_cls(self.api_id, self.api_secret)) def test_get_comments(self): self.responses.add( responses.GET, f"{V2_URL}/{self.index}/{self.document_id}/comments", status=200, json=GET_COMMENTS_RESPONSE, ) results = self.api.get_comments(self.document_id) assert results == GET_COMMENTS_RESPONSE["result"]["comments"] def test_add_comment(self): self.responses.add( responses.POST, f"{V2_URL}/{self.index}/{self.document_id}/comments", status=200, json=ADD_COMMENTS_RESPONSE, match=[responses.json_params_matcher({"contents": TEST_COMMENT})], ) results = self.api.add_comment(self.document_id, TEST_COMMENT) assert results == ADD_COMMENTS_RESPONSE["result"] def test_get_comment(self): self.responses.add( responses.GET, f"{V2_URL}/{self.index}/{self.document_id}/comments/comment-id", status=200, json=GET_COMMENT_RESPONSE, ) results = self.api.get_comment(self.document_id, "comment-id") assert results == GET_COMMENT_RESPONSE["result"] def test_delete_comment(self): self.responses.add( responses.DELETE, f"{V2_URL}/{self.index}/{self.document_id}/comments/comment-id", status=209, ) self.api.delete_comment(self.document_id, "comment-id") def test_update_comment(self): self.responses.add( responses.PUT, f"{V2_URL}/{self.index}/{self.document_id}/comments/comment-id", status=200, json={"code": 200, "status": "OK"}, match=[responses.json_params_matcher({"contents": TEST_COMMENT})], ) results = self.api.update_comment(self.document_id, "comment-id", TEST_COMMENT) assert results == {"code": 200, "status": "OK"} censys-censys-python-7a57a89/tests/search/v2/test_hosts.py000066400000000000000000000673361524433232300237350ustar00rootroot00000000000000import datetime import json from copy import deepcopy from typing import Any, Optional import pytest import responses from responses import matchers from censys.common.exceptions import CensysInternalServerException from censys.search import CensysHosts, SearchClient from tests.utils import V2_URL, CensysTestCase TEST_HOST = "8.8.8.8" TEST_SEARCH_QUERY = "services.service_name: HTTP" VIEW_HOST_JSON = { "code": 200, "status": "OK", "result": { "services": [ { "transport_protocol": "UDP", "truncated": False, "service_name": "DNS", "_decoded": "dns", "source_ip": "167.248.133.40", "extended_service_name": "DNS", "observed_at": "2021-04-01T13:40:03.755876935Z", "dns": {"server_type": "FORWARDING"}, "perspective_id": "PERSPECTIVE_NTT", "port": 53, "software": [], } ], "ip": "8.8.8.8", "location_updated_at": "2021-03-30T14:53:12.980328Z", "location": { "country": "United States", "coordinates": {"latitude": 37.751, "longitude": -97.822}, "registered_country": "United States", "registered_country_code": "US", "postal_code": "", "country_code": "US", "timezone": "America/Chicago", "continent": "North America", }, "last_updated_at": "2021-04-01T14:10:10.712Z", }, } SEARCH_HOSTS_JSON = { "code": 200, "status": "OK", "result": { "query": "services.service_name: HTTP", "hits": [ { "services": [ {"service_name": "HTTP", "port": 443}, {"service_name": "HTTP", "port": 80}, ], "ip": "1.0.0.0", }, { "services": [ {"service_name": "HTTP", "port": 443}, {"service_name": "HTTP", "port": 80}, ], "ip": "1.0.0.1", }, ], "total": 146857082, "links": { "prev": "eyJBZnRlciI6WyIxIiwiMS4wLjAuMCJdLCJSZXZlcnNlIjp0cnVlfQ==", "next": "eyJBZnRlciI6WyIxIiwiMS4wLjAuNDkiXSwiUmV2ZXJzZSI6ZmFsc2V9", }, }, } AGGREGATE_HOSTS_JSON = { "code": 200, "status": "OK", "result": { "total_omitted": 358388380, "buckets": [ {"count": 47637476, "key": "80"}, {"count": 35073802, "key": "443"}, {"count": 17256198, "key": "7547"}, {"count": 13216884, "key": "22"}, ], "potential_deviation": 605118, "field": "services.port", "query": "services.service_name: HTTP", "total": 149575980, }, } VIEW_HOST_NAMES_JSON = { "code": 200, "status": "OK", "result": {"names": ["google.com", "google.co.uk", "google.com.au", "dns.google"]}, "links": {"prev": "prevCursorToken", "next": "nextCursorToken"}, } HOST_METADATA_JSON = { "code": 200, "status": "OK", "result": {"services": ["HTTP", "IMAP", "MQTT", "SSH", "..."]}, } VIEW_HOST_DIFF_JSON = { "code": 200, "status": "OK", "result": { "a": {"ip": "1.1.1.1", "last_updated_at": "2022-01-10T15:41:27.416Z"}, "b": {"ip": "1.1.1.2", "last_updated_at": "2022-01-10T15:41:27.416Z"}, "patch": [{}], }, } VIEW_HOST_EVENTS_JSON = { "code": 200, "status": "OK", "result": { "ip": "8.8.8.8", "events": [ { "_event": "service_observed", "service_observed": { "id": { "port": 443, "transport_protocol": "TCP", "service_name": "HTTP", }, "observed_at": "2021-07-27T18:00:11.296Z", "perspective_id": "PERSPECTIVE_NTT", "changed_fields": [{"field_name": "services.banner"}], }, "timestamp": "2021-07-27T18:00:11.296Z", }, { "_event": "location_updated", "location_updated": { "location": { "continent": "North America", "country": "United States", "country_code": "US", "postal_code": "48104", "timezone": "America/Michigan", "coordinates": {"latitude": "42.273", "longitude": "-83.751"}, "registered_country": "United States", "registered_country_code": "US", } }, "timestamp": "2021-07-27T18:00:11.297Z", }, ], }, } RATE_LIMIT_ERROR_JSON = { "error": "Rate limit exceeded. See https://search.censys.io/account for rate limit details.", "status": "error", "error_type": "rate_limit_exceeded", } TOO_MANY_REQUESTS_ERROR_JSON = { "code": 429, "status": "Too Many Requests", "error": "You have used your full quota for this billing period. Please see https://search.censys.io/account or contact support@censys.io.", } SERVER_ERROR_JSON = { "code": 500, "status": "Internal Server Error", "error": "An unexpected error occurred. Please try again later.", } class TestHosts(CensysTestCase): api: CensysHosts def setup_method(self): self.setUpApi(SearchClient(self.api_id, self.api_secret).v2.hosts) def test_view(self): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}", status=200, json=VIEW_HOST_JSON, ) res = self.api.view(TEST_HOST) assert res == VIEW_HOST_JSON["result"] def test_view_at_time(self): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}?at_time=2021-03-01T00:00:00.000000Z", # noqa: E231 status=200, json=VIEW_HOST_JSON, ) date = datetime.date(2021, 3, 1) res = self.api.view(TEST_HOST, at_time=date) assert res == VIEW_HOST_JSON["result"] def test_bulk_view(self): ips = ["1.1.1.1", "1.1.1.2", "1.1.1.3"] expected = {} for ip in ips: host_json = deepcopy(VIEW_HOST_JSON) host_json["result"]["ip"] = ip self.responses.add( responses.GET, f"{V2_URL}/hosts/{ip}", status=200, json=host_json, ) expected[ip] = deepcopy(host_json["result"]) results = self.api.bulk_view(ips) assert results == expected def test_bulk_view_at_time(self): ips = ["1.1.1.1", "1.1.1.2", "1.1.1.3"] expected = {} for ip in ips: host_json = deepcopy(VIEW_HOST_JSON) host_json["result"]["ip"] = ip self.responses.add( responses.GET, f"{V2_URL}/hosts/{ip}?at_time=2021-03-01T00:00:00.000000Z", # noqa: E231 status=200, json=host_json, ) expected[ip] = deepcopy(host_json["result"]) date = datetime.date(2021, 3, 1) results = self.api.bulk_view(ips, at_time=date) assert results == expected def test_bulk_view_with_error(self): ips = ["1.1.1.1", "1.1.1.2", "1.1.1.3"] expected = {} for ip in ips[:-1]: host_json = deepcopy(VIEW_HOST_JSON) host_json["result"]["ip"] = ip self.responses.add( responses.GET, f"{V2_URL}/hosts/{ip}", status=200, json=host_json, ) expected[ip] = deepcopy(host_json["result"]) self.responses.add( responses.GET, f"{V2_URL}/hosts/{ips[-1]}", status=429, json=RATE_LIMIT_ERROR_JSON, ) expected[ips[-1]] = { "error": "429 (rate_limit_exceeded): Rate limit exceeded. See https://search.censys.io/account for rate limit details." } results = self.api.bulk_view(ips) assert results == expected @pytest.mark.parametrize( ("method_name", "raw"), [ ("search_post_raw", True), ("raw_search", True), ("search_post", False), ], ) def test_search_post(self, method_name: str, raw: bool): self.responses.add( responses.POST, f"{V2_URL}/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": TEST_SEARCH_QUERY, "per_page": 100, } ) ], ) method = getattr(self.api, method_name) result = method(TEST_SEARCH_QUERY) if raw: assert result == SEARCH_HOSTS_JSON else: assert result == SEARCH_HOSTS_JSON["result"] @pytest.mark.parametrize( ("fields", "sort", "cursor", "virtual_hosts"), [ (None, None, None, None), (["ip", "services.port"], None, None, None), (None, "RELEVANCE", None, None), (["ip", "services.port"], "RELEVANCE", None, None), (None, None, None, "ONLY"), (None, None, "nextCursorToken", None), ], ) def test_search( self, fields: Optional[list[str]], sort: Optional[str], cursor: Optional[str], virtual_hosts: Optional[str], ): self.responses.add( responses.POST, f"{V2_URL}/hosts/search", status=200, json=SEARCH_HOSTS_JSON, ) query = self.api.search( TEST_SEARCH_QUERY, fields=fields, sort=sort, cursor=cursor, virtual_hosts=virtual_hosts, ) assert next(query) == SEARCH_HOSTS_JSON["result"]["hits"] def test_search_per_page(self): test_per_page = 50 self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[matchers.json_params_matcher({"q": "services.service_name: HTTP", "per_page": test_per_page})], ) query = self.api.search("services.service_name: HTTP", per_page=test_per_page) assert next(query) == SEARCH_HOSTS_JSON["result"]["hits"] @pytest.mark.parametrize( ("params", "expected_params"), [ ({}, {"q": TEST_SEARCH_QUERY, "per_page": 100}), ({"per_page": 1}, {"q": TEST_SEARCH_QUERY, "per_page": 1}), ( {"cursor": "nextCursorToken"}, {"q": TEST_SEARCH_QUERY, "cursor": "nextCursorToken", "per_page": 100}, ), ( {"fields": ["ip"]}, {"q": TEST_SEARCH_QUERY, "fields": "ip", "per_page": 100}, ), ( { "fields": [ "ip", "services.port", "services.extended_service_name", ] }, { "q": TEST_SEARCH_QUERY, "fields": [ "ip", "services.port", "services.extended_service_name", ], "per_page": 100, }, ), ( {"sort": "RELEVANCE"}, { "q": TEST_SEARCH_QUERY, "sort": "RELEVANCE", "per_page": 100, }, ), ], ) def test_search_get(self, params: dict[str, Any], expected_params: dict[str, Any]): self.responses.add( responses.GET, f"{V2_URL}/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[matchers.query_param_matcher(expected_params)], ) result = self.api.search_get(TEST_SEARCH_QUERY, **params) assert result == SEARCH_HOSTS_JSON["result"] def test_search_with_error_retry(self): is_first_request = True def request_callback(_): nonlocal is_first_request if is_first_request: is_first_request = False return (429, {}, json.dumps(RATE_LIMIT_ERROR_JSON)) return (200, {}, json.dumps(SEARCH_HOSTS_JSON)) self.responses.add_callback( responses.POST, V2_URL + "/hosts/search", callback=request_callback, content_type="application/json", ) query = self.api.search("services.service_name: HTTP") assert query() == SEARCH_HOSTS_JSON["result"]["hits"] def test_search_invalid_query(self): invalid_query = "some_bad_query" no_hosts_json = deepcopy(SEARCH_HOSTS_JSON) no_hosts_json["result"]["hits"] = [] no_hosts_json["result"]["total"] = 0 no_hosts_json["result"]["links"]["next"] = "" self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=no_hosts_json, match=[matchers.json_params_matcher({"q": invalid_query, "per_page": 100})], ) query = self.api.search(invalid_query) assert next(query) == no_hosts_json["result"]["hits"] assert query.pages == 0 with pytest.raises(StopIteration): next(query) def test_search_pages(self): self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[matchers.json_params_matcher({"q": "services.service_name: HTTP", "per_page": 100})], ) page_2_json = deepcopy(SEARCH_HOSTS_JSON) hits = page_2_json["result"]["hits"] new_hits = [ { "services": [ {"service_name": "HTTP", "port": 443}, {"service_name": "HTTP", "port": 80}, ], "ip": "1.0.0.2", } ] next_cursor = SEARCH_HOSTS_JSON["result"]["links"]["next"] page_2_json["result"]["hits"] = new_hits page_2_json["result"]["links"]["next"] = None self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=page_2_json, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "cursor": next_cursor, } ) ], ) expected = [hits, new_hits] query = self.api.search("services.service_name: HTTP", pages=-1) for i, page in enumerate(query): assert expected[i] == page def test_search_pages_retry_with_server_error(self): first_request_to_second_page = True def request_callback(_): nonlocal first_request_to_second_page if first_request_to_second_page: first_request_to_second_page = False return (500, {}, json.dumps(SERVER_ERROR_JSON)) return (200, {}, json.dumps(SEARCH_HOSTS_JSON)) self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[matchers.json_params_matcher({"q": "services.service_name: HTTP", "per_page": 100})], ) self.responses.add_callback( responses.POST, V2_URL + "/hosts/search", callback=request_callback, content_type="application/json", ) query = self.api.search("services.service_name: HTTP", pages=2) assert next(query) == SEARCH_HOSTS_JSON["result"]["hits"] assert next(query) == SEARCH_HOSTS_JSON["result"]["hits"], "Retry did not fail" def test_search_pages_retry_fail(self): self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[matchers.json_params_matcher({"q": "services.service_name: HTTP", "per_page": 100})], ) self.responses.add( responses.POST, V2_URL + "/hosts/search", status=500, json=SERVER_ERROR_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "cursor": "eyJBZnRlciI6WyIxIiwiMS4wLjAuNDkiXSwiUmV2ZXJzZSI6ZmFsc2V9", } ) ], ) query = self.api.search("services.service_name: HTTP", pages=2) assert next(query) == SEARCH_HOSTS_JSON["result"]["hits"] with pytest.raises(CensysInternalServerException): next(query) def test_search_virtual_hosts(self): self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "virtual_hosts": "EXCLUDE", } ) ], ) query = self.api.search("services.service_name: HTTP", virtual_hosts="EXCLUDE") assert query() == SEARCH_HOSTS_JSON["result"]["hits"] def test_search_fields(self): self.responses.add( responses.POST, V2_URL + "/hosts/search", status=200, json=SEARCH_HOSTS_JSON, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": 100, "fields": ["ip", "services.port"], } ) ], ) query = self.api.search("services.service_name: HTTP", fields=["ip", "services.port"]) assert query() == SEARCH_HOSTS_JSON["result"]["hits"] def test_aggregate(self): self.responses.add( responses.GET, V2_URL + "/hosts/aggregate?field=services.port&q=services.service_name: HTTP&num_buckets=4", status=200, json=AGGREGATE_HOSTS_JSON, ) self.maxDiff = None res = self.api.aggregate("services.service_name: HTTP", "services.port", num_buckets=4) assert res == AGGREGATE_HOSTS_JSON["result"] def test_aggregate_virtual_hosts(self): self.responses.add( responses.GET, V2_URL + "/hosts/aggregate?field=services.port&q=services.service_name: HTTP&num_buckets=5" + "&virtual_hosts=INCLUDE", status=200, json=AGGREGATE_HOSTS_JSON, ) self.maxDiff = None res = self.api.aggregate( "services.service_name: HTTP", "services.port", num_buckets=5, virtual_hosts="INCLUDE", ) assert res == AGGREGATE_HOSTS_JSON["result"] def test_search_view_all(self): test_per_page = 50 ips = ["1.1.1.1", "1.1.1.2"] search_json = deepcopy(SEARCH_HOSTS_JSON) search_json["result"]["hits"] = [{"ip": ip} for ip in ips] search_json["result"]["total"] = len(ips) search_json["result"]["links"]["next"] = "" self.responses.add( responses.POST, f"{V2_URL}/hosts/search", status=200, json=search_json, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": test_per_page, } ) ], ) expected = {} for ip in ips: view_json = deepcopy(VIEW_HOST_JSON) view_json["result"]["ip"] = ip self.responses.add( responses.GET, f"{V2_URL}/hosts/{ip}", status=200, json=view_json, ) expected[ip] = deepcopy(view_json["result"]) query = self.api.search("services.service_name: HTTP", per_page=test_per_page) results = query.view_all() assert results == expected def test_search_view_all_virtual_hosts(self): test_per_page = 50 search_json = deepcopy(SEARCH_HOSTS_JSON) hits = [{"ip": "1.1.1.1", "name": "one.one.one.one"}, {"ip": "1.0.0.1"}] search_json["result"]["hits"] = hits search_json["result"]["total"] = len(hits) search_json["result"]["links"]["next"] = "" self.responses.add( responses.POST, f"{V2_URL}/hosts/search", status=200, json=search_json, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": test_per_page, } ) ], ) expected = {} for hit in hits: view_json = deepcopy(VIEW_HOST_JSON) view_json["result"]["ip"] = hit["ip"] document_key = hit["ip"] if "name" in hit: document_key += "+" + hit["name"] self.responses.add( responses.GET, f"{V2_URL}/hosts/{document_key}", status=200, json=view_json, ) expected[document_key] = deepcopy(view_json["result"]) query = self.api.search("services.service_name: HTTP", per_page=test_per_page) results = query.view_all() assert results == expected def test_search_view_all_error(self): test_per_page = 50 ips = ["1.1.1.1", "1.1.1.2", "1.1.1.3"] search_json = deepcopy(SEARCH_HOSTS_JSON) search_json["result"]["hits"] = [{"ip": ip} for ip in ips] search_json["result"]["total"] = len(ips) search_json["result"]["links"]["next"] = "" self.responses.add( responses.POST, f"{V2_URL}/hosts/search", status=200, json=search_json, match=[ matchers.json_params_matcher( { "q": "services.service_name: HTTP", "per_page": test_per_page, } ) ], ) expected = {} for ip in ips[:-1]: view_json = deepcopy(VIEW_HOST_JSON) view_json["result"]["ip"] = ip self.responses.add( responses.GET, f"{V2_URL}/hosts/{ip}", status=200, json=view_json, ) expected[ip] = deepcopy(view_json["result"]) self.responses.add( responses.GET, f"{V2_URL}/hosts/{ips[-1]}", status=429, json=RATE_LIMIT_ERROR_JSON, ) expected[ips[-1]] = { "error": "429 (rate_limit_exceeded): Rate limit exceeded. See https://search.censys.io/account for rate limit details." } query = self.api.search("services.service_name: HTTP", per_page=test_per_page) results = query.view_all() assert results == expected def test_view_host_names(self): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}/names", status=200, json=VIEW_HOST_NAMES_JSON, ) results = self.api.view_host_names(TEST_HOST) assert results == VIEW_HOST_NAMES_JSON["result"]["names"] def test_view_host_names_pages(self): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}/names?per_page=50", status=200, json=VIEW_HOST_NAMES_JSON, ) results = self.api.view_host_names(TEST_HOST, per_page=50) assert results == VIEW_HOST_NAMES_JSON["result"]["names"] def test_host_metadata(self): self.responses.add( responses.GET, f"{V2_URL}/metadata/hosts", status=200, json=HOST_METADATA_JSON, ) results = self.api.metadata() assert results == HOST_METADATA_JSON["result"] def test_view_host_diff(self): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}/diff", status=200, json=VIEW_HOST_DIFF_JSON, ) results = self.api.view_host_diff(TEST_HOST) assert results == VIEW_HOST_DIFF_JSON["result"] @pytest.mark.parametrize( ("kwargs", "query_params"), [ ({"ip_b": "1.1.1.2"}, "ip_b=1.1.1.2"), ( { "at_time": datetime.date(2021, 7, 1), "at_time_b": datetime.date(2021, 7, 31), }, "at_time=2021-07-01T00%3A00%3A00.000000Z&at_time_b=2021-07-31T00%3A00%3A00.000000Z", ), ], ) def test_view_host_diff_params(self, kwargs: dict, query_params: str): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}/diff?{query_params}", status=200, json=VIEW_HOST_DIFF_JSON, ) results = self.api.view_host_diff(TEST_HOST, **kwargs) assert results == VIEW_HOST_DIFF_JSON["result"] @pytest.mark.parametrize( ("kwargs", "query_params"), [ ({}, ""), ({"per_page": 50}, "per_page=50"), ( { "start_time": datetime.date(2021, 7, 1), "end_time": datetime.date(2021, 7, 31), }, "start_time=2021-07-01T00%3A00%3A00.000000Z&end_time=2021-07-31T00%3A00%3A00.000000Z", ), ( {"cursor": "nextCursor", "reversed": True}, "cursor=nextCursor&reversed=True", ), ], ) def test_view_host_events_params(self, kwargs: dict, query_params: str): url = f"{V2_URL}/experimental/hosts/{TEST_HOST}/events" if query_params: url += "?" + query_params self.responses.add( responses.GET, url, status=200, json=VIEW_HOST_EVENTS_JSON, ) results = self.api.view_host_events(TEST_HOST, **kwargs) assert results == VIEW_HOST_EVENTS_JSON["result"] @pytest.mark.parametrize( ("kwargs", "query_params"), [ ({}, {"per_page": 100}), ({"per_page": 50}, {"per_page": 50}), ( { "start_time": datetime.date(2021, 7, 1), }, {"per_page": 100, "start_time": "2021-07-01T00:00:00.000000Z"}, ), ( {"cursor": "nextCursor"}, {"per_page": 100, "cursor": "nextCursor"}, ), ], ) def test_view_host_certificates(self, kwargs: dict, query_params: dict): self.responses.add( responses.GET, f"{V2_URL}/hosts/{TEST_HOST}/certificates", status=200, json=VIEW_HOST_EVENTS_JSON, match=[matchers.query_param_matcher(query_params)], ) results = self.api.view_host_certificates(TEST_HOST, **kwargs) assert results == VIEW_HOST_EVENTS_JSON["result"] censys-censys-python-7a57a89/tests/search/v2/test_tags.py000066400000000000000000000135331524433232300235210ustar00rootroot00000000000000import pytest import responses from censys.search.v2 import CensysCerts, CensysHosts from censys.search.v2.api import CensysSearchAPIv2 from tests.utils import BASE_URL, CensysTestCase TEST_TAG_NAME = "is-honeypot" TEST_TAG_COLOR = "#ff0000" TEST_TAG_ID = "123" LIST_TAGS_RESPONSE = { "code": 200, "status": "OK", "result": { "tags": [ { "id": TEST_TAG_ID, "name": TEST_TAG_NAME, "metadata": {"color": TEST_TAG_COLOR}, "created_at": "2021-01-01T00:00:00.000000Z", "updated_at": "2021-02-01T00:00:00.000000Z", } ] }, } CREATE_TAG_RESPONSE = { "code": 200, "status": "OK", "result": { "id": TEST_TAG_ID, "name": TEST_TAG_NAME, "metadata": {"color": TEST_TAG_COLOR}, "created_at": "2021-01-01T00:00:00.000000Z", "updated_at": "2021-02-01T00:00:00.000000Z", }, } LIST_HOSTS_RESPONSE = { "code": 200, "status": "OK", "result": {"hosts": [{"ip": "1.1.1.1", "tagged_at": "2021-01-01T12:00:00.000000Z"}]}, } LIST_CERTS_RESPONSE = { "code": 200, "status": "OK", "result": { "certs": [ { "fingerprint": "e58e89a726d80bb0219b218c3ab9d818b4be75d77959508400d660ebe1c1be3d", "tagged_at": "2021-01-01T12:00:00.000000Z", } ] }, } INDEX_PARAMS = [ {"index": "hosts", "index_cls": CensysHosts, "document_id": "1.0.0.0"}, { "index": "certificates", "index_cls": CensysCerts, "document_id": "fb444eb8e68437bae06232b9f5091bccff62a768ca09e92eb5c9c2cf9d17c426", }, ] class CensysTagsTests(CensysTestCase): index: str index_cls: CensysSearchAPIv2 document_id: str api: CensysSearchAPIv2 @pytest.fixture(autouse=True, params=INDEX_PARAMS, ids=["hosts", "certificates"]) def _index_config(self, request): self.index = request.param["index"] self.index_cls = request.param["index_cls"] self.document_id = request.param["document_id"] self.setUpApi(self.index_cls(self.api_id, self.api_secret)) def test_list_all_tags(self): self.responses.add( responses.GET, BASE_URL + self.api.tags_path, status=200, json=LIST_TAGS_RESPONSE, ) results = self.api.list_all_tags() assert results == LIST_TAGS_RESPONSE["result"]["tags"] def test_create_tag(self): self.responses.add( responses.POST, BASE_URL + self.api.tags_path, status=200, json=CREATE_TAG_RESPONSE, match=[responses.json_params_matcher({"name": TEST_TAG_NAME, "metadata": {"color": TEST_TAG_COLOR}})], ) results = self.api.create_tag(TEST_TAG_NAME, TEST_TAG_COLOR) assert results == CREATE_TAG_RESPONSE["result"] def test_get_tag(self): self.responses.add( responses.GET, BASE_URL + self.api.tags_path + "/" + TEST_TAG_ID, status=200, json=CREATE_TAG_RESPONSE, ) results = self.api.get_tag(TEST_TAG_ID) assert results == CREATE_TAG_RESPONSE["result"] def test_update_tag(self): self.responses.add( responses.PUT, BASE_URL + self.api.tags_path + "/" + TEST_TAG_ID, status=200, json=CREATE_TAG_RESPONSE, match=[responses.json_params_matcher({"name": TEST_TAG_NAME, "metadata": {"color": TEST_TAG_COLOR}})], ) results = self.api.update_tag(TEST_TAG_ID, TEST_TAG_NAME, TEST_TAG_COLOR) assert results == CREATE_TAG_RESPONSE["result"] def test_delete_tag(self): self.responses.add( responses.DELETE, BASE_URL + self.api.tags_path + "/" + TEST_TAG_ID, status=204, ) self.api.delete_tag(TEST_TAG_ID) assert len(self.responses.calls) == 1 def test_list_tags_on_document(self): self.responses.add( responses.GET, BASE_URL + self.api.view_path + self.document_id + "/tags", status=200, json=LIST_TAGS_RESPONSE, ) results = self.api.list_tags_on_document(self.document_id) assert results == LIST_TAGS_RESPONSE["result"]["tags"] def test_add_tag_to_document(self): self.responses.add( responses.PUT, f"{BASE_URL}{self.api.view_path}{self.document_id}/tags/{TEST_TAG_ID}", status=200, ) self.api.add_tag_to_document(self.document_id, TEST_TAG_ID) assert len(self.responses.calls) == 1 def test_remove_tag_to_document(self): self.responses.add( responses.DELETE, f"{BASE_URL}{self.api.view_path}{self.document_id}/tags/{TEST_TAG_ID}", status=200, ) self.api.remove_tag_from_document(self.document_id, TEST_TAG_ID) assert len(self.responses.calls) == 1 def test_list_hosts_with_tag(self): if self.index == "certificates": pytest.skip("Only applicable to hosts assets") self.responses.add( responses.GET, BASE_URL + self.api.tags_path + "/" + TEST_TAG_ID + "/hosts", status=200, json=LIST_HOSTS_RESPONSE, ) results = self.api.list_hosts_with_tag(TEST_TAG_ID) assert results == [host["ip"] for host in LIST_HOSTS_RESPONSE["result"]["hosts"]] def test_list_certs_with_tag(self): if self.index == "hosts": pytest.skip("Only applicable to certs assets") self.responses.add( responses.GET, BASE_URL + self.api.tags_path + "/" + TEST_TAG_ID + "/certificates", status=200, json=LIST_CERTS_RESPONSE, ) results = self.api.list_certs_with_tag(TEST_TAG_ID) assert results == LIST_CERTS_RESPONSE["result"]["certs"] censys-censys-python-7a57a89/tests/test_base.py000066400000000000000000000100331524433232300216710ustar00rootroot00000000000000import pytest import requests.utils import responses from requests.models import Response from censys.common import __version__ from censys.common.base import CensysAPIBase from censys.common.exceptions import CensysAPIException, CensysException from .utils import CensysTestCase TEST_URL = "https://url" TEST_ENDPOINT = "/endpoint" ERROR_JSON = { "error": "Test Error", "error_type": "Test", "errorCode": 200, "details": "This is a test error", } class CensysAPIBaseTests(CensysTestCase): def test_base_get_exception_class(self): # Mock base = CensysAPIBase("url") # Actual call/assertions assert base._get_exception_class(Response()) == CensysAPIException def test_no_api_url(self): # Mock self.mocker.patch.dict("os.environ", {"CENSYS_API_URL": ""}) # Actual call/error raising with pytest.raises(CensysException, match="No API url configured."): CensysAPIBase() def test_successful_empty_json_response(self): # Mock response self.responses.add( responses.GET, TEST_URL + TEST_ENDPOINT, status=200, body=None, ) # Actual call base = CensysAPIBase(TEST_URL) # Assertions assert base._get(TEST_ENDPOINT) == { "status": "OK", "code": 200, } def test_successful_error_json_response(self): # Mock response self.responses.add( responses.GET, TEST_URL + TEST_ENDPOINT, status=200, json=ERROR_JSON, ) # Actual call base = CensysAPIBase(TEST_URL) # Assertion/error raising with pytest.raises(CensysAPIException, match=ERROR_JSON["error"]): base._get(TEST_ENDPOINT) def test_invalid_json_response(self): # Mock response self.responses.add( responses.GET, TEST_URL + TEST_ENDPOINT, status=400, body="

Definitely not JSON

", ) # Actual call base = CensysAPIBase(TEST_URL) # Assertion/error raising with pytest.raises(CensysAPIException, match="is not valid JSON and cannot be decoded"): base._get(TEST_ENDPOINT) def test_default_user_agent(self): # Mock/actual call base = CensysAPIBase(TEST_URL) # Assertions assert base._session.headers["User-Agent"] == f"{requests.utils.default_user_agent()} censys-python/{__version__}" def test_user_agent(self): # Mock/actual call base = CensysAPIBase(TEST_URL, user_agent="test") # Assertions assert base._session.headers["User-Agent"] == requests.utils.default_user_agent() + " test" def test_request_id(self): id_value = "my-request-id" # Test request id value is present base = CensysAPIBase(TEST_URL, request_id=id_value) assert base.request_id == id_value assert base._session.headers.get("x-request-id") == id_value # Test request id value is not present base.request_id = None assert base.request_id is None assert base._session.headers.get("x-request-id") is None @pytest.mark.filterwarnings("ignore:HTTP proxies will not be used.") def test_proxies(self): # Mock/actual call base = CensysAPIBase(TEST_URL, proxies={"http": "test", "https": "tests"}) # Assertions assert list(base._session.proxies.keys()) == ["https"] def test_cookies(self): # Mock/actual call base = CensysAPIBase(TEST_URL, cookies={"_ga": "GA"}) # Assertions assert list(base._session.cookies.keys()) == ["_ga"] def test_verify_and_cert(self): # Mock/actual call base = CensysAPIBase( TEST_URL, cert=("/path/to/clientcert", "/path/to/clientkey"), verify="/path/to/cacert", ) assert base._session.cert == ("/path/to/clientcert", "/path/to/clientkey") assert base._session.verify == "/path/to/cacert" censys-censys-python-7a57a89/tests/test_client.py000066400000000000000000000017621524433232300222460ustar00rootroot00000000000000from censys.search import SearchClient from .utils import CensysTestCase ALL_INDEXES = { "v1": ["data"], "v2": ["hosts", "certificates"], } class CensysSearchClientTest(CensysTestCase): def setup_method(self): self.expected_auth = (self.api_id, self.api_secret) def test_api_creds_args(self): # Actual call client = SearchClient(self.api_id, self.api_secret) for version, indexes in ALL_INDEXES.items(): v = getattr(client, version) for index in indexes: # Assertions assert getattr(v, index)._session.auth == self.expected_auth def test_api_creds_kwargs(self): # Actual call client = SearchClient(api_id=self.api_id, api_secret=self.api_secret) for version, indexes in ALL_INDEXES.items(): v = getattr(client, version) for index in indexes: # Assertions assert getattr(v, index)._session.auth == self.expected_auth censys-censys-python-7a57a89/tests/test_utils.py000066400000000000000000000016121524433232300221220ustar00rootroot00000000000000import datetime import pytest from censys.common.utils import format_iso8601, format_rfc3339 class UtilsTest: @pytest.mark.parametrize( ("since", "actual"), [ ("2021-01-01", "2021-01-01"), (datetime.date(2021, 1, 1), "2021-01-01T00:00:00.000000Z"), ( datetime.datetime(2021, 1, 1, 12, 15, 20, 40), "2021-01-01T12:15:20.000040Z", ), ], ) def test_format_rfc3339(self, since, actual): assert format_rfc3339(since) == actual @pytest.mark.parametrize( ("since", "actual"), [ ("2021-01-01", "2021-01-01"), (datetime.date(2021, 1, 1), "2021-01-01"), (datetime.datetime(2021, 1, 1, 12, 15, 20, 40), "2021-01-01"), ], ) def test_format_iso8601(self, since, actual): assert format_iso8601(since) == actual censys-censys-python-7a57a89/tests/utils.py000066400000000000000000000025371524433232300210720ustar00rootroot00000000000000from typing import Optional import pytest import responses from pytest_mock import MockerFixture from censys.common.base import CensysAPIBase BASE_URL = "https://search.censys.io/api" V1_URL = BASE_URL + "/v1" V2_URL = BASE_URL + "/v2" class CensysTestCase: api_id = "test-api-id" api_secret = "test-api-secret" api_key = "test-api-key" cli_args = [ "--api-id", api_id, "--api-secret", api_secret, ] asm_cli_args = [ "--api-key", api_key, ] api: CensysAPIBase mocker: MockerFixture @pytest.fixture(autouse=True) def _setup(self, mocker: MockerFixture): self.mocker = mocker mocker.patch("time.sleep", return_value=None) rsps = responses.RequestsMock(assert_all_requests_are_fired=False) rsps.start() self.responses = rsps yield rsps.stop() rsps.reset() def setUpApi(self, api: CensysAPIBase): # noqa: N802 self.api = api self.base_url = self.api._api_url def patch_args( self, args: list[str], search_auth: Optional[bool] = False, asm_auth: Optional[bool] = False, ): if search_auth: args.extend(self.cli_args) if asm_auth: args.extend(self.asm_cli_args) self.mocker.patch("argparse._sys.argv", args) censys-censys-python-7a57a89/uv.lock000066400000000000000000012312251524433232300175210ustar00rootroot00000000000000version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] [[package]] name = "alabaster" version = "0.7.16" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, ] [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] [[package]] name = "argcomplete" version = "3.7.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284, upload-time = "2026-06-30T22:28:22.249Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575, upload-time = "2026-06-30T22:28:20.547Z" }, ] [[package]] name = "argcomplete" version = "3.7.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/87/6f/5a73f04007ca950701765949209f068da628bd11f9c2da287278ce91e0ee/argcomplete-3.7.2.tar.gz", hash = "sha256:aad8b69a0b9969edb62db0d1752354c0d50717b10e0cbb00e2a958381b9fc6b9", size = 74473, upload-time = "2026-08-06T04:53:21.662Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", hash = "sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", size = 43294, upload-time = "2026-08-06T04:53:20.246Z" }, ] [[package]] name = "babel" version = "2.18.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] name = "backoff" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, ] [[package]] name = "censys" version = "2.3.0" source = { editable = "." } dependencies = [ { name = "argcomplete", version = "3.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "argcomplete", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "backoff" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "rich" }, ] [package.dev-dependencies] dev = [ { name = "mypy", version = "1.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "mypy", version = "1.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-mock" }, { name = "responses" }, { name = "ruff" }, { name = "types-requests", version = "2.32.4.20260107", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "types-requests", version = "2.33.0.20260712", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] docs = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-copybutton" }, { name = "sphinx-prompt", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx-prompt", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx-prompt", version = "1.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-tabs", version = "3.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx-tabs", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "sphinxcontrib-autoprogram" }, ] [package.metadata] requires-dist = [ { name = "argcomplete", specifier = ">=2.0.0,<4.0.0" }, { name = "backoff", specifier = ">=2.0.0,<3.0.0" }, { name = "requests", specifier = ">=2.32.0" }, { name = "rich", specifier = ">=10.16.2" }, ] [package.metadata.requires-dev] dev = [ { name = "mypy", specifier = ">=1.5.1,<3" }, { name = "pytest", specifier = ">=7.3,<10" }, { name = "pytest-cov", specifier = ">=4" }, { name = "pytest-mock", specifier = ">=3.10.0,<4" }, { name = "responses", specifier = ">=0.23.1" }, { name = "ruff", specifier = ">=0.9.0" }, { name = "types-requests", specifier = ">=2.29.0.0,<3" }, ] docs = [ { name = "sphinx", specifier = ">=7.2.6" }, { name = "sphinx-copybutton", specifier = ">=0.5.2" }, { name = "sphinx-prompt", specifier = ">=1.8.0" }, { name = "sphinx-rtd-theme", specifier = ">=3.0.2" }, { name = "sphinx-tabs", specifier = ">=3.4.1" }, { name = "sphinxcontrib-autoprogram", specifier = ">=0.1.9" }, ] [[package]] name = "certifi" version = "2026.7.22" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, ] [[package]] name = "charset-normalizer" version = "3.5.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/71/aa/554e2614f38fc34c58ff1d0911ae8535ad2516440d5482d76fe59f1088b0/charset_normalizer-3.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa", size = 369072, upload-time = "2026-08-15T08:16:22.964Z" }, { url = "https://files.pythonhosted.org/packages/03/6d/439231dfc3ccfa6f8c06477b7da2219cbd41a2de3d49084df8ec7b5100f2/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda", size = 251142, upload-time = "2026-08-15T08:16:24.81Z" }, { url = "https://files.pythonhosted.org/packages/55/53/7d819bd23a00ef45039146fa2cce1daa2f0771e758c5653ee1f6edac91ed/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45", size = 240714, upload-time = "2026-08-15T08:16:26.392Z" }, { url = "https://files.pythonhosted.org/packages/b2/2c/45847198c16f4b38090cc7423b2b6a9008e438704d8ab413211832498d31/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab", size = 279637, upload-time = "2026-08-15T08:16:27.961Z" }, { url = "https://files.pythonhosted.org/packages/69/2b/d8be3523ddf9f0b0f3e56d1359034aa10653a4d11564c697f802b4775766/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a", size = 276543, upload-time = "2026-08-15T08:16:29.399Z" }, { url = "https://files.pythonhosted.org/packages/32/cd/4f564b8f132de25db594efc706897069f016790cea63a5669c9df2675f64/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3", size = 261644, upload-time = "2026-08-15T08:16:30.722Z" }, { url = "https://files.pythonhosted.org/packages/f5/e3/38b975422534a608f98c360e79c2f07c763d66dd4272300d45fb1fee54b0/charset_normalizer-3.5.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb", size = 259609, upload-time = "2026-08-15T08:16:32.248Z" }, { url = "https://files.pythonhosted.org/packages/87/bd/fbc24d825c66f1c74f6ccdea3742c3d8354a4888e86d1315a197fee69061/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f", size = 252457, upload-time = "2026-08-15T08:16:33.849Z" }, { url = "https://files.pythonhosted.org/packages/b9/2d/918d0e98a0e679469ed05bb2d90c2088b4d315bb612969d8499f76fb5210/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea", size = 242240, upload-time = "2026-08-15T08:16:35.396Z" }, { url = "https://files.pythonhosted.org/packages/20/c8/c36f6e0b2dfec351bd38cbc05362697e58bcd073d7dbd95154290c9714ce/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f", size = 280308, upload-time = "2026-08-15T08:16:36.825Z" }, { url = "https://files.pythonhosted.org/packages/ca/7b/311b3e02e8c4092400c449c850a760d8c45d900983c83a70cc07208c551d/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182", size = 258679, upload-time = "2026-08-15T08:16:38.22Z" }, { url = "https://files.pythonhosted.org/packages/b9/90/082cc45599c392f28c036a497f49e0634041a785fc3849c80ccf396d096f/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa", size = 277221, upload-time = "2026-08-15T08:16:39.62Z" }, { url = "https://files.pythonhosted.org/packages/58/ad/b9aecf38d805cbcf84fa94f14c5d972a16561e20296a11dc799a5dcf3763/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818", size = 263799, upload-time = "2026-08-15T08:16:40.885Z" }, { url = "https://files.pythonhosted.org/packages/b7/23/b38a20598d5a825f85d9d7636860e56ff0db1479f86497a6e485aa9326f7/charset_normalizer-3.5.1-cp310-cp310-win32.whl", hash = "sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20", size = 182037, upload-time = "2026-08-15T08:16:42.198Z" }, { url = "https://files.pythonhosted.org/packages/d2/21/83fffb77864408b8bf0fe1ca603926401d6f8775a8e150b39aacc9958f8a/charset_normalizer-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d", size = 206030, upload-time = "2026-08-15T08:16:43.787Z" }, { url = "https://files.pythonhosted.org/packages/86/2e/b93135b5034b1157fb29554b0d06d4844ce62282f0e0a14036f93d7ee2e7/charset_normalizer-3.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5", size = 185092, upload-time = "2026-08-15T08:16:45.177Z" }, { url = "https://files.pythonhosted.org/packages/6a/b6/034f6802e9c3f6418966cfabb7db8c9252cc2429c5098f41cc43af804149/charset_normalizer-3.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30", size = 363585, upload-time = "2026-08-15T08:16:46.646Z" }, { url = "https://files.pythonhosted.org/packages/d5/fa/6a7e2a7c4b5451912b8c417732df79574354443592a88d616de03da66ae5/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488", size = 251189, upload-time = "2026-08-15T08:16:48.287Z" }, { url = "https://files.pythonhosted.org/packages/a4/c8/ab42b07cfd82e919f427fcfaa7c41abae8242833ad1aad66d42bae40b669/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22", size = 239724, upload-time = "2026-08-15T08:16:49.67Z" }, { url = "https://files.pythonhosted.org/packages/e7/80/b9348b5d3041209f98b4cdad7655766369233f1d533f4f4f7558e9717bec/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731", size = 280078, upload-time = "2026-08-15T08:16:51.228Z" }, { url = "https://files.pythonhosted.org/packages/82/38/083a24028304bc85bb9e376fed801178423dcbb67495f73b6ea0624e1894/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c", size = 276650, upload-time = "2026-08-15T08:16:52.625Z" }, { url = "https://files.pythonhosted.org/packages/0d/35/731ac04aa0a097fc1c97f0994c375bdb230c6c96619db794208fe664e9ce/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8", size = 262325, upload-time = "2026-08-15T08:16:54.085Z" }, { url = "https://files.pythonhosted.org/packages/f5/28/c2028e7021fb89c6e56868ed0e387b8e9aa811abdd2ab3208d6578d2c930/charset_normalizer-3.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486", size = 261140, upload-time = "2026-08-15T08:16:55.604Z" }, { url = "https://files.pythonhosted.org/packages/28/f0/0c0ceec6d98b7daa62e361e418135d59685811d79ba11529aad5cdf15e84/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f", size = 252791, upload-time = "2026-08-15T08:16:57.103Z" }, { url = "https://files.pythonhosted.org/packages/f0/3e/48f4cd187b1c33189d86039e9cbe4f92c05454175504b44ff81806d4d1bf/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c", size = 240730, upload-time = "2026-08-15T08:16:58.418Z" }, { url = "https://files.pythonhosted.org/packages/42/85/f9e22af69af67c54cce42be9455d9c81294f918b4ccc454db01f66efcac2/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18", size = 280791, upload-time = "2026-08-15T08:16:59.918Z" }, { url = "https://files.pythonhosted.org/packages/fd/4c/9044135f42127630b6fa742feb51256353f6ab87a78f2fdd1de3de955a7f/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5", size = 259598, upload-time = "2026-08-15T08:17:01.421Z" }, { url = "https://files.pythonhosted.org/packages/ba/ed/1dd7cfebb4e75812934c49ca3b79757d11948053f7937ab7070c151f3c55/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b", size = 278217, upload-time = "2026-08-15T08:17:02.782Z" }, { url = "https://files.pythonhosted.org/packages/bf/eb/239c84503cc9e3ba6eb34686a24bc66e84f3924efdd7e38e751a19f6bc10/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6", size = 263417, upload-time = "2026-08-15T08:17:04.216Z" }, { url = "https://files.pythonhosted.org/packages/37/ab/4e4510e1e288478e2c8333131d1c1382382ba8cd2165053c79e39d1da961/charset_normalizer-3.5.1-cp311-cp311-win32.whl", hash = "sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b", size = 181774, upload-time = "2026-08-15T08:17:05.58Z" }, { url = "https://files.pythonhosted.org/packages/e3/57/32f0ccea59e8612057c61d6fd22ef2cb63cca93c9fe594094919696ac170/charset_normalizer-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9", size = 206653, upload-time = "2026-08-15T08:17:07.075Z" }, { url = "https://files.pythonhosted.org/packages/17/d4/b65c433fc521e58b5f54293982a5e51c05cb5f2dd3f1c7a6acb65b75324e/charset_normalizer-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10", size = 185630, upload-time = "2026-08-15T08:17:08.502Z" }, { url = "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", size = 344456, upload-time = "2026-08-15T08:17:10.072Z" }, { url = "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", size = 238530, upload-time = "2026-08-15T08:17:11.563Z" }, { url = "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", size = 230200, upload-time = "2026-08-15T08:17:12.919Z" }, { url = "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", size = 262222, upload-time = "2026-08-15T08:17:14.571Z" }, { url = "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", size = 258951, upload-time = "2026-08-15T08:17:16.158Z" }, { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, { url = "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", size = 244070, upload-time = "2026-08-15T08:17:19.191Z" }, { url = "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", size = 240110, upload-time = "2026-08-15T08:17:20.547Z" }, { url = "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", size = 232836, upload-time = "2026-08-15T08:17:21.895Z" }, { url = "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", size = 262712, upload-time = "2026-08-15T08:17:23.727Z" }, { url = "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", size = 242977, upload-time = "2026-08-15T08:17:25.157Z" }, { url = "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", size = 260207, upload-time = "2026-08-15T08:17:26.602Z" }, { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, { url = "https://files.pythonhosted.org/packages/62/16/46556278c2168d12df9da7fede5dc6fc70e60301b26a82bbeec238c9cfe3/charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2", size = 178507, upload-time = "2026-08-15T08:17:29.277Z" }, { url = "https://files.pythonhosted.org/packages/9d/7a/4c6c298171e6b3e745633180ff59350fc0ca0db1ffd28df1e369e0579f71/charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2", size = 200551, upload-time = "2026-08-15T08:17:30.668Z" }, { url = "https://files.pythonhosted.org/packages/cd/d7/eb95a042f0dd22e304b0b6472b154f3546a1a039a9ee89ccb2a7f61591fc/charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a", size = 180700, upload-time = "2026-08-15T08:17:32.028Z" }, { url = "https://files.pythonhosted.org/packages/bc/61/2cb6ad133dbbb449fa2d37ccae973232f4827e799af258d15e589a3d1e9e/charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9", size = 211584, upload-time = "2026-08-15T08:17:33.597Z" }, { url = "https://files.pythonhosted.org/packages/18/57/a305c968be1ca13f3dd1b32f445877e97addf55d80b65c7cb35fac82b777/charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491", size = 223359, upload-time = "2026-08-15T08:17:35.022Z" }, { url = "https://files.pythonhosted.org/packages/09/0a/d3646670292ce8d8f8cc11ac067d44885e697a5591f57a9221128da5e7b3/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7", size = 194464, upload-time = "2026-08-15T08:17:36.452Z" }, { url = "https://files.pythonhosted.org/packages/de/93/d51ec556e01042fed6f993ea859311bc7917b466684182fbbceb6ca24762/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e", size = 197676, upload-time = "2026-08-15T08:17:37.819Z" }, { url = "https://files.pythonhosted.org/packages/a4/a0/562247944386f7d4ef94467e84876600cc1e0f1b93239aaa9213d2bc3cbd/charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d", size = 340473, upload-time = "2026-08-15T08:17:39.303Z" }, { url = "https://files.pythonhosted.org/packages/31/e7/1d994be1b93d41e9502b8b0460eaa88a1dd8df335df415db87d6c3e91ab2/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a", size = 240156, upload-time = "2026-08-15T08:17:40.66Z" }, { url = "https://files.pythonhosted.org/packages/09/53/27923ce5cc6cbccb832037b27dca98882d9c53e9b69e866bbbef4aae7fc8/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe", size = 228246, upload-time = "2026-08-15T08:17:42.003Z" }, { url = "https://files.pythonhosted.org/packages/ce/48/5a97e84d63af1d55c07439cb80e56d99a8efb4295700eb4e18c0d1615d2c/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac", size = 263660, upload-time = "2026-08-15T08:17:43.627Z" }, { url = "https://files.pythonhosted.org/packages/7a/c2/071575791dcc88316c0a9a65ce38897a82e4cfe4a325f0f7fe1b1ac47bcf/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e", size = 260354, upload-time = "2026-08-15T08:17:45.094Z" }, { url = "https://files.pythonhosted.org/packages/fb/af/63240b0c0248c075c2535a1f1bd992821d8251b9f173abc13329661d09e4/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3", size = 250638, upload-time = "2026-08-15T08:17:46.496Z" }, { url = "https://files.pythonhosted.org/packages/4d/66/70dfad64f15be09c15ccfee81330a7e515895dbe296dd23114e9a231268a/charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876", size = 244583, upload-time = "2026-08-15T08:17:47.963Z" }, { url = "https://files.pythonhosted.org/packages/c0/24/ef36367d38b9ddd4bccbf72888c342e8de1f5ae506fa0b2dcf970e2732a1/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6", size = 242038, upload-time = "2026-08-15T08:17:49.481Z" }, { url = "https://files.pythonhosted.org/packages/db/ab/55e683ba0fff2e43adafc10daa3001eac90fdaa419a97227d5a7067eedde/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2", size = 233677, upload-time = "2026-08-15T08:17:50.845Z" }, { url = "https://files.pythonhosted.org/packages/bd/67/0f40eaf8d1b6e7cf15e82382a2965efaca787fc1c2794b7021d37aaf5036/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591", size = 264491, upload-time = "2026-08-15T08:17:52.61Z" }, { url = "https://files.pythonhosted.org/packages/5c/64/12b4c2a11ee8df4fcc518c78b0d93e3a92bd3d5253d1617ce74ff0e8c7ef/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c", size = 245196, upload-time = "2026-08-15T08:17:54.023Z" }, { url = "https://files.pythonhosted.org/packages/37/2e/651d910af6d0fba325eee1cda37ec5443462ed25360e666c144166eb6091/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c", size = 261660, upload-time = "2026-08-15T08:17:55.491Z" }, { url = "https://files.pythonhosted.org/packages/90/c6/b09e05e6db7f64338e0dc067c79577b1138da86c1e38369096851d96be88/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f", size = 252618, upload-time = "2026-08-15T08:17:57.025Z" }, { url = "https://files.pythonhosted.org/packages/76/4e/362d4f9fdcdf5556fb2aa3ce7d4a58ebce03ed1ff03aa1d9aca8d02f13f3/charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4", size = 140362, upload-time = "2026-08-15T08:17:58.425Z" }, { url = "https://files.pythonhosted.org/packages/b4/d4/703be739b26acce318bd29eb3b25b7209e1b1f527f9eae3d1f1f01fdde2b/charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3", size = 177755, upload-time = "2026-08-15T08:18:00.037Z" }, { url = "https://files.pythonhosted.org/packages/8a/33/56d97ade41c8db611e727168c52ae46c9224c362ec28d4b65d7e9869e8da/charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6", size = 199295, upload-time = "2026-08-15T08:18:01.506Z" }, { url = "https://files.pythonhosted.org/packages/5b/75/5b20dd1e6573a01a08158fe104104fa2c8abf941745596954185726cd46c/charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0", size = 179856, upload-time = "2026-08-15T08:18:02.929Z" }, { url = "https://files.pythonhosted.org/packages/29/cd/2b812ce5e888f1ce69a5350281e58aab07ae64a958ecae8912f30865718e/charset_normalizer-3.5.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8", size = 212318, upload-time = "2026-08-15T08:18:04.403Z" }, { url = "https://files.pythonhosted.org/packages/9e/4a/a6ee107430768a5334e6d63f31f148a04a1a491ef161a1ac9415a73f2fa8/charset_normalizer-3.5.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102", size = 224897, upload-time = "2026-08-15T08:18:05.997Z" }, { url = "https://files.pythonhosted.org/packages/c3/d9/35ae3f64f29d0179c35c3baefe575904df2913dde519129c7f75995a2b1d/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5", size = 194848, upload-time = "2026-08-15T08:18:07.397Z" }, { url = "https://files.pythonhosted.org/packages/74/76/f2fc7380f056cc273a53af37f50d08ad54b2c59f61078f31432edcf1c2bd/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3", size = 198163, upload-time = "2026-08-15T08:18:08.989Z" }, { url = "https://files.pythonhosted.org/packages/e9/40/095ce62fa078483cccc1fa2b36e6bc9580b85422a20ee9f925341c50e44f/charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c", size = 341823, upload-time = "2026-08-15T08:18:10.458Z" }, { url = "https://files.pythonhosted.org/packages/f1/5a/0e58b1c04a1596e0256f407274a92d5fb2ee21324409d1fab1da48a65b5b/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0", size = 242458, upload-time = "2026-08-15T08:18:11.989Z" }, { url = "https://files.pythonhosted.org/packages/22/95/b4618ce912e6db0b1aae89ba788e38e8a7eba0f3025cc66e8c0699f977b2/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96", size = 226717, upload-time = "2026-08-15T08:18:13.401Z" }, { url = "https://files.pythonhosted.org/packages/8a/76/c681192bbda3d55356db5dadd64381d5202b37c6b598fcda5282e88b5d3d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc", size = 266111, upload-time = "2026-08-15T08:18:14.961Z" }, { url = "https://files.pythonhosted.org/packages/88/be/55127bfca72c0cff6c022488d140d7c5b04c771e3b72e9bdb4836d54979d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f", size = 263128, upload-time = "2026-08-15T08:18:16.515Z" }, { url = "https://files.pythonhosted.org/packages/e0/91/39c3af510b0aa32bbda03374259200f28430febfd1bf5e511fe765282ce5/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90", size = 251240, upload-time = "2026-08-15T08:18:18.127Z" }, { url = "https://files.pythonhosted.org/packages/1c/a5/cbe418bbc6ecdfc3e05a0116002897c4b403a5e838d697e64c78e9f0190d/charset_normalizer-3.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506", size = 245282, upload-time = "2026-08-15T08:18:19.625Z" }, { url = "https://files.pythonhosted.org/packages/cc/a4/689bb42e8e7cd492f3cb64907c6bc00ad247ec9a3628cd3f8eed126e8ae1/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5", size = 244597, upload-time = "2026-08-15T08:18:21.121Z" }, { url = "https://files.pythonhosted.org/packages/c1/ce/9962938e179cf9f699d3f1e7b3114b5d7642dee6a893745229f9dd04f274/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e", size = 231376, upload-time = "2026-08-15T08:18:22.57Z" }, { url = "https://files.pythonhosted.org/packages/85/54/46000450ada53bd9eac5429a2c8c54cd2d9b39c0c255f229aea9af0948a5/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5", size = 266715, upload-time = "2026-08-15T08:18:24.235Z" }, { url = "https://files.pythonhosted.org/packages/3d/bb/618749d70f792b44252a777bf89bfb86823b9bbc1ea13fe8ce759b07f38a/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3", size = 245848, upload-time = "2026-08-15T08:18:25.726Z" }, { url = "https://files.pythonhosted.org/packages/7e/3f/ffb64458527c7668031d5eb095d978de561958dc9f5b53f8e488a533e603/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3", size = 264521, upload-time = "2026-08-15T08:18:27.193Z" }, { url = "https://files.pythonhosted.org/packages/4f/ab/74a55fd803916a35ac461daf002708191aac19b546b80dc8cabfedc63d98/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36", size = 253054, upload-time = "2026-08-15T08:18:28.568Z" }, { url = "https://files.pythonhosted.org/packages/a0/2a/6a9034b7d3c60b17499afb482df5878bf9fa20b50cc3887d5ef017a833db/charset_normalizer-3.5.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7", size = 140580, upload-time = "2026-08-15T08:18:30.214Z" }, { url = "https://files.pythonhosted.org/packages/f3/46/1d362e1a00d035d66b9869e1281eee115907f7e390a16a07824ab5737360/charset_normalizer-3.5.1-cp314-cp314-win32.whl", hash = "sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b", size = 180325, upload-time = "2026-08-15T08:18:31.877Z" }, { url = "https://files.pythonhosted.org/packages/7a/7c/4938c329b6a9d446f6a59aa2092ff7118f274209b5ed0e26893d1d30a63c/charset_normalizer-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b", size = 204175, upload-time = "2026-08-15T08:18:33.466Z" }, { url = "https://files.pythonhosted.org/packages/ac/33/eeb384dbd8dec570661354592f4f2e1b2fcc92585624d146a000caf53841/charset_normalizer-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687", size = 184123, upload-time = "2026-08-15T08:18:34.913Z" }, { url = "https://files.pythonhosted.org/packages/1c/6c/c73fa9d5a85f6ab05395de61c5f6984e0a9ff40bb5ff888d46dff02526c6/charset_normalizer-3.5.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348", size = 381682, upload-time = "2026-08-15T08:18:36.349Z" }, { url = "https://files.pythonhosted.org/packages/30/c7/63565f860921457feba93bae6c86fb7746deb4cffeed2f375cb845318146/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef", size = 240826, upload-time = "2026-08-15T08:18:37.887Z" }, { url = "https://files.pythonhosted.org/packages/06/ae/7ae8807410dfa33f8e6f1715740adeaafa8a816cc4cb33508f54b1f7c896/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885", size = 227861, upload-time = "2026-08-15T08:18:39.315Z" }, { url = "https://files.pythonhosted.org/packages/e9/a3/887c1642f0da26000b0e0652d91071113c0e72cea33952e225cf589f49a9/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375", size = 260758, upload-time = "2026-08-15T08:18:40.88Z" }, { url = "https://files.pythonhosted.org/packages/3e/11/e6f5b9a3d0e55b0ef7505cd3765cdd48f22db89994c947b316f52f801fd8/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1", size = 259950, upload-time = "2026-08-15T08:18:42.351Z" }, { url = "https://files.pythonhosted.org/packages/1b/ee/e4e10a94d51cd1ee638aa7e00b65399e6b2a4e8376ab6d2eac9f95586671/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65", size = 249329, upload-time = "2026-08-15T08:18:43.914Z" }, { url = "https://files.pythonhosted.org/packages/c4/25/d5f4198819e6059735a84e8d0bfb72dc33976da67b97adcd3fb5a5e07ec6/charset_normalizer-3.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5", size = 243137, upload-time = "2026-08-15T08:18:45.368Z" }, { url = "https://files.pythonhosted.org/packages/a5/e9/e925ca7569cf9fb9701fd82503fee73eea5268fdb856bdd64947092d3daa/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af", size = 242820, upload-time = "2026-08-15T08:18:46.842Z" }, { url = "https://files.pythonhosted.org/packages/34/17/672c251a888ed2aebcdd2fe830ad0104e25ff83c43f5c4f9c15e9fc6853c/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1", size = 230504, upload-time = "2026-08-15T08:18:48.353Z" }, { url = "https://files.pythonhosted.org/packages/3f/fc/f6a85abebd42ce4da2f1db0aa56cc6a0df1995e318b3875d14401b8381d1/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9", size = 263087, upload-time = "2026-08-15T08:18:49.859Z" }, { url = "https://files.pythonhosted.org/packages/98/66/7c42677e739ba66746b297e2046918d793078094dc239e1e72768cffccc6/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a", size = 243269, upload-time = "2026-08-15T08:18:51.601Z" }, { url = "https://files.pythonhosted.org/packages/de/d8/a50b79237f417af10f8c2a501ce8d1ca87829a22e69117891ca4ba20a69e/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032", size = 258766, upload-time = "2026-08-15T08:18:53.23Z" }, { url = "https://files.pythonhosted.org/packages/2e/1d/0fc91aeaeb3c83b748f532399ce67cf84604b48297405d740000f7a9e786/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e", size = 250814, upload-time = "2026-08-15T08:18:54.768Z" }, { url = "https://files.pythonhosted.org/packages/ae/10/3d8c777cf9024615295aa1b808324ad5b4a77855869c00824bad74ffaf8a/charset_normalizer-3.5.1-cp314-cp314t-win32.whl", hash = "sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4", size = 191074, upload-time = "2026-08-15T08:18:56.305Z" }, { url = "https://files.pythonhosted.org/packages/4d/81/ae557d3c44d1a1d688696d60563413a0866a91b7ebc50f20df838be3d8c8/charset_normalizer-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00", size = 216476, upload-time = "2026-08-15T08:18:57.889Z" }, { url = "https://files.pythonhosted.org/packages/27/e9/61c01fb8b804692569c036b3fc50495814502dcf13a60649c6055390b02c/charset_normalizer-3.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f", size = 194115, upload-time = "2026-08-15T08:18:59.418Z" }, { url = "https://files.pythonhosted.org/packages/4a/4e/8544831ef59d8f27ce92c80871380fdacc8076a8a56ed62f82e54f991333/charset_normalizer-3.5.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af", size = 342048, upload-time = "2026-08-15T08:19:01.054Z" }, { url = "https://files.pythonhosted.org/packages/7f/a6/e3b46852424246065355644f4fb6dbccc0239a42a2eee27ecfc8957f0bcd/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8", size = 242997, upload-time = "2026-08-15T08:19:02.492Z" }, { url = "https://files.pythonhosted.org/packages/03/3b/0cc9a26777334ab2f2e3089b948bbf4e4fe72ea70b897715ef6415043ec8/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90", size = 237014, upload-time = "2026-08-15T08:19:03.943Z" }, { url = "https://files.pythonhosted.org/packages/8c/c2/027335f0aa337a2a2e121bac1ad88c4f02ba6053ea0926802784f3db11af/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20", size = 266174, upload-time = "2026-08-15T08:19:05.598Z" }, { url = "https://files.pythonhosted.org/packages/86/d3/e367787febe4e74769dec0f406f2c3c8d1b955fce5aee1fd0f94e8367a45/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449", size = 263361, upload-time = "2026-08-15T08:19:07.251Z" }, { url = "https://files.pythonhosted.org/packages/af/3d/391b193eb9f3e84b02f9314088c386debdc0debee843535aaea2e2c6715d/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a", size = 252143, upload-time = "2026-08-15T08:19:08.816Z" }, { url = "https://files.pythonhosted.org/packages/2e/57/de221f1745a90d418199761967e2776bfe2c275a1194220985e8c1d37833/charset_normalizer-3.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0", size = 252086, upload-time = "2026-08-15T08:19:10.255Z" }, { url = "https://files.pythonhosted.org/packages/c8/e3/d119f86a01f9331e8186175f24873b1d74a7ee9e2e4b4d68f9947dae5afd/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e", size = 245231, upload-time = "2026-08-15T08:19:11.807Z" }, { url = "https://files.pythonhosted.org/packages/26/de/d8e48c135ae480879539cdb179c8d3b50c7879497d75dd899b5763b69cee/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2", size = 241546, upload-time = "2026-08-15T08:19:13.416Z" }, { url = "https://files.pythonhosted.org/packages/67/c4/217755fd1abc50d326c252922cd642002758095a81ff45010337b8b3ef65/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626", size = 267033, upload-time = "2026-08-15T08:19:14.981Z" }, { url = "https://files.pythonhosted.org/packages/b8/d7/34d8e404e358d2adcc5a228c2134643af00104c8fb0bf525f3688d756f05/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5", size = 252045, upload-time = "2026-08-15T08:19:16.618Z" }, { url = "https://files.pythonhosted.org/packages/5e/fa/40414471acf0aa0692ca77305aa00e434fcd8288f0941c93c30e9a5f8f2f/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774", size = 264866, upload-time = "2026-08-15T08:19:18.101Z" }, { url = "https://files.pythonhosted.org/packages/32/90/fcc850bae791abd2e0c041847f13e270aa08692a79f3e00de6d2dce1cb50/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7", size = 253932, upload-time = "2026-08-15T08:19:19.734Z" }, { url = "https://files.pythonhosted.org/packages/af/af/53afe99068b3c10b4cbae592a52ef72a7c92c0188440e83ee3a078fd8f75/charset_normalizer-3.5.1-cp315-cp315-win32.whl", hash = "sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9", size = 180320, upload-time = "2026-08-15T08:19:21.37Z" }, { url = "https://files.pythonhosted.org/packages/c9/bc/f46a132041b29e4a8779ed712d3df1bf112e94ca8de58b66d7ec2c0cf8b9/charset_normalizer-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712", size = 204174, upload-time = "2026-08-15T08:19:23.088Z" }, { url = "https://files.pythonhosted.org/packages/a1/5d/9ed554480eda8e447b673648628fdc29574d23dbad01fe11837adedd1cae/charset_normalizer-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7", size = 184126, upload-time = "2026-08-15T08:19:24.471Z" }, { url = "https://files.pythonhosted.org/packages/3b/32/9b8929bf384061ee1fe5d9c27c6f9776d3d824039ad4e14c88ec00c7808e/charset_normalizer-3.5.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663", size = 381441, upload-time = "2026-08-15T08:19:26.038Z" }, { url = "https://files.pythonhosted.org/packages/96/10/e9aa7923d3ddac652c99a1c5f7be494e737e151566a44abe018daf757f2c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11", size = 241742, upload-time = "2026-08-15T08:19:27.532Z" }, { url = "https://files.pythonhosted.org/packages/28/53/a2d249ebddf47b889a100c0bdcb61a2f9dbb8bc24ef325cc062e4f476877/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc", size = 235298, upload-time = "2026-08-15T08:19:29.274Z" }, { url = "https://files.pythonhosted.org/packages/7d/07/469f78af590f7d5cd48e20d8dbfa3d66deeff9ba37768c04d886b5afd45c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a", size = 262500, upload-time = "2026-08-15T08:19:30.955Z" }, { url = "https://files.pythonhosted.org/packages/55/66/3bb56a47f7dcba014055b1a1d33c6f08bbe9c1e74dba154cfa25f90ae885/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4", size = 258888, upload-time = "2026-08-15T08:19:32.458Z" }, { url = "https://files.pythonhosted.org/packages/ff/c1/2adc2800903fb013210349313b710a5376856578d9e33e6b9a1d8b36714a/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004", size = 250243, upload-time = "2026-08-15T08:19:33.94Z" }, { url = "https://files.pythonhosted.org/packages/95/b5/a18d0dd1157ab655cc2cb14a545f4a4784bbad70ab3502412e36097502d9/charset_normalizer-3.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b", size = 249871, upload-time = "2026-08-15T08:19:35.413Z" }, { url = "https://files.pythonhosted.org/packages/ad/c3/525f508cd1e58d0450ac55ed40ac75bc3a97482c59def5278456a5fbf03c/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263", size = 243580, upload-time = "2026-08-15T08:19:36.886Z" }, { url = "https://files.pythonhosted.org/packages/7c/c1/49a91fe7e97c8140094ca5c64161ab623a70d9f636bf834eace14048acb5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee", size = 239807, upload-time = "2026-08-15T08:19:38.392Z" }, { url = "https://files.pythonhosted.org/packages/d3/58/56a48c296601274c4689b864a8e2dfb209b81dfcb39472753ce95eea662b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c", size = 264083, upload-time = "2026-08-15T08:19:39.856Z" }, { url = "https://files.pythonhosted.org/packages/10/4c/dc48409274a1817ff349711d26c62aa0c597df865d4d69ef79160c859193/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e", size = 250317, upload-time = "2026-08-15T08:19:41.53Z" }, { url = "https://files.pythonhosted.org/packages/81/58/d325912115caec62d6bdd77bbab5e0b7da5d234a9f20affdffcbcb530d0b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d", size = 258173, upload-time = "2026-08-15T08:19:43.07Z" }, { url = "https://files.pythonhosted.org/packages/34/f7/b13b1ccae2c8ec63980d13be1890eb73f8aeabbfce02a24aabc0908788f5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61", size = 251960, upload-time = "2026-08-15T08:19:44.587Z" }, { url = "https://files.pythonhosted.org/packages/1e/25/ed3f9919c5aef8cc818be1f972f565f7610d7b2076b8ebb98839516ffc3c/charset_normalizer-3.5.1-cp315-cp315t-win32.whl", hash = "sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f", size = 191186, upload-time = "2026-08-15T08:19:46.293Z" }, { url = "https://files.pythonhosted.org/packages/69/d5/43c2b3e9d8267092b913eb8b0603f0f71993c395632886bd37a7223f96cf/charset_normalizer-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb", size = 215947, upload-time = "2026-08-15T08:19:47.853Z" }, { url = "https://files.pythonhosted.org/packages/a8/76/9aad3e9c8865e5e0efa9a7f6f81c37a67635a985145ecd44528a81e088ee/charset_normalizer-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a", size = 193909, upload-time = "2026-08-15T08:19:49.383Z" }, { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, { url = "https://files.pythonhosted.org/packages/d0/91/bc145e42f93d6601b9a26f5421af2d7c3093ae6e6d03b8e583c9cebbf530/charset_normalizer-3.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85de3134b5379856e323ba37c19c9256d39425f7b76a63af52b09fb4664c2e8f", size = 368830, upload-time = "2026-08-15T08:20:17.272Z" }, { url = "https://files.pythonhosted.org/packages/58/67/62df6a907162461f372e95cbbc1bc64c7457e86abcc851feb84409a11eff/charset_normalizer-3.5.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e5e1224c0a6a90e05843e07adfec669edebec17801c67072f51e59561d63c0b", size = 251725, upload-time = "2026-08-15T08:20:18.942Z" }, { url = "https://files.pythonhosted.org/packages/a4/2d/64a13610fd28c80f97aff0ea5cf31cf255d220a8243ac0c78c66fd3d874d/charset_normalizer-3.5.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5e2d0e146dcb57034f8b97dc58d2d512cb90aba253960ce449f695fec6a82c6f", size = 241254, upload-time = "2026-08-15T08:20:20.641Z" }, { url = "https://files.pythonhosted.org/packages/84/79/a88c181e7f4a7579696fedb34fa63844ede2ff7caf44c5f321cec57d92fb/charset_normalizer-3.5.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e06efa066f7dbadbc84ebc126a97c452a6451dfcf589d89d788484949e1cf795", size = 281944, upload-time = "2026-08-15T08:20:22.219Z" }, { url = "https://files.pythonhosted.org/packages/64/60/7c5469f455f4fa65d39da9f088dffc1a586560bfb9e3279441eed78bd469/charset_normalizer-3.5.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:994e883d17c559cdfd38c84003c8b27d25424a1077272a17e7cd27bfe0bf57b2", size = 278350, upload-time = "2026-08-15T08:20:23.744Z" }, { url = "https://files.pythonhosted.org/packages/9f/cf/7568d8c1c9100b7c8bab9035215a6b36b32b39bb50cabaee9389c4606887/charset_normalizer-3.5.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:789b8982559ae28dad2356519f841655756cdcd96616410590ae0b17454ee64f", size = 262670, upload-time = "2026-08-15T08:20:25.387Z" }, { url = "https://files.pythonhosted.org/packages/fa/ec/3a616c3806ec3f957337e6bf874ae7d64693185039edfbbf87103b8c8631/charset_normalizer-3.5.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a951ad59cad9145664a730d3036b40b844e74d2d3683da40111463cd3a83845d", size = 260445, upload-time = "2026-08-15T08:20:27.012Z" }, { url = "https://files.pythonhosted.org/packages/b2/e0/489aa2a33b944077d4c2c705c245d833dc12cd571a52fc67eaf273f5373a/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:55261ac0d2941c42f196dd576f543d87a8ee03cd6f5e30dfb4d807b2e3b9121a", size = 253263, upload-time = "2026-08-15T08:20:28.6Z" }, { url = "https://files.pythonhosted.org/packages/d3/bc/f528dfb78d3bfdc8ee6aeea81eb22e6918d03e4442d373a79717f17de45e/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5ca0555312ae2fe82715cada7fac375530c2f3349e1eaa1bcb33d0283ac79a18", size = 242879, upload-time = "2026-08-15T08:20:30.186Z" }, { url = "https://files.pythonhosted.org/packages/c0/8c/58efc6393e405a8d52b241d31dd9118352c247e4017110c3edfdb4618f0d/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2e9cf9253119d8e5d111f05d71626786fd3d6193817316eab1ca088cdb8593cf", size = 282086, upload-time = "2026-08-15T08:20:31.826Z" }, { url = "https://files.pythonhosted.org/packages/02/fc/0d9ab98fa7a61394353e8acd0f5f60fc6e94a4615f574af8be0eca14a7ef/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:433c5a81eade63b47e522303bad236f59dba55ea6951746f5558355eeed8c75d", size = 259212, upload-time = "2026-08-15T08:20:33.442Z" }, { url = "https://files.pythonhosted.org/packages/79/71/6ee3a48a21e844e5079d8e9b2e91c641da5a7912a748e9e94c9e3ab9ce1c/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:13e3afe97712e8887cd516e960c63f0b93122971e5b5e4b2622fe7701771e838", size = 278949, upload-time = "2026-08-15T08:20:35.133Z" }, { url = "https://files.pythonhosted.org/packages/64/77/9ae101cb33bd9f681551e82a2b9e08eec99ff715458340931370f4228de9/charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb12fb2ba69ffa05f8695f61c69e591dc4b4a12ac3757ac8af8adb259bf56d17", size = 264513, upload-time = "2026-08-15T08:20:36.887Z" }, { url = "https://files.pythonhosted.org/packages/d1/91/249943372195935ff7393eae5842c7dae6fd04401e512bbd69dab1aae40b/charset_normalizer-3.5.1-cp39-cp39-win32.whl", hash = "sha256:56490c595a28b1bb27dfc583e816152a9767721ef58b2c03b13f954d2f707420", size = 182431, upload-time = "2026-08-15T08:20:38.498Z" }, { url = "https://files.pythonhosted.org/packages/53/31/7f79c671d827080d6eecd697fbbeb4f0f6f8507bf4c5625b5f6398ec5876/charset_normalizer-3.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:012a22b88a77ca2e59b98ac5889b0deb604147666032f45e6d6e217634d2550d", size = 206385, upload-time = "2026-08-15T08:20:40.242Z" }, { url = "https://files.pythonhosted.org/packages/c4/58/c9295c61e3f826ba7d874f0fd1c5e335dbec928d7b9146b33b48d14a25f1/charset_normalizer-3.5.1-cp39-cp39-win_arm64.whl", hash = "sha256:29880d17a8eb0b5cfdfd8944b468322928059aa35f1f5fa8ff22b149ec0b42f8", size = 185470, upload-time = "2026-08-15T08:20:41.765Z" }, { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "coverage" version = "7.10.7" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", size = 246625, upload-time = "2025-09-21T20:03:36.09Z" }, { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", size = 248399, upload-time = "2025-09-21T20:03:38.342Z" }, { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", size = 245142, upload-time = "2025-09-21T20:03:40.591Z" }, { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", size = 246284, upload-time = "2025-09-21T20:03:42.355Z" }, { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", size = 244353, upload-time = "2025-09-21T20:03:44.218Z" }, { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", size = 244430, upload-time = "2025-09-21T20:03:46.065Z" }, { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", size = 245311, upload-time = "2025-09-21T20:03:48.19Z" }, { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", size = 220500, upload-time = "2025-09-21T20:03:50.024Z" }, { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", size = 221408, upload-time = "2025-09-21T20:03:51.803Z" }, { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, ] [package.optional-dependencies] toml = [ { name = "tomli", marker = "python_full_version < '3.10'" }, ] [[package]] name = "coverage" version = "7.15.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/be/c3/4f2195f512fb172aa425a8803a874b2baa9ba7f80ff7b6080998761fc701/coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00", size = 936952, upload-time = "2026-08-06T13:50:24.442Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/30/70/b052a519a584663a7bd052841a2debe11c8309ec49a7786340003f9c0a02/coverage-7.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264", size = 222245, upload-time = "2026-08-06T13:46:55.253Z" }, { url = "https://files.pythonhosted.org/packages/67/39/892fa511aba3d1c3c8f49509a0ff5c71eab9f9f88d08e1a38da395821660/coverage-7.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d", size = 222762, upload-time = "2026-08-06T13:46:57.848Z" }, { url = "https://files.pythonhosted.org/packages/9f/95/b2c724ce1e64bc23cb5b1d7eeffa9548dc3d811f7a6297b2d01607f4e062/coverage-7.15.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe20cc8cf8821d4fe54f89106cbf06aa27f37b5bbe3535568065a81539b4150", size = 249498, upload-time = "2026-08-06T13:46:59.012Z" }, { url = "https://files.pythonhosted.org/packages/0b/4f/b1973f67a1382af65b572a31ed692f8e490a6ad707191eab59148376832a/coverage-7.15.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:83cf06cdd687677742caff1a9134833b7a8b75f111519d2cb0e0ba1b9a851e15", size = 251328, upload-time = "2026-08-06T13:47:00.764Z" }, { url = "https://files.pythonhosted.org/packages/a2/09/03efa6722a132abcac91b32a60b64b240dd707c189c64eee697e48992c96/coverage-7.15.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fa4de68e2a752468ff14b4e15db7def689a71be759e826a31ccecbef69c5fd0", size = 253194, upload-time = "2026-08-06T13:47:01.976Z" }, { url = "https://files.pythonhosted.org/packages/45/63/8299201d9c80fb65551ce99c966cab83d706ec4066ac999bef08201346de/coverage-7.15.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4dff9daa47d83120c3ec38ce921214242944a832aa04e903e50b5b7ebac8972d", size = 255106, upload-time = "2026-08-06T13:47:03.281Z" }, { url = "https://files.pythonhosted.org/packages/ee/16/26fd8a691eb8d9a230128685f6d23309d7402cb030aa553001788c8c50fc/coverage-7.15.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a093fd37229918976f602aa07aa59e0973cde82186f220c8e197f721f5be0ce4", size = 250177, upload-time = "2026-08-06T13:47:04.713Z" }, { url = "https://files.pythonhosted.org/packages/ad/ef/3c7556f33783a0a566e01443ca62bd8eb2cdfe22d271efdc02e08beb5654/coverage-7.15.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:317db01a2cb02552fd67e2b1cca77a4b528a2a277176c5e0bf2cecbb639d3f54", size = 251234, upload-time = "2026-08-06T13:47:06.104Z" }, { url = "https://files.pythonhosted.org/packages/29/49/640a34043edac950738f36a3567832db5731d4cb2ed84b59cdb89c6bccbf/coverage-7.15.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8ee3838dcb656602c3b51e16aed9bfb0822f8d8d6d1c5966d32ec8c104be8e20", size = 249237, upload-time = "2026-08-06T13:47:07.467Z" }, { url = "https://files.pythonhosted.org/packages/48/f5/e80f212669dd1be954ff844f883ef11a437ef4fd0089c6e0effc7b66b15d/coverage-7.15.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:425920379052ff1fe465268f3361d35804a241bbdd5a1b592c8cb60df4c52325", size = 253050, upload-time = "2026-08-06T13:47:08.748Z" }, { url = "https://files.pythonhosted.org/packages/c7/e9/e5da0fe39f7fde1bca9edc09c60921bb5fdba4cec7db5bbad41ddfd8c230/coverage-7.15.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:69bb2400abef928e365ea7d4d9925169ada78ed2295546780002d4b65de3df88", size = 249508, upload-time = "2026-08-06T13:47:10.072Z" }, { url = "https://files.pythonhosted.org/packages/7d/38/41bf25774a0c8bba6b467f917cb1c9a0a2605e02dc93aad489fc7050ed59/coverage-7.15.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:81661f82d302484e3119e7c80c519c02fa9bcc2a6b339baf67d67bc89c580f04", size = 250110, upload-time = "2026-08-06T13:47:11.35Z" }, { url = "https://files.pythonhosted.org/packages/89/6e/26f2e54b79acc29d179ee4272922625aedb69198c4eb61f7ff4f098f3c78/coverage-7.15.4-cp310-cp310-win32.whl", hash = "sha256:cb476b2e828ecb71cb6b6a928d23fd20a7ddb501188022dae1c37499149cc338", size = 224294, upload-time = "2026-08-06T13:47:12.753Z" }, { url = "https://files.pythonhosted.org/packages/7b/06/9a318fc3ae040d4d6cb2d86101c6aa963fab20899a5c58666adf52cde0ca/coverage-7.15.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fc2130bf37df31852a8384f12601563a45a0024bccc6624f38355cba7a8d360", size = 224919, upload-time = "2026-08-06T13:47:14.17Z" }, { url = "https://files.pythonhosted.org/packages/2a/66/edcec7d7a0b524aa8923e22925fde6fe50ce005a113dca13ae1581455c4c/coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490", size = 222367, upload-time = "2026-08-06T13:47:15.578Z" }, { url = "https://files.pythonhosted.org/packages/e6/c6/ab8de429e2e8548faf58ec7e1674a4ce00414b4113942d3fe87109cf0f68/coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e", size = 222874, upload-time = "2026-08-06T13:47:16.961Z" }, { url = "https://files.pythonhosted.org/packages/be/c4/3b7b49587e8a6b9af79b3eb468d443d6042b6d65b47aa26586846a0d6566/coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7", size = 253287, upload-time = "2026-08-06T13:47:18.291Z" }, { url = "https://files.pythonhosted.org/packages/fb/65/ec03b743a2a229c72cc1eff3e57be9d3564e9c6b4d5aba2d70744a3fc0d8/coverage-7.15.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6", size = 255199, upload-time = "2026-08-06T13:47:19.765Z" }, { url = "https://files.pythonhosted.org/packages/41/4b/5163729e4b6582d61975cfd3ccab45b4ec53e21cf156d9941cb025188468/coverage-7.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d", size = 257308, upload-time = "2026-08-06T13:47:21.206Z" }, { url = "https://files.pythonhosted.org/packages/86/08/2167a0f08fb87d702fa423a48578a32865464b7c9e1db3911ad7812ab414/coverage-7.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce", size = 259268, upload-time = "2026-08-06T13:47:22.503Z" }, { url = "https://files.pythonhosted.org/packages/1e/e5/68eebae3053dbd48508edea559c21b23fbdf3460784f91370c83a86a6acd/coverage-7.15.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7", size = 253392, upload-time = "2026-08-06T13:47:23.88Z" }, { url = "https://files.pythonhosted.org/packages/1a/46/fd4ced40a2b691c774e515c9b69500bfa64c7960b67fcee4b2f6fad97fc3/coverage-7.15.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b", size = 255001, upload-time = "2026-08-06T13:47:25.469Z" }, { url = "https://files.pythonhosted.org/packages/53/25/ae2e5fa710bb6957a9aadeb9e3598d3b3e4af6587ce857ad42e8639a3f30/coverage-7.15.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc", size = 253061, upload-time = "2026-08-06T13:47:26.845Z" }, { url = "https://files.pythonhosted.org/packages/d7/31/67ddc0365db2c6e93ac8580bc4bbc50f65273262f973f63ebcdbc15c0495/coverage-7.15.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571", size = 256831, upload-time = "2026-08-06T13:47:28.217Z" }, { url = "https://files.pythonhosted.org/packages/f6/78/82b8fd18f57fb13f12d98fe874995bb2c4f9f17be8aff762c426323fdb96/coverage-7.15.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719", size = 252781, upload-time = "2026-08-06T13:47:29.712Z" }, { url = "https://files.pythonhosted.org/packages/0a/eb/6c74ef4dd12b252e573c49bdef9e2ac265bf3dbb79b8d7feb3266e084e9e/coverage-7.15.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7", size = 253692, upload-time = "2026-08-06T13:47:31.192Z" }, { url = "https://files.pythonhosted.org/packages/5a/66/eb9aed1c3fd2d36ee00eb173f434b14fa607fc056739c9a89ff4244010ea/coverage-7.15.4-cp311-cp311-win32.whl", hash = "sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e", size = 224461, upload-time = "2026-08-06T13:47:32.572Z" }, { url = "https://files.pythonhosted.org/packages/e2/6d/81fa4161dfb3ed9d74e40d58647eff83a56b7612e78352581280fce2f477/coverage-7.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc", size = 224937, upload-time = "2026-08-06T13:47:34.205Z" }, { url = "https://files.pythonhosted.org/packages/5b/c1/d8dacf683c6cad3cf85ce68fd3774a6774ec402128822fdfaed920f11e6a/coverage-7.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890", size = 224479, upload-time = "2026-08-06T13:47:36.118Z" }, { url = "https://files.pythonhosted.org/packages/1d/48/bc8d4ba7b37551a767bd863f15b3f80182b271c2f55975356f5f7dbe94c2/coverage-7.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22", size = 222543, upload-time = "2026-08-06T13:47:37.562Z" }, { url = "https://files.pythonhosted.org/packages/20/dd/88d6f83f1fffc974a3691a34a97951c5b12df7512a6782c5963883cbc058/coverage-7.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97", size = 222905, upload-time = "2026-08-06T13:47:38.927Z" }, { url = "https://files.pythonhosted.org/packages/bd/5c/54ee0d4748585bb0acab9891cd8d92f2d3593165b4e59fc9de113bfb3140/coverage-7.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d", size = 254407, upload-time = "2026-08-06T13:47:40.488Z" }, { url = "https://files.pythonhosted.org/packages/8c/3f/f0642a372f494bd0d7dad3b497083b910194a5f1c88be2c94fef707c3b59/coverage-7.15.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2", size = 257145, upload-time = "2026-08-06T13:47:41.931Z" }, { url = "https://files.pythonhosted.org/packages/71/17/8b46d0ed68251016002ec972c8fc0119961a765d0984cafb8bf317c43758/coverage-7.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931", size = 258257, upload-time = "2026-08-06T13:47:43.527Z" }, { url = "https://files.pythonhosted.org/packages/30/b8/8498a0e72d0adbe15477dd07463d2b3bb2c9f6a4815e8589e50939e2c3ae/coverage-7.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8", size = 260517, upload-time = "2026-08-06T13:47:45.121Z" }, { url = "https://files.pythonhosted.org/packages/41/e1/7dce19c3bdb1e3dd63e769508216500edad81bd5f69a26d724e32aceaf78/coverage-7.15.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9", size = 254785, upload-time = "2026-08-06T13:47:46.541Z" }, { url = "https://files.pythonhosted.org/packages/dd/b1/e1494703c675a2561723cd9b89f45c9168782c31280c611b1f767851e57c/coverage-7.15.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839", size = 256176, upload-time = "2026-08-06T13:47:48.155Z" }, { url = "https://files.pythonhosted.org/packages/73/76/a5629d270fb638a43a4b10466f51e2f49d532c1aa4da2913cbbb150bbe0a/coverage-7.15.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72", size = 254321, upload-time = "2026-08-06T13:47:49.757Z" }, { url = "https://files.pythonhosted.org/packages/ff/4f/9c44447218435d5766b911534f9d798144a5560f85e9a54ebe5f3f5d19f9/coverage-7.15.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52", size = 258390, upload-time = "2026-08-06T13:47:51.248Z" }, { url = "https://files.pythonhosted.org/packages/de/36/c1e127616fb3fa18a9ff71e76c417f2fd7424332a4870015ac224ef4c039/coverage-7.15.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c", size = 253894, upload-time = "2026-08-06T13:47:52.816Z" }, { url = "https://files.pythonhosted.org/packages/e9/b9/fdb92c8ae7a8bb9b850cc253b7b3b9c8526f68130002048b5671cd510d09/coverage-7.15.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4", size = 255763, upload-time = "2026-08-06T13:47:54.296Z" }, { url = "https://files.pythonhosted.org/packages/6f/c0/a7d51b2587c7bdb76e71b0896d2565bf7d60436b5122fc83e511adb1f7cd/coverage-7.15.4-cp312-cp312-win32.whl", hash = "sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b", size = 224597, upload-time = "2026-08-06T13:47:56.074Z" }, { url = "https://files.pythonhosted.org/packages/49/b9/5c5f80cc55f5acaaca6dee677626bfcec8c87204a7809b438b08e84f4571/coverage-7.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd", size = 225135, upload-time = "2026-08-06T13:47:57.52Z" }, { url = "https://files.pythonhosted.org/packages/47/e4/2a4561f89ff6bf7c925c287d0f2cce8bdf139c3a33735c87e3203401cf94/coverage-7.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f", size = 224515, upload-time = "2026-08-06T13:47:58.977Z" }, { url = "https://files.pythonhosted.org/packages/f1/84/651a9310859673aaa3b3203f1aa1641ca60fcf2494683e1c9474c7172780/coverage-7.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921", size = 222565, upload-time = "2026-08-06T13:48:00.796Z" }, { url = "https://files.pythonhosted.org/packages/82/f9/4dcf700137e8af550670f4d74d1b63828ce93e1e2b05e5f10710eb2ea987/coverage-7.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e", size = 222936, upload-time = "2026-08-06T13:48:02.391Z" }, { url = "https://files.pythonhosted.org/packages/07/4a/612ff1e780b3fbfd637486f542f84adc5503873d8b5d279dec1ffeef9414/coverage-7.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36", size = 253926, upload-time = "2026-08-06T13:48:04.382Z" }, { url = "https://files.pythonhosted.org/packages/b0/04/d1cff1c2ead4708a6a79c01d3736b6a25bd38a36678398f72a8dd33dfad9/coverage-7.15.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4", size = 256523, upload-time = "2026-08-06T13:48:05.996Z" }, { url = "https://files.pythonhosted.org/packages/b9/80/d34e13fb4b293cbdb9665838cf5522077b8ad14ef947550631a4bced36a5/coverage-7.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c", size = 257759, upload-time = "2026-08-06T13:48:08.036Z" }, { url = "https://files.pythonhosted.org/packages/0f/e7/2c5fe7636fdb0732fe0f09f308a5b066864078b7fc61f6678e8478554f2e/coverage-7.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7", size = 259890, upload-time = "2026-08-06T13:48:09.834Z" }, { url = "https://files.pythonhosted.org/packages/92/28/9689f0858dfff59c2ea688938ab9fa2925631235df67126a42b6c5c70ae1/coverage-7.15.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25", size = 254121, upload-time = "2026-08-06T13:48:11.459Z" }, { url = "https://files.pythonhosted.org/packages/f9/e2/785077c230c157243eb5aa9a26c3be260ecd02001bead54a3cada3df8e03/coverage-7.15.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b", size = 255891, upload-time = "2026-08-06T13:48:13.209Z" }, { url = "https://files.pythonhosted.org/packages/d4/90/e20371b17b40f912f21305c2db2f30efa3de306f7320fc916804872c85a4/coverage-7.15.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78", size = 253859, upload-time = "2026-08-06T13:48:14.736Z" }, { url = "https://files.pythonhosted.org/packages/05/49/25371987ee459a5f67c0427fb75c74f9358e65f2c71fe75bf41c1b6c5fcb/coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f", size = 258011, upload-time = "2026-08-06T13:48:16.464Z" }, { url = "https://files.pythonhosted.org/packages/30/6e/32e67467f6154bf4f1c4f63b05acc5097cba4237d45bbeeea446b52e8ac1/coverage-7.15.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d", size = 253676, upload-time = "2026-08-06T13:48:18.493Z" }, { url = "https://files.pythonhosted.org/packages/03/c1/8b24192e89286399765155251f99ee9f070a9d637109018ac23d99b99f6f/coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff", size = 255453, upload-time = "2026-08-06T13:48:20.057Z" }, { url = "https://files.pythonhosted.org/packages/16/6f/8b41ebdf67c87854e17c035336a90f1cfbad0c14c2a584301be6ff148718/coverage-7.15.4-cp313-cp313-win32.whl", hash = "sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c", size = 224605, upload-time = "2026-08-06T13:48:21.655Z" }, { url = "https://files.pythonhosted.org/packages/e0/e2/2946c7f0b42b152ecb21ff1bdad72e3d301e790c0c487e4a86e8c9f69347/coverage-7.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4", size = 225148, upload-time = "2026-08-06T13:48:23.376Z" }, { url = "https://files.pythonhosted.org/packages/9e/83/3f4a69957f48ae7a0aba76c34743f88963d607b19e03f3f8e66f91cae0f9/coverage-7.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf", size = 224536, upload-time = "2026-08-06T13:48:25.117Z" }, { url = "https://files.pythonhosted.org/packages/ea/ac/748cf29eeb2d6be34a3176ce26a4f49e38085ee08e8935f05f6f26ed7e0f/coverage-7.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f", size = 222608, upload-time = "2026-08-06T13:48:26.806Z" }, { url = "https://files.pythonhosted.org/packages/0b/02/1abbf5c984677b0aa439cdacaccbf38d248939d8ef8fe1cc7a50d73edb77/coverage-7.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c", size = 222940, upload-time = "2026-08-06T13:48:28.432Z" }, { url = "https://files.pythonhosted.org/packages/eb/e1/ff8f9f53d9fcf586125b55d0b1f04ec1c14955fee41e83d5814bee141bb5/coverage-7.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082", size = 253985, upload-time = "2026-08-06T13:48:29.995Z" }, { url = "https://files.pythonhosted.org/packages/a1/26/595759762e514e81be1d7d01ed03444303bcd152226a6529998d253f9201/coverage-7.15.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac", size = 256492, upload-time = "2026-08-06T13:48:31.634Z" }, { url = "https://files.pythonhosted.org/packages/24/68/b79aabac54d482be23b5fcdd4f4662bff24a78edc4ee29201726929936d5/coverage-7.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734", size = 257837, upload-time = "2026-08-06T13:48:33.186Z" }, { url = "https://files.pythonhosted.org/packages/09/0f/bf7f297885a5bf6fd71e5782404e0ff059ca09e8711ceb3a08544abde45a/coverage-7.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d", size = 260152, upload-time = "2026-08-06T13:48:34.75Z" }, { url = "https://files.pythonhosted.org/packages/fd/f1/296744e854ff8368542343457414380465e9ceefb9192342feb9d3bc461d/coverage-7.15.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf", size = 253978, upload-time = "2026-08-06T13:48:36.434Z" }, { url = "https://files.pythonhosted.org/packages/55/b0/bbdb2e9057493e66220a2e149ca2d301ba0e3a58a83bd6b90de9826d16f3/coverage-7.15.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b", size = 255846, upload-time = "2026-08-06T13:48:38.317Z" }, { url = "https://files.pythonhosted.org/packages/96/e4/38015b2b6d21258713bd17e76b59d033b191efb5703589cffd037dfbca20/coverage-7.15.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25", size = 253808, upload-time = "2026-08-06T13:48:39.993Z" }, { url = "https://files.pythonhosted.org/packages/0b/64/0d515c1e60ee6fbfd1a0e79c07cd87d388a233b7adc37758735677203808/coverage-7.15.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303", size = 258081, upload-time = "2026-08-06T13:48:41.971Z" }, { url = "https://files.pythonhosted.org/packages/91/71/04d9e7a3642146c6351338aef4ef85ab11dbbb54744c13245caba1aad1c0/coverage-7.15.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f", size = 253624, upload-time = "2026-08-06T13:48:43.731Z" }, { url = "https://files.pythonhosted.org/packages/b4/a7/6c28b74c81ebff66987b0e2522ba5cffa3e90b0c33cb6a2eb264d4ee8cf1/coverage-7.15.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5", size = 255280, upload-time = "2026-08-06T13:48:45.58Z" }, { url = "https://files.pythonhosted.org/packages/52/af/bc19996a7014b98d7bbb0f0939453c67074af65784a3aa16a789a07381fa/coverage-7.15.4-cp314-cp314-win32.whl", hash = "sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7", size = 224768, upload-time = "2026-08-06T13:48:47.525Z" }, { url = "https://files.pythonhosted.org/packages/ee/90/219484e476d6e101ba0a444852579e05f5b75c37c611a42ed1190f73ef62/coverage-7.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425", size = 225259, upload-time = "2026-08-06T13:48:49.513Z" }, { url = "https://files.pythonhosted.org/packages/b7/66/fa77daf4e383e5f776dac62c2409b6af81910ae6fe326bd5170dba74cc63/coverage-7.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8", size = 224684, upload-time = "2026-08-06T13:48:51.235Z" }, { url = "https://files.pythonhosted.org/packages/58/5b/f03bf0ce362bbf3f785fa5219620d00778d4ac6fc9e407734828e9c672f6/coverage-7.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8", size = 223338, upload-time = "2026-08-06T13:48:52.896Z" }, { url = "https://files.pythonhosted.org/packages/0f/76/e77d0ae22501831cc9f92193e8a957a5caa1dd177f90a6d1d9b106242d92/coverage-7.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a", size = 223609, upload-time = "2026-08-06T13:48:54.688Z" }, { url = "https://files.pythonhosted.org/packages/82/1a/b1f089da8d38ac612fa2dd6dc7f4a1a7657d12f3e261d2996edd3a838d0b/coverage-7.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b", size = 264970, upload-time = "2026-08-06T13:48:56.403Z" }, { url = "https://files.pythonhosted.org/packages/bf/31/e66d98d6e9c7fcc88470f1e234eaf6b1950dc0dfbf797f7282c1c861da24/coverage-7.15.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5", size = 267088, upload-time = "2026-08-06T13:48:58.41Z" }, { url = "https://files.pythonhosted.org/packages/59/a1/ae94eb2c541add426378408379f233591e069040b1e2cdb33df9498a0682/coverage-7.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba", size = 269508, upload-time = "2026-08-06T13:49:00.42Z" }, { url = "https://files.pythonhosted.org/packages/9c/c7/88a10694a1c6a213569766aba9f25847b28155d4ac731b13226db216356d/coverage-7.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982", size = 270629, upload-time = "2026-08-06T13:49:02.234Z" }, { url = "https://files.pythonhosted.org/packages/b3/34/d8b8232e5e55169933b59aabcef2fedfa4b9d8897361bb80fcbda146505f/coverage-7.15.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c", size = 264043, upload-time = "2026-08-06T13:49:04.102Z" }, { url = "https://files.pythonhosted.org/packages/7e/35/58b009dbf8c471c7224716478b9fed4a7e1af15320e1ed41660978504663/coverage-7.15.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57", size = 266963, upload-time = "2026-08-06T13:49:05.821Z" }, { url = "https://files.pythonhosted.org/packages/62/aa/57fbda1b42c892968273c56b6ee9dc0f1310850859230a507bc7873b1f65/coverage-7.15.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26", size = 264569, upload-time = "2026-08-06T13:49:07.706Z" }, { url = "https://files.pythonhosted.org/packages/98/8a/360e6e7f24d477b7e889703af0afa878d15b6d4d8d2a822b2835c169a879/coverage-7.15.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429", size = 268299, upload-time = "2026-08-06T13:49:09.587Z" }, { url = "https://files.pythonhosted.org/packages/4e/89/6f701261aee21b6b5fa8f7872229406dc917e125069448292223bf213606/coverage-7.15.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017", size = 263413, upload-time = "2026-08-06T13:49:11.604Z" }, { url = "https://files.pythonhosted.org/packages/3f/0f/6f04036edc260ed425af83e834f627fad48941ce97b50bfe6edd8b6fa623/coverage-7.15.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839", size = 265725, upload-time = "2026-08-06T13:49:13.38Z" }, { url = "https://files.pythonhosted.org/packages/c4/ce/d19b5d4d5c49a7bfb925fd74310fee7d28bc99520ac3367ccbc54e662518/coverage-7.15.4-cp314-cp314t-win32.whl", hash = "sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85", size = 225079, upload-time = "2026-08-06T13:49:15.265Z" }, { url = "https://files.pythonhosted.org/packages/26/bb/7aa1b3b173faee0679037ca950bbbe1247273656697994d8d13f80f8d4b4/coverage-7.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e", size = 225911, upload-time = "2026-08-06T13:49:17.279Z" }, { url = "https://files.pythonhosted.org/packages/81/1c/4ea9e47426d80038d9222db3c4534cb6021a74b237d3ff97ffd33b6600dd/coverage-7.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e", size = 225219, upload-time = "2026-08-06T13:49:19.293Z" }, { url = "https://files.pythonhosted.org/packages/2b/c4/dc5d2ac8f9142e7ec7de66e7bf0591db29d78955a040bd915870d9c0e657/coverage-7.15.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9", size = 222604, upload-time = "2026-08-06T13:49:21.279Z" }, { url = "https://files.pythonhosted.org/packages/70/39/33e63df81fe2ee100897451841c821467635923e58e37c6bd4b46dd8106c/coverage-7.15.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a", size = 222944, upload-time = "2026-08-06T13:49:23.187Z" }, { url = "https://files.pythonhosted.org/packages/99/1f/ef3ffb5557febc75a0d97aa459d0266d7d741110265121cc6d8539343d44/coverage-7.15.4-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54", size = 254050, upload-time = "2026-08-06T13:49:25.008Z" }, { url = "https://files.pythonhosted.org/packages/6f/f5/1f0f6f77698c3601ca0ae7431e34b24c62ca2f06fecb23b73ed1f651d2be/coverage-7.15.4-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb", size = 256967, upload-time = "2026-08-06T13:49:26.896Z" }, { url = "https://files.pythonhosted.org/packages/03/7a/2ed9bed79925f4367c83c77f66a89e5ca7229c288d2d19ad5f36d1ca0070/coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed", size = 258587, upload-time = "2026-08-06T13:49:28.692Z" }, { url = "https://files.pythonhosted.org/packages/45/8c/fa34044f71b7cc4ecb6da9c2408770959b0591fa9b5fb6fb6bca38f94298/coverage-7.15.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce", size = 260785, upload-time = "2026-08-06T13:49:30.472Z" }, { url = "https://files.pythonhosted.org/packages/4f/54/d5727ce36b4524a7394ab9f5f1df378e1f23affcdab01037dc8655185cc7/coverage-7.15.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c", size = 254545, upload-time = "2026-08-06T13:49:32.271Z" }, { url = "https://files.pythonhosted.org/packages/dc/e6/6e3783e576719590194bdffb6dd6d85490801785b7c331e35a245d8cb8b5/coverage-7.15.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced", size = 256682, upload-time = "2026-08-06T13:49:34.089Z" }, { url = "https://files.pythonhosted.org/packages/dc/f2/bacdbde18b69ed2de424fcf64d9fb0a4913753d4f0eca8bae9daad69f4bd/coverage-7.15.4-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800", size = 254560, upload-time = "2026-08-06T13:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/6c/a3/1fb927196e3477c1b48831169ab58ba08f451ba87ae311ff1de68b26a616/coverage-7.15.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3", size = 258792, upload-time = "2026-08-06T13:49:38.01Z" }, { url = "https://files.pythonhosted.org/packages/41/58/30d4c149c69053de0edfe325614c1d28d508f62b1783e0e4a234d2e49136/coverage-7.15.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768", size = 253968, upload-time = "2026-08-06T13:49:39.934Z" }, { url = "https://files.pythonhosted.org/packages/89/e4/77f639371b918aad30dda4051f95404b43578f7f2e2f87ba73e02ed1ff37/coverage-7.15.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753", size = 255893, upload-time = "2026-08-06T13:49:41.825Z" }, { url = "https://files.pythonhosted.org/packages/5c/62/13be29b3ddab35f14c87967a4820a05106d2a3eccb4fa4ff550bf30b75e0/coverage-7.15.4-cp315-cp315-win32.whl", hash = "sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2", size = 224768, upload-time = "2026-08-06T13:49:44.08Z" }, { url = "https://files.pythonhosted.org/packages/a1/70/af0c6be0f964af6954f6b74bc109b0dbca02824696d2520fb17fe1ab06e3/coverage-7.15.4-cp315-cp315-win_amd64.whl", hash = "sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809", size = 225242, upload-time = "2026-08-06T13:49:45.899Z" }, { url = "https://files.pythonhosted.org/packages/4f/2d/f3bd3aab899fc9efc18b53133ee68f5f98574ef480649b23e12962226387/coverage-7.15.4-cp315-cp315-win_arm64.whl", hash = "sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d", size = 224674, upload-time = "2026-08-06T13:49:48.322Z" }, { url = "https://files.pythonhosted.org/packages/f5/ca/f69251cd63eabc6438321aea22148754cce758a26bde07dd490e3fe7cfc5/coverage-7.15.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d", size = 223333, upload-time = "2026-08-06T13:49:50.293Z" }, { url = "https://files.pythonhosted.org/packages/a7/a7/037b53b2885b0d8447064432491a4d5a1014cd9f97a594d53acd0c04541a/coverage-7.15.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26", size = 223630, upload-time = "2026-08-06T13:49:52.637Z" }, { url = "https://files.pythonhosted.org/packages/80/4f/152b8a4779ae90da11bb24f7467df8a59f0be48a5c52acb856325ca48289/coverage-7.15.4-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645", size = 264489, upload-time = "2026-08-06T13:49:54.52Z" }, { url = "https://files.pythonhosted.org/packages/10/2d/84b4b9e0e1dd6528a51920ff7031f35b789382e467a28ec6a5a578cb8812/coverage-7.15.4-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a", size = 267567, upload-time = "2026-08-06T13:49:56.721Z" }, { url = "https://files.pythonhosted.org/packages/53/fc/ba01cc25299f9f8a2c8b02d3b28c53f3543d9fbfbe4e74fa2760b48f163e/coverage-7.15.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624", size = 270123, upload-time = "2026-08-06T13:49:58.736Z" }, { url = "https://files.pythonhosted.org/packages/cf/d0/db2647cbf40b14f8c308f94ff7bf89c06d564e59f396906edf50086ec788/coverage-7.15.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa", size = 271107, upload-time = "2026-08-06T13:50:00.811Z" }, { url = "https://files.pythonhosted.org/packages/70/ff/4d2d17924552c458bb4f77dd631f0e3bc92fbbdf2d2d916cd4b33bbfd5b1/coverage-7.15.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f", size = 264955, upload-time = "2026-08-06T13:50:03.023Z" }, { url = "https://files.pythonhosted.org/packages/ee/de/dc010c7a3691f396d93bbc26bfcafa1c2a3a351cd520470f15faf5795bd5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f", size = 267949, upload-time = "2026-08-06T13:50:05.557Z" }, { url = "https://files.pythonhosted.org/packages/78/ea/dc96a11375e83c045c2f7c61fb6918277cfe9401db7c0f7b1d111a84b2e5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67", size = 264421, upload-time = "2026-08-06T13:50:07.612Z" }, { url = "https://files.pythonhosted.org/packages/c8/86/b77131a0f9503ce461cd577076147d7a9040f0c5dda772686f729e2cc9cb/coverage-7.15.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc", size = 269121, upload-time = "2026-08-06T13:50:09.58Z" }, { url = "https://files.pythonhosted.org/packages/24/24/944bc35007862955e7ebf05754e645419dcf5d7526c52735cfa2715e8ebf/coverage-7.15.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88", size = 264565, upload-time = "2026-08-06T13:50:11.722Z" }, { url = "https://files.pythonhosted.org/packages/c7/cc/a3bb9f93e7e740659163e2ea584f8196ddcd2c456a5dbe15f6c50105fec1/coverage-7.15.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc", size = 266522, upload-time = "2026-08-06T13:50:13.786Z" }, { url = "https://files.pythonhosted.org/packages/49/dd/e0e40f3560d878d888c580698ff5ad1179f5e1c3ac949684ef66b41a3817/coverage-7.15.4-cp315-cp315t-win32.whl", hash = "sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a", size = 225068, upload-time = "2026-08-06T13:50:15.825Z" }, { url = "https://files.pythonhosted.org/packages/c6/7e/37732ea80eebc30e976e4cdab15c190bc42d96959a42e38ddf6f8c60468f/coverage-7.15.4-cp315-cp315t-win_amd64.whl", hash = "sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b", size = 225895, upload-time = "2026-08-06T13:50:17.928Z" }, { url = "https://files.pythonhosted.org/packages/c6/08/1e00f7923eaaba45fb3d51dd794125fc766304b1df264f3a9c6557bfb30e/coverage-7.15.4-cp315-cp315t-win_arm64.whl", hash = "sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278", size = 225213, upload-time = "2026-08-06T13:50:19.981Z" }, { url = "https://files.pythonhosted.org/packages/b4/d9/e70c286c979378f061d8266e279b686ab0b0b688e1fe0af864684f23a77d/coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84", size = 214332, upload-time = "2026-08-06T13:50:22.192Z" }, ] [package.optional-dependencies] toml = [ { name = "tomli", marker = "python_full_version >= '3.10' and python_full_version <= '3.11'" }, ] [[package]] name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] [[package]] name = "docutils" version = "0.22.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, ] [[package]] name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] name = "idna" version = "3.19" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, ] [[package]] name = "imagesize" version = "1.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/cf/59/4b0dd64676aa6fb4986a755790cb6fc558559cf0084effad516820208ec3/imagesize-1.5.0.tar.gz", hash = "sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f", size = 1281127, upload-time = "2026-03-03T01:59:54.651Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/b1/a0662b03103c66cf77101a187f396ea91167cd9b7d5d3a2e465ad2c7ee9b/imagesize-1.5.0-py2.py3-none-any.whl", hash = "sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899", size = 5763, upload-time = "2026-03-03T01:59:52.343Z" }, ] [[package]] name = "imagesize" version = "2.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/fb/5e/513ff06670c84e7b9887c1fdf61b2d42b4f574a831f2f1d2222023049d8a/imagesize-2.0.1.tar.gz", hash = "sha256:b2ba6a4dea487a7ebcd53248d3476aca449d30db12a2dde5e0c5ca9624fd77e5", size = 1883774, upload-time = "2026-08-24T12:35:19.13Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/01/f9/575c8d760eae1fc99651b7cc5efd96ad5379ca4d6b53750b0fb4fe983f34/imagesize-2.0.1-py3-none-any.whl", hash = "sha256:ea0c9a0384df69ed86a943a15cde37d0360b82491b3910dc2215e202e62b5b02", size = 14794, upload-time = "2026-08-24T12:35:12.548Z" }, ] [[package]] name = "importlib-metadata" version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] 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 = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] 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 = "librt" version = "0.15.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" }, { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" }, { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" }, { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" }, { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" }, { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" }, { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" }, { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" }, { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" }, { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" }, { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" }, { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" }, { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" }, { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" }, { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" }, { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" }, { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" }, { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" }, { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" }, { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" }, { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" }, { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" }, { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" }, { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" }, { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" }, { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" }, { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" }, { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" }, { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" }, { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" }, { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" }, { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" }, { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" }, { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" }, { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" }, { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" }, { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" }, { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" }, { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" }, { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" }, { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" }, { url = "https://files.pythonhosted.org/packages/a8/93/949053fb462eecc4a9a5ee770a81f4b40be7b79538b245545d4aebc6b58b/librt-0.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f5de7feedc56337a088eb15cd9fafa9938367362221d8cc62c642b7f94821993", size = 149833, upload-time = "2026-08-07T10:47:39.86Z" }, { url = "https://files.pythonhosted.org/packages/61/ca/8281aa6cd560a3420e4497729f6b704b53be3eeaaef82d5aeadddaf7441f/librt-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c0eb900c0e91f4aebe680845242e614f1864edfd44106380d0752ac29522bf8", size = 154088, upload-time = "2026-08-07T10:47:41.065Z" }, { url = "https://files.pythonhosted.org/packages/dd/02/1a1662dceaba6a086360891448d5ce9a7d3555976cae59a31a39d744b9c7/librt-0.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8c9a650a188e38bac005048cbe6342e81407782944d01934540ab75e417df21", size = 494215, upload-time = "2026-08-07T10:47:42.388Z" }, { url = "https://files.pythonhosted.org/packages/69/84/99211619dc656370a3740c33d2b0b6d5a3fb1e73689314f6ed477a397dc4/librt-0.15.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:92bfed8deec93df30286b9fe9e3b1dd17329cc076a192b4ee5ec223841d54953", size = 491173, upload-time = "2026-08-07T10:47:43.683Z" }, { url = "https://files.pythonhosted.org/packages/d4/aa/5448d0b05f4579b635d3899176817ebf561af0e57bacd425b5b1887264c1/librt-0.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec4b19788f835711a2072f9dbe6b03b3bf32ed1f0fb30cf399bdd59d9f0c33fa", size = 505512, upload-time = "2026-08-07T10:47:45.314Z" }, { url = "https://files.pythonhosted.org/packages/95/82/01940e40b83c43a546c4a3c896cf34ca272a9690899d55914e4827b3dcce/librt-0.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4c7bacb70930f3d0a56f4ecf1be474a1f0d941b01dd73b756f3c256d42cb879", size = 523073, upload-time = "2026-08-07T10:47:46.66Z" }, { url = "https://files.pythonhosted.org/packages/88/fa/759c0030f3ee371439eb26de34fc745807caf0abb878af7af4b8b7c3dd3d/librt-0.15.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e79f05e4a08b4d880342673312bbc895b56df7765605796f15902eb5367d3ae", size = 515080, upload-time = "2026-08-07T10:47:48.319Z" }, { url = "https://files.pythonhosted.org/packages/0b/27/894e072228fcb159703c655da69f8cd10dbed489c36e3df7dd032a2483be/librt-0.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a417149c0cba4d50b61e992e5a15e69eaf96746609b461cc4ed168aeef6b79dd", size = 534164, upload-time = "2026-08-07T10:47:49.875Z" }, { url = "https://files.pythonhosted.org/packages/98/a3/0078e91c1f36f8815db17827de15650b9a3fe56c55fbf998c854b34e40d3/librt-0.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:da7a94d6a3411f579d72aa3e3bc5fbca7ed4549f3dbd7e5de3aa567333374285", size = 540616, upload-time = "2026-08-07T10:47:51.408Z" }, { url = "https://files.pythonhosted.org/packages/86/33/81a29b796dd52a45e9ef7974c7732926e8f10f15b8d2be505665979f896d/librt-0.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:856f743ae607f2c1380eccb566c0038a9fb3eabf0fc2be2704d76d9f73557239", size = 545890, upload-time = "2026-08-07T10:47:52.818Z" }, { url = "https://files.pythonhosted.org/packages/05/82/8be1baa1350e5d30cfd70ae79d0a6f4dc5862ef47f7bb2808aabc9bb86e5/librt-0.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:779a6e7c894737e5983e7790a9c78c4000c30e23c9aada08081bdbea53b0fa60", size = 523287, upload-time = "2026-08-07T10:47:54.165Z" }, { url = "https://files.pythonhosted.org/packages/c6/4f/d1be6a01a35c20ef734e0e44113f87d4af756a9354a89dcfbe3b4f8af5e1/librt-0.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96bb17dbe8bab3c0954fbebfc69ed395599de75b6bbc35e3270a878e15d4dd65", size = 565868, upload-time = "2026-08-07T10:47:55.566Z" }, { url = "https://files.pythonhosted.org/packages/67/88/649cfa33f5825927b160610f670bdab012a64d627eddb94fa795ea4292fd/librt-0.15.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:7220697efaa6e5348fc3d18ee7f8563d4bfecd9872b37ffb915bfc1d08840622", size = 81619, upload-time = "2026-08-07T10:47:56.886Z" }, { url = "https://files.pythonhosted.org/packages/22/31/8e88a8d5e48fc8d1a817787fb6811dfff6499acd6c8683dd83934aa6ede0/librt-0.15.0-cp314-cp314-win32.whl", hash = "sha256:f54598964d357b1c5ab77cf5d92f21e598fe0e23cdbe9618480807f81b4eba15", size = 100138, upload-time = "2026-08-07T10:47:58.093Z" }, { url = "https://files.pythonhosted.org/packages/80/92/20fd6c4b6a1b1a564b076d55cd3d427d8428217d7638dc25a654cc4791d4/librt-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ff5893a2c23d886aa9ce786de5ac6ddc74aeeaf90743682b74d920e117d2e28", size = 121258, upload-time = "2026-08-07T10:47:59.564Z" }, { url = "https://files.pythonhosted.org/packages/fc/28/6af430b44d9ebb897b865a3c363b6dcace51357be2347cc0f8f869656a86/librt-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:3722a099730704c9a3d70c879fc0f51daec25fe5f1555672d97bc595abeafb95", size = 106467, upload-time = "2026-08-07T10:48:01.097Z" }, { url = "https://files.pythonhosted.org/packages/7e/aa/b42bb798942ced219f6d63b27e07f91237887a8d0bd0921666db79a13790/librt-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:38c0c7d4b6fc06c3324b3f9162c8391bfc4fd9dde53afe1033ce7edb48d5a714", size = 159523, upload-time = "2026-08-07T10:48:02.442Z" }, { url = "https://files.pythonhosted.org/packages/75/03/1b53cd4ef904e73b1d828a5f90143bf94a2967d7cfff0b9ccf93e12aa9b4/librt-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b2fdd7ead3c995c37940a790690660d0ca006c302db26cc51933f6766866fc3", size = 161638, upload-time = "2026-08-07T10:48:03.725Z" }, { url = "https://files.pythonhosted.org/packages/ac/c4/9f9c9fba097d49e9e694c2b4dc331df31884645ecbc58a93b4b5fc69d2c5/librt-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fde98cf1fc4bac144ce23c2c4c017b924ba714509ea9334977b0b27050c837d", size = 701795, upload-time = "2026-08-07T10:48:05.135Z" }, { url = "https://files.pythonhosted.org/packages/4c/05/0966840bda0380c8ae167b9043c6230202941cc90ea29c48e096964c765e/librt-0.15.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e3b461183c5fa7681b48560f91515f53a953122fb30c71e07abc67d7ddf58c38", size = 682147, upload-time = "2026-08-07T10:48:06.555Z" }, { url = "https://files.pythonhosted.org/packages/18/af/1c47ca573c30ea47d195aec26133af522fea1104afaace028d7b32247ea8/librt-0.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4bbcc257e3babea20a91715c361b24554ec4e8f51aa578568afc230799fe1a19", size = 696397, upload-time = "2026-08-07T10:48:08.03Z" }, { url = "https://files.pythonhosted.org/packages/2e/0f/1aed6223d4f9f9d1171a8596ff100ea4c3f7699fea7a4ba657c3e60daa6c/librt-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b845b8d48088fad0cadc84be4b8fda63203be7e9237b71015b3925443c1f35ab", size = 722542, upload-time = "2026-08-07T10:48:09.569Z" }, { url = "https://files.pythonhosted.org/packages/c6/22/9e3a929aea456c97d69e6ef3884efea56d4807f97399471cc946baebd8af/librt-0.15.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b30e600e8f337b9bd7f39b86d9fdfedc73cc46e3d0f745931a23a234220bb7e2", size = 729709, upload-time = "2026-08-07T10:48:11.129Z" }, { url = "https://files.pythonhosted.org/packages/e9/1b/c327ef6018e3a9ca0b8e7c5eddeeb331ba8f9b76c24e126d37d0f6d62faf/librt-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:64b0c8c35aa4c4ed79896359f3e0b285cbe4e610042106500da4811c322cc108", size = 752891, upload-time = "2026-08-07T10:48:12.558Z" }, { url = "https://files.pythonhosted.org/packages/d7/d1/d5f1ea02c56930087009e39db9b70660a663e76c730b27b925d786718457/librt-0.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0da0d94cb802f32a0524653e7201f2cef72d5f700a5407678f5290483d4fcd08", size = 745301, upload-time = "2026-08-07T10:48:14.55Z" }, { url = "https://files.pythonhosted.org/packages/d9/3c/5f7c585d15ebb2250c73e7c0ee4e9e47be72c65d520c07ddbcdc62037674/librt-0.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4a6369168d371207339b1e50d4532b06a7121586141f82599505a3f315751d47", size = 747921, upload-time = "2026-08-07T10:48:16.453Z" }, { url = "https://files.pythonhosted.org/packages/7f/52/1443a446486eba966bcbca1696b472e4f210320ec42f490a47f48fbf0fdc/librt-0.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c434e072557ade9cbc642d052c89d031efe47d5c9614523619d0d74a02378e81", size = 727561, upload-time = "2026-08-07T10:48:18.089Z" }, { url = "https://files.pythonhosted.org/packages/79/91/2270a9380f11725cf83ce1925a5e32dd1dde2be9bba597f25c10a38644e7/librt-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7eec6a42018bc1d45763b1c162d3d2bf7c3b9a1b0ed30d3e91dcba390efefcc", size = 774417, upload-time = "2026-08-07T10:48:19.611Z" }, { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" }, { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" }, { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" }, { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" }, { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" }, { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" }, { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" }, { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" }, { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" }, { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" }, { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" }, { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" }, { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" }, { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" }, { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" }, { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" }, { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" }, { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" }, { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" }, { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" }, { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" }, { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" }, { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" }, { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" }, { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" }, { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" }, { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" }, { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" }, { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" }, { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" }, { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" }, { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" }, { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" }, { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, { url = "https://files.pythonhosted.org/packages/fd/c8/3f33f32a612139e0e59f9a3798104850e85b70752b0e2cbad586623abc4b/librt-0.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0e2d0c0acf5b0ada7d045912b7cf787c21315c95b38b1fa939ef72d45d366b3d", size = 149546, upload-time = "2026-08-07T10:49:17.007Z" }, { url = "https://files.pythonhosted.org/packages/8c/d8/e19702379d43f6c9fd54b2dcf0b7824ac01277be581ab701ed066fbe8ee8/librt-0.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9ca190fe9edc0eb08eec558a509a16d28d91c35667b8f043cba40ed5e77a959", size = 154470, upload-time = "2026-08-07T10:49:18.627Z" }, { url = "https://files.pythonhosted.org/packages/f0/12/9cc66774b023d4a0fb770e2015064aee60745968395c90a97eb4f6b79576/librt-0.15.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80811e1c42386ea95c6fb30571d3250ad43d7863f883f787f70517f441150e59", size = 493643, upload-time = "2026-08-07T10:49:20.352Z" }, { url = "https://files.pythonhosted.org/packages/4d/46/79b7ff1e6d58731ce12d23d9a9cbf969a1b48f7a2db893680783d36c6d2b/librt-0.15.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:88c2a17815c266e6d8180204ff62cb739ab869ada4a746d4c505331526ac58f1", size = 484421, upload-time = "2026-08-07T10:49:22.128Z" }, { url = "https://files.pythonhosted.org/packages/0c/1a/7f2142227c37147f17820b65601d1221c7ff15393c769fdb8450e6aadd8b/librt-0.15.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a5fa8f1f916988d0bf1afea005bda37f56ac41a18016e813ccf0097a8d460ca4", size = 497493, upload-time = "2026-08-07T10:49:23.905Z" }, { url = "https://files.pythonhosted.org/packages/c5/91/053c7396a7d4fe0f731a52ea87803f64251e42a175e28086c79e89738e58/librt-0.15.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:355e3a4c725225a14262004fc1872a552b9d3634b4f791a0dfc80804aafbfd55", size = 515104, upload-time = "2026-08-07T10:49:25.871Z" }, { url = "https://files.pythonhosted.org/packages/06/dd/34eb3aff3207b44f8c5562f46ab96b821a915c647e8724ac355df8e2eb0f/librt-0.15.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc1ed11c4ad0b91af24def2050f2840ea4567828e3dd058fbe608d982f6e5465", size = 508969, upload-time = "2026-08-07T10:49:27.728Z" }, { url = "https://files.pythonhosted.org/packages/f4/04/3a30b3f0c5103c7c02d902dc0ea27f123b857f63d4a26cd181860d7e7ea2/librt-0.15.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1f4ef2e71db33df4309167ed7f1520c4fae5e611226e159fa9cf33f93e6ddb3d", size = 531810, upload-time = "2026-08-07T10:49:29.736Z" }, { url = "https://files.pythonhosted.org/packages/ba/50/2964fa8fd1392e65a898938e8bd9ccf19314d7f92abcbc73403abc072051/librt-0.15.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1a1a8cd430c7dd0c083f455cb1b328d7fc682b05c31b940906f7845bdff80881", size = 536891, upload-time = "2026-08-07T10:49:31.687Z" }, { url = "https://files.pythonhosted.org/packages/9a/b5/724157f9cb5df4bdd3904caf1b1a17031de40dfd35942c3ef66f9e116860/librt-0.15.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:04d5387b908676c0b8d5d2f5fb58373b4ea382d81f7a6f0fab8ea2a462bb4738", size = 535985, upload-time = "2026-08-07T10:49:33.532Z" }, { url = "https://files.pythonhosted.org/packages/27/00/8bc5b14983c0346abba77ef11b28ef3d3a0e5452301a3cd6c99a15bb73e7/librt-0.15.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:1172c6ad2a88b646e7fe3b480e3fac4ab4418b3443fd8a4061fdd531e0622fc7", size = 516616, upload-time = "2026-08-07T10:49:35.459Z" }, { url = "https://files.pythonhosted.org/packages/06/b6/4938502caf417c8c0c88b60fe6bb5ed8f0d1b59549dd76929f2e1bbf6cd8/librt-0.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52e8db01f603f5da0ca30987479acff98769382efc8e142fa3962395dcf3ffdb", size = 558549, upload-time = "2026-08-07T10:49:37.256Z" }, { url = "https://files.pythonhosted.org/packages/37/3c/a6682d0c2682f366aeb9d9cec02d6d38c428bb087864fe79914468e25fb0/librt-0.15.0-cp39-cp39-win32.whl", hash = "sha256:e4c911f15a1652ca94ae9f1abd92e74cbb1b3597d2d92fdd556202f94e8cd455", size = 104936, upload-time = "2026-08-07T10:49:39.131Z" }, { url = "https://files.pythonhosted.org/packages/25/49/d8cd057754799fb59c9d90c8dfb65fc043ee14032e6238a77a5d99556cad/librt-0.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:68242379c9b65a582b6e97318a1e9fbd6d445e58954f2d437991c4804ab11578", size = 125606, upload-time = "2026-08-07T10:49:40.854Z" }, ] [[package]] name = "markdown-it-py" version = "3.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "mdurl", marker = "python_full_version < '3.10'" }, ] 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 = "markdown-it-py" version = "4.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "mdurl", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623, upload-time = "2025-09-27T18:37:29.296Z" }, { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049, upload-time = "2025-09-27T18:37:30.234Z" }, { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923, upload-time = "2025-09-27T18:37:31.177Z" }, { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", size = 20543, upload-time = "2025-09-27T18:37:32.168Z" }, { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", size = 20585, upload-time = "2025-09-27T18:37:33.166Z" }, { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", size = 21387, upload-time = "2025-09-27T18:37:34.185Z" }, { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", size = 20133, upload-time = "2025-09-27T18:37:35.138Z" }, { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", size = 20588, upload-time = "2025-09-27T18:37:36.096Z" }, { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", size = 14566, upload-time = "2025-09-27T18:37:37.09Z" }, { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", size = 15053, upload-time = "2025-09-27T18:37:38.054Z" }, { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", size = 13928, upload-time = "2025-09-27T18:37:39.037Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.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.19.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "librt", marker = "python_full_version < '3.10' and platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions", marker = "python_full_version < '3.10'" }, { name = "pathspec", marker = "python_full_version < '3.10'" }, { name = "tomli", marker = "python_full_version < '3.10'" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, ] [[package]] name = "mypy" version = "1.20.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "librt", marker = "python_full_version >= '3.10' and platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions", marker = "python_full_version >= '3.10'" }, { name = "pathspec", marker = "python_full_version >= '3.10'" }, { name = "tomli", marker = "python_full_version == '3.10.*'" }, { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" }, { url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" }, { url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" }, { url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" }, { url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" }, { url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" }, { url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" }, { url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" }, { url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" }, { url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" }, { url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" }, { url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" }, { url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" }, { url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" }, { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, { url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" }, { url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" }, { url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" }, { url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" }, { url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" }, { url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" }, { url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" }, { url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" }, { url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" }, { url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" }, { url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" }, { url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" }, { url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" }, { url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" }, { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.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 = "packaging" version = "26.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, ] [[package]] name = "pathspec" version = "1.1.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pygments" version = "2.21.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, ] [[package]] name = "pytest" version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-cov" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version < '3.10'" }, { name = "coverage", version = "7.15.4", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version >= '3.10'" }, { name = "pluggy" }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] name = "pytest-mock" version = "3.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, ] [[package]] name = "requests" version = "2.32.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "certifi", marker = "python_full_version < '3.10'" }, { name = "charset-normalizer", marker = "python_full_version < '3.10'" }, { name = "idna", marker = "python_full_version < '3.10'" }, { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] name = "requests" version = "2.34.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "certifi", marker = "python_full_version >= '3.10'" }, { name = "charset-normalizer", marker = "python_full_version >= '3.10'" }, { name = "idna", marker = "python_full_version >= '3.10'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] [[package]] name = "responses" version = "0.26.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/47/f216a33221db8eff328987661cf18371afee89c62a62b434b963d6b509c9/responses-0.26.3.tar.gz", hash = "sha256:b0c11ca8131b8b227b8d5108e6ed39772222bd5aab030ed430e8f99057c4c409", size = 86335, upload-time = "2026-08-26T19:17:24.373Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6d/86/ca7958de70cb0752350575e98229368a3a2f746a2942034b3364e17312bb/responses-0.26.3-py3-none-any.whl", hash = "sha256:74474f799334ac4f37d93b6437ecc3bb1bb5c77a8d31780a338643be2dce0af8", size = 36289, upload-time = "2026-08-26T19:17:23.176Z" }, ] [[package]] name = "rich" version = "15.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] [[package]] name = "roman-numerals" version = "4.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, ] [[package]] name = "ruff" version = "0.16.5" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, ] [[package]] name = "snowballstemmer" version = "3.1.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/f8/0a71edf031f03c40db17503cb8ca78a69a171254e568e7db241b0ab57ea1/snowballstemmer-3.1.1.tar.gz", hash = "sha256:e07bbc54a0d798fe6010a12398422e62a8bfbba95c394fd0956ef58cb4d3e260", size = 123314, upload-time = "2026-06-03T00:56:40.194Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4c/07/2ebca9b11fb9be7340a818d8d6f63feaebb146be2c4afbd6061701d6df6e/snowballstemmer-3.1.1-py3-none-any.whl", hash = "sha256:7e207fa178741da09cdee59d3ecec3827ad5f92b1fc5c9ff3755b639f71f5752", size = 104164, upload-time = "2026-06-03T00:56:38.614Z" }, ] [[package]] name = "sphinx" version = "7.4.7" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "babel", marker = "python_full_version < '3.10'" }, { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "imagesize", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jinja2", marker = "python_full_version < '3.10'" }, { name = "packaging", marker = "python_full_version < '3.10'" }, { name = "pygments", marker = "python_full_version < '3.10'" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "snowballstemmer", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" }, { name = "tomli", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, ] [[package]] name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "babel", marker = "python_full_version == '3.10.*'" }, { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "imagesize", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "jinja2", marker = "python_full_version == '3.10.*'" }, { name = "packaging", marker = "python_full_version == '3.10.*'" }, { name = "pygments", marker = "python_full_version == '3.10.*'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "snowballstemmer", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*'" }, { name = "tomli", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, ] [[package]] name = "sphinx" version = "9.0.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "babel", marker = "python_full_version == '3.11.*'" }, { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "imagesize", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "jinja2", marker = "python_full_version == '3.11.*'" }, { name = "packaging", marker = "python_full_version == '3.11.*'" }, { name = "pygments", marker = "python_full_version == '3.11.*'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" }, ] [[package]] name = "sphinx" version = "9.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", ] dependencies = [ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "babel", marker = "python_full_version >= '3.12'" }, { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "imagesize", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "jinja2", marker = "python_full_version >= '3.12'" }, { name = "packaging", marker = "python_full_version >= '3.12'" }, { name = "pygments", marker = "python_full_version >= '3.12'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, ] [[package]] name = "sphinx-copybutton" version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, ] [[package]] name = "sphinx-prompt" version = "1.8.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "pygments", marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e7/fb/7a07b8df1ca2418147a6b13e3f6b445071f2565198b45efa631d0d6ef0cd/sphinx_prompt-1.8.0.tar.gz", hash = "sha256:47482f86fcec29662fdfd23e7c04ef03582714195d01f5d565403320084372ed", size = 5121, upload-time = "2023-09-14T12:46:13.449Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/39/49/f890a2668b7cbf375f5528b549c8d36dd2e801b0fbb7b2b5ef65663ecb6c/sphinx_prompt-1.8.0-py3-none-any.whl", hash = "sha256:369ecc633f0711886f9b3a078c83264245be1adf46abeeb9b88b5519e4b51007", size = 7298, upload-time = "2023-09-14T12:46:12.373Z" }, ] [[package]] name = "sphinx-prompt" version = "1.9.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ { name = "certifi", marker = "python_full_version == '3.10.*'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "idna", marker = "python_full_version == '3.10.*'" }, { name = "pygments", marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/fe/ac4e24f35b5148b31ac717ae7dcc7a2f7ec56eb729e22c7252ed8ad2d9a5/sphinx_prompt-1.9.0.tar.gz", hash = "sha256:471b3c6d466dce780a9b167d9541865fd4e9a80ed46e31b06a52a0529ae995a1", size = 5340, upload-time = "2024-08-07T15:46:51.428Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/98/e90ca466e0ede452d3e5a8d92b8fb68db6de269856e019ed9cab69440522/sphinx_prompt-1.9.0-py3-none-any.whl", hash = "sha256:fd731446c03f043d1ff6df9f22414495b23067c67011cc21658ea8d36b3575fc", size = 7311, upload-time = "2024-08-07T15:46:50.329Z" }, ] [[package]] name = "sphinx-prompt" version = "1.10.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", ] dependencies = [ { name = "certifi", marker = "python_full_version >= '3.11'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "idna", marker = "python_full_version >= '3.11'" }, { name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d0/a3/91293c0e0f0b76d0697ba7a41541929ca3f5457671d008bd84a9bde17e21/sphinx_prompt-1.10.2.tar.gz", hash = "sha256:47b592ba75caebd044b0eddf7a5a1b6e0aef6df587b034377cd101a999b686ba", size = 5566, upload-time = "2025-11-28T09:23:18.057Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a9/f4/44ce4d0179fb4e9cfe181a8aa281bba23e40158a609fb3680774529acaaa/sphinx_prompt-1.10.2-py3-none-any.whl", hash = "sha256:6594337962c4b1498602e6984634bed4a0dc7955852e3cfc255eb0af766ed859", size = 7474, upload-time = "2025-11-28T09:23:17.154Z" }, ] [[package]] name = "sphinx-rtd-theme" version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-jquery" }, ] sdist = { url = "https://files.pythonhosted.org/packages/84/68/a1bfbf38c0f7bccc9b10bbf76b94606f64acb1552ae394f0b8285bfaea25/sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c", size = 7620915, upload-time = "2026-01-12T16:03:31.17Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89", size = 7655617, upload-time = "2026-01-12T16:03:28.101Z" }, ] [[package]] name = "sphinx-tabs" version = "3.4.7" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "pygments", marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6a/53/a9a91995cb365e589f413b77fc75f1c0e9b4ac61bfa8da52a779ad855cc0/sphinx-tabs-3.4.7.tar.gz", hash = "sha256:991ad4a424ff54119799ba1491701aa8130dd43509474aef45a81c42d889784d", size = 15891, upload-time = "2024-10-08T13:37:27.887Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6b/c6/f47505b564b918a3ba60c1e99232d4942c4a7e44ecaae603e829e3d05dae/sphinx_tabs-3.4.7-py3-none-any.whl", hash = "sha256:c12d7a36fd413b369e9e9967a0a4015781b71a9c393575419834f19204bd1915", size = 9727, upload-time = "2024-10-08T13:37:26.192Z" }, ] [[package]] name = "sphinx-tabs" version = "3.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygments", marker = "python_full_version >= '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/30/ca5b0de830f369968d8e3483dd45a8908fd10169c05cd9837f0bd075982e/sphinx_tabs-3.5.0.tar.gz", hash = "sha256:91dba1187e4c35fd37380a56ac228bbd54c6c649b2351829f3bf033718277537", size = 17006, upload-time = "2026-03-03T23:00:30.404Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2e/45/6adc5efeb19fd5fed4027e520b5c668ce58236a2b271ade5533c4c116276/sphinx_tabs-3.5.0-py3-none-any.whl", hash = "sha256:154be49de4d5c8249ea08c5d9bf88ca8f9c31e00a178305a93cbc33e000339e5", size = 9871, upload-time = "2026-03-03T23:00:28.89Z" }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, ] [[package]] name = "sphinxcontrib-autoprogram" version = "0.1.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c8/5f/044c87c306c9cbac68a4dccacb18ca0fdd4973096fdd92304ea069b11c59/sphinxcontrib-autoprogram-0.1.9.tar.gz", hash = "sha256:219655507fadca29b3062b5d86c37d94db48f03bde4b58d61526872bf72f57cc", size = 18843, upload-time = "2024-03-13T19:10:14.445Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/13/4c/5d9fb7caee7a765c2daf48cdf097c396eef3e1370290589f1b9e30266694/sphinxcontrib_autoprogram-0.1.9-py2.py3-none-any.whl", hash = "sha256:79a5282d7640337e4bf11f624970a43709f1b704c5c59a59756d45e824db5301", size = 8900, upload-time = "2024-03-13T19:10:12.528Z" }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, ] [[package]] name = "sphinxcontrib-jquery" version = "4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] [[package]] name = "tomli" version = "2.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] [[package]] name = "types-requests" version = "2.32.4.20260107" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" }, ] [[package]] name = "types-requests" version = "2.33.0.20260712" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/51/703318f7b7be8bee126ec13bf615050f932d0179b8784420f3a0199cc769/types_requests-2.33.0.20260712.tar.gz", hash = "sha256:2141b67ab534a5c5cd2dac5034f2a35f42e699c5bf185eee608c5246a069d7fb", size = 25084, upload-time = "2026-07-12T05:14:20.455Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/62/e7/010c87f559e216d83f9dc51e939633fd0d0ead3377340181ab0e223cd3b5/types_requests-2.33.0.20260712-py3-none-any.whl", hash = "sha256:de027e28c171d3da529689cbfa023b0b4eab188c8dfa22fd834eebd2cee6e7bb", size = 21392, upload-time = "2026-07-12T05:14:19.616Z" }, ] [[package]] name = "typing-extensions" version = "4.16.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] name = "urllib3" version = "2.6.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] name = "urllib3" version = "2.7.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version == '3.11.*'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] [[package]] name = "zipp" version = "3.23.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ]