From c06358e51ebe26eb5d1a3d1d70420ef7fdcda56c Mon Sep 17 00:00:00 2001 From: Antoni Jagodka Date: Tue, 28 Jul 2026 13:12:53 -0500 Subject: [PATCH] Preserve the validation error when parsing a malformed statement Statement(contents=...) discarded the pydantic ValidationError, leaving callers with a bare "malformed in-toto statement" and no __cause__, so a rejected digest algorithm, a missing field and a bad _type were indistinguishable. StatementBuilder.build() already surfaces the underlying error; this makes the parsing path match. Signed-off-by: Antoni Jagodka --- CHANGELOG.md | 7 +++++++ sigstore/dsse/__init__.py | 4 ++-- test/unit/test_dsse.py | 25 ++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6baab377..02be76c18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ All versions prior to 0.9.0 are untracked. ## [Unreleased] +### Fixed + +* Parsing a malformed in-toto statement now includes the underlying validation + 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. + ## [4.5.0] ### Fixed diff --git a/sigstore/dsse/__init__.py b/sigstore/dsse/__init__.py index f08fd1a9f..0f9eaa664 100644 --- a/sigstore/dsse/__init__.py +++ b/sigstore/dsse/__init__.py @@ -95,8 +95,8 @@ def __init__(self, contents: bytes | _Statement) -> None: self._contents = contents try: self._inner = _Statement.model_validate_json(contents) - except ValidationError: - raise Error("malformed in-toto statement") + except ValidationError as e: + raise Error(f"malformed in-toto statement: {e}") from e else: self._contents = contents.model_dump_json(by_alias=True).encode() self._inner = contents diff --git a/test/unit/test_dsse.py b/test/unit/test_dsse.py index 9eddd943d..d791082ab 100644 --- a/test/unit/test_dsse.py +++ b/test/unit/test_dsse.py @@ -18,7 +18,7 @@ import pytest from sigstore import dsse -from sigstore.dsse import InvalidEnvelope +from sigstore.dsse import Error, InvalidEnvelope class TestEnvelope: @@ -84,3 +84,26 @@ def test_multiple_signatures(self): with pytest.raises(InvalidEnvelope, match="one signature"): dsse.Envelope._from_json(raw) + + +class TestStatement: + def test_malformed_statement_reports_why(self): + # An unsupported digest algorithm is rejected by design, but the caller + # is left guessing: the same message covers a missing field, a bad + # _type, and a rejected digest. StatementBuilder.build() already + # surfaces the underlying validation error; parsing should too. + raw = json.dumps( + { + "_type": "https://in-toto.io/Statement/v1", + "subject": [{"name": "foo", "digest": {"gitCommit": "a" * 40}}], + "predicateType": "https://example.com/predicate/v1", + "predicate": {}, + } + ) + + with pytest.raises(Error, match="malformed in-toto statement") as exc: + dsse.Statement(raw.encode()) + + # the cause is preserved, and names the offending field + assert exc.value.__cause__ is not None + assert "digest" in str(exc.value)