Skip to content

Commit e2cfcc3

Browse files
Pigbibicodex
andcommitted
tighten raw period bundle bindings
Co-Authored-By: Codex <noreply@openai.com>
1 parent f08924a commit e2cfcc3

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

src/political_event_tracking_research/trusted_period_bundle_raw.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def reject_constant(_: str) -> None:
204204
if snapshot["snapshot_version"] != SNAPSHOT_VERSION:
205205
raise _error("bundle_snapshot_version_invalid")
206206
_string(snapshot["source_run_id"], _RUN_ID_RE, "bundle_snapshot_run_invalid")
207-
if snapshot["source_attempt"] != 1:
207+
if type(snapshot["source_attempt"]) is not int or snapshot["source_attempt"] != 1:
208208
raise _error("bundle_snapshot_attempt_invalid")
209209
_string(snapshot["workflow_sha"], _SHA1_RE, "bundle_snapshot_workflow_invalid")
210210
_string(snapshot["producer_sha"], _SHA1_RE, "bundle_snapshot_producer_invalid")
@@ -324,6 +324,9 @@ def pairs(items: list[tuple[str, object]]) -> dict[str, object]:
324324
_safe_int(parsed["artifact_id"], "bundle_manifest_invalid")
325325
if type(parsed["source_attempt"]) is not int or parsed["source_attempt"] != 1:
326326
raise _error("bundle_manifest_invalid")
327+
_string(parsed["source_run_id"], _RUN_ID_RE, "bundle_manifest_invalid")
328+
if parsed["lock_version"] != LOCK_VERSION or parsed["snapshot_version"] != SNAPSHOT_VERSION:
329+
raise _error("bundle_manifest_version_invalid")
327330
if type(parsed["retention_days"]) is not int or not 1 <= parsed["retention_days"] <= 90:
328331
raise _error("bundle_manifest_invalid")
329332
if _canonical_json(parsed, "bundle_manifest_invalid") != value:
@@ -360,6 +363,14 @@ def _manifest_bytes(
360363
)
361364

362365

366+
def _validate_snapshot_binding(
367+
snapshot: dict[str, object], lock: PoliticalEventWeeklyPeriodLockV1, reviewed_workflow_sha: str
368+
) -> None:
369+
expected = _expected_snapshot(lock, reviewed_workflow_sha)
370+
if snapshot != expected:
371+
raise _error("bundle_snapshot_mismatch")
372+
373+
363374
def build_period_bundle(
364375
lock_bytes: object,
365376
snapshot_bytes: object,
@@ -376,7 +387,8 @@ def build_period_bundle(
376387
_expected_snapshot(lock, identity["reviewed_workflow_sha"]), "bundle_snapshot_invalid"
377388
)
378389
parsed_snapshot = _snapshot(snapshot_bytes)
379-
if snapshot_bytes != expected_snapshot or parsed_snapshot != json.loads(expected_snapshot):
390+
_validate_snapshot_binding(parsed_snapshot, lock, identity["reviewed_workflow_sha"])
391+
if snapshot_bytes != expected_snapshot:
380392
raise _error("bundle_snapshot_mismatch")
381393
evidence = _artifact(artifact, lock.source_run_id)
382394
manifest = _manifest_bytes(lock, identity, evidence, lock_bytes, snapshot_bytes)
@@ -425,6 +437,8 @@ def verify_period_bundle(
425437
raise _error("bundle_lock_mismatch")
426438
if values["snapshot_bytes"] != expected_snapshot_bytes or actual_snapshot != expected_snapshot:
427439
raise _error("bundle_snapshot_mismatch")
440+
_validate_snapshot_binding(expected_snapshot, expected_lock, expected_identity["reviewed_workflow_sha"])
441+
_validate_snapshot_binding(actual_snapshot, actual_lock, manifest["reviewed_workflow_sha"])
428442
if actual_evidence != expected_evidence:
429443
raise _error("bundle_artifact_mismatch")
430444
if actual_lock.workflow_ref != TRUSTED_WORKFLOW_REF:

tests/test_trusted_period_bundle_raw.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ def test_manifest_identity_fields_are_validated_before_reconstructed_equality()
135135
verify_period_bundle(value, lock_bytes(), snapshot_bytes(), IDENTITY_BYTES, artifact())
136136

137137

138+
@pytest.mark.parametrize("attempt", [True, 1.0, 2**53, (1, 2)])
139+
def test_source_attempt_wire_type_is_exact(attempt: object) -> None:
140+
value = snapshot_value()
141+
value["source_attempt"] = attempt
142+
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
143+
with pytest.raises(TrustedPeriodBundleError, match="bundle_snapshot_"):
144+
build_period_bundle(lock_bytes(), raw, IDENTITY_BYTES, artifact())
145+
146+
147+
def test_hash_correct_semantic_snapshot_forgery_is_rejected() -> None:
148+
forged = snapshot_value()
149+
forged["source_snapshot_digest"] = "0" * 64
150+
forged_bytes = json.dumps(forged, sort_keys=True, separators=(",", ":")).encode()
151+
with pytest.raises(TrustedPeriodBundleError, match="bundle_snapshot_mismatch"):
152+
verify_period_bundle(bundle(), lock_bytes(), forged_bytes, IDENTITY_BYTES, artifact())
153+
154+
155+
def test_manifest_source_attempt_type_is_exact() -> None:
156+
value = bundle()
157+
manifest = json.loads(value["manifest_bytes"])
158+
manifest["source_attempt"] = True
159+
value["manifest_bytes"] = json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()
160+
with pytest.raises(TrustedPeriodBundleError, match="bundle_manifest_"):
161+
verify_period_bundle(value, lock_bytes(), snapshot_bytes(), IDENTITY_BYTES, artifact())
162+
163+
138164
@pytest.mark.parametrize("field", ["artifact", "identity", "lock_expected", "snapshot_expected"])
139165
def test_context_tamper_fails_closed(field: str) -> None:
140166
value = bundle()

0 commit comments

Comments
 (0)