From 7975b09325010b0ed248defc222a493af98d6f02 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 15 Jun 2026 10:09:30 -0400 Subject: [PATCH] fix(python): bound galaxy-browse --depth; assert no _text leak in JSON Guard _galaxy_browse against unbounded recursion by rejecting --depth values outside [0, 50] with a descriptive BadParameter. Add test coverage for --depth 99 and --depth -1 rejection, and assert _text is never present in the JSON output from galaxy-browse. --- .../src/zb_mom_ww_mxgateway_cli/commands.py | 4 ++-- clients/python/tests/test_cli.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/clients/python/src/zb_mom_ww_mxgateway_cli/commands.py b/clients/python/src/zb_mom_ww_mxgateway_cli/commands.py index ccc296a..9f45e1c 100644 --- a/clients/python/src/zb_mom_ww_mxgateway_cli/commands.py +++ b/clients/python/src/zb_mom_ww_mxgateway_cli/commands.py @@ -1086,8 +1086,8 @@ async def _galaxy_discover(**kwargs: Any) -> dict[str, Any]: async def _galaxy_browse(**kwargs: Any) -> dict[str, Any]: depth = int(kwargs["depth"]) - if depth < 0: - raise click.BadParameter("must be non-negative", param_hint="--depth") + if depth < 0 or depth > 50: + raise click.BadParameter("--depth must be between 0 and 50", param_hint="--depth") options = BrowseChildrenOptions( category_ids=tuple(kwargs.get("category_ids") or ()), template_chain_contains=tuple(kwargs.get("template_chain_contains") or ()), diff --git a/clients/python/tests/test_cli.py b/clients/python/tests/test_cli.py index e9cf95a..09011d4 100644 --- a/clients/python/tests/test_cli.py +++ b/clients/python/tests/test_cli.py @@ -392,6 +392,7 @@ def test_galaxy_browse_serializes_nested_nodes(monkeypatch: pytest.MonkeyPatch) assert result.exit_code == 0, result.output payload = json.loads(result.output) + assert "_text" not in payload assert payload["command"] == "galaxy-browse" assert len(payload["nodes"]) == 1 node = payload["nodes"][0] @@ -491,3 +492,20 @@ def test_galaxy_commands_are_registered() -> None: result = runner.invoke(main, [command, "--help"]) assert result.exit_code == 0, result.output assert "--endpoint" in result.output + + +@pytest.mark.parametrize("depth_arg", ["99", "-1"]) +def test_galaxy_browse_rejects_out_of_range_depth( + monkeypatch: pytest.MonkeyPatch, + depth_arg: str, +) -> None: + """--depth values outside [0, 50] must be rejected with a non-zero exit.""" + _patch_galaxy_connect(monkeypatch, _FakeGalaxyClient(browse_roots=[])) + + result = CliRunner().invoke( + main, + ["galaxy-browse", "--plaintext", "--depth", depth_arg, "--json"], + ) + + assert result.exit_code != 0 + assert "--depth must be between 0 and 50" in result.output