diff --git a/CHANGELOG.md b/CHANGELOG.md index e6baab377..f0a810a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ All versions prior to 0.9.0 are untracked. ## [Unreleased] +### Fixed + +* `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] ### Fixed 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()