From 15dc087b76cb2b22978754d3beae736e3b9c3597 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Sat, 5 Sep 2026 14:35:16 +0800 Subject: [PATCH] fix: reject malformed broker order responses Co-Authored-By: Codex --- application/firstrade_client.py | 9 +++++---- tests/test_firstrade_client.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/application/firstrade_client.py b/application/firstrade_client.py index f9456b9..3668130 100644 --- a/application/firstrade_client.py +++ b/application/firstrade_client.py @@ -451,14 +451,15 @@ def get_positions(self, account: str) -> dict[str, Any]: def get_orders(self, account: str, *, per_page: int = 0) -> list[dict[str, Any]]: _, account_data = self.require_connected() payload = account_data.get_orders(account, per_page=per_page) - if isinstance(payload, list): - return [dict(row) for row in payload if isinstance(row, dict)] if isinstance(payload, dict): for key in ("items", "orders", "data", "result"): value = payload.get(key) if isinstance(value, list): - return [dict(row) for row in value if isinstance(row, dict)] - return [] + payload = value + break + if not isinstance(payload, list) or any(not isinstance(row, dict) for row in payload): + raise FirstradePlatformError("Firstrade returned an invalid order response.") + return [dict(row) for row in payload] def get_order_status(self, account: str, order_id: str) -> dict[str, Any] | None: normalized_order_id = str(order_id or "").strip() diff --git a/tests/test_firstrade_client.py b/tests/test_firstrade_client.py index 76bd079..c92eed2 100644 --- a/tests/test_firstrade_client.py +++ b/tests/test_firstrade_client.py @@ -7,6 +7,7 @@ from application.firstrade_client import ( FirstradeBrokerClient, FirstradeCredentials, + FirstradePlatformError, FirstradeSafetyError, StockOrderRequest, mask_account_id, @@ -314,3 +315,29 @@ def test_client_reuses_persisted_session_cache_when_local_cache_is_missing(tmp_p assert second_client.session_reused is True assert ReusableFakeSession.login_calls == 1 assert store.writes == 2 + + +@pytest.mark.parametrize( + "payload", + [None, {}, {"error": "private-provider-response"}, [None], {"items": [{}, "private-row"]}], +) +def test_order_reads_reject_incomplete_payload_instead_of_empty_success(payload): + client = FirstradeBrokerClient(FirstradeCredentials(username="unused", password="unused")) + client.session = object() + client.account_data = SimpleNamespace(get_orders=lambda *_args, **_kwargs: payload) + + for read in (lambda: client.get_orders("test-account"), lambda: client.get_order_status("test-account", "test-order")): + with pytest.raises(FirstradePlatformError) as error: + read() + assert str(error.value) == "Firstrade returned an invalid order response." + + +@pytest.mark.parametrize("wrapper", [None, "items", "orders", "data", "result"]) +@pytest.mark.parametrize("rows", [[], [{"order_id": "test-order", "status": "Submitted"}]]) +def test_order_reads_preserve_supported_complete_payloads(wrapper, rows): + payload = rows if wrapper is None else {wrapper: rows} + client = FirstradeBrokerClient(FirstradeCredentials(username="unused", password="unused")) + client.session = object() + client.account_data = SimpleNamespace(get_orders=lambda *_args, **_kwargs: payload) + + assert client.get_orders("test-account") == rows