Skip to content

Commit 3c45968

Browse files
Pigbibicodex
andcommitted
Harden exact legacy payload validation
Co-Authored-By: Codex <noreply@openai.com>
1 parent 2fb6e5f commit 3c45968

2 files changed

Lines changed: 56 additions & 2 deletions

File tree

src/publish.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ def _sha256_file(path: Path) -> str:
134134
return digest.hexdigest()
135135

136136

137+
def _reject_non_standard_json_constant(value: str) -> None:
138+
raise ValueError(f"Non-standard JSON constant is not allowed: {value}")
139+
140+
137141
def _validate_runtime_evidence_identity(
138142
*,
139143
identity: Any,
@@ -315,8 +319,11 @@ def build_firestore_payload(
315319
except UnicodeDecodeError as exc:
316320
raise ValueError("live_pool_legacy.json exact bytes must be valid UTF-8.") from exc
317321
try:
318-
exact_payload = json.loads(exact_text)
319-
except json.JSONDecodeError as exc:
322+
exact_payload = json.loads(
323+
exact_text,
324+
parse_constant=_reject_non_standard_json_constant,
325+
)
326+
except ValueError as exc:
320327
raise ValueError("live_pool_legacy.json exact bytes must contain valid JSON.") from exc
321328
if not isinstance(exact_payload, dict):
322329
raise ValueError("live_pool_legacy.json exact bytes must contain a JSON object.")
@@ -343,6 +350,12 @@ def build_firestore_payload(
343350
or exact_payload.get("symbols") != artifacts.live_pool_legacy.get("symbols")
344351
or exact_payload.get("symbol_map")
345352
!= artifacts.live_pool_legacy.get("symbol_map")
353+
or exact_payload.get("pool_size")
354+
!= artifacts.live_pool_legacy.get("pool_size")
355+
or exact_payload.get("pool_size") != artifacts.live_pool.get("pool_size")
356+
or exact_payload.get("source_project")
357+
!= artifacts.live_pool_legacy.get("source_project")
358+
or exact_payload.get("source_project") != settings.source_project
346359
or list(exact_payload.get("symbols", {})) != symbols
347360
or exact_payload.get("symbol_map") != symbol_map
348361
):

tests/test_release_contract.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,51 @@ def test_firestore_payload_rejects_top_level_convenience_field_mismatch(self) ->
337337
storage_layout,
338338
)
339339

340+
def test_firestore_payload_rejects_pool_size_or_source_project_mismatch(self) -> None:
341+
cases = {
342+
"pool_size_mismatch": ("pool_size", 4),
343+
"pool_size_missing": ("pool_size", None),
344+
"source_project_mismatch": ("source_project", "wrong-source"),
345+
"source_project_missing": ("source_project", None),
346+
}
347+
for label, (field, value) in cases.items():
348+
with self.subTest(label=label), tempfile.TemporaryDirectory() as tmp_dir:
349+
root = Path(tmp_dir)
350+
output_dir = self.build_runtime_identity_outputs(root)
351+
legacy_path = output_dir / "live_pool_legacy.json"
352+
legacy_payload = json.loads(legacy_path.read_text(encoding="utf-8"))
353+
if value is None:
354+
legacy_payload.pop(field)
355+
else:
356+
legacy_payload[field] = value
357+
write_json(legacy_path, legacy_payload)
358+
359+
manifest_path = output_dir / "artifact_manifest.json"
360+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
361+
digest = sha256_file(legacy_path)
362+
manifest["artifacts"]["live_pool_legacy"]["sha256"] = digest
363+
manifest["runtime_evidence_identity"]["artifacts"][
364+
"live_pool_legacy"
365+
]["sha256"] = digest
366+
write_json(manifest_path, manifest)
367+
368+
artifacts, settings, storage_layout = self.load_publish_context(
369+
output_dir
370+
)
371+
with self.assertRaisesRegex(ValueError, "convenience fields"):
372+
build_firestore_payload(
373+
settings,
374+
artifacts,
375+
storage_layout,
376+
)
377+
340378
def test_firestore_payload_rejects_invalid_legacy_artifact_bytes(self) -> None:
341379
cases = {
342380
"invalid_utf8": (b"\xff", "valid UTF-8"),
343381
"invalid_json": (b"{", "valid JSON"),
382+
"nan": (b'{"value": NaN}', "valid JSON"),
383+
"infinity": (b'{"value": Infinity}', "valid JSON"),
384+
"negative_infinity": (b'{"value": -Infinity}', "valid JSON"),
344385
"non_object": (b"[]", "JSON object"),
345386
}
346387
for label, (invalid_bytes, expected_error) in cases.items():

0 commit comments

Comments
 (0)