├── .github ├── actions │ └── python-setup │ │ └── action.yml ├── dependabot.yml ├── pull_request_template.md ├── release.yml └── workflows │ ├── auto-pr-review.yml │ ├── bandit.yml │ ├── python-ci.yml │ ├── python-publish.yml │ └── scorecards.yml ├── .gitignore ├── .vscode ├── cspell.json └── settings.json ├── LICENSE ├── README.rst ├── SECURITY.md ├── poetry.lock ├── pyproject.toml ├── src └── epss_api │ ├── __init__.py │ └── epss.py └── tests └── epss_api └── test_epss.py /.github/actions/python-setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup python 2 | 3 | inputs: 4 | python-version: 5 | default: "3.10" 6 | required: false 7 | description: "python version" 8 | 9 | poetry-version: 10 | default: "1.2.2" 11 | required: false 12 | description: "poetry version" 13 | 14 | runs: 15 | using: composite 16 | steps: 17 | - name: Setup python environment 18 | uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b 19 | with: 20 | python-version: ${{ inputs.python-version }} 21 | 22 | - name: Setup poetry environment 23 | shell: bash 24 | run: pip install poetry 25 | 26 | - name: Install dependencies 27 | shell: bash 28 | run: poetry install 29 | 30 | # - name: Setup poetry environment 31 | # uses: snok/install-poetry@2bf112a0f6979928eb6b011f39700db589c5961e 32 | # with: 33 | # version: ${{ inputs.poetry-version }} 34 | # virtualenvs-create: true 35 | # virtualenvs-in-project: true 36 | # installer-parallel: true 37 | 38 | # - name: Cache dependencies 39 | # uses: actions/cache@c3f1317a9e7b1ef106c153ac8c0f00fed3ddbc0d 40 | # id: poetry_cache_id 41 | # with: 42 | # path: .venv 43 | # key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ inputs.poetry-version }}-${{ hashFiles('**/poetry.lock') }} 44 | 45 | # - name: Install dependencies 46 | # if: steps.poetry_cache_id.outputs.cache-hit != 'true' 47 | # shell: bash 48 | # run: poetry install 49 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | allow: 8 | - dependency-name: "*" 9 | dependency-type: "production" 10 | 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Describe your changes 2 | 3 | ## Issue ticket number and link 4 | 5 | ## Checklist before requesting a review 6 | 7 | - [ ] I have performed a self-review of my code 8 | - [ ] If it is a core feature, I have added thorough tests. 9 | - [ ] Do we need to implement analytics? 10 | - [ ] Will this be part of a product update? If yes, please write one phrase about this update. 11 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Added 4 | labels: 5 | - Add 6 | - title: Changed 7 | labels: 8 | - Change 9 | - title: Deprecated 10 | labels: 11 | - Deprecate 12 | - title: Removed 13 | labels: 14 | - Remove 15 | - title: Fixed 16 | labels: 17 | - Fix 18 | - title: Security 19 | labels: 20 | - Security 21 | - title: Other Changes 22 | labels: 23 | - "*" 24 | -------------------------------------------------------------------------------- /.github/workflows/auto-pr-review.yml: -------------------------------------------------------------------------------- 1 | name: Auto PR Review 2 | 3 | on: 4 | pull_request: 5 | 6 | permissions: read-all 7 | 8 | jobs: 9 | review-dependabot: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | if: ${{ github.actor == 'dependabot[bot]' }} 14 | steps: 15 | - name: Get Dependabot metadata 16 | id: metadata 17 | uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b 18 | with: 19 | github-token: "${{ secrets.GITHUB_TOKEN }}" 20 | 21 | - name: Enable auto merge for Dependabot PRs 22 | if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' }} 23 | run: gh pr merge --auto --merge "$PR_URL" 24 | env: 25 | PR_URL: ${{github.event.pull_request.html_url}} 26 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 27 | 28 | review-snyk: 29 | runs-on: ubuntu-latest 30 | permissions: 31 | contents: write 32 | if: ${{ github.actor == 'snyk-bot' }} 33 | steps: 34 | - name: Enable auto merge for Snyk PRs 35 | run: gh pr merge --auto --merge "$PR_URL" 36 | env: 37 | PR_URL: ${{github.event.pull_request.html_url}} 38 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 39 | 40 | review-license: 41 | runs-on: ubuntu-latest 42 | permissions: 43 | contents: read 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 47 | 48 | - name: Review license 49 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 50 | with: 51 | fail-on-severity: high 52 | deny-licenses: AGPL-1.0-only, AGPL-1.0-or-later, AGPL-3.0-only, AGPL-3.0-or-later, GPL-1.0-only, GPL-1.0-or-later, GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later, LGPL-2.0-only, LGPL-2.0-or-later, LGPL-2.1-only, LGPL-2.1-or-later, LGPL-3.0-only, LGPL-3.0-or-later 53 | -------------------------------------------------------------------------------- /.github/workflows/bandit.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # Bandit is a security linter designed to find common security issues in Python code. 7 | # This action will run Bandit on your codebase. 8 | # The results of the scan will be found under the Security tab of your repository. 9 | 10 | # https://github.com/marketplace/actions/bandit-scan is ISC licensed, by abirismyname 11 | # https://pypi.org/project/bandit/ is Apache v2.0 licensed, by PyCQA 12 | 13 | name: Bandit 14 | on: 15 | push: 16 | branches: ["main"] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: ["main"] 20 | schedule: 21 | - cron: "24 21 * * 2" 22 | 23 | jobs: 24 | bandit: 25 | permissions: 26 | contents: read # for actions/checkout to fetch code 27 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 33 | - name: Bandit Scan 34 | uses: shundor/python-bandit-scan@9cc5aa4a006482b8a7f91134412df6772dbda22c 35 | with: # optional arguments 36 | # exit with 0, even with results found 37 | exit_zero: true # optional, default is DEFAULT 38 | # Github token of the repository (automatically created by Github) 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information. 40 | # File or directory to run bandit on 41 | path: src # optional, default is . 42 | # Report only issues of a given severity level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) 43 | # level: # optional, default is UNDEFINED 44 | # Report only issues of a given confidence level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) 45 | # confidence: # optional, default is UNDEFINED 46 | # comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) 47 | # excluded_paths: # optional, default is DEFAULT 48 | # comma-separated list of test IDs to skip 49 | # skips: # optional, default is DEFAULT 50 | # path to a .bandit file that supplies command line arguments 51 | # ini_path: # optional, default is DEFAULT 52 | -------------------------------------------------------------------------------- /.github/workflows/python-ci.yml: -------------------------------------------------------------------------------- 1 | name: Python CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | schedule: 9 | - cron: "0 15 * * 0" 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | permissions: # for EnricoMi/publish-unit-test-result-action 17 | checks: write 18 | pull-requests: write # for comment_mode 19 | contents: read # only for private repository 20 | issues: read # only for private repository 21 | strategy: 22 | matrix: 23 | python-version: ["3.10"] 24 | poetry-version: ["1.2.2"] 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 28 | 29 | - name: Setup python environment 30 | uses: ./.github/actions/python-setup 31 | with: 32 | python-version: ${{ matrix.python-version }} 33 | poetry-version: ${{ matrix.poetry-version }} 34 | 35 | - name: Lint python code by flake8 36 | continue-on-error: true 37 | run: poetry run flake8 src/ tests/ --format junit-xml --output-file report.xml 38 | 39 | - name: Lint python code by flake8 40 | continue-on-error: true 41 | run: poetry run flake8 src/ tests/ --format html --htmldir=flake8 42 | 43 | - name: Publish flake8 report to PR 44 | uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 45 | with: 46 | check_name: flake8 results 47 | junit_files: report.xml 48 | 49 | - name: Upload flake8 report to artifact 50 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 51 | with: 52 | name: flake8-report 53 | path: flake8/ 54 | 55 | - name: Test python code by pytest 56 | continue-on-error: true 57 | run: poetry run pytest 58 | 59 | - name: Publish pytest report to PR 60 | uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 61 | with: 62 | check_name: pytest results 63 | junit_files: report.xml 64 | 65 | - name: Upload test results to codecov 66 | if: ${{ github.ref_name == 'main' && !cancelled() }} 67 | uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f 68 | with: 69 | token: ${{ secrets.CODECOV_TOKEN }} 70 | files: report.xml 71 | 72 | - name: Upload pytest report to artifact 73 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 74 | with: 75 | name: pytest-report 76 | path: report.html 77 | 78 | - name: Upload pytest coverage report to artifact 79 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 80 | with: 81 | name: pytest-cov-report 82 | path: htmlcov/ 83 | 84 | - name: Upload coverage to codecov 85 | if: ${{ github.ref_name == 'main' && !cancelled() }} 86 | uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 87 | with: 88 | token: ${{ secrets.CODECOV_TOKEN }} 89 | env_vars: OS,PYTHON 90 | fail_ci_if_error: true 91 | files: ./coverage.xml 92 | flags: unittests 93 | verbose: true 94 | 95 | sast-codeql: 96 | runs-on: ubuntu-latest 97 | permissions: 98 | actions: read 99 | contents: read 100 | security-events: write 101 | strategy: 102 | fail-fast: false 103 | matrix: 104 | language: ["python"] 105 | steps: 106 | - name: Checkout repository 107 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 108 | 109 | - name: Initialize CodeQL 110 | uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e 111 | with: 112 | languages: ${{ matrix.language }} 113 | 114 | - name: Build automatically 115 | uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e 116 | 117 | - name: Perform CodeQL Analysis 118 | uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e 119 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Python Publish 2 | 3 | on: 4 | workflow_dispatch: # for debug 5 | push: 6 | branches: [main] 7 | tags: 8 | - v*.*.* 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | if: ${{ github.ref_name != 'main' }} 16 | permissions: 17 | contents: write 18 | packages: write 19 | id-token: write 20 | attestations: write 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 24 | 25 | - name: Setup python environment 26 | uses: ./.github/actions/python-setup 27 | 28 | - name: Build package 29 | id: build 30 | run: | 31 | poetry run seman sbom 32 | poetry build 33 | echo "package_tar=`ls dist/ | grep .tar.gz`" >> $GITHUB_OUTPUT 34 | echo "package_whl=`ls dist/ | grep .whl`" >> $GITHUB_OUTPUT 35 | 36 | - name: Upload package to artifact 37 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 38 | with: 39 | name: package 40 | path: dist/ 41 | 42 | - name: Upload package to release asset 43 | uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | with: 47 | generate_release_notes: true 48 | files: | 49 | dist/${{ steps.build.outputs.package_tar }} 50 | dist/${{ steps.build.outputs.package_whl }} 51 | 52 | - name: Upload package to pypi 53 | uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc 54 | with: 55 | user: __token__ 56 | password: ${{ secrets.PYPI_API_TOKEN }} 57 | 58 | - name: attest build provenance 59 | uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd 60 | with: 61 | subject-path: | 62 | dist/${{ steps.build.outputs.package_tar }} 63 | dist/${{ steps.build.outputs.package_whl }} 64 | -------------------------------------------------------------------------------- /.github/workflows/scorecards.yml: -------------------------------------------------------------------------------- 1 | name: Supply Chain Security Scan 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["main"] # Scorecard support only default branch 7 | branch_protection_rule: 8 | schedule: 9 | - cron: "50 6 * * 6" 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | analyze: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | security-events: write 18 | id-token: write # Used to receive a badge. (Upcoming feature) 19 | contents: read # Needs for private repositories. 20 | actions: read # Needs for private repositories. 21 | steps: 22 | - name: Checkout code 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 24 | with: 25 | persist-credentials: false 26 | 27 | - name: Scan by Scorecard 28 | uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde 29 | with: 30 | results_file: results.sarif 31 | results_format: sarif 32 | # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if: 33 | # - you want to enable the Branch-Protection check on a *public* repository, or 34 | # - you are installing Scorecards on a *private* repository 35 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 36 | repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} 37 | 38 | # Publish the results for public repositories to enable scorecard badges. For more details, see 39 | # https://github.com/ossf/scorecard-action#publishing-results. 40 | # For private repositories, `publish_results` will automatically be set to `false`, regardless 41 | # of the value entered here. 42 | publish_results: true 43 | 44 | - name: Upload Scorecard report to artifact 45 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 46 | with: 47 | name: SARIF file 48 | path: results.sarif 49 | 50 | - name: Publish Scorecard report to GitHub Security 51 | uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e 52 | with: 53 | sarif_file: results.sarif 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .tmp 132 | report.html 133 | report.xml 134 | -------------------------------------------------------------------------------- /.vscode/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "ignorePaths": [], 4 | "dictionaryDefinitions": [], 5 | "dictionaries": [], 6 | "words": [ 7 | "EPSS" 8 | ], 9 | "ignoreWords": [], 10 | "import": [] 11 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.formatOnPaste": true, 4 | "editor.formatOnSave": true, 5 | "editor.formatOnType": true 6 | }, 7 | "explorer.excludeGitIgnore": false, 8 | "git-graph.repository.commits.showSignatureStatus": true, 9 | "git-graph.repository.sign.commits": true, 10 | "git-graph.repository.sign.tags": true, 11 | "git.enableCommitSigning": true, 12 | "python.formatting.provider": "autopep8", 13 | "python.linting.flake8Enabled": true, 14 | "python.linting.pylintEnabled": false, 15 | "python.testing.pytestArgs": [ 16 | "--no-cov" 17 | ], 18 | "python.testing.pytestEnabled": true, 19 | "python.testing.unittestEnabled": false, 20 | "files.exclude": { 21 | "**/.pytest_cache": true, 22 | "**/__pycache__": true, 23 | "**/.venv": true 24 | }, 25 | "python.autoComplete.extraPaths": [ 26 | "src/epss_api" 27 | ], 28 | "python.analysis.extraPaths": [ 29 | "src/epss_api" 30 | ], 31 | "esbonio.sphinx.confDir": "" 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 kannkyo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | EPSS API Client 3 | ================= 4 | 5 | .. image:: https://badge.fury.io/py/epss-api.svg 6 | :target: https://badge.fury.io/py/epss-api 7 | 8 | .. image:: https://img.shields.io/pypi/dw/epss-api?style=flat 9 | :target: https://pypistats.org/packages/epss-api 10 | 11 | .. image:: https://github.com/kannkyo/epss-api/actions/workflows/python-ci.yml/badge.svg 12 | :target: https://github.com/kannkyo/epss-api/actions/workflows/python-ci.yml 13 | 14 | .. image:: https://codecov.io/gh/kannkyo/epss-api/branch/main/graph/badge.svg?token=R40FT0KITO 15 | :target: https://codecov.io/gh/kannkyo/epss-api 16 | 17 | .. image:: https://github.com/kannkyo/epss-api/actions/workflows/scorecards.yml/badge.svg 18 | :target: https://github.com/kannkyo/epss-api/actions/workflows/scorecards.yml 19 | 20 | EPSS(Exploit Prediction Scoring System) API client. 21 | 22 | EPSS is the one of famous vulnerability score developed by FIRST (the Forum of Incident Response and Security Teams). 23 | 24 | EPSS's definition: 25 | 26 | The Exploit Prediction Scoring System (EPSS) is an open, 27 | data-driven effort for estimating the likelihood (probability) that a software vulnerability will be exploited in the wild. 28 | Our goal is to assist network defenders to better prioritize vulnerability remediation efforts. 29 | While other industry standards have been useful for capturing innate characteristics of a vulnerability and provide measures of severity, 30 | they are limited in their ability to assess threat. 31 | EPSS fills that gap because it uses current threat information from CVE and real-world exploit data. 32 | The EPSS model produces a probability score between 0 and 1 (0 and 100%). 33 | The higher the score, the greater the probability that a vulnerability will be exploited. 34 | 35 | https://www.first.org/epss/ 36 | 37 | This package is most easiest and efficient EPSS api client. 38 | 39 | Usage 40 | ============= 41 | 42 | EPSS has some methods. 43 | 44 | .. code-block:: python 45 | >>> from epss_api import EPSS 46 | >>> 47 | >>> client = EPSS() 48 | 49 | >>> print(client.scores()[0]) 50 | {'cve': 'CVE-1999-0013', 'epss': 0.00042, 'percentile': 0.05071} 51 | 52 | >>> print(client.score('CVE-2024-0001')) 53 | {'cve': 'CVE-2024-0001', 'epss': 0.00091, 'percentile': 0.4063} 54 | 55 | >>> print(client.csv()[1]) 56 | cve,epss,percentile 57 | 58 | >>> print(client.epss('CVE-2024-0001')) 59 | 0.00091 60 | 61 | >>> print(client.epss_ge(0.50003)[0]) 62 | {'cve': 'CVE-2022-0651', 'epss': 0.50003, 'percentile': 0.97652} 63 | 64 | >>> print(client.epss_gt(0.50003)[0]) 65 | {'cve': 'CVE-2018-0851', 'epss': 0.50036, 'percentile': 0.97653} 66 | 67 | >>> print(client.epss_le(0.49982)[-1]) 68 | {'cve': 'CVE-2014-8074', 'epss': 0.49982, 'percentile': 0.97651} 69 | 70 | >>> print(client.epss_lt(0.49982)[-1]) 71 | {'cve': 'CVE-2018-8011', 'epss': 0.49981, 'percentile': 0.97651} 72 | 73 | >>> print(client.percentile('CVE-2024-0001')) 74 | 0.4063 75 | 76 | >>> print(client.percentile_ge(0.5)[0]) 77 | {'cve': 'CVE-2019-5426', 'epss': 0.00137, 'percentile': 0.5} 78 | 79 | >>> print(client.percentile_gt(0.5)[0]) 80 | {'cve': 'CVE-2021-43464', 'epss': 0.00137, 'percentile': 0.50004} 81 | 82 | >>> print(client.percentile_le(0.5)[-1]) 83 | {'cve': 'CVE-2022-27777', 'epss': 0.00137, 'percentile': 0.5} 84 | 85 | >>> print(client.percentile_lt(0.5)[-1]) 86 | {'cve': 'CVE-2021-1625', 'epss': 0.00137, 'percentile': 0.49999} 87 | 88 | If you call either one method, EPSS client cache all CVE's score in memory. 89 | After caching, you can get all data very fast. 90 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | < 1.0 | :white_check_mark: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | Please report a vulnerability by [security advisories](https://github.com/kannkyo/epss-api/security/advisories/new). Vulunerability reports should contain 4 elements. 15 | 16 | * Impact : What kind of vulnerability is it? Who is impacted? 17 | * Patches : Has the problem been patched? What versions should users upgrade to? 18 | * Workarounds : Is there a way for users to fix or remediate the vulnerability without upgrading? 19 | * References : Are there any links users can visit to find out more? 20 | 21 | ## SBOM 22 | 23 | SBOM (Cyclonedx format) is automatically generated and included in package as `sbom.xml`. 24 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "chardet" 5 | version = "5.2.0" 6 | description = "Universal encoding detector for Python 3" 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, 11 | {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, 12 | ] 13 | 14 | [[package]] 15 | name = "colorama" 16 | version = "0.4.6" 17 | description = "Cross-platform colored terminal text." 18 | optional = false 19 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 20 | files = [ 21 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 22 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 23 | ] 24 | 25 | [[package]] 26 | name = "coverage" 27 | version = "7.6.1" 28 | description = "Code coverage measurement for Python" 29 | optional = false 30 | python-versions = ">=3.8" 31 | files = [ 32 | {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, 33 | {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, 34 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, 35 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, 36 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, 37 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, 38 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, 39 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, 40 | {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, 41 | {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, 42 | {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, 43 | {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, 44 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, 45 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, 46 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, 47 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, 48 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, 49 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, 50 | {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, 51 | {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, 52 | {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, 53 | {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, 54 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, 55 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, 56 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, 57 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, 58 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, 59 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, 60 | {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, 61 | {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, 62 | {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, 63 | {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, 64 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, 65 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, 66 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, 67 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, 68 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, 69 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, 70 | {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, 71 | {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, 72 | {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, 73 | {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, 74 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, 75 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, 76 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, 77 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, 78 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, 79 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, 80 | {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, 81 | {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, 82 | {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, 83 | {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, 84 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, 85 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, 86 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, 87 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, 88 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, 89 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, 90 | {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, 91 | {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, 92 | {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, 93 | {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, 94 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, 95 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, 96 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, 97 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, 98 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, 99 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, 100 | {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, 101 | {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, 102 | {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, 103 | {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, 104 | ] 105 | 106 | [package.dependencies] 107 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 108 | 109 | [package.extras] 110 | toml = ["tomli"] 111 | 112 | [[package]] 113 | name = "cyclonedx-bom" 114 | version = "3.11.7" 115 | description = "CycloneDX Software Bill of Materials (SBOM) generation utility" 116 | optional = false 117 | python-versions = ">=3.6,<4.0" 118 | files = [ 119 | {file = "cyclonedx_bom-3.11.7-py3-none-any.whl", hash = "sha256:dd57c39c22f435745a95b06406cdb039b7ae6536e735115ab4c40cbac09c4723"}, 120 | {file = "cyclonedx_bom-3.11.7.tar.gz", hash = "sha256:656a6df47511abff82c159dfa947a6f881ec971d1b9861ec14802bc50bc29e05"}, 121 | ] 122 | 123 | [package.dependencies] 124 | chardet = ">=5.0,<6.0" 125 | cyclonedx-python-lib = ">=2.0.0,<4.0.0" 126 | packageurl-python = ">=0.9" 127 | pip-requirements-parser = ">=32.0.0,<33.0.0" 128 | setuptools = ">=47.0.0" 129 | toml = ">=0.10.0,<0.11.0" 130 | 131 | [[package]] 132 | name = "cyclonedx-python-lib" 133 | version = "3.1.5" 134 | description = "A library for producing CycloneDX SBOM (Software Bill of Materials) files." 135 | optional = false 136 | python-versions = ">=3.6,<4.0" 137 | files = [ 138 | {file = "cyclonedx_python_lib-3.1.5-py3-none-any.whl", hash = "sha256:8981ca462fba91469c268d684a03f72c89c7a807674d884f83a28d8c2822a9b6"}, 139 | {file = "cyclonedx_python_lib-3.1.5.tar.gz", hash = "sha256:1ccd482024a30b95c4fffb3fe567a9df97b705f34c1075f8abde8537867600c3"}, 140 | ] 141 | 142 | [package.dependencies] 143 | packageurl-python = ">=0.9" 144 | setuptools = ">=47.0.0" 145 | sortedcontainers = ">=2.4.0,<3.0.0" 146 | toml = ">=0.10.0,<0.11.0" 147 | 148 | [[package]] 149 | name = "exceptiongroup" 150 | version = "1.2.2" 151 | description = "Backport of PEP 654 (exception groups)" 152 | optional = false 153 | python-versions = ">=3.7" 154 | files = [ 155 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 156 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 157 | ] 158 | 159 | [package.extras] 160 | test = ["pytest (>=6)"] 161 | 162 | [[package]] 163 | name = "fire" 164 | version = "0.5.0" 165 | description = "A library for automatically generating command line interfaces." 166 | optional = false 167 | python-versions = "*" 168 | files = [ 169 | {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, 170 | ] 171 | 172 | [package.dependencies] 173 | six = "*" 174 | termcolor = "*" 175 | 176 | [[package]] 177 | name = "flake8" 178 | version = "4.0.1" 179 | description = "the modular source code checker: pep8 pyflakes and co" 180 | optional = false 181 | python-versions = ">=3.6" 182 | files = [ 183 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 184 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 185 | ] 186 | 187 | [package.dependencies] 188 | mccabe = ">=0.6.0,<0.7.0" 189 | pycodestyle = ">=2.8.0,<2.9.0" 190 | pyflakes = ">=2.4.0,<2.5.0" 191 | 192 | [[package]] 193 | name = "flake8-formatter-junit-xml" 194 | version = "0.0.6" 195 | description = "JUnit XML Formatter for flake8" 196 | optional = false 197 | python-versions = "*" 198 | files = [ 199 | {file = "flake8_formatter_junit_xml-0.0.6-py2.py3-none-any.whl", hash = "sha256:6358a44ecafdf0f9c8ee5314859b8d6f553dc2e55e946a24c538185e1eba7ce6"}, 200 | {file = "flake8_formatter_junit_xml-0.0.6.tar.gz", hash = "sha256:1ddd9356bb30ba736c3f14c769c837cfacf4f79c3d383ab963ef9d38eea05a9c"}, 201 | ] 202 | 203 | [package.dependencies] 204 | flake8 = ">3.0.0" 205 | junit-xml = ">=1.8" 206 | 207 | [[package]] 208 | name = "flake8-html" 209 | version = "0.4.3" 210 | description = "Generate HTML reports of flake8 violations" 211 | optional = false 212 | python-versions = "*" 213 | files = [ 214 | {file = "flake8-html-0.4.3.tar.gz", hash = "sha256:8b870299620cc4a06f73644a1b4d457799abeca1cc914c62ae71ec5bf65c79a5"}, 215 | {file = "flake8_html-0.4.3-py2.py3-none-any.whl", hash = "sha256:8f126748b1b0edd6cd39e87c6192df56e2f8655b0aa2bb00ffeac8cf27be4325"}, 216 | ] 217 | 218 | [package.dependencies] 219 | flake8 = ">=3.3.0" 220 | jinja2 = ">=3.1.0" 221 | pygments = ">=2.2.0" 222 | 223 | [[package]] 224 | name = "iniconfig" 225 | version = "2.0.0" 226 | description = "brain-dead simple config-ini parsing" 227 | optional = false 228 | python-versions = ">=3.7" 229 | files = [ 230 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 231 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 232 | ] 233 | 234 | [[package]] 235 | name = "jinja2" 236 | version = "3.1.5" 237 | description = "A very fast and expressive template engine." 238 | optional = false 239 | python-versions = ">=3.7" 240 | files = [ 241 | {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, 242 | {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, 243 | ] 244 | 245 | [package.dependencies] 246 | MarkupSafe = ">=2.0" 247 | 248 | [package.extras] 249 | i18n = ["Babel (>=2.7)"] 250 | 251 | [[package]] 252 | name = "junit-xml" 253 | version = "1.9" 254 | description = "Creates JUnit XML test result documents that can be read by tools such as Jenkins" 255 | optional = false 256 | python-versions = "*" 257 | files = [ 258 | {file = "junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f"}, 259 | {file = "junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"}, 260 | ] 261 | 262 | [package.dependencies] 263 | six = "*" 264 | 265 | [[package]] 266 | name = "markupsafe" 267 | version = "2.1.5" 268 | description = "Safely add untrusted strings to HTML/XML markup." 269 | optional = false 270 | python-versions = ">=3.7" 271 | files = [ 272 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, 273 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, 274 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, 275 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, 276 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, 277 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, 278 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, 279 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, 280 | {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, 281 | {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, 282 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, 283 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, 284 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, 285 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, 286 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, 287 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, 288 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, 289 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, 290 | {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, 291 | {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, 292 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, 293 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, 294 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, 295 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, 296 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, 297 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, 298 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, 299 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, 300 | {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, 301 | {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, 302 | {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, 303 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, 304 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, 305 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, 306 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, 307 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, 308 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, 309 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, 310 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, 311 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, 312 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, 313 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, 314 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, 315 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, 316 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, 317 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, 318 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, 319 | {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, 320 | {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, 321 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, 322 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, 323 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, 324 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, 325 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, 326 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, 327 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, 328 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, 329 | {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, 330 | {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, 331 | {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, 332 | ] 333 | 334 | [[package]] 335 | name = "mccabe" 336 | version = "0.6.1" 337 | description = "McCabe checker, plugin for flake8" 338 | optional = false 339 | python-versions = "*" 340 | files = [ 341 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 342 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 343 | ] 344 | 345 | [[package]] 346 | name = "packageurl-python" 347 | version = "0.16.0" 348 | description = "A purl aka. Package URL parser and builder" 349 | optional = false 350 | python-versions = ">=3.8" 351 | files = [ 352 | {file = "packageurl_python-0.16.0-py3-none-any.whl", hash = "sha256:5c3872638b177b0f1cf01c3673017b7b27ebee485693ae12a8bed70fa7fa7c35"}, 353 | {file = "packageurl_python-0.16.0.tar.gz", hash = "sha256:69e3bf8a3932fe9c2400f56aaeb9f86911ecee2f9398dbe1b58ec34340be365d"}, 354 | ] 355 | 356 | [package.extras] 357 | build = ["setuptools", "wheel"] 358 | lint = ["black", "isort", "mypy"] 359 | sqlalchemy = ["sqlalchemy (>=2.0.0)"] 360 | test = ["pytest"] 361 | 362 | [[package]] 363 | name = "packaging" 364 | version = "24.2" 365 | description = "Core utilities for Python packages" 366 | optional = false 367 | python-versions = ">=3.8" 368 | files = [ 369 | {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, 370 | {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, 371 | ] 372 | 373 | [[package]] 374 | name = "pip-requirements-parser" 375 | version = "32.0.1" 376 | description = "pip requirements parser - a mostly correct pip requirements parsing library because it uses pip's own code." 377 | optional = false 378 | python-versions = ">=3.6.0" 379 | files = [ 380 | {file = "pip-requirements-parser-32.0.1.tar.gz", hash = "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3"}, 381 | {file = "pip_requirements_parser-32.0.1-py3-none-any.whl", hash = "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526"}, 382 | ] 383 | 384 | [package.dependencies] 385 | packaging = "*" 386 | pyparsing = "*" 387 | 388 | [package.extras] 389 | docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)"] 390 | testing = ["aboutcode-toolkit (>=6.0.0)", "black", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] 391 | 392 | [[package]] 393 | name = "pluggy" 394 | version = "1.5.0" 395 | description = "plugin and hook calling mechanisms for python" 396 | optional = false 397 | python-versions = ">=3.8" 398 | files = [ 399 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 400 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 401 | ] 402 | 403 | [package.extras] 404 | dev = ["pre-commit", "tox"] 405 | testing = ["pytest", "pytest-benchmark"] 406 | 407 | [[package]] 408 | name = "py" 409 | version = "1.11.0" 410 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 411 | optional = false 412 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 413 | files = [ 414 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 415 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 416 | ] 417 | 418 | [[package]] 419 | name = "pycodestyle" 420 | version = "2.8.0" 421 | description = "Python style guide checker" 422 | optional = false 423 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 424 | files = [ 425 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 426 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 427 | ] 428 | 429 | [[package]] 430 | name = "pyflakes" 431 | version = "2.4.0" 432 | description = "passive checker of Python programs" 433 | optional = false 434 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 435 | files = [ 436 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 437 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 438 | ] 439 | 440 | [[package]] 441 | name = "pygments" 442 | version = "2.18.0" 443 | description = "Pygments is a syntax highlighting package written in Python." 444 | optional = false 445 | python-versions = ">=3.8" 446 | files = [ 447 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 448 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 449 | ] 450 | 451 | [package.extras] 452 | windows-terminal = ["colorama (>=0.4.6)"] 453 | 454 | [[package]] 455 | name = "pyparsing" 456 | version = "3.1.4" 457 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 458 | optional = false 459 | python-versions = ">=3.6.8" 460 | files = [ 461 | {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, 462 | {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, 463 | ] 464 | 465 | [package.extras] 466 | diagrams = ["jinja2", "railroad-diagrams"] 467 | 468 | [[package]] 469 | name = "pytest" 470 | version = "7.4.4" 471 | description = "pytest: simple powerful testing with Python" 472 | optional = false 473 | python-versions = ">=3.7" 474 | files = [ 475 | {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, 476 | {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, 477 | ] 478 | 479 | [package.dependencies] 480 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 481 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 482 | iniconfig = "*" 483 | packaging = "*" 484 | pluggy = ">=0.12,<2.0" 485 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 486 | 487 | [package.extras] 488 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 489 | 490 | [[package]] 491 | name = "pytest-cov" 492 | version = "4.1.0" 493 | description = "Pytest plugin for measuring coverage." 494 | optional = false 495 | python-versions = ">=3.7" 496 | files = [ 497 | {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, 498 | {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, 499 | ] 500 | 501 | [package.dependencies] 502 | coverage = {version = ">=5.2.1", extras = ["toml"]} 503 | pytest = ">=4.6" 504 | 505 | [package.extras] 506 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 507 | 508 | [[package]] 509 | name = "pytest-html" 510 | version = "3.2.0" 511 | description = "pytest plugin for generating HTML reports" 512 | optional = false 513 | python-versions = ">=3.6" 514 | files = [ 515 | {file = "pytest-html-3.2.0.tar.gz", hash = "sha256:c4e2f4bb0bffc437f51ad2174a8a3e71df81bbc2f6894604e604af18fbe687c3"}, 516 | {file = "pytest_html-3.2.0-py3-none-any.whl", hash = "sha256:868c08564a68d8b2c26866f1e33178419bb35b1e127c33784a28622eb827f3f3"}, 517 | ] 518 | 519 | [package.dependencies] 520 | py = ">=1.8.2" 521 | pytest = ">=5.0,<6.0.0 || >6.0.0" 522 | pytest-metadata = "*" 523 | 524 | [[package]] 525 | name = "pytest-metadata" 526 | version = "3.1.1" 527 | description = "pytest plugin for test session metadata" 528 | optional = false 529 | python-versions = ">=3.8" 530 | files = [ 531 | {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, 532 | {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, 533 | ] 534 | 535 | [package.dependencies] 536 | pytest = ">=7.0.0" 537 | 538 | [package.extras] 539 | test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] 540 | 541 | [[package]] 542 | name = "seman" 543 | version = "0.1.3" 544 | description = "" 545 | optional = false 546 | python-versions = ">=3.8,<4.0" 547 | files = [ 548 | {file = "seman-0.1.3-py3-none-any.whl", hash = "sha256:7fd5cb309a172fcf8f09d554696aae984c75bc2766e7b286e4e1465fe39fe7bd"}, 549 | {file = "seman-0.1.3.tar.gz", hash = "sha256:e8843613a1bae99cceec4ca06ca8c67e3a0c944c4c7483a91d8c17e545875193"}, 550 | ] 551 | 552 | [package.dependencies] 553 | cyclonedx-bom = ">=3.11.0,<4.0.0" 554 | fire = ">=0.5.0,<0.6.0" 555 | toml = ">=0.10.2,<0.11.0" 556 | 557 | [[package]] 558 | name = "setuptools" 559 | version = "75.3.0" 560 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 561 | optional = false 562 | python-versions = ">=3.8" 563 | files = [ 564 | {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, 565 | {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, 566 | ] 567 | 568 | [package.extras] 569 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] 570 | core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] 571 | cover = ["pytest-cov"] 572 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 573 | enabler = ["pytest-enabler (>=2.2)"] 574 | test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] 575 | type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] 576 | 577 | [[package]] 578 | name = "six" 579 | version = "1.17.0" 580 | description = "Python 2 and 3 compatibility utilities" 581 | optional = false 582 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 583 | files = [ 584 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, 585 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, 586 | ] 587 | 588 | [[package]] 589 | name = "sortedcontainers" 590 | version = "2.4.0" 591 | description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" 592 | optional = false 593 | python-versions = "*" 594 | files = [ 595 | {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, 596 | {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, 597 | ] 598 | 599 | [[package]] 600 | name = "termcolor" 601 | version = "2.4.0" 602 | description = "ANSI color formatting for output in terminal" 603 | optional = false 604 | python-versions = ">=3.8" 605 | files = [ 606 | {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, 607 | {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, 608 | ] 609 | 610 | [package.extras] 611 | tests = ["pytest", "pytest-cov"] 612 | 613 | [[package]] 614 | name = "toml" 615 | version = "0.10.2" 616 | description = "Python Library for Tom's Obvious, Minimal Language" 617 | optional = false 618 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 619 | files = [ 620 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 621 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 622 | ] 623 | 624 | [[package]] 625 | name = "tomli" 626 | version = "2.2.1" 627 | description = "A lil' TOML parser" 628 | optional = false 629 | python-versions = ">=3.8" 630 | files = [ 631 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 632 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 633 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 634 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 635 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 636 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 637 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 638 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 639 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 640 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 641 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 642 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 643 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 644 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 645 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 646 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 647 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 648 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 649 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 650 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 651 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 652 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 653 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 654 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 655 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 656 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 657 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 658 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 659 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 660 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 661 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 662 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 663 | ] 664 | 665 | [[package]] 666 | name = "urllib3" 667 | version = "2.2.3" 668 | description = "HTTP library with thread-safe connection pooling, file post, and more." 669 | optional = false 670 | python-versions = ">=3.8" 671 | files = [ 672 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 673 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 674 | ] 675 | 676 | [package.extras] 677 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 678 | h2 = ["h2 (>=4,<5)"] 679 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 680 | zstd = ["zstandard (>=0.18.0)"] 681 | 682 | [metadata] 683 | lock-version = "2.0" 684 | python-versions = "^3.8" 685 | content-hash = "c7519a41e1f642d1a3cce342ae46b39facb4c36a88df3c94ab52b4f6fc4e7b46" 686 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | authors = ["kannkyo <15080890+kannkyo@users.noreply.github.com>"] 3 | classifiers = [ 4 | "Topic :: Software Development :: Libraries :: Python Modules", 5 | "Topic :: Security", 6 | ] 7 | description = "EPSS API Python Client" 8 | documentation = "https://github.com/kannkyo/epss-api" 9 | homepage = "https://github.com/kannkyo/epss-api" 10 | include = ["sbom.xml"] 11 | keywords = ["EPSS", "security", "vulnerability"] 12 | license = "MIT" 13 | name = "epss-api" 14 | readme = "README.rst" 15 | repository = "https://github.com/kannkyo/epss-api" 16 | version = "1.3.5" 17 | 18 | [tool.poetry.dependencies] 19 | python = "^3.8" 20 | urllib3 = ">=1.26.13,<3.0.0" 21 | 22 | [tool.poetry.group.dev.dependencies] 23 | flake8 = "^4.0.1" 24 | flake8-formatter-junit-xml = "^0.0.6" 25 | flake8-html = "^0.4.3" 26 | pytest = "^7.2.2" 27 | pytest-cov = "^4.0.0" 28 | pytest-html = "^3.2.0" 29 | seman = "^0.1.3" 30 | 31 | [build-system] 32 | build-backend = "poetry.core.masonry.api" 33 | requires = ["poetry-core"] 34 | 35 | [tool.pytest.ini_options] 36 | addopts = [ 37 | "--cov=src", 38 | "--cov-report=term-missing", 39 | "--cov-report=html", 40 | "--cov-report=xml", 41 | "--junitxml=report.xml", 42 | "--verbose", 43 | "--html=report.html", 44 | "--self-contained-html", 45 | ] 46 | testpaths = [ 47 | "tests", 48 | ] 49 | -------------------------------------------------------------------------------- /src/epss_api/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.0.0" 2 | 3 | from epss_api.epss import EPSS # noqa: F401 4 | from epss_api.epss import Score # noqa: F401 5 | -------------------------------------------------------------------------------- /src/epss_api/epss.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import csv 4 | from bisect import bisect_left, bisect_right 5 | from gzip import GzipFile 6 | from urllib.request import urlopen 7 | 8 | 9 | class Score(object): 10 | """ EPSS Score Object""" 11 | 12 | def __init__(self, cve: str, epss: str, percentile: str): 13 | """Initialize EPSS Score Object 14 | 15 | Args: 16 | cve (str): CVE-yyyy-n 17 | epss (str): [0-1] 18 | percentile (str): [0-1] 19 | """ 20 | self.cve = cve 21 | self.epss = float(epss) 22 | self.percentile = float(percentile) 23 | 24 | def __str__(self): 25 | return f"{{'cve': '{self.cve}', 'epss': {self.epss}, " \ 26 | + "'percentile': {self.percentile}}}" 27 | 28 | 29 | class EPSS(object): 30 | def __init__(self) -> None: 31 | 32 | url = 'https://epss.cyentia.com/epss_scores-current.csv.gz' 33 | 34 | with urlopen(url) as res: 35 | dec = GzipFile(fileobj=res) 36 | epss_scores_str: str = dec.read().decode("utf-8") 37 | epss_scores_list = epss_scores_str.split('\n') 38 | 39 | self._download = epss_scores_list 40 | 41 | scores = [row for row in csv.DictReader(self._download[1:])] 42 | 43 | self._byCVE = {row['cve']: 44 | Score(row['cve'], row['epss'], row['percentile']) 45 | for row in scores} 46 | 47 | self._sortedScoresByPercentile = sorted(self._byCVE.values(), 48 | key=lambda x: x.percentile) 49 | 50 | self._sortedScoresByEpss = sorted(self._byCVE.values(), 51 | key=lambda x: x.epss) 52 | 53 | def scores(self) -> list[Score]: 54 | """Get all CVE's EPSS scores (downloaded data is cached in memory) 55 | 56 | Example 57 | 58 | [ 59 | {'cve': 'CVE-2022-39952', 'epss': '0.09029', 'percentile': '0.94031'}, 60 | {'cve': 'CVE-2023-0669', 'epss': '0.78437', 'percentile': '0.99452'}, 61 | ... 62 | ] 63 | 64 | Returns: 65 | list[Score]: EPSS score's csv list 66 | """ 67 | return list(self._sortedScoresByEpss) 68 | 69 | def score(self, cve_id: str) -> Score: 70 | """Get EPSS score and percentile 71 | 72 | Example 73 | {'cve': 'CVE-2022-39952', 'epss': 0.0095, 'percentile': 0.32069} 74 | 75 | Args: 76 | cve_id (str): CVE ID (CVE-nnnn) 77 | 78 | Returns: 79 | Score | None: EPSS score percentile 80 | """ 81 | 82 | return self._byCVE.get(cve_id, None) 83 | 84 | def epss(self, cve_id: str) -> float: 85 | """Get EPSS score 86 | 87 | Args: 88 | cve_id (str): CVE ID (CVE-nnnn) 89 | 90 | Returns: 91 | float | None: EPSS score (0.0-1.0) 92 | """ 93 | 94 | score = self._byCVE.get(cve_id, None) 95 | if score is None: 96 | return None 97 | else: 98 | return score.epss 99 | 100 | def percentile(self, cve_id: str) -> float: 101 | """Get EPSS percentile 102 | 103 | Args: 104 | cve_id (str): CVE ID (CVE-nnnn) 105 | 106 | Returns: 107 | float | None: EPSS percentile (0.0-1.0) 108 | """ 109 | score = self._byCVE.get(cve_id, None) 110 | if score is None: 111 | return None 112 | else: 113 | return score.percentile 114 | 115 | def epss_gt(self, min: float) -> list[Score]: 116 | """Get CVEs with EPSS score greater than the parameter 117 | 118 | Args: 119 | min (float): limit of EPSS score 120 | 121 | Returns: 122 | list[Score] | None: EPSS score object list 123 | """ 124 | i = bisect_right(self._sortedScoresByEpss, min, 125 | key=lambda x: x.epss) 126 | 127 | return list(self._sortedScoresByEpss[i:]) 128 | 129 | def percentile_gt(self, min: float) -> list[Score]: 130 | """Get CVEs with percentile greater than the parameter 131 | 132 | Args: 133 | min (float): limit of percentile 134 | 135 | Returns: 136 | list[Score] | None: EPSS score object list 137 | """ 138 | i = bisect_right(self._sortedScoresByPercentile, min, 139 | key=lambda x: x.percentile) 140 | 141 | return list(self._sortedScoresByPercentile[i:]) 142 | 143 | def epss_ge(self, min: float) -> list[Score]: 144 | """Get CVEs with EPSS score greater than or equal to the parameter 145 | 146 | Args: 147 | min (float): limit of EPSS score 148 | 149 | Returns: 150 | list[Score] | None: EPSS score object list 151 | """ 152 | i = bisect_left(self._sortedScoresByEpss, min, 153 | key=lambda x: x.epss) 154 | 155 | return list(self._sortedScoresByEpss[i:]) 156 | 157 | def percentile_ge(self, min: float) -> list[Score]: 158 | """Get CVEs with percentile greater than or equal to the parameter 159 | 160 | Args: 161 | min (float): limit of percentile 162 | 163 | Returns: 164 | list[Score] | None: EPSS score object list 165 | """ 166 | i = bisect_left(self._sortedScoresByPercentile, min, 167 | key=lambda x: x.percentile) 168 | 169 | return list(self._sortedScoresByPercentile[i:]) 170 | 171 | def epss_lt(self, max: float) -> list[Score]: 172 | """Get CVEs with EPSS score lower than the parameter 173 | 174 | Args: 175 | max (float): limit of EPSS score 176 | 177 | Returns: 178 | list[Score] | []: EPSS score object list 179 | """ 180 | i = bisect_left(self._sortedScoresByEpss, max, 181 | key=lambda x: x.epss) 182 | 183 | return list(self._sortedScoresByEpss[:i]) 184 | 185 | def percentile_lt(self, max: float) -> list[Score]: 186 | """Get CVEs with percentile less than the parameter 187 | 188 | Args: 189 | max (float): limit of percentile 190 | 191 | Returns: 192 | list[Score] | []: EPSS score object list 193 | """ 194 | i = bisect_left(self._sortedScoresByPercentile, max, 195 | key=lambda x: x.percentile) 196 | 197 | return list(self._sortedScoresByPercentile[:i]) 198 | 199 | def epss_le(self, max: float) -> list[Score]: 200 | """Get CVEs with EPSS score less than or equal to the parameter 201 | 202 | Args: 203 | max (float): limit of EPSS score 204 | 205 | Returns: 206 | list[Score] | []: EPSS score object list 207 | """ 208 | i = bisect_right(self._sortedScoresByEpss, max, 209 | key=lambda x: x.epss) 210 | 211 | return list(self._sortedScoresByEpss[:i]) 212 | 213 | def percentile_le(self, max: float) -> list[Score]: 214 | """Get CVEs with percentile less than or equal to the parameter 215 | 216 | Args: 217 | max (float): limit of percentile 218 | 219 | Returns: 220 | list[Score] | []: EPSS score object list 221 | """ 222 | i = bisect_right(self._sortedScoresByPercentile, max, 223 | key=lambda x: x.percentile) 224 | 225 | return list(self._sortedScoresByPercentile[:i]) 226 | 227 | def csv(self) -> list[str]: 228 | """Get csv data containing all epss scores. 229 | 230 | Example 231 | #model_version:v2022.01.01,score_date:2023-03-03T00:00:00+0000 232 | cve,epss,percentile 233 | CVE-2014-6271,0.96235,0.99992 234 | CVE-2014-7169,0.83508,0.99607 235 | ... 236 | 237 | Returns: 238 | list[str]: csv data 239 | """ 240 | return self._download 241 | -------------------------------------------------------------------------------- /tests/epss_api/test_epss.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import abspath, dirname, join, pardir 3 | import pytest 4 | 5 | from epss_api.epss import EPSS, Score 6 | 7 | sys.path.append(abspath(join(dirname(__file__), pardir, pardir, "src"))) 8 | 9 | epss = EPSS() 10 | 11 | 12 | def test_scores(): 13 | value = epss.scores() 14 | assert len(value) >= 1000 15 | assert value[0].cve.startswith('CVE-') 16 | assert 0 <= value[0].epss <= 1 17 | assert 0 <= value[0].percentile <= 1 18 | 19 | 20 | def test_csv(): 21 | rows = epss.csv() 22 | assert rows[0].startswith('#model_version') 23 | assert rows[1] == 'cve,epss,percentile' 24 | assert len(rows[2:]) >= 1000 25 | 26 | 27 | @pytest.mark.parametrize("min", [-1, 0, 0.5, 1, 2]) 28 | def test_epss_gt(min): 29 | scores = epss.epss_gt(min) 30 | for s in scores: 31 | assert s.epss > min 32 | for s in list(set(scores) - set(epss.scores())): 33 | assert s.epss <= min 34 | 35 | 36 | @pytest.mark.parametrize("min", [-1, 0, 0.5, 1, 2]) 37 | def test_percentile_gt(min): 38 | scores = epss.percentile_gt(min) 39 | for s in scores: 40 | assert s.percentile > min 41 | for s in list(set(scores) - set(epss.scores())): 42 | assert s.percentile <= min 43 | 44 | 45 | @pytest.mark.parametrize("min", [-1, 0, 0.5, 1, 2]) 46 | def test_epss_ge(min): 47 | scores = epss.epss_ge(min) 48 | for s in scores: 49 | assert s.epss >= min 50 | for s in list(set(scores) - set(epss.scores())): 51 | assert s.epss < min 52 | 53 | 54 | @pytest.mark.parametrize("min", [-1, 0, 0.5, 1, 2]) 55 | def test_percentile_ge(min): 56 | scores = epss.percentile_ge(min) 57 | for s in scores: 58 | assert s.percentile >= min 59 | for s in list(set(scores) - set(epss.scores())): 60 | assert s.percentile < min 61 | 62 | 63 | @pytest.mark.parametrize("max", [-1, 0, 0.5, 1, 2]) 64 | def test_epss_lt(max): 65 | scores = epss.epss_lt(max) 66 | for s in scores: 67 | assert s.epss < max 68 | for s in list(set(scores) - set(epss.scores())): 69 | assert s.epss >= max 70 | 71 | 72 | @pytest.mark.parametrize("max", [-1, 0, 0.5, 1, 2]) 73 | def test_percentile_lt(max): 74 | scores = epss.percentile_lt(max) 75 | for s in scores: 76 | assert s.percentile < max 77 | for s in list(set(scores) - set(epss.scores())): 78 | assert s.percentile >= max 79 | 80 | 81 | @pytest.mark.parametrize("max", [-1, 0, 0.5, 1, 2]) 82 | def test_epss_le(max): 83 | scores = epss.epss_le(max) 84 | for s in scores: 85 | assert s.epss <= max 86 | for s in list(set(scores) - set(epss.scores())): 87 | assert s.epss > max 88 | 89 | 90 | @pytest.mark.parametrize("max", [-1, 0, 0.5, 1, 2]) 91 | def test_percentile_le(max): 92 | scores = epss.percentile_le(max) 93 | for s in scores: 94 | assert s.percentile <= max 95 | for s in list(set(scores) - set(epss.scores())): 96 | assert s.percentile > max 97 | 98 | 99 | def test_score(): 100 | score = epss.score(cve_id='CVE-2022-0669') 101 | assert score.cve.startswith('CVE-') 102 | assert 0 <= score.epss <= 1 103 | assert 0 <= score.percentile <= 1 104 | score = epss.score(cve_id='CVE-1000-123') 105 | assert score is None 106 | 107 | 108 | def test_epss(): 109 | score = epss.epss(cve_id='CVE-2022-0669') 110 | assert 0 <= score <= 1 111 | score = epss.epss(cve_id='CVE-1000-123') 112 | assert score is None 113 | 114 | 115 | def test_percentile(): 116 | score = epss.percentile(cve_id='CVE-2022-0669') 117 | assert 0 <= score <= 1 118 | score = epss.percentile(cve_id='CVE-1000-123') 119 | assert score is None 120 | 121 | 122 | def test_score_dict(): 123 | score = Score(cve='CVE-2022-39952', 124 | epss='0.09029', 125 | percentile='0.94031') 126 | dict_score = score.__dict__ 127 | assert 'cve' in dict_score.keys() 128 | assert 'epss' in dict_score.keys() 129 | assert 'percentile' in dict_score.keys() 130 | --------------------------------------------------------------------------------