diff --git a/main.py b/main.py index c7810580..8451a6ea 100644 --- a/main.py +++ b/main.py @@ -273,6 +273,21 @@ def is_valid_rule(rule: str) -> bool: return True +def is_valid_folder_name(name: str) -> bool: + """ + Validates that a folder name is safe. + Rejects potentially dangerous characters (XSS/Injection). + """ + if not name or not name.isprintable(): + return False + + # Block XSS and injection chars + dangerous_chars = set("<>\"'`") + if any(c in dangerous_chars for c in name): + return False + + return True + def validate_folder_data(data: Dict[str, Any], url: str) -> bool: if not isinstance(data, dict): log.error(f"Invalid data from {sanitize_for_log(url)}: Root must be a JSON object.") @@ -286,6 +301,16 @@ def validate_folder_data(data: Dict[str, Any], url: str) -> bool: if "group" not in data["group"]: log.error(f"Invalid data from {sanitize_for_log(url)}: Missing 'group.group' (folder name).") return False + + folder_name = data["group"]["group"] + if not isinstance(folder_name, str): + log.error(f"Invalid data from {sanitize_for_log(url)}: Folder name must be a string.") + return False + + if not is_valid_folder_name(folder_name): + log.error(f"Invalid data from {sanitize_for_log(url)}: Folder name contains unsafe characters.") + return False + return True def _api_get(client: httpx.Client, url: str) -> httpx.Response: diff --git a/tests/test_security.py b/tests/test_security.py index 579c60d1..c11fd344 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -85,3 +85,33 @@ def test_push_rules_filters_xss_payloads(): finally: main._api_post_form = original_post_form main.log = original_log + +def test_validate_folder_data_sanitizes_names(): + """ + Verify that validate_folder_data rejects unsafe folder names (XSS prevention). + """ + # Mock logger to check error messages + mock_log = MagicMock() + original_log = main.log + main.log = mock_log + + try: + # 1. Valid Folder Name + valid_data = {"group": {"group": "My Safe Folder"}} + assert main.validate_folder_data(valid_data, "https://example.com/valid.json") is True + + # 2. XSS Payload in Folder Name + xss_data = {"group": {"group": ""}} + assert main.validate_folder_data(xss_data, "https://example.com/xss.json") is False + assert mock_log.error.called + + # 3. Non-string Folder Name + bad_type_data = {"group": {"group": 12345}} + assert main.validate_folder_data(bad_type_data, "https://example.com/bad_type.json") is False + + # 4. Dangerous chars (quote) + quote_data = {"group": {"group": "Folder \"Bad\""}} + assert main.validate_folder_data(quote_data, "https://example.com/quote.json") is False + + finally: + main.log = original_log