From 30cab3ad7300dd69435f1f1b9c104f6efe67452b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:01:51 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[MEDIUM]=20?= =?UTF-8?q?Fix=20missing=20input=20validation=20on=20external=20JSON=20dat?= =?UTF-8?q?a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added validation for `rules` and `rule_groups` types in `validate_folder_data` to prevent crashes. - Added comprehensive unit tests in `test_main.py`. - Fixed existing test mocks to handle `clear_cache` argument. Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com> --- main.py | 24 ++++++++++++++++++++++++ test_main.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/main.py b/main.py index c835ff1..34c155f 100644 --- a/main.py +++ b/main.py @@ -1112,6 +1112,30 @@ def validate_folder_data(data: Dict[str, Any], url: str) -> bool: ) return False + # Validate 'rules' if present (must be a list) + if "rules" in data and not isinstance(data["rules"], list): + log.error(f"Invalid data from {sanitize_for_log(url)}: 'rules' must be a list.") + return False + + # Validate 'rule_groups' if present (must be a list of dicts) + if "rule_groups" in data: + if not isinstance(data["rule_groups"], list): + log.error( + f"Invalid data from {sanitize_for_log(url)}: 'rule_groups' must be a list." + ) + return False + for i, rg in enumerate(data["rule_groups"]): + if not isinstance(rg, dict): + log.error( + f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}] must be an object." + ) + return False + if "rules" in rg and not isinstance(rg["rules"], list): + log.error( + f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}].rules must be a list." + ) + return False + return True diff --git a/test_main.py b/test_main.py index e8c1932..d585ef3 100644 --- a/test_main.py +++ b/test_main.py @@ -229,6 +229,7 @@ def test_interactive_prompts_show_hints(monkeypatch, capsys): mock_args.dry_run = False mock_args.no_delete = False mock_args.plan_json = None + mock_args.clear_cache = False monkeypatch.setattr(m, "parse_args", lambda: mock_args) # Mock internal functions to abort execution safely after prompts @@ -434,6 +435,7 @@ def test_interactive_input_extracts_id(monkeypatch, capsys): mock_args.dry_run = False mock_args.no_delete = False mock_args.plan_json = None + mock_args.clear_cache = False monkeypatch.setattr(m, "parse_args", lambda: mock_args) # Mock sync_profile to catch the call @@ -723,3 +725,51 @@ def test_check_env_permissions_secure(monkeypatch): assert mock_open.called assert not mock_fchmod.called mock_close.assert_called_with(123) + + +def test_validate_folder_data_structure(monkeypatch): + """Test validation of 'rules' and 'rule_groups' structures.""" + m = reload_main_with_env(monkeypatch) + mock_log = MagicMock() + monkeypatch.setattr(m, "log", mock_log) + + valid_base = { + "group": {"group": "ValidFolder"} + } + + # 1. Invalid 'rules' type (string instead of list) + invalid_rules = valid_base.copy() + invalid_rules["rules"] = "not_a_list" + assert m.validate_folder_data(invalid_rules, "url") is False + assert "rules" in str(mock_log.error.call_args) + mock_log.reset_mock() + + # 2. Invalid 'rule_groups' type (string instead of list) + invalid_rg_type = valid_base.copy() + invalid_rg_type["rule_groups"] = "not_a_list" + assert m.validate_folder_data(invalid_rg_type, "url") is False + assert "rule_groups" in str(mock_log.error.call_args) + mock_log.reset_mock() + + # 3. Invalid 'rule_groups' content (list of strings instead of dicts) + invalid_rg_content = valid_base.copy() + invalid_rg_content["rule_groups"] = ["not_a_dict"] + assert m.validate_folder_data(invalid_rg_content, "url") is False + assert "must be an object" in str(mock_log.error.call_args) + mock_log.reset_mock() + + # 4. Invalid 'rules' inside 'rule_groups' + invalid_rg_rules = valid_base.copy() + invalid_rg_rules["rule_groups"] = [{"rules": "not_a_list"}] + assert m.validate_folder_data(invalid_rg_rules, "url") is False + assert "must be a list" in str(mock_log.error.call_args) + mock_log.reset_mock() + + # 5. Valid cases + valid_rules = valid_base.copy() + valid_rules["rules"] = [{"PK": "rule1"}] + assert m.validate_folder_data(valid_rules, "url") is True + + valid_rg = valid_base.copy() + valid_rg["rule_groups"] = [{"rules": [{"PK": "rule1"}]}] + assert m.validate_folder_data(valid_rg, "url") is True