From 095b26cc051078da4f01ce63e9f2a2dad149730d Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 28 Apr 2026 01:34:11 -0700 Subject: [PATCH] fix: harden CLI config parsing for empty/non-object YAML --- promptlens/cli.py | 7 +++++++ tests/test_cli_config_validation.py | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/test_cli_config_validation.py diff --git a/promptlens/cli.py b/promptlens/cli.py index 235963a..00be6f2 100644 --- a/promptlens/cli.py +++ b/promptlens/cli.py @@ -102,6 +102,13 @@ def run( with open(config, "r") as f: config_data = yaml.safe_load(f) + if config_data is None: + console.print("[red]Invalid configuration: configuration file is empty[/red]") + sys.exit(1) + if not isinstance(config_data, dict): + console.print("[red]Invalid configuration: top-level YAML must be a mapping/object[/red]") + sys.exit(1) + # Override with CLI options if golden_set: config_data["golden_set"] = golden_set diff --git a/tests/test_cli_config_validation.py b/tests/test_cli_config_validation.py new file mode 100644 index 0000000..35af73e --- /dev/null +++ b/tests/test_cli_config_validation.py @@ -0,0 +1,27 @@ +from pathlib import Path + +from click.testing import CliRunner + +from promptlens.cli import cli + + +def test_run_rejects_empty_config_file(tmp_path: Path) -> None: + config = tmp_path / "empty.yaml" + config.write_text("", encoding="utf-8") + + runner = CliRunner() + result = runner.invoke(cli, ["run", str(config), "--dry-run"]) + + assert result.exit_code == 1 + assert "configuration file is empty" in result.output + + +def test_run_rejects_non_mapping_top_level_config(tmp_path: Path) -> None: + config = tmp_path / "list.yaml" + config.write_text("- a\n- b\n", encoding="utf-8") + + runner = CliRunner() + result = runner.invoke(cli, ["run", str(config), "--dry-run"]) + + assert result.exit_code == 1 + assert "top-level YAML must be a mapping/object" in result.output