From 5e4bc55697089af3d0d9a31eda00b24cf5761770 Mon Sep 17 00:00:00 2001 From: Mubashir Rahim Date: Wed, 3 Jun 2026 14:58:39 +0500 Subject: [PATCH] fix: reject non-finite numeric metadata supplied as strings _coerce_number rejects NaN and infinite values for numeric inputs because they break JSON serialization and Postgres double precision storage, but the string branch returned float(text) directly. Strings like "inf", "-inf", "Infinity", "nan", and overflowing literals such as "1e400" (which Python parses to inf) slipped through type coercion when a field was declared as a number, corrupting the stored document. Apply the same finite-value check to the parsed string result. Extends the existing rejection test to cover string inputs; the new cases fail before and pass after the fix. Co-Authored-By: Claude Opus 4.8 --- core/tests/unit/test_typed_metadata.py | 12 ++++++++++++ core/utils/typed_metadata.py | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/tests/unit/test_typed_metadata.py b/core/tests/unit/test_typed_metadata.py index 198c3397..177cd1ed 100644 --- a/core/tests/unit/test_typed_metadata.py +++ b/core/tests/unit/test_typed_metadata.py @@ -157,6 +157,18 @@ def test_number_coercion_rejects_nan_and_infinity(self): with pytest.raises(TypedMetadataError, match="cannot store NaN or infinite"): _normalize_values({"value": float("inf")}, {"value": "number"}) + def test_number_coercion_rejects_nan_and_infinity_strings(self): + """Non-finite values supplied as strings must be rejected too. + + Regression: the string branch parsed "inf"/"nan" (and overflowing + literals like "1e400") into non-finite floats without the finite check + applied to numeric inputs, letting them through to storage where they + break JSON serialization and Postgres double precision columns. + """ + for token in ("inf", "-inf", "Infinity", "nan", "1e400"): + with pytest.raises(TypedMetadataError, match="cannot store NaN or infinite"): + _normalize_values({"value": token}, {"value": "number"}) + def test_decimal_coercion(self): """Test decimal coercion from various types.""" metadata = { diff --git a/core/utils/typed_metadata.py b/core/utils/typed_metadata.py index 307dc825..c9f9ff8a 100644 --- a/core/utils/typed_metadata.py +++ b/core/utils/typed_metadata.py @@ -238,9 +238,12 @@ def _coerce_number(value: Any, field: str) -> int | float: try: if all(ch.isdigit() or ch in {"+", "-", "_"} for ch in text.replace("_", "")) and "." not in text: return int(text.replace("_", "")) - return float(text) + parsed = float(text) except ValueError as exc: # noqa: BLE001 raise TypedMetadataError(f"Metadata field '{field}' expects a numeric value.") from exc + if math.isnan(parsed) or math.isinf(parsed): + raise TypedMetadataError(f"Metadata field '{field}' cannot store NaN or infinite values.") + return parsed raise TypedMetadataError(f"Metadata field '{field}' expects a numeric value.")