From ea47029cef6980733d4c9eef90d3bf33d0c41290 Mon Sep 17 00:00:00 2001 From: Facundo Tuesca Date: Mon, 21 Oct 2024 19:28:01 +0200 Subject: [PATCH] Initial implementation --- .github/dependabot.yml | 20 ++ .github/workflows/docs.yml | 45 +++++ .github/workflows/lint.yml | 29 +++ .github/workflows/release.yml | 53 +++++ .github/workflows/tests.yml | 33 ++++ .gitignore | 8 + LICENSE | 177 +++++++++++++++++ Makefile | 82 ++++++++ README.md | 12 ++ pyproject.toml | 97 ++++++++++ src/pip_plugin_pep740/__init__.py | 7 + src/pip_plugin_pep740/_impl.py | 121 ++++++++++++ src/pip_plugin_pep740/py.typed | 0 test/__init__.py | 0 .../abi3info-2024.10.3-py3-none-any.whl | Bin 0 -> 19282 bytes ...info-2024.10.3-py3-none-any.whl.provenance | 1 + .../abi3info-2024.10.8-py3-none-any.whl | Bin 0 -> 19295 bytes ...info-2024.10.8-py3-none-any.whl.provenance | 1 + test/assets/abi3info-2024.10.8.tar.gz | Bin 0 -> 19901 bytes .../abi3info-2024.10.8.tar.gz.provenance | 1 + test/test_impl.py | 183 ++++++++++++++++++ test/test_init.py | 9 + 22 files changed, 879 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 src/pip_plugin_pep740/__init__.py create mode 100644 src/pip_plugin_pep740/_impl.py create mode 100644 src/pip_plugin_pep740/py.typed create mode 100644 test/__init__.py create mode 100644 test/assets/abi3info-2024.10.3-py3-none-any.whl create mode 100644 test/assets/abi3info-2024.10.3-py3-none-any.whl.provenance create mode 100644 test/assets/abi3info-2024.10.8-py3-none-any.whl create mode 100644 test/assets/abi3info-2024.10.8-py3-none-any.whl.provenance create mode 100644 test/assets/abi3info-2024.10.8.tar.gz create mode 100644 test/assets/abi3info-2024.10.8.tar.gz.provenance create mode 100644 test/test_impl.py create mode 100644 test/test_init.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..99d80f9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 + +updates: + - package-ecosystem: pip + directory: / + groups: + python: + patterns: + - "*" + schedule: + interval: daily + + - package-ecosystem: github-actions + directory: / + groups: + actions: + patterns: + - "*" + schedule: + interval: daily diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ca6c622 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,45 @@ +name: Deploy Documentation + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + + - name: setup + run: | + make dev INSTALL_EXTRA=doc + + - name: build docs + run: | + make doc + + - name: upload docs artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./html/ + + deploy: + needs: build + runs-on: ubuntu-latest + permissions: + # NOTE: Needed to push to the repository. + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..77eb058 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + + - name: install uv + run: > + curl --no-progress-meter --location --fail + --proto '=https' --tlsv1.2 + "https://astral.sh/uv/install.sh" + | sh + + - name: lint + run: make lint INSTALL_EXTRA=lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d9e86c8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +on: + release: + types: + - published + +name: release + +jobs: + build: + name: Build distributions + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + + - name: Build distributions + run: uv build + + - name: Upload distributions + uses: actions/upload-artifact@v4 + with: + name: distributions + path: dist/ + + publish: + name: Publish Python distributions to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/pip-plugin-pep740 + needs: [build] + permissions: + # Used to sign the release's artifacts with sigstore-python. + # Used to publish to PyPI with Trusted Publishing. + id-token: write + steps: + - name: Download distributions + uses: actions/download-artifact@v4 + with: + name: distributions + path: dist/ + + - name: Publish distributions + uses: pypa/gh-action-pypi-publish@release/v1 + with: + attestations: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..ad5aaa7 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,33 @@ +name: Unit tests + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + strategy: + matrix: + python: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + + - name: Install Python ${{ matrix.python }} + run: uv python install ${{ matrix.python }} + + - name: test + run: make test INSTALL_EXTRA=test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b90ba49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +env/ +pip-wheel-metadata/ +*.egg-info/ +__pycache__/ +.coverage* +.idea +html/ +dist/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + 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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..36193c2 --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +SHELL := /bin/bash + +PY_IMPORT = pip_plugin_pep740 + +ALL_PY_SRCS := $(shell find src -name '*.py') \ + $(shell find test -name '*.py') + +# Optionally overriden by the user, if they're using a virtual environment manager. +VENV ?= env + +# On Windows, venv scripts/shims are under `Scripts` instead of `bin`. +VENV_BIN := $(VENV)/bin +ifeq ($(OS),Windows_NT) + VENV_BIN := $(VENV)/Scripts +endif + +# Optionally overridden by the user in the `release` target. +BUMP_ARGS := + +# Optionally overridden by the user in the `test` target. +TESTS := + +# Optionally overridden by the user/CI, to limit the installation to a specific +# subset of development dependencies. +INSTALL_EXTRA := dev + +# If the user selects a specific test pattern to run, set `pytest` to fail fast +# and only run tests that match the pattern. +# Otherwise, run all tests and enable coverage assertions, since we expect +# complete test coverage. +ifneq ($(TESTS),) + TEST_ARGS := -x -k $(TESTS) + COV_ARGS := +else + TEST_ARGS := + COV_ARGS := --fail-under 100 +endif + +.PHONY: all +all: + @echo "Run my targets individually!" + +.PHONY: dev +dev: $(VENV)/pyvenv.cfg + +$(VENV)/pyvenv.cfg: pyproject.toml + uv venv $(VENV) + @. $(VENV_BIN)/activate && uv pip install -e '.[$(INSTALL_EXTRA)]' --prerelease=allow + +.PHONY: lint +lint: $(VENV)/pyvenv.cfg + . $(VENV_BIN)/activate && \ + ruff format --check && \ + ruff check && \ + mypy + . $(VENV_BIN)/activate && \ + interrogate -c pyproject.toml . + +.PHONY: reformat +reformat: + . $(VENV_BIN)/activate && \ + ruff format && \ + ruff check --fix + +.PHONY: test tests +test tests: $(VENV)/pyvenv.cfg + . $(VENV_BIN)/activate && \ + pytest --cov=$(PY_IMPORT) $(T) $(TEST_ARGS) && \ + python -m coverage report -m $(COV_ARGS) + +.PHONY: doc +doc: $(VENV)/pyvenv.cfg + . $(VENV_BIN)/activate && \ + pdoc -o html $(PY_IMPORT) + +.PHONY: package +package: $(VENV)/pyvenv.cfg + uvx --from build pyproject-build --installer uv + +.PHONY: edit +edit: + $(EDITOR) $(ALL_PY_SRCS) diff --git a/README.md b/README.md new file mode 100644 index 0000000..240f835 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Pip plugin pep740 + + +[![CI](https://github.com/facutuesca/pip-plugin-pep740/actions/workflows/tests.yml/badge.svg)](https://github.com/facutuesca/pip-plugin-pep740/actions/workflows/tests.yml) +[![PyPI version](https://badge.fury.io/py/pip-plugin-pep740.svg)](https://pypi.org/project/pip-plugin-pep740) +[![Packaging status](https://repology.org/badge/tiny-repos/python:pip-plugin-pep740.svg)](https://repology.org/project/python:pip-plugin-pep740/versions) + + +An implementation of a "dist-inspector" [pip](https://pypi.org/project/pip/) plugin +that verifies PEP-740 attestations before installing a package, and +aborts the installation if verification fails +(as discussed on the pip [issue tracker](https://github.com/pypa/pip/issues/12766)). diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1dbcdf2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,97 @@ +[project] +name = "pip-plugin-pep740" +dynamic = ["version"] +description = "A pip plugin that verifies PEP 740 attestations before package installation" +readme = "README.md" +license = { file = "LICENSE" } +authors = [ + { name = "Trail of Bits", email = "opensource@trailofbits.com" }, +] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", +] +dependencies = [ + "packaging", + # Change to >=0.0.13 once the PR is merged + "pypi-attestations @ git+https://github.com/trailofbits/pypi-attestations@refs/pull/64/head", + "rfc3986", + "sigstore~=3.3", +] +requires-python = ">=3.9" + +[tool.setuptools.dynamic] +version = { attr = "pip_plugin_pep740.__version__" } + +[project.entry-points."pip.plugins"] +pep740-distinspector = "pip_plugin_pep740" + +[project.optional-dependencies] +doc = ["pdoc"] +test = ["pytest", "pytest-cov", "pretend", "coverage[toml]", "requests-mock"] +lint = [ + # NOTE: ruff is under active development, so we pin conservatively here + # and let Dependabot periodically perform this update. + "ruff ~= 0.6.2", + "mypy >= 1.0", + "types-html5lib", + "types-requests", + "types-toml", + "interrogate", +] +dev = ["pip-plugin-pep740[doc,test,lint]", "twine", "build"] + + + +[project.urls] +Homepage = "https://pypi.org/project/pip-plugin-pep740" +Documentation = "https://facutuesca.github.io/pip-plugin-pep740/" +Issues = "https://github.com/facutuesca/pip-plugin-pep740/issues" +Source = "https://github.com/facutuesca/pip-plugin-pep740" + +[tool.coverage.run] +# don't attempt code coverage for the CLI entrypoints +omit = ["src/pip_plugin_pep740/_cli.py"] + +[tool.mypy] +mypy_path = "src" +packages = "pip_plugin_pep740" +allow_redefinition = true +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +ignore_missing_imports = true +no_implicit_optional = true +show_error_codes = true +sqlite_cache = true +strict_equality = true +warn_no_return = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + +[tool.ruff] +line-length = 100 +include = ["src/**/*.py", "test/**/*.py"] + +[tool.ruff.lint] +select = ["ALL"] +# D203 and D213 are incompatible with D211 and D212 respectively. +# COM812 and ISC001 can cause conflicts when using ruff as a formatter. +# See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules. +ignore = ["D203", "D213", "COM812", "ISC001"] + +[tool.ruff.lint.per-file-ignores] + +"test/**/*.py" = [ + "D", # no docstrings in tests + "S101", # asserts are expected in tests +] +[tool.interrogate] +# don't enforce documentation coverage for packaging, testing, the virtual +# environment, or the CLI (which is documented separately). +exclude = ["env", "test", "src/pip_plugin_pep740/_cli.py"] +ignore-semiprivate = true +fail-under = 100 diff --git a/src/pip_plugin_pep740/__init__.py b/src/pip_plugin_pep740/__init__.py new file mode 100644 index 0000000..5e5e43c --- /dev/null +++ b/src/pip_plugin_pep740/__init__.py @@ -0,0 +1,7 @@ +"""The `pip-plugin-pep740` APIs.""" + +__version__ = "0.0.1" + +from ._impl import plugin_type, pre_download, pre_extract + +__all__ = ["plugin_type", "pre_extract", "pre_download"] diff --git a/src/pip_plugin_pep740/_impl.py b/src/pip_plugin_pep740/_impl.py new file mode 100644 index 0000000..c8181b7 --- /dev/null +++ b/src/pip_plugin_pep740/_impl.py @@ -0,0 +1,121 @@ +"""The `pip-plugin-pep740` implementation.""" + +from __future__ import annotations + +from json import JSONDecodeError +from typing import TYPE_CHECKING, Literal + +import requests +from packaging.utils import parse_sdist_filename, parse_wheel_filename +from pydantic import ValidationError +from pypi_attestations import ( + AttestationBundle, + AttestationError, + Distribution, + GitHubPublisher, + GitLabPublisher, + Provenance, +) +from rfc3986 import builder +from sigstore.verify import Verifier, policy + +if TYPE_CHECKING: + from pathlib import Path # pragma: no cover + +PluginType = Literal["dist-inspector"] + + +def _get_provenance(filename: str) -> Provenance | None: + """Download the provenance for a given distribution.""" + if filename.endswith(".tar.gz"): + name, version = parse_sdist_filename(filename) + elif filename.endswith(".whl"): + name, version, _, _ = parse_wheel_filename(filename) + else: + # Unexpected file, ignore + return None + + # This currently only works when installing packages from PyPI + # In order to make it general, we need to get the provenance URL from the index API instead + # of hardcoding the URL. This can be done once + # https://github.com/pypi/warehouse/pull/16801 is merged + provenance_url = ( + builder.URIBuilder() + .add_scheme("https") + .add_host("pypi.org") + .add_path(f"integrity/{name}/{version}/{filename}/provenance") + .geturl() + ) + try: + r = requests.get(url=provenance_url, params={"Accept": "application/json"}, timeout=5) + r.raise_for_status() + except requests.HTTPError as e: + # If there is no provenance available, continue + if e.response.status_code == requests.codes.not_found: + return None + raise ValueError(e) from e + except requests.RequestException as e: + msg = f"Error downloading provenance file: {e}" + raise ValueError(msg) from e + + try: + return Provenance.model_validate(r.json()) + except ValidationError as e: + msg = f"Invalid provenance: {e}" + raise ValueError(msg) from e + except JSONDecodeError as e: + msg = f"Invalid provenance JSON: {e}" + raise ValueError(msg) from e + + +def _get_verification_policy(bundle: AttestationBundle) -> policy.VerificationPolicy: + """Construct a verification policy from the Trusted Publisher in the bundle.""" + publisher = bundle.publisher + if isinstance(publisher, GitHubPublisher): + issuer = "https://token.actions.githubusercontent.com" + repository = f"https://github.com/{publisher.repository}" + elif isinstance(publisher, GitLabPublisher): + issuer = "https://gitlab.com" + repository = f"https://gitlab.com/{publisher.repository}" + + return policy.AllOf( + [ + policy.OIDCIssuerV2(issuer), + policy.OIDCSourceRepositoryURI(repository), + ] + ) + + +def plugin_type() -> PluginType: + """Return the plugin type.""" + return "dist-inspector" + + +def pre_download(url: str, filename: str, digest: str) -> None: # noqa: ARG001 + """Inspect the file about to be downloaded by pip. + + This hook is called right before pip downloads a distribution + file. It doesn't return anything, and it can only raise `ValueError` + to signal to pip that the operation should be aborted. + """ + provenance = _get_provenance(filename) + if not provenance: + return + distribution = Distribution(name=filename, digest=digest) + verifier = Verifier.production() + for bundle in provenance.attestation_bundles: + # Each bundle has their own trusted publisher information, so each + # needs its own verification policy. + policy = _get_verification_policy(bundle) + try: + for a in bundle.attestations: + a.verify(verifier=verifier, policy=policy, dist=distribution) + except AttestationError as e: + msg = f"Provenance failed verification: {e}" + raise ValueError(msg) from e + return + + +def pre_extract(dist: Path) -> None: # noqa: ARG001 + """Check before extraction.""" + return diff --git a/src/pip_plugin_pep740/py.typed b/src/pip_plugin_pep740/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/assets/abi3info-2024.10.3-py3-none-any.whl b/test/assets/abi3info-2024.10.3-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..ad5230eddaee2cd30ddca103a3076e686c59f1ab GIT binary patch literal 19282 zcmZ5{V{k29&~9wowrv|HJh77#pV+o-+u5;CY}>YN>*oEcZq@znubx_a&zf1?vwA)K zbhn}`C>RaT)TE32apn7O7fp5SSPjrjQ@>78jfo<&eI&pkbCu z^}fY$nth%!JKq_F&{=;ZRrC|-d+vae$I74^`lM^-^qwKVLTYA{AY#V5lWSV(SCWA3 zzIayI{w3$57Fwfa$Q&%lfZi{vS=4GNhTw`uzGcw0-!*lRt4BLu?L4T-p9<}$^ycNs zpi$E*9B0iD7s$c$qYpk_oY)Gc`QfUi*)7V-2qVDpRNWnG7jg`U9OJ10hc|l%QP=xQ z%+6W)JEuckHFUf!3`-sL^?qS$$J8`Sri%!3t$!~H!^q|u z<#pVmW|2cp2z77#%8eP}J(Iy7;;~?k63yr0n}Kd7$*Jl-TTz0Fy3h^oUa& zO-{D^vU^`~yZ&u?D|kQYQ|;zcwkB-?ocHCt;t@V|^=kS#u;hG^FC36-cAiv0`FYwu zj7a#^ka!;Y5A6(Dpsv!fMK1FDlrG^eoSM8&=UkFY-}Oor@G%@}z;!ZJO&PdTu$~!J z>RX;OxQ#(F@m->PKJS0B{k`?N@!EYiM2R`S;x_p}@*VgI( zHP6|4Wi9qx{b?M#%OL~lYbZ7B>#aD--+DD^xs`lIzv(Mmy|n)-s4`k?-({w8Lshp(=Yjmsz0fEa@?Iwb}w{if^{yq@U3`=Bl!AW zQR>oPWJ{;VU`-IuJ^n(0lm2qNZmwhZxP$54wIJO>OV_==^+vw3zx1wx4-V^$yAkFw zbWc4Uy^y+NYEOU8`DoT#?xXgIEYNYp)djj4rKMD&qpD>;5}3a;yLSU&Td#DP>$dTr z8si>(dQWHNvVX^zv8}!g=w*|+dpmwX**M`=^G&&%kyz}VEHgnCRGq}l&VO3RU_9uQ z#i^6DpE99oeY3rU-{Sk?^qu)wMJzVDjHuAc{glOdF+NwFI*Q+Az7)ab&)1$JpzseG08}72n@5r@Ypk4a1v=EKaDJK3o@~ynVkGRBO7td8_Bdjb=nV zUza0wTOQl0aH`1bf3_^kc*=A=S28wYAc?OL)Tdzneo4jOCl@p$-*Xh5lVu&!b-rRi zmtFK>{Pww?J|>acGuLI;N5m1XDOgB$^#{z2N9?&V;|42Rsudw8q(h5WFGpkQzrIn& zN5ai6$de~@#r(z)*dYUCi<7za+ii)nhN{8w_rE!g4v^L!r{mz{M&{a@=zz z%>yLx(oJ(iM*|S+?S#dgn6U-5ig%=ic;E)Y084ueVM z#PDk-r^8B2Nt!N_BxU)OK%-Ue5(MFM;?d;jV1!l0q8u2UvkY8Zkm57GvIrio++ToF z8VaNy<|-AHl&>St_6|R=mzW;=W-XuG+1)w)o6Nn!8|zcg)HO07FsgP`Ngh=2=_FZW z6dhHjHYAkT3aB3I?{G*o=^#!lOyuL!-ZShG5@5$6_TZJaWho z#kIOjp)y=>(hq{R!{Ih&9P>vWYn5jQNzn$a2b!BNc_zutDxl4{OsYU7E8S_qr`k_@ zlq@5dB*&W&@Y0VMF^@YvDJZ?dKXcv^r4Q6C26D&4aX*XFB=G?C*b?hWvKH-9lwrV;B9Lgs#NA|$JeYlAg97d#- z|ND&`c=#I;E1AR%XhMQG;VhCa8p|qfWGWvWmoJnLyr%mB%%j35_Pi}}9(=st(?EO| znoMSiSREPUJ)KHL`6gPp5{smY^>*aXL&vEsc>)9Gqfs(9f(9(wqnoav|E1~0bPs&uxC|@Y#zv#r z#ueC3PBW7vC|Ngj3S?GHLsP^knRhy|Srvr)U{=R|O+rfQL!!v`_%VQUpq5|whSM#D zH%_tncxDOW5hT`uv(#sT!?mn-Cj&PQe_>fE`bx1e!)0vnk1t;Nps{5{H^NEhI!st3 z%;&M0i?l4rM1XxC@t8!!i+X8jBV-h6gxX@rQLzIV@NV{%!UXE6PC~lTMI2N)dR)Zr z{dQ^Fn$tI8Rh;+mG76C;ljGiKiN-Jz1=tFT7V1PaICo5u-&8N@Hpzv!=gP2CN3_37 zAvakD#0xx_Ta*GgoU-U-dhb4D@dQyLbP#EQCz)slS#xqRNXGIj^!W^eBBb0GL<*9A z!(wW%+fV7k+6P3wcidI5JK zgmQAm4YO$PRjV~Lm5(y73j>@5u>+)3o}8PDR087m;|P%{z}$MM&697CY_A%rg$f&Ci|B17mVamq%3t8Hu_*4 zdp{If#qZUCDa3j*bXvhlywr=toxm5 ztH|0MBIm(7PjBQ4)-5Hsw&6VO^+Sao-Ox_Eq72Ln`JhyAH(wKdlFo~$-nUFWF0IMC z%QK#W0>NVp*BGn+nWZ?IOfFQ#Zt#QJUh*#;zXj+xB>b6yqm7PwZ$IS%TOg^!msLjIa(YCv}a zQ{%cga2a9tInKKC27KHAbaV3GC6X`V=@RrdnUQ^RzeHO$6%Bt8^ob2dF_FB#F|pG4 zE=;o`N(H>JNj!{EQ{V-YviJ)L5@)0`QcK;juvO&6J5-AzP8GF5s1mhNs09X% z3_xE2j|Tj~5ef3xE`H~wELqZLUwsXp3sE$xcy(x65&}^^&4-gcX1F4P*U&#uu`-vb z{dPPTv}{!IvQSrqdd7HVj5p%B|MbAcvl3n~vroGiaL}W|DjJr=pn(RgFyCMl)<}Uj zJU)?+EPL=KaZ_5)7+5k2V2BqxMCVI%z!p*aQEHYAJr)0fNt9I#r4$ zGCp>bNqG7`3;-4fKyJ4WP8OgB$@{k$K{Wz`--KOr$d!P9adJi~+5u~S7!M*@0xvsh zG6!i<&42S5I7QsV$|E3UYw7b<`;{iNN;x?kcKKl0P#~3Skt~v8_Vf>T*g0hszR(yE zO9GH9**v7P-g;$7Q^lrQ^i&*9?=SS|lwe3O<8&Si!W`lyYOmk6q#QPxt#INYqjYX* z+(E85kA_Jxvv1H*Z$VU-hKewU7*e8y92PB@r^!JPwA@KYogd@HPLeCpWvV}9175e5 z=Bdar@tO&c#S0Qa6#&hSP=hmPF&NDz%(!(`R-JlxyeW~ANo&1*1*iiS zsV$YlKFP))w2O2_`37^DoIdmMI@QG+UR^0Ldx7NXaV?-g*>DAo60{3A`8 z!id^B#-l*eeO*RK*!5o4m#adY!)LNs_~LfJO5(dd{sEe zGCe16^H`HgJOk7d>+2WK?z}UCj@22^Z26-G7@cM%o>pBIXi8r2GJ?`$MRkYhD8NTU z>k>9+8k94RmIY0L_&y@UnGEVn$i#ot-n`P4k}f;c8vUj21@yJ%ovIy zD-h)age*=M|C}_2gzEsSgZ+~Va+sMEyTIeAYsm5W&9o3jrrert+;b>ygHgWY993>! zf-G~FCX1YN+mQ*{lJY>FFfKo9GASmbDK@@zw%cT(Bc!Zaxnxm+aLOBoswTmnck<9x z3wO@XRygu_vn7fdr-I&mT9hqH>ymy@sETv@1Kv+mq99awPL=2sc~pKJ&IaI7VX7R5Av}6|@S~dmLlLsn&vh1rLI9;h%6D&?0#__Q*G}}b| zbdv}x1&Cn(SOAD_wb%y`2pJqBMG$v-(|g{Vo}-a>!r_6`QGd%}ZtqwNUgF`@1yoeC z_Xz~aqm}ESg!a_j+hEigOo_qaM&0Dk%3$>1pJr3Z@JDgW6Z{<37G!>)fdXtYqr~HJ z4(0x0)SbOHKcegePiOpn_uQfNa4MZE^=@r`4wQz`cXfB3{o^{I)znSrJ))Y z+zWn#*3kH8NUw+o^lmM{#Uf`1Y?QAkb&C2NkROJ*DDBL|$r&Z4gkc!NTYVZ?v}h6xem^m2 z0HxDdyJT&Cx+pEgr5*Q=myfxLr#;k8NPv)5>3+=e-3zR^`a|c}Fo9bO9KKn`%vy$g7~2 zcL6QM`0yDiogk%TiqnaX%3Ku*`P=YRdO+@*wgw>$(cR7|2fD5dj)>ayEGQKF1c zxt6)kjX5wgQi3|uB%&gO()lsgOC=>`#4ki0RSha*W%U_v#40Qn`Y2nhp92=p2_q0) zG`~rnv-2MT?6tp*JVx@4De5SLnjuhLZvvM0QVG7%a_?*hxq@M!V zn-Dh9Z|%lTu&MGLm4@4;49}7+QiO;6&jrmAI5iB<;4KpPHDUMviK?*sgJw~@pTVEs z-n>~nc+c zJf<)=C>~wM-!3a}wj}O4>wRb|I`cA1@F*KHl@2^M-GKQ==24}}Gn`91XQ2#b9ra1d zt*&!M(PfpKb$==ET5?(WFLT@*{6M8Y!GA%i${iNv`sDs#Ho*6l+15p_LKtrRx(rI7 z$!*XX*e;}~1C<8w8Y-*{$I=;7OQJuABnkwauim^1=6kG%FVqWu`O*I+Rv!O~y0nCt z19Yv+e(<^l+71SsFEJ(5flHQ!+tX!ciqk-C9c_dcECyZ|p8}Jr1`$~9{eTfwZ)UU~ zIHXg9CnD^`b~_@@8AurhFePcduva)QS_Wc4{ZAV{&UvQn+r)GmY;5+nK)!!s? z@13w5QoQU_x>i1@I&TMPD~;6=QNVlNkB?BZ@GrI~QIdfv!p4L^D%NVfr;Evg7B8nv zME~N{BBZHfGi_V5tD5N$eyZF@jwCtWYBpW3(+Nx4WsAA^`Sb^_f2;lMYv#~Aud;!; zvh6)QAraNiWMg)Ou-3RaMA?eyl=MkDFX828z&clS{cON+>y|1wB_yw3ko0SJxKKKm zEeJ9|mpL8wCY?rrO=J|hx)@!uoLf$n&kjxQ$F1d@fsWaKMA0et!d3J!{rGG#w^(l0 z;i;d{-V<33g<~5n5isOP%(}&f@Rgns8GhMFaEsK9Rx0IMeCJ0U%<|PqJlN0^mCV%aTU0m1! z#57-ra+M=6ulz^`l%_u%}U|-o{N5%JH=>dHd?=;GCMX?o2A?bKI-H}8Y_wQM4g5pV@+L= zn?=%CdW-;L(3tG5YjFEAfu{ zu*_HFx_pjQbSTHX(ayZGY&>%+nKx<$Lr{6Wh2p0Ks0=(b-a+x(7LRb3l;KO6wo#)q zqb^2&JT+tfGJU3lacTsZlT?XYLY1&G-s_|HYX(qV4U^v-RdClJJVqM^dNH8%LLE`a z{AS}O4Hv4m^r+yJd1f>{{vDSK870rG@~!$A35lR>d?;F&sB;sxFRf+alz=_$oE0E> zLkX@xlzoQZ41iq;sf;u`?=3<#-L(yl<5R>Rg@n_D{P7{e3~D{Ad~#($a~^&|>d5A! z=RkAicV_XB?ysyNW%!b%tx0%{?AArY%Z~1;#l+5?J{c8utV97z8~3BdctiW;+Gh5Fo62zaVEXw|*o7`Kemm1STk;e_P6C^Z1F7Y`J)^!u;vSz{eeVzBt=sevr-#d zqoF@mk`f?0FJKqROc^1sV8)0}=^bM?)=$X9wkgGvvXl9Hmw_7MN#z+V3p`*r3*ph-LFD0>7DN+sid8A*ZG1nUstDW9ze$k|u;qq5e z!#WX)-NqmqG_NSo#--=KI8A-9OSQl_{0D>kmL0v37L}A$wpeUM8?mxf zdibZ|X+u9u!kOX#>?a`KTDqjcsrNm%h!Z4;AmaX<2J>E}rY-Kpy>mIXmFJ>h9$5QhfBImczoav@uKD{*5rH&yqHY1aQ6EGW1*{|hj0r-n4LF?B^*{oLeV`H*8kay3 zhvr4IGHSR~M$$MIjR_u%9CYHKk!m+Fg;utv60>LFm*;`?3h{|jw3)lbCj1dX3eJZL zt!JF~h9td#AZjQ!xV0P3He5LxAwUbUh}+&9f00rU_gC^0uap~_wgBQO`n-H7MR}P-8onqE7YVjV*YSdR-JBO1%TM$ z+zMo6gV6q=e5BvDL?Nm0q6vDoye>P`oA7yHW4)(5$LYIh?+~nTR#-4qcNdP8^9x?? zTLs_Ilv+638Ag=XW9^Ec+oX*Pb^aZTUEhHd2#(1UJ zHKEvWzoJ?_`oxQB`E?k>W_2FPl~>L@tU)&FdnSmO-@rvj;qvV~R$oMHnP6BP3#dL^ zJQLLG)~z6cagmj(U2D zX+!oNm5^};u(W)=V(NG%P$?>fvGelZZe3Oz{_4W%ss9=#*g^Pk2ysLs9BL>wnZD zhuF?ADcd5o-}xDta!R{&+f_qWK3bb8hM`9c%f^D!4V7bcSV8K)(`Yh`p2cza-hr!~ z!nqAPk$&h{E>MJTZY4CI8iX)mbAneCYAeoCcpt-C5$6JzbB&&D)Q>ckp?O!Pr#}gg z>wx|k2M9s01=!>3SI~^(Xuu;Xc0CmgLwh3YiUj))e<67z1IroNXZPFVSK3G%EE#9Q zm}|uKc`(`S=8zdHA^tz!LdfVPhX~7`XhXxsLyHj{TB3VW=5Me`(-8FX;s_a=T1`^* zMIFz93tsTC3z7#e{f!K~0#We!`5({x-#s60UnPPC>01s@M6dOgcm4_TXu;b@&S&>! zna5P5&!%XdgncO`IYJ`p$=!g4HY}}-w)o52LD@h0Ou_HqvcJ(YKe=KYx&or<-C4Db zqwFiqMOnf>Z4%h#bk0-UP9M__*<2rc^3f|Z#jQ0=sDCNo#`it(DKRQ@;sU#(G#Ejh zXE4&utNL#V`4X7~D7Shrokmx9JrQi#u$WjElDsC@*mr!UKMbXvZYKRQ{H~{lZe-sj zQChilVf(H!n5CQL74*aVbfae;++gpAmA!kqQ)Mz#z`U`p*Ci9-4RT!m5$({D^Q?tX zpCL1Hb(eTB4RlA9rQXT>cpn?7w!5w@O<9Euq?C;}0e(1J~A_}M!j za4MZU$m#w0X&WM4^oJW-NuVUg<&!=)y;xEt7ME~;qLhs%2D#%Sq+7iyQR`p#2#x|k zyreSAq)#VKPXq~Q@AeTTU*@R6N_$qXR}>sfp#$|7x$jhO?u!l?OuIv+{xmSb;A^a4 zk09=fVv)~s*iT9{-SD*uLUbCA#Hn9~7dHHD^L<{y{92Db;$yQPg{|Ivn;(v!HSA@x zKmM=knDskbDcx`W49&Y9L)OPnw`-fX8pK}lmY;ek)rm*f+HQKWv~T+H!&3QHrHTJr z)MB|gGd6Efhh6n0YxbQ7=&x0%KKZ(_N8Owx+`7#=Ew^-@vDWxwZt8D3EJChu9&RG^ zXM=zI>aYFJg$}oC7eY(A%Ju_vM$Q(qf=yyfIDLF#;qO+0O#EowwE7q3Ro>JKCEd}# z;q5&~<@ok$xk9Q^_Y?Geo}D$QiH}N~h_-N;9>-BUfU@QVE)dL$u6Sfk2#%rZu|+IR zdS~1vEFQCv#@vQKbKboTiYbzHOPGD@Go-!g=vnxn73{P`&vVlR7x(t= zm*#MR7a3k+q)bV zx@{pMkNw*|=&kPGhiWU+DlCaArMEwOhE`OO4@shznpSiT{!|{&QtXNA+WR1_VRazI zRup~J*#zK_&L4~-P16)HGi_bWX^d}x6Ch9j!m9gOY^&2xZXtg8kE?%l-91^kdvY^y zv^|L$ecszdH?(C`RJ~_irKC+jhM6a;GC{a;T|QEH?8w=)w6}z>e9XuZ#1b5dKzbO_hX410CZe#dkKE2 zodZBe*TRl@AF7U*9guQ&ut+Ys-KX5u(RT3i%ZAGKsXsO(b1e72i4dNc%~cJ^lsV}J z3c7sw{^cuf7prB^FwzU~5JmjIMVV#vfViMWRFb-4fQ4~?z=?vp=4n0h@&##%Q0?XOYYr1lHWRtuTNAGhIe6VD6O%*~4PeOWV zFbp>Q={R0wpUV80ojGFKoY?9#_jz7k6x0U42w#LJyB5jdJ{7F^rm2IqK&bqt_z@5b zfpwYl%jc4oE&lMeCi1v0r44$|9~}&sSAAJCf`lTEzC)ip2NXrXb~O_fUmEC}^IOJF z?6?Ya+*2Q%Q=Z>Csk9uI-g%V_bpRqo0T$lr>`#gImS7|hd5`DZ&RzmRQ*azJhU&2}r-{dx1FzxWB(Pphz zm=*H0bK%GQ_GO$L?YA1~1TT^{%G4eCxXpj3RU9w-?sna-&*o{`z5R`ze0mlb0bP3- zcT-+=+yG-cie~t0Q8-SjalAj?GPwNoEIDg6uwS-Za_~=pfxj9a@#d|J(#`tU);a0? z^V$PcO{|wsvvI3{fNr%$!?uwx zm)Du82mqf-g$%iocrjH1oorXnrlXo_bFa{?)T^Qx?XNCoYp=I%Ko0^$_X4p#2(E*( z97h-5_anU%=3LxL=T)rfqw2Ab#=cvQs~M^4-{{bOi_P(ew}6sq*WauF%NHXYr^UjJ z^SLzIxxn$*a`AgMH?LKfcAKbOdtY^%Wq2PC{`;n^6+PZdXV$|`WP6w5h;w14PMn39 z_%1MxgB*k@iBx~f@F?5BRm^O`l9W(=*GJ^s2KnjX&Kg^rXNT0l9aGVOD4}_J*rnK$s=?aB0le zmZz=mKt(XVhw!lJG+*b?evwUl7zJ0Xl!Jr7CDat;y94zNR2f;gpB4s4@C?tfF12RU z5{BtfV1bI*+~0JqkVFSHxMZ9Fwa6ro^(K2Jddd(Hxu%$%*BNnQWLefuNcNd=nEaW! z&{S2(nul>!dL}^yoM7{M8PYGfhXzro#)pzMkKb?x(5C}4<R6#lYd9>m_-FvN2lk3-> zL$L|H{}jce2Z}w{9xNJ*q^ZUW9=0A_ z)b!X@y>Ua~!|;ZxAdvdDNf`%dPN`#+o9)kx35U-7lqWS|XZE8`g5=L>l|m(FM~jfT z;7=!#=p{j3_kw$y8y6gEK6+PwM;e~_7pme9lmT4u>S!{IX&+4!1~mtmj>cb5EP|PF zIR1N$A@YdaWKlBpDUIM`W!exac+yF&R6XBAmQz@K?5J2-0XC4?{IDL`p$j+|YJPZ- z6Jt?Ds?md7z8r_L6j;$bmmx=V;vjN60wR!~Y|i10Njd{e{7R7E-al?3F%P>M4rub* z&7eV7JmNtwMX?CG1`KH3TC}4-z=|hHl+(m$j|47eQ(ZB$@JQj3nb6-O-cP+Hc?d$DI2 zUcikGickY3ADnI!PC`Oayy*I>O zEj6g<#jiI`FhNGV;L```mQIZQKhJ+1n5nWJ(0AAxM7o+5pF|@qtqiI^z2zrvPe zXj5TRZI@TRs;NQ2IGOu$bGp-KP3y(&91mZXg4U&+5a|?Dn~!tV;^$4 zo_#9{_S75-XQWKMWv8Jv@=uMZa`sJa9M|ZnSUk0aPW)h=x(u?@`|cl%|{a=zu)E9ryhZ11IE^;&Cd?|P$i?rqr0iZ8KY+OD-FZ1B4*6RwZ) z-nUFp&cw*?I+zgM5tGmk^-&$;&CvW$W$-FmS>$v_$}`H`L3To=&WoBXACwi4?oDOo zoQlFHyTi|eZeot}1bZK?Vd9fN=zya+ojS z(HMy$H^Q0&MZW&FKKSqDjg*ptnrc(yB;YHz6hEc8^*)+|A5mQJ?$jQ&yY3!;lVGkA z_$+65JLU!o-uyfE@zitF$T;;zemm-GeJU`u%@})kW=#KbZV;(KNMeUbey1?ryUH1i z3|r>x($}Ab^5x*k1+b(+{s(!>^1IveG~d74I6up)?e)L9qzm&u-f#ddc~6&a=oetV zzHtZlcM~&fcRNgt(CT+#4N=-Fwl(H`+nzNcbQR+mp}RIuu@o-*n4gn`uNxo5E$`pJ z|GRkw$cw~v4;lz4nGgsF<^OG7v9>ifv2y(BT$$9gc3kH``Ks3UH#DtIke!!a+k!mI zl4eMqFewS=ch1<7qaYO_qspfMh5)i|SpIsxVi1Z*pwqPR=1fhGGDcp}<$J$?O;JLN z{_4dyOP&(9(9DN-l2OjDCeq|M}W-qsC^sUQs! z2M)hc)?~2rZ~@g#>prK}DS&+ZG69!KZQ2(j9W>pb+`6JQB=B~^9G1{z^ell_LwD8q zK}m}{wb+TC_fo|>LMHo`B|@{2>T$!j9pi)LjSnYU??EEAcTGQt=hFZOnf3PY0iKcG z>u=Iwa2kE9?p(uYSV{wIy)lo`4Pt)(G@+Pv5c-(EiCJYI-VA51#K?_`9Cqt-5xS~9X1-#jXyWZ7A%+za9KP50&M$CM#->e`w(|b-1h#d z0Hv#s6_L}&gSfd-`y#uG^*AinXW&vclpKW6H#tvB$@~efGY`=2)AOAH)Ww*{R#g!( zn5>d(&df+WKJ^B2AcI6?=QS*L%zHoHhV7%wp*GeuVxDsbpAi@UjL#F&_M zNmz_bVQHfEOq1ONG~{z^>o2*Vx!99E1I$*^6`F4p_G~Z~+p2eCvH=@P-KMEmloPRd zy#Wg1MiMtc_P`|gIQUG5NR6$*dx0a;C-@Pw!{kateOm4QF;si$^^B0hpD>?bybydd z%;BXb?~_MrlO4=&vR2g;V$I4h({JHH=(n4eYz&bJHqPhz8_$hfB#fse_jQ80XEQ=Q zg;m~aKLwM>BPDa-NgAJ1QQ`v*rr#Cd18>SdE4CUw?yzqIkuZr<9>mO*msJuDAj+t# z`UR8mzz&vEO~h9m40jrQ3qHUeWT+H@8;*_3w(l@5GZU4YR}J2ecYw7(mki<=md2N0)kCXcR^tB`>{-VZ+8VV`T{d1f0c9l04^p{#9EJF1eoLxa>fzEHG?LlI|21il zg}G$Vexx)p+2;;EHI7T5$50=)aFVMHDxBU5qKxRM3FLc1+z;MsK~tn)^(e=jW$o0< z#pBEsoaTZKX$`1#38v5e9XWyWpR$!ts#{vK_D-KC+w`x(0qVA>4@Ogofpx$h*0PI`dsUpyn?(#_G-~n&a$cf-K4b3#9w!i?^hic zJ4Rhqfw2T<(A;g9OxJ5_k;xoEGlgzleqzL&dV9BrgBsK)%%_*5+kH-3jzZv$!*CCt zqVsafOM+6-8%^eM_B~!Z?xNxMwk$Ghw+`3<_~}5WkzU-ZJHdkYL3kCn<#)m}E16K4 z6E+JXvA!v$84v8Q&IXp^$cB|bHjffVZT$ROzSfIG(|w4G zu0z&-n;pIE?e0>far63mPjz{}>6XkhZlFYZu%_q44JHU~yUh&%3pgD`(Go_CNbAdFGBW#A-kFlO7xCl`Bv1 zu@vU!h`GmZ_KhAnWvA2lftPe4M${Ce2JU|rN@G~^C@{VDI)0zTt^;2>D-$E!%|=Uv z2{u*8s^ei75eQu{a}HFqPM_M_1CCTR^Qr*t_4j#Ys%vG$0rsqHj=WE8q$h{#n+?s$ zZnnJuhJ8=G6WjC8D#I-sOXK^YSzSF5kAPefxq!l}DVgP7A3jiI;(sDbC_`JpN#@a1 z27(Vw+#8gwxl}+{1O(#h;%!eX4+L8@yEiDmD#h4CokR6UPKA8O;DHlEZ~M8Y7~Nj= z?Zht036G%ffWb3|QOUH?qLgZ37-Op_Zdj=VB0mG;v~S^HN{V(-={EVh&zL;slMsR9 zO0#r9%YkM?9hxX{k?RlWV+;r|@@7YJQE3q5Fa+WI+4Lx7`Fak4;Jh8$75Ym4{AuIh z0u%}Npr?`}HV|apJZ@5KX*Q7%uZQE`^7IOy82s`y_!pAZ!RX$1`N#8Gw%LlNIwsmL zlY%B#b<)ykSJo`b+8Nr_>iWRi+WC)VC;Mf43eECzj{zQAt-dJT_MW0)ez4L*jdCnWHKbg(e2ucKgNvI-Y@<>qA z#>gTc6y$(Y@~?Qd)7{r|IrG$KJHT}rFQ{nQW-k&-(in(Ku@h*>R*3{R(2!4t3k>oZ z@Sk?3ZE{;0IA5D(w1YN!0hn8*GHU^5bXNlBo$*;%#d8{JV6st3kR z_A@P^T-Qt5$qWh<6Ic~M^Jm+0jOl#Xl zFR>vXm;9w>jT(IpeZoM8JTl>F-$t{9O;G53y&M=SCZ*FB1{>lJX%l zw@#F0AUqc;xv*ZUYa_!vYgX_C+A9zjh+RQJ=-IvtOJ@+UqyneB z{+T9U-|-|U{p4TtjoMX{dxHYQG-z7Gv z7I)9p;F}_eF845n%xh_({I@dGO5J1bg=H;=hrE)1RT{$A!T7?qK#GFovu*5Hq(oWA=jJxX}a>uD>2pop@w5H(3B){?JFIf0z zbaxSkY+i49BmTdo_CnIE(`*Rp9JF7|&O||L+RM*ZBD0p9Y$+(Owow*d0EDZW^o9+2 z!GC^gwiqWs$FUV#xnAmz{CE@IhGl;tC1$j<;t#xg`qu%W4Y+2V-+u)1zqEwXYaU`~ zf$M`r4r^GDq3PVhA4vkT7tXyZfO*lT9|-QyZ>$izjZ?&kd@yB_$PXe$<>F=h(O7(y};e3 zw1bJE;aTn;6ANnJL32e&cUMm}TzNo^jAch@=4e~0xZ z7}~-NV79Pg2VZ}nCg0XPQpExyNPL`=ZSU=M6(UPCY@D=~JZ&f6NpMlBuX z$xR70Ec1q49l2)$-Ok!22Gw6m@fhf%6?8dPChF!I$xRZ>hW0hm(akRfy}DDZT2n1k ziqupUi-{$(6aYI?WRt5w6~^J29)W>+xN4P;muC@AMIzjg=aX@|sM#t>eb~b(ZFC8*L~!SUSB0Z-RJIT#BVDssIozk%=n!`kCi@IgXs)P10aw;$kf1PyvEn$DL zY}V0|l*ay$x4gYe?N3yfo8uQgGnd%gh#6hboY!&x-p}sd z>-Sm@8u)Zct};ld8;|Jeh_LE`Wj}vippUI{$X3k9>+I0!nolnlCp-#I8=24BPXWgv zG@^K`PO%twq)Ly+4nK43CV?dQ5>_LAv!i~K}eu4q|WNI!>ngx|i8jJ^~u?NiT8 zO3FXl0Ti0|uFno%t?g9ZTcfOKlI2cU>O>mLm82%SApu ze&RwMa5^Tn!(ZsIZ3+#+;KBu%twt9Nq%B%2pFfP{6{xR^uUN5;JF`FA`L{PgE0gAd z+M->4{zj_~QDf9nFX^zY(34FKN4u}HSGR(SlKQ4nCcmtNXo-b2()e9?Jfghv;`l8b zXrVkDnuiMP(rkT@ReYJNq9>E%@cWZ89npu|d?9!2F?ZRsy1{zn&t8&t49X#B&!uyL zL3G?yC)-s>AG{`6vH`UwAbQ>KT#>g=wmW|YKCo(T3G4>D(QzO*kR&oZFW?ijfjsws z>7!3n<#EBh6lC38F`iQquZNBh*F=>lcpiH#j?41ea9A&J#J%Nn7rGcoc zc4t7m;l+LF-!qTAY;e7iCvVB0YV)iI@IgA@YlL8$m~4V(m5Z^jNYKpeMB;`y69rU^ z;=LBzl2k@Hh_USdV{stXLp!=p81l4D$_$5|#~T$Fmcy{%vIy+0B_X*M{oWcUYHzIm z!wLC%x4JR{e{@h&08O6CjOr$TCpNZvtU0@N`a9-V94;7|KibEsHoEAYez3QQjxbO= zAE8kYoL_~7A{TDP+`nAMSYY0bWjqLu%{02_Bw!O4qm*j{%>7*RO(&ywzT@E=)y?|0 zFfG1!X`U7wx}JG{t6q$OCY>*jPInr#^rNlE`_Yj;xYwicMH*VRd~(iDUmqi1Y*=q$ z{i;Rpa`KG!FH9oh<-TH$<+6>E5~EH$`=S+pbYT zjhkjD(Aw)UaB=UPRgFEr@7v<|`_i149=3pBneqTx8c2G#r5SFhZv{7q={3hhDgyw$ zE$9Rd83W2a?&slUg@x8AiUC`~7iep3K1nUF>YBUKf>5dihmawh6n$6TKrFUsmiq+Q z7_~Xu7<|S}j_}bPAr@+gAU~4p7)pQV#5dopcgxDy7NNTL@Uy_SFcq7cD=rIjFeut- zTHF&dsOZ%!GCVTEX(z+&@}4~#$a2=9E#pvV&>0bU@>X+m^7w<`Hi42aPh(r*4^t{o zL9#Xyzp-DVWKCj}nmZRHPeV7H^x0xEH{ts1>8CR2T&cjm8j1wYx2)@3l}`7qL2oC% z`#Oju?rIASkwjPNq7egOvql;Oav+WVs`@b!dSOfIuLyY+O`6mOVorIls zJ%7am0K|xP)~Ih2elhRaiP(8)x{ZiB@eAU=N2ojFcS`K-c)ad*{Qnc&zY_mR;cOE< zVY?9j)H^$QJN3yn@7r#?|EQIn$eneyjl>)8lHy-gW^K;F`7>xAt8uf6Q^oXW@849r BG@k$f literal 0 HcmV?d00001 diff --git a/test/assets/abi3info-2024.10.3-py3-none-any.whl.provenance b/test/assets/abi3info-2024.10.3-py3-none-any.whl.provenance new file mode 100644 index 0000000..a005486 --- /dev/null +++ b/test/assets/abi3info-2024.10.3-py3-none-any.whl.provenance @@ -0,0 +1 @@ +{"attestation_bundles":[{"attestations":[{"envelope":{"signature":"MEUCIEAYNKAdEGWZvAmtKTGXcZXGurccs4NW3Q7sFOgKNGA9AiEAmC/tResW9BKvNZAac1Deb3R4YZ1ULVCmZ3blPRRFL64=","statement":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiYWJpM2luZm8tMjAyNC4xMC4zLXB5My1ub25lLWFueS53aGwiLCJkaWdlc3QiOnsic2hhMjU2IjoiMDE1NzdlMjMxNDA5MzAxMTg1NGVjN2U4ODhkYzNmZDI1ZTE5MDMxYmMxMjFhOGMzOTQ0MzBkYTA2ZTFmZDRkYyJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2RvY3MucHlwaS5vcmcvYXR0ZXN0YXRpb25zL3B1Ymxpc2gvdjEiLCJwcmVkaWNhdGUiOm51bGx9"},"verification_material":{"certificate":"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","transparency_entries":[{"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZDQ0OGFmM2Q2MGM2ZWUxNDc2ZmJmNTYxMDMxMmUzYjU1NDdhMzE1NDE2MTQwNjRhOWQyMWRjN2Q0NDVjYTJlZSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjNiNmJlYjRmNmNhOGNlM2RmOGRmYzMyYzZlNzE5MjA1OTAwODZmOGI0ZTA5OTYzNzU3N2FhNGU0ZTM5YTI2M2IifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lFQVlOS0FkRUdXWnZBbXRLVEdYY1pYR3VyY2NzNE5XM1E3c0ZPZ0tOR0E5QWlFQW1DL3RSZXNXOUJLdk5aQWFjMURlYjNSNFlaMVVMVkNtWjNibFBSUkZMNjQ9IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VkNmVrTkRRbXhUWjBGM1NVSkJaMGxWUzNjNWNYSkpVRnB1UlZVclZYTmhNalpSZGprME9XTkdlVVJSZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmVFMUVRWHBOYWtFd1RVUkpOVmRvWTA1TmFsRjRUVVJCZWsxcVFURk5SRWsxVjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVlRka1JQZUUxSmJFWnhUVE5DVldOMFRpOTJUV1pXWm5GNmRuZ3dkMWhNVUhCeVMxQUtNamxHVVZnM05VbFJhRFZNWTJab2QxZEpkREF4U1M5SE5WWmtUbXRIUzJ0b2NIRXlNbmxNTDJGSVF6WjJUVzlSUjNGUFEwSllUWGRuWjFaMlRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVm5jMXBIQ2tOblRrMDNUSHBxWkVWcmQyaERRbVJCT1ZWb1ZuUkpkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMXAzV1VSV1VqQlNRVkZJTDBKR01IZFhORnBhWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBURE5rZG1JeVVubGtWMXB0WkhrNWFBcFpiV3Q2WVZjMWJXSjVPSFZhTW13d1lVaFdhVXd6WkhaamJYUnRZa2M1TTJONU9YbGFWM2hzV1ZoT2JFeHViSFJpUlVKNVdsZGFla3d6VW1oYU0wMTJDbVJxU1hkTmFsRjFUVlJCZFUxRVRYZFBVVmxMUzNkWlFrSkJSMFIyZWtGQ1FWRlJjbUZJVWpCalNFMDJUSGs1TUdJeWRHeGlhVFZvV1ROU2NHSXlOWG9LVEcxa2NHUkhhREZaYmxaNldsaEthbUl5TlRCYVZ6VXdURzFPZG1KVVFWWkNaMjl5UW1kRlJVRlpUeTlOUVVWRFFrRmtlVnBYZUd4WldFNXNUVVJaUndwRGFYTkhRVkZSUW1jM09IZEJVVTFGUzBSQ2JVNUVRbTFPUjFsNlRsUmthVTlVUlRCTlYxWnFUWHBSTTAxcVl6Tk5WMUV3V1RKSk5VMUVVbXhOYWtrMUNsbHFRbXRhVkVsM1JsRlpTMHQzV1VKQ1FVZEVkbnBCUWtKQlVVaGpiVlp6V2xkR2VscFVRV2RDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pLTTJJeU9Xc0tZMjVXYlZwdVkzWlpWMHB3VFRKc2RWcHRPSGRKZDFsTFMzZFpRa0pCUjBSMmVrRkNRbWRSVm1OdFZtMWplVGt3V1Zka2Vrd3pXWGxOUkVrd1RHcEZkd3BNYWtGNlRVUnpSME5wYzBkQlVWRkNaemM0ZDBGUlowVk1VWGR5WVVoU01HTklUVFpNZVRrd1lqSjBiR0pwTldoWk0xSndZakkxZWt4dFpIQmtSMmd4Q2xsdVZucGFXRXBxWWpJMU1GcFhOVEJNYlU1MllsUkNjRUpuYjNKQ1owVkZRVmxQTDAxQlJVcENSbk5OVjFkb01HUklRbnBQYVRoMldqSnNNR0ZJVm1rS1RHMU9kbUpUT1ROaU1qbHJZMjVXYlZwdVkzWlpWMHB3VFRKc2RWcHRPSFpNYldSd1pFZG9NVmxwT1ROaU0wcHlXbTE0ZG1RelRYWmpiVlp6V2xkR2VncGFVelUxWWxkNFFXTnRWbTFqZVRrd1dWZGtla3d6V1hsTlJFa3dUR3BGZDB4cVFYcE5SR2RIUTJselIwRlJVVUpuTnpoM1FWRnZSVXRuZDI5TlIxa3dDazFIV1RCYWFrMHhUakpKTlUxVVVYaGFWMDE2VGtSamVVNTZZM2hhUkZKcVdXcHJkMDVIVlhsTmFteHBUVWRTYkUxcVFXUkNaMjl5UW1kRlJVRlpUeThLVFVGRlRFSkJPRTFFVjJSd1pFZG9NVmxwTVc5aU0wNHdXbGRSZDA1UldVdExkMWxDUWtGSFJIWjZRVUpFUVZGdVJFTldiMlJJVW5kamVtOTJUREprY0Fwa1IyZ3hXV2sxYW1JeU1IWmtNamwyV2toS01WcHRXak5NTWtacFlWUk9jR0p0V25aTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZFd1JVdG5kMjlOUjFrd0NrMUhXVEJhYWsweFRqSkpOVTFVVVhoYVYwMTZUa1JqZVU1NlkzaGFSRkpxV1dwcmQwNUhWWGxOYW14cFRVZFNiRTFxUVd4Q1oyOXlRbWRGUlVGWlR5OEtUVUZGVDBKQ1kwMUdXRXBzV201TmRtUkhSbTVqZVRreVRXcEJlVTVETkhoTlF6UjNUWHBCV2tKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhOTlExUlZlZ3BQUkVrMVRYcEZOVTU2UVhOQ1oyOXlRbWRGUlVGWlR5OU5RVVZSUWtJMFRVaEhhREJrU0VKNlQyazRkbG95YkRCaFNGWnBURzFPZG1KVE9UTmlNamxyQ21OdVZtMWFibU4zUm5kWlMwdDNXVUpDUVVkRWRucEJRa1ZSVVVwRVFXTjZUVVJWTlUxcVJYZE5SMnRIUTJselIwRlJVVUpuTnpoM1FWSkpSVmQzZUZvS1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROa2RtSXlVbmxrVjFwdFpIazVhRmx0YTNwaFZ6VnRZbms0ZFZveWJEQmhTRlpwVEROa2RncGpiWFJ0WWtjNU0yTjVPWGxhVjNoc1dWaE9iRXh1YkhSaVJVSjVXbGRhZWt3elVtaGFNMDEyWkdwSmQwMXFVWFZOVkVGMVRVUk5kMDlCV1V0TGQxbENDa0pCUjBSMmVrRkNSWGRSY1VSRFozZGFhbEYzV21wU2JVMTZWVE5aYW10NFRrUkdiRmw2VFRCT2Vra3pUbnBHYTA1SFRtbFBWRUV3V2xSSmVVOVhTWGNLV2tkVmVVMUNZMGREYVhOSFFWRlJRbWMzT0hkQlVsRkZRMUYzU0dOdFZuTmFWMFo2V2xSQ1drSm5iM0pDWjBWRlFWbFBMMDFCUlZaQ1JYTk5VMWRvTUFwa1NFSjZUMms0ZGxveWJEQmhTRlpwVEcxT2RtSlRPVE5pTWpsclkyNVdiVnB1WTNaWlYwcHdUVEpzZFZwdE9IWlpWMDR3WVZjNWRXTjVPWGxrVnpWNkNreDZSWGhOVkZrMVRYcHJkMDVVU1hsTU1rWXdaRWRXZEdOSVVucE1la1YzUm1kWlMwdDNXVUpDUVVkRWRucEJRa1puVVVsRVFWcDNaRmRLYzJGWFRYY0taMWx2UjBOcGMwZEJVVkZDTVc1clEwSkJTVVZtUVZJMlFVaG5RV1JuUkdSUVZFSnhlSE5qVWsxdFRWcElhSGxhV25walEyOXJjR1YxVGpRNGNtWXJTQXBwYmt0QlRIbHVkV3BuUVVGQldrcFZTRTV0TkVGQlFVVkJkMEpJVFVWVlEwbFJRMHBLU1VaRkswWTJSWFpSSzI5b05sbHNiVFV4VG5KSmVubFJNbVJRQ2xSTU5XSlFkV1ZsU2xsTWF6aEJTV2RGZWxWS01GaG9lbVpCVG1kd2NqZE1RV2hyZGxSeEsyazNNRm8yTDFkdFpYQXJiaXRPY0VsckwxRjNkME5uV1VrS1MyOWFTWHBxTUVWQmQwMUVZVkZCZDFwblNYaEJUV0ZMZUM5dGJrSkhUelpqU0RFd2NtTkJSVWRZZDBSTGFqbFhaMlpzT1U5U2VHVnpTbmxEWldkUlRBcHJVWFZWVml0cGVEYzFTV0kxTlZwSWVEbEpjalpSU1hoQlNYRTFSa0k1YTB0NVIxaE5SRzlPTW5waFRYaFRTekExVEdJMVEwZEhhbmt6UlV4VFYzbDVDbE5rVUVKMVNUVlpWM1UyY1N0SUsyaHdia3g0YTBFd2RESlJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In1dfX0=","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCmNyYsPowG1JhsTdxqiIX3U/7Au6XaXYGUSOpqH4vm4wIhAKWKlfBqYEnAS4PmnpFU8urVnWRyX+RgYG/xC3R3IoBl"},"inclusionProof":{"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n14715023\nIyQHfWBCjwz8lnRQzUXashtTN7aPUC4oZXd4RLHvMWc=\n\n— rekor.sigstore.dev wNI9ajBFAiBPiiso077c0r3RNk2ZBBj5Uktbye/J5o+9w/++ta0uIAIhAKtaFqOdKFkpROfsNf2iFu+p56pKoHldBqOttJZrKd1p\n"},"hashes":["9ThPYz4yEosFImoMc7oyLeN1He3ieULNUMj5yNiH5rA=","95k7tsx18wk/s+gC0VwVIWJSpsFLbM9Va9OXxwg1QoM=","PPRIClKANC4yMimtdRsbqF4uuSTISvGXTKykY7YIm9g=","5E9UT2AALNmYJDhBfdQHpi/0sNDhOIrVOFpRs17dO5w=","AIchu5T6uNVFYxxOKziKSag2JbfWSkIyM20jzCUh1O0=","IWeCvjCw+7vtXTKqMOGXaVsiczrTGcP9sBsjjhfFHkA=","QzDOEWyBb/3k5BehbANgpJnqCv18gWmRP6KhEoXU01g=","Vy4gMeigrzl2h7E1v0/hMaOzFXSdIQN01k9twthAt24=","UOIKRNrO4SY8vQWPM9XszYB37SeuO8WzM8T/KZG+nwA=","m8jmAdc3HEDKqvvIKmGhqoilAvqBxZhsktXmXh58WiA="],"logIndex":"14715021","rootHash":"IyQHfWBCjwz8lnRQzUXashtTN7aPUC4oZXd4RLHvMWc=","treeSize":"14715023"},"integratedTime":"1727988030","kindVersion":{"kind":"dsse","version":"0.0.1"},"logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"logIndex":"136619283"}]},"version":1}],"publisher":{"claims":null,"environment":"","kind":"GitHub","repository":"woodruffw/abi3info","workflow":"release.yml"}}],"version":1} diff --git a/test/assets/abi3info-2024.10.8-py3-none-any.whl b/test/assets/abi3info-2024.10.8-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e468b7c141ae128ff9d6cc1323c6a501bdea93af GIT binary patch literal 19295 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 Pgm||IuR~2veE<3rtVKhW literal 0 HcmV?d00001 diff --git a/test/assets/abi3info-2024.10.8-py3-none-any.whl.provenance b/test/assets/abi3info-2024.10.8-py3-none-any.whl.provenance new file mode 100644 index 0000000..3d8e1fa --- /dev/null +++ b/test/assets/abi3info-2024.10.8-py3-none-any.whl.provenance @@ -0,0 +1 @@ +{"attestation_bundles":[{"attestations":[{"envelope":{"signature":"MEQCIELMZRhsNcO/dBwR7PgmYb8XmSMJlgpr2T7t4J1Qa2aQAiAYp7swRtCbhlbi7U0ruMfLkVYe9FJUd58EDuKCix8k8A==","statement":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiYWJpM2luZm8tMjAyNC4xMC44LXB5My1ub25lLWFueS53aGwiLCJkaWdlc3QiOnsic2hhMjU2IjoiYjAyMzZjNjcwNzc4M2Y5Mzk3MTI3NDEwMWUxMTkwNTUxOTJjZWYwOTI1ZjViN2NlYmRhZDAzYzY5ZGM1YTQ5OSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2RvY3MucHlwaS5vcmcvYXR0ZXN0YXRpb25zL3B1Ymxpc2gvdjEiLCJwcmVkaWNhdGUiOm51bGx9"},"verification_material":{"certificate":"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","transparency_entries":[{"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMWYzNjE5NTAzZmNkYTZiNjcwYTZiMmUwMjYxZTc5MDZhOWViNjM1MzBmYWQ4ODhjMjUzNTBlMjBlNmNiNzhlYSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6Ijc3NmNmZjRhNGY4ZDY5ZjEyYWE4NGRjNmExMDRlNjM1MWEzNGIyODViZDVkNTExNWQ3YWExZjMzYmM1Y2E1ZjYifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVRQ0lFTE1aUmhzTmNPL2RCd1I3UGdtWWI4WG1TTUpsZ3ByMlQ3dDRKMVFhMmFRQWlBWXA3c3dSdENiaGxiaTdVMHJ1TWZMa1ZZZTlGSlVkNThFRHVLQ2l4OGs4QT09IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VkNmVrTkRRbXhUWjBGM1NVSkJaMGxWU0U1RFRYWjRZbE5UY2xGaFUwRlBVRFpxYkcwelRtZ3hhRWhWZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmVFMUVRVFJOYWtGNFRucEpORmRvWTA1TmFsRjRUVVJCTkUxcVFYbE9la2swVjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVlVhVWg0V1hkM1kwcFVMMUJTU0hGRk0wbFZWRmxpTlZsallVUktkVXRSWTJwNksxSUtNMGcyU2sxSVJtMHlSblpGWVVkVVNYUlJVMDFsYm1WeEsxYzJTRTl1V0dwd2JtVXpOWEYyTmxKNWNFMVBkM1UwUkhGUFEwSllUWGRuWjFaMlRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVlhNbVEwQ21GSGNVTkpWSHBTTlRkUU1sVlFkR1kwUW13M1pVeFZkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMXAzV1VSV1VqQlNRVkZJTDBKR01IZFhORnBhWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBURE5rZG1JeVVubGtWMXB0WkhrNWFBcFpiV3Q2WVZjMWJXSjVPSFZhTW13d1lVaFdhVXd6WkhaamJYUnRZa2M1TTJONU9YbGFWM2hzV1ZoT2JFeHViSFJpUlVKNVdsZGFla3d6VW1oYU0wMTJDbVJxU1hkTmFsRjFUVlJCZFUxRVozZFBVVmxMUzNkWlFrSkJSMFIyZWtGQ1FWRlJjbUZJVWpCalNFMDJUSGs1TUdJeWRHeGlhVFZvV1ROU2NHSXlOWG9LVEcxa2NHUkhhREZaYmxaNldsaEthbUl5TlRCYVZ6VXdURzFPZG1KVVFWWkNaMjl5UW1kRlJVRlpUeTlOUVVWRFFrRmtlVnBYZUd4WldFNXNUVVJaUndwRGFYTkhRVkZSUW1jM09IZEJVVTFGUzBkTmVGbHFhR2hhYlZGNldrUmFhbGxVUVhwUFIxSnFUVzFLYVU1VVJYbE9WRUY2V1hwWmVFOVhXbXBaVjBrd0NrOUViR3ROYlVsM1JsRlpTMHQzV1VKQ1FVZEVkbnBCUWtKQlVVaGpiVlp6V2xkR2VscFVRV2RDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa0pLTTJJeU9Xc0tZMjVXYlZwdVkzWlpWMHB3VFRKc2RWcHRPSGRKZDFsTFMzZFpRa0pCUjBSMmVrRkNRbWRSVm1OdFZtMWplVGt3V1Zka2Vrd3pXWGxOUkVrd1RHcEZkd3BNYWtFMFRVUnpSME5wYzBkQlVWRkNaemM0ZDBGUlowVk1VWGR5WVVoU01HTklUVFpNZVRrd1lqSjBiR0pwTldoWk0xSndZakkxZWt4dFpIQmtSMmd4Q2xsdVZucGFXRXBxWWpJMU1GcFhOVEJNYlU1MllsUkNjRUpuYjNKQ1owVkZRVmxQTDAxQlJVcENSbk5OVjFkb01HUklRbnBQYVRoMldqSnNNR0ZJVm1rS1RHMU9kbUpUT1ROaU1qbHJZMjVXYlZwdVkzWlpWMHB3VFRKc2RWcHRPSFpNYldSd1pFZG9NVmxwT1ROaU0wcHlXbTE0ZG1RelRYWmpiVlp6V2xkR2VncGFVelUxWWxkNFFXTnRWbTFqZVRrd1dWZGtla3d6V1hsTlJFa3dUR3BGZDB4cVFUUk5SR2RIUTJselIwRlJVVUpuTnpoM1FWRnZSVXRuZDI5WmVrWnBDazlIUm0xYVJFNXJUbTFPYUUxRVRUUmFSMDE1V1cxSk1VMVVTVEZOUkU1cVRtcEZOVnB0VG1oWmFsRTBUMWRSZVZscVFXUkNaMjl5UW1kRlJVRlpUeThLVFVGRlRFSkJPRTFFVjJSd1pFZG9NVmxwTVc5aU0wNHdXbGRSZDA1UldVdExkMWxDUWtGSFJIWjZRVUpFUVZGdVJFTldiMlJJVW5kamVtOTJUREprY0Fwa1IyZ3hXV2sxYW1JeU1IWmtNamwyV2toS01WcHRXak5NTWtacFlWUk9jR0p0V25aTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZFd1JVdG5kMjlaZWtacENrOUhSbTFhUkU1clRtMU9hRTFFVFRSYVIwMTVXVzFKTVUxVVNURk5SRTVxVG1wRk5WcHRUbWhaYWxFMFQxZFJlVmxxUVd4Q1oyOXlRbWRGUlVGWlR5OEtUVUZGVDBKQ1kwMUdXRXBzV201TmRtUkhSbTVqZVRreVRXcEJlVTVETkhoTlF6UjNUMFJCV2tKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhOTlExUlZlZ3BQUkVrMVRYcEZOVTU2UVhOQ1oyOXlRbWRGUlVGWlR5OU5RVVZSUWtJMFRVaEhhREJrU0VKNlQyazRkbG95YkRCaFNGWnBURzFPZG1KVE9UTmlNamxyQ21OdVZtMWFibU4zUm5kWlMwdDNXVUpDUVVkRWRucEJRa1ZSVVVwRVFXTjZUVVJWTlUxcVJYZE5SMnRIUTJselIwRlJVVUpuTnpoM1FWSkpSVmQzZUZvS1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROa2RtSXlVbmxrVjFwdFpIazVhRmx0YTNwaFZ6VnRZbms0ZFZveWJEQmhTRlpwVEROa2RncGpiWFJ0WWtjNU0yTjVPWGxhVjNoc1dWaE9iRXh1YkhSaVJVSjVXbGRhZWt3elVtaGFNMDEyWkdwSmQwMXFVWFZOVkVGMVRVUm5kMDlCV1V0TGQxbENDa0pCUjBSMmVrRkNSWGRSY1VSRGFHcE5WMGswV1ZkYWEwMHlVVEpaTWtWM1RYcG9hMWw2U21sWmFsVjRUV3BWZDAweVRUSk5WR3h0V1RKR2FVNUVaelVLV2tSS2FVMUNZMGREYVhOSFFWRlJRbWMzT0hkQlVsRkZRMUYzU0dOdFZuTmFWMFo2V2xSQ1drSm5iM0pDWjBWRlFWbFBMMDFCUlZaQ1JYTk5VMWRvTUFwa1NFSjZUMms0ZGxveWJEQmhTRlpwVEcxT2RtSlRPVE5pTWpsclkyNVdiVnB1WTNaWlYwcHdUVEpzZFZwdE9IWlpWMDR3WVZjNWRXTjVPWGxrVnpWNkNreDZSWGhOYWxGNlRVUlZlVTlVWnpSTU1rWXdaRWRXZEdOSVVucE1la1YzUm1kWlMwdDNXVUpDUVVkRWRucEJRa1puVVVsRVFWcDNaRmRLYzJGWFRYY0taMWx2UjBOcGMwZEJVVkZDTVc1clEwSkJTVVZtUVZJMlFVaG5RV1JuUkdSUVZFSnhlSE5qVWsxdFRWcElhSGxhV25walEyOXJjR1YxVGpRNGNtWXJTQXBwYmt0QlRIbHVkV3BuUVVGQldrcDBlRFZNT1VGQlFVVkJkMEpJVFVWVlEwbFJRMEV6UWxaVllVODNVbmRWWkdrckt6WlJibFZZWVRsaVYzVTRPRmxOQ2pBMmJHTkhiRU5IWWxOV2VrSm5TV2RaYmtkTE9FbGtaU3RIVVdFd1NFaERiR3RNWTNVclQxa3lhbnBMU204d2JWaHhhMEZDVTBOME4za3dkME5uV1VrS1MyOWFTWHBxTUVWQmQwMUVZVkZCZDFwblNYaEJTa2hIYldOTFR6aEVkSFV4Vkhkak0wd3pVMGRPUXpSbGVVYzJTemsxUzJaRmFWZGtlRnBIYjBKeE5RcDJRVVl5YjBWMFkycEZjVnBuZGtScVZWbzVlazlSU1hoQlRERklaMkZMZG1Sc05HVmFOa2R1VFU1TFZ6QTJVMEV4VGt0Q09ERm1VVVprWkdOU1ZtUjJDbWRQZVhkUVduQjRkR3RoZVdGdVpqRjRVWGxMWVM5cmJHNVJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In1dfX0=","inclusionPromise":{"signedEntryTimestamp":"MEUCIQCHVJJECS48TrdhijyH7ULyJkjnyL6nRFlKqIdOXakhJAIgNuPNkvJIoMuAfsKKuzezeW2Ux/hPp06UGiK7W3V+FiE="},"inclusionProof":{"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n16252918\nx+s+MbBeSodo41RziQ5f7jApTPKCxJVXzZuVmmOGfHw=\n\n— rekor.sigstore.dev wNI9ajBGAiEAl0MZpdlQeukwKGt7ct3x25dQ1kbqAtQlPeSNEvqdmHoCIQC42T3rlo3Ucgu8ULb5/5Mz9Mh+8QD4d8Q7o/qP5wdGkQ==\n"},"hashes":["u7KzRpzc/+2bXTuTiJDaHsT4Z4fMuxPj6DNnqkhdJKM=","vL0vOau+P5oSiAePe2L1lDPMisYfg0uAz05a23d3UoY=","4jbqWRVLBIMRWy69DybZxJjIh35jkCpf/6jM4qNYOng=","TJvk9HAUbc0ozZaOaloVPXs+k2dL6h3Rhms80uRjCi0=","pS46P4FtY5cP+P7DPfPeDDI7K1LlNlSJB8fzvygG2FE=","bEoYqfsu+Lp0xiQKRVpif829vmAflYrdmrMs8h+vNfc=","0fg1FZVlvhL54JIUvvwf0DqlWu8ol6dA/4eco+GDqzs=","iw5fwvdQnYx1KafraPteZzxhtUJ+cLSeIRM+u4yN3x0=","J5/1+QGJDN5TsthZ3GiWydkb0x1T5Rlzapmy8H/b2So=","uQ9Qo22fH48W6tYEuoT57E4y3I5BlrFAUp9EsXjPiTk=","Y13KY3NsPaSUawdzdimEjKICRBs0o5JAwxA+X9SKktA=","ATDrBjVGXZq0SXE3l93icSsbQO/GPwSxIi4C5mmVA5E=","pAYZ0udCZhKomfpSblidBPOUqHywHJhYRsFkr8ErUXM=","U8z9kuBu9lsBt8aThPBJMFxNkgsN5Sj56XKcZD+SDRc=","kMFisEs5aXocgiyVwbQ086AueGVgsj2kO1W2yt9BFwA=","wXgmSa32VbZBEcq2/StIvZog6n3YPGMkJW71c9A8Spw=","9/8OCCPxXk/mOWRs/Yky2HsQ/+cdzOKcuHctd3dJMrQ=","66A5KssTkbRaPhP6q0qwMpzgjor0Mwy/s2XTZ66cMpc=","Vy4gMeigrzl2h7E1v0/hMaOzFXSdIQN01k9twthAt24=","UOIKRNrO4SY8vQWPM9XszYB37SeuO8WzM8T/KZG+nwA=","m8jmAdc3HEDKqvvIKmGhqoilAvqBxZhsktXmXh58WiA="],"logIndex":"16252917","rootHash":"x+s+MbBeSodo41RziQ5f7jApTPKCxJVXzZuVmmOGfHw=","treeSize":"16252918"},"integratedTime":"1728418649","kindVersion":{"kind":"dsse","version":"0.0.1"},"logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"logIndex":"138157179"}]},"version":1}],"publisher":{"claims":null,"environment":"","kind":"GitHub","repository":"woodruffw/abi3info","workflow":"release.yml"}}],"version":1} diff --git a/test/assets/abi3info-2024.10.8.tar.gz b/test/assets/abi3info-2024.10.8.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b89049b6a671edff07e206b1a1677d02cf774aec GIT binary patch literal 19901 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 Y<>%$+<>%$+pYG@X0~{F}000mK03m|o None: + assert pip_plugin_pep740.plugin_type() == "dist-inspector" + + @pytest.mark.parametrize( + ("version", "filename", "provenance_file", "digest"), + [ + (PACKAGE_VERSION_1, DIST_FILE_1.name, PROVENANCE_FILE_1, DIST_DIGEST_1), + (PACKAGE_VERSION_3, DIST_FILE_3.name, PROVENANCE_FILE_3, DIST_DIGEST_3), + ], + ) + def test_pre_download_valid_provenance( + self, version: str, filename: str, provenance_file: Path, digest: str + ) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{version}/{filename}/provenance", + text=provenance_file.read_text(), + ) + pip_plugin_pep740.pre_download( + url="url", + filename=filename, + digest=digest, + ) + + def test_pre_download_invalid_filename(self) -> None: + assert ( + pip_plugin_pep740.pre_download( + url="url", + filename="not_a_dist.docx", + digest="digest", + ) + is None + ) + + def test_pre_download_no_provenance_found(self) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + status_code=404, + ) + assert ( + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + is None + ) + + def test_pre_download_provenance_download_error(self) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + status_code=403, + ) + with pytest.raises(ValueError, match="403 Client Error"): + assert ( + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + is None + ) + + def test_pre_download_provenance_timeout(self) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + exc=requests.exceptions.ConnectTimeout, + ) + with pytest.raises(ValueError, match="Error downloading provenance file"): + assert ( + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + is None + ) + + def test_pre_download_invalid_provenance(self) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + text=PROVENANCE_FILE_2.read_text(), + ) + with pytest.raises( + ValueError, + match="subject does not match distribution name", + ): + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + + def test_pre_download_invalid_provenance_json(self) -> None: + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + text="invalidjson", + ) + with pytest.raises( + ValueError, + match="Invalid provenance JSON", + ): + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + + def test_pre_download_malformed_provenance_valid_json(self) -> None: + provenance = json.loads(PROVENANCE_FILE_1.read_text()) + provenance["attestation_bundles"] = "invalid" + with requests_mock.Mocker(real_http=True) as m: + m.get( + f"https://pypi.org/integrity/{PACKAGE_NAME}/{PACKAGE_VERSION_1}/{DIST_FILE_1.name}/provenance", + text=json.dumps(provenance), + ) + with pytest.raises( + ValueError, + match="Invalid provenance: 1 validation error for Provenance", + ): + pip_plugin_pep740.pre_download( + url="url", + filename=DIST_FILE_1.name, + digest=DIST_DIGEST_1, + ) + + def test_get_verification_policy_gitlab(self) -> None: + bundle = AttestationBundle( + publisher=GitLabPublisher(repository="namespace/pkg"), attestations=[] + ) + policy = pip_plugin_pep740._impl._get_verification_policy(bundle) # noqa: SLF001 + assert isinstance(policy, AllOf) + issuer_policy = policy._children[0] # noqa: SLF001 + assert isinstance(issuer_policy, OIDCIssuerV2) + assert issuer_policy._value == "https://gitlab.com" # noqa: SLF001 + repository_policy = policy._children[1] # noqa: SLF001 + assert isinstance(repository_policy, OIDCSourceRepositoryURI) + assert repository_policy._value == "https://gitlab.com/namespace/pkg" # noqa: SLF001 + + def test_pre_extract(self) -> None: + assert pip_plugin_pep740.pre_extract(dist=Path("filename")) is None diff --git a/test/test_init.py b/test/test_init.py new file mode 100644 index 0000000..74b1baa --- /dev/null +++ b/test/test_init.py @@ -0,0 +1,9 @@ +"""Tests for the module's init.""" + +import pip_plugin_pep740 + + +def test_version() -> None: + version = getattr(pip_plugin_pep740, "__version__", None) + assert version is not None + assert isinstance(version, str)