Skip to content

Commit 3d0d852

Browse files
committed
Merge remote-tracking branch 'origin/main' into andrew/bundled-trust-root
2 parents 77a3ce8 + 37483bc commit 3d0d852

File tree

11 files changed

+159
-190
lines changed

11 files changed

+159
-190
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
122122

123123
- name: publish
124-
uses: pypa/gh-action-pypi-publish@c7f29f7adef1a245bd91520e94867e5c6eedddcc
124+
uses: pypa/gh-action-pypi-publish@22b4d1f12511f2696162c08546dafbaa903448a2
125125
with:
126126
user: __token__
127127
password: ${{ secrets.PYPI_TOKEN }}

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ All versions prior to 0.9.0 are untracked.
1919

2020
* Replaced ambient credential detection logic with the `id` package
2121
([#535](https://github.com/sigstore/sigstore-python/pull/535))
22+
* Revamped error diagnostics reporting. All errors with diagnostics now implement
23+
`sigstore.errors.Error`.
2224

2325
### Fixed
2426

install/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,9 @@ typing-extensions==4.5.0 \
371371
--hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \
372372
--hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4
373373
# via pydantic
374-
urllib3==1.26.14 \
375-
--hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \
376-
--hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1
374+
urllib3==1.26.15 \
375+
--hash=sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305 \
376+
--hash=sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42
377377
# via requests
378378
zipp==3.15.0 \
379379
--hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ lint = [
6464
"mypy ~= 1.1",
6565
# NOTE(ww): ruff is under active development, so we pin conservatively here
6666
# and let Dependabot periodically perform this update.
67-
"ruff < 0.0.255",
67+
"ruff < 0.0.256",
6868
"types-requests",
6969
# Needed for protocol typing in 3.7; remove when our minimum Python is 3.8.
7070
"typing-extensions; python_version < '3.8'",

sigstore/_cli.py

Lines changed: 60 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@
2424
from typing import Optional, TextIO, Union, cast
2525

2626
from cryptography.x509 import load_pem_x509_certificates
27-
from id import GitHubOidcPermissionCredentialError, detect_credential
2827
from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle
2928

3029
from sigstore import __version__
31-
from sigstore._errors import Error
3230
from sigstore._internal.ctfe import CTKeyring
3331
from sigstore._internal.fulcio.client import DEFAULT_FULCIO_URL, FulcioClient
3432
from sigstore._internal.keyring import Keyring
@@ -40,21 +38,23 @@
4038
)
4139
from sigstore._internal.tuf import TrustUpdater
4240
from sigstore._utils import PEMCert
41+
from sigstore.errors import Error
4342
from sigstore.oidc import (
4443
DEFAULT_OAUTH_ISSUER_URL,
4544
STAGING_OAUTH_ISSUER_URL,
4645
Issuer,
46+
detect_credential,
4747
)
4848
from sigstore.sign import Signer
4949
from sigstore.transparency import LogEntry
5050
from sigstore.verify import (
5151
CertificateVerificationFailure,
5252
LogEntryMissing,
53-
VerificationFailure,
5453
VerificationMaterials,
5554
Verifier,
5655
policy,
5756
)
57+
from sigstore.verify.models import VerificationFailure
5858

5959
logging.basicConfig()
6060
logger = logging.getLogger(__name__)
@@ -844,6 +844,60 @@ def _collect_verification_state(
844844
return (verifier, all_materials)
845845

846846

847+
class VerificationError(Error):
848+
"""Raised when the verifier returns a `VerificationFailure` result."""
849+
850+
def __init__(self, result: VerificationFailure):
851+
self.message = f"Verification failed: {result.reason}"
852+
self.result = result
853+
854+
def diagnostics(self) -> str:
855+
message = f"Failure reason: {self.result.reason}\n"
856+
857+
if isinstance(self.result, CertificateVerificationFailure):
858+
message += dedent(
859+
f"""
860+
The given certificate could not be verified against the
861+
root of trust.
862+
863+
This may be a result of connecting to the wrong Fulcio instance
864+
(for example, staging instead of production, or vice versa).
865+
866+
Additional context:
867+
868+
{self.result.exception}
869+
"""
870+
)
871+
elif isinstance(self.result, LogEntryMissing):
872+
message += dedent(
873+
f"""
874+
These signing artifacts could not be matched to a entry
875+
in the configured transparency log.
876+
877+
This may be a result of connecting to the wrong Rekor instance
878+
(for example, staging instead of production, or vice versa).
879+
880+
Additional context:
881+
882+
Signature: {self.result.signature}
883+
884+
Artifact hash: {self.result.artifact_hash}
885+
"""
886+
)
887+
else:
888+
message += dedent(
889+
f"""
890+
A verification error occurred.
891+
892+
Additional context:
893+
894+
{self.result}
895+
"""
896+
)
897+
898+
return message
899+
900+
847901
def _verify_identity(args: argparse.Namespace) -> None:
848902
verifier, files_with_materials = _collect_verification_state(args)
849903

@@ -861,64 +915,8 @@ def _verify_identity(args: argparse.Namespace) -> None:
861915
if result:
862916
print(f"OK: {file}")
863917
else:
864-
result = cast(VerificationFailure, result)
865918
print(f"FAIL: {file}")
866-
print(f"Failure reason: {result.reason}", file=sys.stderr)
867-
868-
if isinstance(result, CertificateVerificationFailure):
869-
# If certificate verification failed, it's either because of
870-
# a chain issue or some outdated state in sigstore itself.
871-
# These might already be resolved in a newer version, so
872-
# we suggest that users try to upgrade and retry before
873-
# anything else.
874-
print(
875-
dedent(
876-
f"""
877-
The given certificate could not be verified against the
878-
root of trust.
879-
880-
This may be a result of connecting to the wrong Fulcio instance
881-
(for example, staging instead of production, or vice versa).
882-
883-
Additional context:
884-
885-
{result.exception}
886-
"""
887-
),
888-
file=sys.stderr,
889-
)
890-
elif isinstance(result, LogEntryMissing):
891-
# If Rekor lookup failed, it's because the certificate either
892-
# wasn't logged after creation or because the user requested the
893-
# wrong Rekor instance (e.g., staging instead of production).
894-
# The latter is significantly more likely, so we add
895-
# some additional context to the output indicating it.
896-
#
897-
# NOTE: Even though the latter is more likely, it's still extremely
898-
# unlikely that we'd hit this -- we should always fail with
899-
# `CertificateVerificationFailure` instead, as the cert store should
900-
# fail to validate due to a mismatch between the leaf and the trusted
901-
# root + intermediates.
902-
print(
903-
dedent(
904-
f"""
905-
These signing artifacts could not be matched to a entry
906-
in the configured transparency log.
907-
908-
This may be a result of connecting to the wrong Rekor instance
909-
(for example, staging instead of production, or vice versa).
910-
911-
Additional context:
912-
913-
Signature: {result.signature}
914-
915-
Artifact hash: {result.artifact_hash}
916-
"""
917-
),
918-
file=sys.stderr,
919-
)
920-
921-
sys.exit(1)
919+
raise VerificationError(cast(VerificationFailure, result))
922920

923921

924922
def _verify_github(args: argparse.Namespace) -> None:
@@ -952,106 +950,14 @@ def _verify_github(args: argparse.Namespace) -> None:
952950
if result:
953951
print(f"OK: {file}")
954952
else:
955-
result = cast(VerificationFailure, result)
956953
print(f"FAIL: {file}")
957-
print(f"Failure reason: {result.reason}", file=sys.stderr)
958-
959-
if isinstance(result, CertificateVerificationFailure):
960-
# If certificate verification failed, it's either because of
961-
# a chain issue or some outdated state in sigstore itself.
962-
# These might already be resolved in a newer version, so
963-
# we suggest that users try to upgrade and retry before
964-
# anything else.
965-
print(
966-
dedent(
967-
f"""
968-
The given certificate could not be verified against the
969-
root of trust.
970-
971-
This may be a result of connecting to the wrong Fulcio instance
972-
(for example, staging instead of production, or vice versa).
973-
974-
Additional context:
975-
976-
{result.exception}
977-
"""
978-
),
979-
file=sys.stderr,
980-
)
981-
elif isinstance(result, LogEntryMissing):
982-
# If Rekor lookup failed, it's because the certificate either
983-
# wasn't logged after creation or because the user requested the
984-
# wrong Rekor instance (e.g., staging instead of production).
985-
# The latter is significantly more likely, so we add
986-
# some additional context to the output indicating it.
987-
#
988-
# NOTE: Even though the latter is more likely, it's still extremely
989-
# unlikely that we'd hit this -- we should always fail with
990-
# `CertificateVerificationFailure` instead, as the cert store should
991-
# fail to validate due to a mismatch between the leaf and the trusted
992-
# root + intermediates.
993-
print(
994-
dedent(
995-
f"""
996-
These signing artifacts could not be matched to a entry
997-
in the configured transparency log.
998-
999-
This may be a result of connecting to the wrong Rekor instance
1000-
(for example, staging instead of production, or vice versa).
1001-
1002-
Additional context:
1003-
1004-
Signature: {result.signature}
1005-
1006-
Artifact hash: {result.artifact_hash}
1007-
"""
1008-
),
1009-
file=sys.stderr,
1010-
)
1011-
1012-
sys.exit(1)
954+
raise VerificationError(cast(VerificationFailure, result))
1013955

1014956

1015957
def _get_identity_token(args: argparse.Namespace) -> Optional[str]:
1016958
token = None
1017959
if not args.oidc_disable_ambient_providers:
1018-
try:
1019-
token = detect_credential(DEFAULT_AUDIENCE)
1020-
except GitHubOidcPermissionCredentialError as exception:
1021-
# Provide some common reasons for why we hit permission errors in
1022-
# GitHub Actions.
1023-
print(
1024-
dedent(
1025-
f"""
1026-
Insufficient permissions for GitHub Actions workflow.
1027-
1028-
The most common reason for this is incorrect
1029-
configuration of the top-level `permissions` setting of the
1030-
workflow YAML file. It should be configured like so:
1031-
1032-
permissions:
1033-
id-token: write
1034-
1035-
Relevant documentation here:
1036-
1037-
https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings
1038-
1039-
Another possible reason is that the workflow run has been
1040-
triggered by a PR from a forked repository. PRs from forked
1041-
repositories typically cannot be granted write access.
1042-
1043-
Relevant documentation here:
1044-
1045-
https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
1046-
1047-
Additional context:
1048-
1049-
{exception}
1050-
"""
1051-
),
1052-
file=sys.stderr,
1053-
)
1054-
sys.exit(1)
960+
token = detect_credential(DEFAULT_AUDIENCE)
1055961

1056962
if not token:
1057963
if args.staging:

sigstore/_internal/sct.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from sigstore._internal.ctfe import CTKeyring
3535
from sigstore._internal.keyring import KeyringError, KeyringLookupError
3636
from sigstore._utils import DERCert, KeyID, key_id
37+
from sigstore.errors import Error
3738

3839
logger = logging.getLogger(__name__)
3940

@@ -134,12 +135,35 @@ def _get_issuer_cert(chain: List[Certificate]) -> Certificate:
134135
return issuer
135136

136137

137-
class InvalidSCTError(Exception):
138+
class InvalidSCTError(Error):
138139
"""
139140
Raised during SCT verification if an SCT is invalid in some way.
140141
"""
141142

142-
pass
143+
def diagnostics(self) -> str:
144+
"""Returns diagnostics for the error."""
145+
# We specialize this error case, since it usually indicates one of
146+
# two conditions: either the current sigstore client is out-of-date,
147+
# or that the SCT is well-formed but invalid for the current configuration
148+
# (indicating that the user has asked for the wrong instance).
149+
if isinstance(self.__cause__, KeyringLookupError):
150+
return dedent(
151+
f"""
152+
Invalid key ID in SCT: not found in current keyring.
153+
154+
This may be a result of an outdated `sigstore` installation.
155+
156+
Consider upgrading with:
157+
158+
python -m pip install --upgrade sigstore
159+
160+
Additional context:
161+
162+
{self.__cause__}
163+
"""
164+
)
165+
166+
return str(self)
143167

144168

145169
def verify_sct(
@@ -190,28 +214,8 @@ def verify_sct(
190214
key_id=KeyID(sct.log_id), signature=sct.signature, data=digitally_signed
191215
)
192216
except KeyringLookupError as exc:
193-
# We specialize this error case, since it usually indicates one of
194-
# two conditions: either the current sigstore client is out-of-date,
195-
# or that the SCT is well-formed but invalid for the current configuration
196-
# (indicating that the user has asked for the wrong instance).
197-
#
198-
# TODO(ww): Longer term, this should be specialized elsewhere.
199217
raise InvalidSCTError(
200-
dedent(
201-
f"""
202-
Invalid key ID in SCT: not found in current keyring.
203-
204-
This may be a result of an outdated `sigstore` installation.
205-
206-
Consider upgrading with:
207-
208-
python -m pip install --upgrade sigstore
209-
210-
Additional context:
211-
212-
{exc}
213-
"""
214-
),
215-
)
218+
"Invalid key ID in SCT: not found in current keyring"
219+
) from exc
216220
except KeyringError as exc:
217221
raise InvalidSCTError from exc

sigstore/_internal/tuf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
from tuf.api import exceptions as TUFExceptions
3838
from tuf.ngclient import RequestsFetcher, Updater
3939

40-
from sigstore._errors import MetadataError, TUFError
4140
from sigstore._utils import read_embedded
41+
from sigstore.errors import MetadataError, TUFError
4242

4343
logger = logging.getLogger(__name__)
4444

0 commit comments

Comments
 (0)