diff --git a/app_config.py b/app_config.py index cd8ebf9..6adb3e7 100644 --- a/app_config.py +++ b/app_config.py @@ -66,6 +66,8 @@ class AppConfig: def _check_keys(section: Dict[str, Any], known: set, where: str) -> None: + if not isinstance(section, dict): + raise AppConfigError(f"config.json: {where} must be a JSON object") unknown = sorted(set(section) - known) if unknown: raise AppConfigError( @@ -141,6 +143,8 @@ def load_app_config(path: str = CONFIG_PATH) -> AppConfig: app.scan_timeframes = [Timeframe[n] for n in names] pair_section = raw.get("pairs", {}) + if not isinstance(pair_section, dict): + raise AppConfigError('config.json: "pairs" must be a JSON object') for symbol, overrides in pair_section.items(): if symbol not in PAIR_INFO: raise AppConfigError( diff --git a/test_all.py b/test_all.py index 2f169a8..ed0dfe7 100644 --- a/test_all.py +++ b/test_all.py @@ -2661,6 +2661,22 @@ def _(): assert telegram_channel_id(app) == "" +@test("app_config: non-object sections raise AppConfigError") +def _(): + for payload in ( + {"telegram": None}, + {"telegram": "oops"}, + {"scanning": 5}, + {"pairs": ["oops"]}, + {"pairs": {"XAU/USD": ["blocked_hours_utc"]}}, + ): + try: + load_app_config(_write_json(payload)) + assert False, f"should have raised for {payload}" + except AppConfigError: + pass + + # ══════════════════════════════════════════════════════════════════════ # SUMMARY # ══════════════════════════════════════════════════════════════════════