From 9302f0fe168b8591559121f90b0df2f2d381f9e7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 11:22:10 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[MEDIUM]=20?= =?UTF-8?q?Add=20input=20length=20limits=20to=20prevent=20DoS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com> --- .jules/sentinel.md | 8 ++++++ main.py | 17 +++++++++--- tests/test_security_limits.py | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/test_security_limits.py diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 038829ea..e149e009 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -61,3 +61,11 @@ **Learning:** Logic errors in security controls often lead to "fail-closed" states that break functionality entirely, or "fail-open" states that bypass security. Implicit returns in Python (`None`) can be dangerous when boolean validation is expected. **Prevention:** Always use explicit return statements for both success and failure paths in validation functions. Use static analysis (linting) to catch unreachable code and implicit returns. Ensure unit tests cover positive cases (valid inputs) as rigorously as negative cases (attack vectors). + +## 2026-10-24 - Missing Input Length Limits (DoS Risk) + +**Vulnerability:** The application accepted unlimited length strings for folder names and rules. While validation checked for dangerous characters, extremely long strings could cause Denial of Service (DoS) or buffer issues in downstream systems/APIs. + +**Learning:** Regex validation and character whitelisting are not enough; explicit length limits are required for complete input validation "Defense in Depth". + +**Prevention:** Define explicit maximum constants (e.g., `MAX_FOLDER_NAME_LENGTH = 64`, `MAX_RULE_LENGTH = 255`) and enforce them in validation functions (`is_valid_folder_name`, `is_valid_rule`). diff --git a/main.py b/main.py index c835ff1b..ce64abe9 100644 --- a/main.py +++ b/main.py @@ -208,6 +208,11 @@ def check_env_permissions(env_path: str = ".env") -> None: # Path separators (prevent confusion and directory traversal attempts) _DANGEROUS_FOLDER_CHARS.update(["/", "\\"]) +# Security: Input length limits +MAX_FOLDER_NAME_LENGTH = 64 +MAX_RULE_LENGTH = 255 +MAX_PROFILE_ID_LENGTH = 64 + # Security: Unicode Bidi control characters (prevent RTLO/homograph attacks) # These characters can be used to mislead users about file extensions or content # See: https://en.wikipedia.org/wiki/Right-to-left_override @@ -993,7 +998,7 @@ def is_valid_profile_id_format(profile_id: str) -> bool: """ if not PROFILE_ID_PATTERN.match(profile_id): return False - if len(profile_id) > 64: + if len(profile_id) > MAX_PROFILE_ID_LENGTH: return False return True @@ -1009,8 +1014,8 @@ def validate_profile_id(profile_id: str, log_errors: bool = True) -> bool: if log_errors: if not PROFILE_ID_PATTERN.match(profile_id): log.error("Invalid profile ID format (contains unsafe characters)") - elif len(profile_id) > 64: - log.error("Invalid profile ID length (max 64 chars)") + elif len(profile_id) > MAX_PROFILE_ID_LENGTH: + log.error(f"Invalid profile ID length (max {MAX_PROFILE_ID_LENGTH} chars)") return False return True @@ -1035,6 +1040,9 @@ def is_valid_rule(rule: str) -> bool: if not rule: return False + if len(rule) > MAX_RULE_LENGTH: + return False + # Strict whitelist to prevent injection if not RULE_PATTERN.match(rule): return False @@ -1056,6 +1064,9 @@ def is_valid_folder_name(name: str) -> bool: if not name or not name.strip() or not name.isprintable(): return False + if len(name) > MAX_FOLDER_NAME_LENGTH: + return False + # Check for dangerous characters (pre-compiled at module level for performance) if any(c in _DANGEROUS_FOLDER_CHARS or c in _BIDI_CONTROL_CHARS for c in name): return False diff --git a/tests/test_security_limits.py b/tests/test_security_limits.py new file mode 100644 index 00000000..f671223a --- /dev/null +++ b/tests/test_security_limits.py @@ -0,0 +1,50 @@ +import pytest +import main + +def test_is_valid_folder_name_length_limit(): + """ + Test that folder names exceeding the maximum length are rejected. + Current behavior: Accepts any length. + Expected behavior: Should reject length > 64. + """ + # Create a name with 65 characters + long_name = "a" * 65 + + # This should return False after the fix, but currently returns True + # We assert False to confirm the "failure" (vulnerability presence) or "success" (fix verification) + assert main.is_valid_folder_name(long_name) is False + +def test_is_valid_folder_name_acceptable_length(): + """Test that folder names within limit are accepted.""" + name = "a" * 64 + assert main.is_valid_folder_name(name) is True + +def test_is_valid_rule_length_limit(): + """ + Test that rules exceeding the maximum length are rejected. + Current behavior: Accepts any length (matching regex). + Expected behavior: Should reject length > 255. + """ + # Create a rule with 256 characters (valid chars) + long_rule = "a" * 256 + ".com" + + # This should return False after the fix + assert main.is_valid_rule(long_rule) is False + +def test_is_valid_rule_acceptable_length(): + """Test that rules within limit are accepted.""" + rule = "a" * 250 + ".com" + assert main.is_valid_rule(rule) is True + +def test_is_valid_profile_id_length_limit_constant(): + """ + Test that profile ID validation respects the length limit. + Note: This function already had a length check, we are just formalizing it with a constant. + """ + # 65 chars + long_id = "a" * 65 + assert main.validate_profile_id(long_id, log_errors=False) is False + + # 64 chars + valid_id = "a" * 64 + assert main.validate_profile_id(valid_id, log_errors=False) is True