Skip to content

Commit 322dde1

Browse files
authored
Add pypi-attestations verify pypi CLI subcommand (#82)
1 parent 7da148b commit 322dde1

File tree

4 files changed

+589
-41
lines changed

4 files changed

+589
-41
lines changed

README.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ print(bundle.to_json())
8484
## Usage as a command line tool
8585

8686
> [!IMPORTANT]
87-
> The `python -m pypi_attestations` CLI is intended primarily for
87+
> The `pypi-attestations` CLI is intended primarily for
8888
> experimentation, and is not considered a stable interface for
8989
> generating or verifying attestations. Users are encouraged to
9090
> generate attestations using [the official PyPA publishing action]
9191
> or via this package's [public Python APIs].
9292
9393
````bash
94-
python -m pypi_attestations --help
94+
pypi-attestations --help
9595
usage: pypi-attestation [-h] [-v] [-V] COMMAND ...
9696

9797
Sign, inspect or verify PEP 740 attestations
@@ -119,7 +119,7 @@ options:
119119
```bash
120120
# Generate a whl file
121121
make package
122-
python -m pypi_attestations sign dist/pypi_attestations-*.whl
122+
pypi-attestations sign dist/pypi_attestations-*.whl
123123
```
124124
125125
### Inspecting a PEP 740 Attestation
@@ -129,7 +129,7 @@ python -m pypi_attestations sign dist/pypi_attestations-*.whl
129129
> the attestation.
130130
131131
```bash
132-
python -m pypi_attestations inspect dist/pypi_attestations-*.whl.publish.attestation
132+
pypi-attestations inspect dist/pypi_attestations-*.whl.publish.attestation
133133
```
134134
135135
### Verifying a PEP 740 Attestation
@@ -140,14 +140,31 @@ python -m pypi_attestations inspect dist/pypi_attestations-*.whl.publish.attesta
140140
> workflow that generated the attestation. The format of that identity
141141
142142
```bash
143-
python -m pypi_attestations verify --staging \
143+
pypi-attestations verify attestation --staging \
144144
--identity william@yossarian.net \
145145
test/assets/rfc8785-0.1.2-py3-none-any.whl
146146
```
147147
148148
The attestation present in the test has been generated using the staging
149149
environment of Sigstore and signed by the identity `william@yossarian.net`.
150150
151+
### Verifying a PyPI package
152+
> [!NOTE]
153+
> The URL must be a direct link to the distribution artifact hosted by PyPI.
154+
> These can be found in the "Download files" section of the project's page,
155+
> e.g: https://pypi.org/project/sigstore/#files
156+
157+
```bash
158+
pypi-attestations verify pypi --repository https://github.com/sigstore/sigstore-python \
159+
https://files.pythonhosted.org/packages/70/f5/324edb6a802438e97e289992a41f81bb7a58a1cda2e49439e7e48896649e/sigstore-3.6.1-py3-none-any.whl
160+
```
161+
162+
This command downloads the artifact from the given URL and gets its provenance
163+
from PyPI. The artifact is then verified against the provenance, while also
164+
checking that the provenance's signing identity matches the repository specified
165+
by the user.
166+
167+
151168
[PEP 740]: https://peps.python.org/pep-0740/
152169
153170
[here]: https://trailofbits.github.io/pypi-attestations

pyproject.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ dependencies = [
1414
"packaging",
1515
"pyasn1 ~= 0.6",
1616
"pydantic >= 2.10.0",
17+
"requests",
18+
"rfc3986",
1719
"sigstore >= 3.5.3, < 3.7",
1820
"sigstore-protobuf-specs",
1921
]
@@ -28,7 +30,7 @@ test = ["pytest", "pytest-cov", "pretend", "coverage[toml]"]
2830
lint = [
2931
# NOTE: ruff is under active development, so we pin conservatively here
3032
# and let Dependabot periodically perform this update.
31-
"ruff ~= 0.2",
33+
"ruff ~= 0.9",
3234
"mypy >= 1.0",
3335
"types-html5lib",
3436
"types-requests",
@@ -39,6 +41,8 @@ lint = [
3941
]
4042
dev = ["pypi-attestations[doc,test,lint]", "build"]
4143

44+
[project.scripts]
45+
pypi-attestations = "pypi_attestations._cli:main"
4246

4347
[project.urls]
4448
Homepage = "https://pypi.org/project/pypi-attestations"
@@ -51,7 +55,7 @@ name = "pypi_attestations"
5155

5256
[tool.coverage.run]
5357
# don't attempt code coverage for the CLI entrypoints
54-
omit = ["src/pypi_attestations/_cli.py", "src/pypi_attestations/__main__.py"]
58+
omit = ["src/pypi_attestations/__main__.py"]
5559

5660
[tool.mypy]
5761
mypy_path = "src"
@@ -80,11 +84,10 @@ target-version = "py39"
8084

8185
[tool.ruff.lint]
8286
select = ["E", "F", "I", "W", "UP", "ANN", "D", "COM", "ISC", "TCH", "SLF"]
83-
# ANN101 and ANN102 are deprecated
8487
# D203 and D213 are incompatible with D211 and D212 respectively.
8588
# COM812 and ISC001 can cause conflicts when using ruff as a formatter.
8689
# See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules.
87-
ignore = ["ANN101", "ANN102", "D203", "D213", "COM812", "ISC001"]
90+
ignore = ["D203", "D213", "COM812", "ISC001"]
8891
# Needed since Pydantic relies on runtime type annotations, and we target Python versions
8992
# < 3.10. See https://docs.astral.sh/ruff/rules/non-pep604-annotation/#why-is-this-bad
9093
pyupgrade.keep-runtime-typing = true
@@ -104,7 +107,6 @@ pyupgrade.keep-runtime-typing = true
104107
exclude = [
105108
"env",
106109
"test",
107-
"src/pypi_attestations/_cli.py",
108110
"src/pypi_attestations/__main__.py",
109111
]
110112
ignore-semiprivate = true

0 commit comments

Comments
 (0)