From 07525bdaf17d8a7c4f280b2f194e3dbd2c25bae5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:18:18 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20XSS=20risk=20in=20folder=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add strict validation for folder names parsed from external JSON files to prevent potential Stored XSS vulnerabilities. - Implement `is_valid_folder_name` to block XSS characters (< > " ' `) - Update `validate_folder_data` to check folder names - Add tests in `tests/test_folder_validation.py` covering XSS payloads and invalid types - Add journal entry for incomplete input validation scope --- .jules/sentinel.md | 7 +++++++ main.py | 26 ++++++++++++++++++++++++++ tests/test_folder_validation.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 tests/test_folder_validation.py diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a1ed0d5d..7f6f3daa 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -60,3 +60,10 @@ **Prevention:** 1. Implement strict validation on all data items from external lists (`is_valid_rule`). 2. Filter out items containing dangerous characters (`<`, `>`, `"` etc.) or control codes. + +## 2026-06-01 - [Incomplete Input Validation Scope] +**Vulnerability:** While rule content was validated for XSS characters, the `folder_name` (parsed from the same JSON structure) was not. This created an inconsistency where one part of the untrusted input was sanitized, but another (which also appears in the UI) was left vulnerable to Stored XSS. +**Learning:** Validation MUST cover the entire schema of untrusted input. Focusing only on the "payload" (rules) while ignoring metadata (names, descriptions) leaves gaps. +**Prevention:** +1. Map out the full JSON schema of external inputs. +2. Apply validation functions to ALL string fields, not just the primary data arrays. diff --git a/main.py b/main.py index c7810580..0046f0c0 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 folder name to prevent XSS and ensure printability. + Allowed: Anything printable except < > " ' ` + """ + if not name or not name.isprintable(): + return False + + # Block XSS and HTML injection characters + 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,17 @@ 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 + + # Security: Validate folder name + 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)}: Unsafe folder name detected.") + return False + return True def _api_get(client: httpx.Client, url: str) -> httpx.Response: diff --git a/tests/test_folder_validation.py b/tests/test_folder_validation.py new file mode 100644 index 00000000..f9c72357 --- /dev/null +++ b/tests/test_folder_validation.py @@ -0,0 +1,32 @@ +import pytest +from unittest.mock import MagicMock +import main + +def test_folder_name_security(): + """ + Verify that validate_folder_data enforces security checks on folder names. + """ + # Mock logger to verify errors + mock_log = MagicMock() + original_log = main.log + main.log = mock_log + + try: + # Case 1: Valid Folder Name + valid_data = {"group": {"group": "Safe Folder Name (Work)"}} + assert main.validate_folder_data(valid_data, "http://valid.com") is True + + # Case 2: XSS Payload + xss_data = {"group": {"group": ""}} + assert main.validate_folder_data(xss_data, "http://evil.com") is False + + # Case 3: Invalid Type + invalid_type_data = {"group": {"group": 123}} + assert main.validate_folder_data(invalid_type_data, "http://badtype.com") is False + + # Case 4: Dangerous characters + dangerous_data = {"group": {"group": "Folder\"Name"}} + assert main.validate_folder_data(dangerous_data, "http://dangerous.com") is False + + finally: + main.log = original_log