From 8c56c143fa4cd78ed3bbafe913f51d4240af238d Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 5 Jun 2026 12:00:20 -0500 Subject: [PATCH] perps(klear): reject non-canonical dates in margin-report range validation Closes #409. `_validate_date_range` used `datetime.date.fromisoformat`, which (3.11+) leniently accepts non-canonical forms like "20260501" or ISO week dates ("2026-W22-1") and then forwarded the raw string to a server expecting strict YYYY-MM-DD. Now, after parsing, the validator asserts `parsed.isoformat() == original` and rejects anything non-canonical at the SDK boundary. Test: extends the existing date-range guard test with the basic-format and week-date cases (both parse but are non-canonical). mypy strict + ruff + 46 klear margin tests green. --- kalshi/perps/klear/resources/margin.py | 13 +++++++++++++ tests/perps/klear/test_margin.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/kalshi/perps/klear/resources/margin.py b/kalshi/perps/klear/resources/margin.py index 40d19a5..6df8147 100644 --- a/kalshi/perps/klear/resources/margin.py +++ b/kalshi/perps/klear/resources/margin.py @@ -63,6 +63,19 @@ def _validate_date_range(start_date: str, end_date: str) -> None: f"start_date / end_date must be YYYY-MM-DD strings (got " f"start_date={start_date!r}, end_date={end_date!r}): {exc}" ) from exc + # ``date.fromisoformat`` (3.11+) also accepts non-canonical forms like + # "20260605" or ISO week dates ("2026-W23-5"), which the raw string would + # then forward to a server expecting strict YYYY-MM-DD. Require the canonical + # form so the SDK rejects them here rather than relying on a server 400. + for label, raw, parsed in ( + ("start_date", start_date, start), + ("end_date", end_date, end), + ): + if parsed.isoformat() != raw: + raise ValueError( + f"{label} must be a canonical YYYY-MM-DD date, got {raw!r} " + f"(parsed as {parsed.isoformat()})" + ) if end < start: raise ValueError( f"end_date ({end_date}) must be on or after start_date ({start_date})" diff --git a/tests/perps/klear/test_margin.py b/tests/perps/klear/test_margin.py index 4c65082..a32bab7 100644 --- a/tests/perps/klear/test_margin.py +++ b/tests/perps/klear/test_margin.py @@ -202,6 +202,12 @@ def test_rejects_malformed_or_inverted_dates_client_side( auth_klear_client.margin.margin_reports(start_date="nope", end_date="2026-06-01") with pytest.raises(ValueError, match="on or after"): auth_klear_client.margin.margin_reports(start_date="2026-06-02", end_date="2026-06-01") + # #409: fromisoformat is lenient — reject non-canonical forms that would + # otherwise be forwarded raw to a server expecting strict YYYY-MM-DD. + with pytest.raises(ValueError, match="canonical YYYY-MM-DD"): + auth_klear_client.margin.margin_reports(start_date="20260501", end_date="2026-06-01") + with pytest.raises(ValueError, match="canonical YYYY-MM-DD"): + auth_klear_client.margin.margin_reports(start_date="2026-05-01", end_date="2026-W22-1") assert not route.called auth_klear_client.close()