-
Notifications
You must be signed in to change notification settings - Fork 1
🛡️ Sentinel: Enforce strict whitelist for rule validation #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a great security improvement! For a performance optimization, consider pre-compiling this regular expression at the module level. Since 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||
|
|
||||||||||
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing module docstring Warning test
Missing module docstring
Check warningCode 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(): | ||||||||||
| """ | ||||||||||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two test cases seem to assert that invalid rule formats are valid, which could lead to incorrect behavior or mask future bugs.
These should likely be expected to fail validation. This would also mean tightening the regex in
Suggested change
|
||||||||||
| ("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 noticeCode 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 noticeCode 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.
|
||||||||||
Check notice
Code scanning / Remark-lint (reported by Codacy)
Warn when references to undefined definitions are found. Note