Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-05-24 - [Weak Blacklist Validation for Rules]

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when references to undefined definitions are found. Note

[no-undefined-references] Found reference to undefined definition

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when shortcut reference links are used. Note

[no-shortcut-reference-link] Use the trailing [] on reference links
**Vulnerability:** The `is_valid_rule` function used a blacklist of dangerous characters (`<>\"'`();{}[]`) to sanitize rules. This missed several dangerous characters like `&`, `|`, and ` ` which could allow command injection or other attacks if the backend processed rules unsafely.
**Learning:** Blacklists are inherently fragile because they require knowing every possible dangerous character. Whitelists are robust because they define exactly what is allowed.
**Prevention:**
1. Replace blacklists with strict whitelists whenever possible.
2. Use regex to enforce allowable character sets (e.g., `^[a-zA-Z0-9.\-_:*\/]+$`).
13 changes: 6 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,15 @@ def validate_profile_id(profile_id: str) -> bool:
def is_valid_rule(rule: str) -> bool:
"""
Validates that a rule is safe to use.
Rejects potential XSS payloads or control characters.
Enforces a strict whitelist of allowed characters.
Allowed: Alphanumeric, hyphen, dot, underscore, asterisk, colon (IPv6), slash (CIDR)
"""
if not rule or not rule.isprintable():
if not rule:
return False

# Block characters common in XSS and injection attacks
# Allowed: Alphanumeric, hyphen, dot, underscore, asterisk, colon (IPv6), slash (CIDR)
# Block: < > " ' ` ( ) ; { } [ ]
dangerous_chars = set("<>\"'`();{}[]")
if any(c in dangerous_chars for c in rule):
# Strict whitelist to prevent injection
# ^[a-zA-Z0-9.\-_:*\/]+$
if not re.match(r"^[a-zA-Z0-9.\-_:*\/]+$", rule):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is a great security improvement! For a performance optimization, consider pre-compiling this regular expression at the module level. Since is_valid_rule is called inside a loop for every rule, pre-compiling avoids the overhead of parsing the regex pattern on each call.

You could do something like this:

# At the top of main.py
_VALID_RULE_RE = re.compile(r"^[a-zA-Z0-9.\-_:*\/]+$")

# Then in is_valid_rule()
if not _VALID_RULE_RE.match(rule):
    return False

return False

return True
Expand Down
34 changes: 34 additions & 0 deletions tests/test_security.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Missing module docstring Warning test

Missing module docstring

Check warning

Code scanning / Pylint (reported by Codacy)

Missing module docstring Warning test

Missing module docstring
import pytest
from unittest.mock import MagicMock
import main
from main import is_valid_rule

def test_push_rules_filters_xss_payloads():
"""
Expand Down Expand Up @@ -85,3 +87,35 @@
finally:
main._api_post_form = original_post_form
main.log = original_log

@pytest.mark.parametrize("rule,expected_validity", [
# Valid Inputs (should pass)
("example.com", True),
("1.2.3.4", True),
("2001:db8::1", True),
("192.168.1.0/24", True),
("*.example.com", True),
("_sip._tcp.example.com", True),
("valid-domain-with-hyphens.com", True),
("sub.domain.example.com", True),

# Invalid Inputs (should fail)
# These contain characters NOT in the whitelist (and some not in the old blacklist)
("example.com && rm -rf /", False), # Contains spaces and &
("$(whoami).example.com", False), # Contains $ and ( )
("example.com|nc 1.2.3.4 80", False),# Contains | and space
("example.com; ls", False), # Contains ; and space
("<script>alert(1)</script>", False),# Contains < > ( )
("invalid@email.com", False), # Contains @ (not allowed in whitelist)
("http://example.com", True), # Safe from injection (contains only allowed chars)
("example.com/path", True), # Allowed (CIDR uses /)
Comment on lines +110 to +111

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

These two test cases seem to assert that invalid rule formats are valid, which could lead to incorrect behavior or mask future bugs.

  • http://example.com is a URL, not a valid domain/IP/CIDR rule for DNS filtering.
  • example.com/path is also not a valid rule format. The / is intended for CIDR notation with IPs, not for paths with domains.

These should likely be expected to fail validation. This would also mean tightening the regex in is_valid_rule to be more semantically strict, which would make the function more robust.

Suggested change
("http://example.com", True), # Safe from injection (contains only allowed chars)
("example.com/path", True), # Allowed (CIDR uses /)
("http://example.com", False), # Not a valid rule format (URL, not domain/IP)
("example.com/path", False), # Not a valid rule format (domain with path)

("foo bar", False), # Space not allowed
("foo\tbar", False), # Tab not allowed
("foo\nbar", False), # Newline not allowed
("`touch hacked`", False), # Backtick not allowed
])
def test_is_valid_rule_strict(rule, expected_validity):
"""
Tests the is_valid_rule function against a strict whitelist of inputs.
"""
assert is_valid_rule(rule) == expected_validity, f"Failed for rule: {rule}"

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check notice

Code scanning / Bandit (reported by Codacy)

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Loading