diff --git a/CHANGELOG.md b/CHANGELOG.md index 02be76c18..7992807df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ All versions prior to 0.9.0 are untracked. error, instead of discarding it. `StatementBuilder.build()` already did this; `Statement(contents=...)` did not, so a rejected digest algorithm, a missing field and a bad `_type` were indistinguishable. +* `Verifier` now raises `VerificationError` when the trusted root contains no + transparency log instances, instead of leaking a raw `IndexError` + ([#1880](https://github.com/sigstore/sigstore-python/pull/1880)) ## [4.5.0] diff --git a/sigstore/verify/verifier.py b/sigstore/verify/verifier.py index 6d4f56d42..7dccb6670 100644 --- a/sigstore/verify/verifier.py +++ b/sigstore/verify/verifier.py @@ -92,7 +92,12 @@ def __init__(self, *, trusted_root: TrustedRoot): # this is an ugly hack needed for verifying "detached" materials # In reality we should be choosing the rekor instance based on the logid - url = trusted_root._inner.tlogs[0].base_url + tlogs = trusted_root._inner.tlogs + if not tlogs: + raise VerificationError( + "trusted root contains no transparency log instances" + ) + url = tlogs[0].base_url self._rekor = RekorClient(url) @classmethod diff --git a/test/unit/verify/test_verifier.py b/test/unit/verify/test_verifier.py index 81287e7f6..de4de1a3c 100644 --- a/test/unit/verify/test_verifier.py +++ b/test/unit/verify/test_verifier.py @@ -21,15 +21,35 @@ import pretend import pytest import rfc3161_client +from sigstore_models.trustroot import v1 as trustroot_v1 from sigstore._internal.trust import CertificateAuthority from sigstore.dsse import StatementBuilder, Subject from sigstore.errors import CertValidationError, VerificationError -from sigstore.models import Bundle +from sigstore.models import Bundle, TrustedRoot from sigstore.verify import policy from sigstore.verify.verifier import Verifier +def test_verifier_rejects_trusted_root_without_tlogs(asset): + """ + A trusted root carrying no transparency log instances should surface a + VerificationError, not an IndexError from indexing an empty list. + """ + raw = json.loads(asset("trusted_root/trustedroot.v1.json").read_bytes()) + raw["tlogs"] = [] + trusted_root = TrustedRoot( + trustroot_v1.TrustedRoot.from_json(json.dumps(raw).encode()) + ) + + # the certificate authorities are left intact, so this reaches the tlog + # lookup rather than failing earlier in get_fulcio_certs() + assert trusted_root.get_fulcio_certs() + + with pytest.raises(VerificationError, match="no transparency log"): + Verifier(trusted_root=trusted_root) + + @pytest.mark.production def test_verifier_production(): verifier = Verifier.production()