-
Notifications
You must be signed in to change notification settings - Fork 1
π‘οΈ Sentinel: [MEDIUM] Fix missing input validation on external JSON data #409
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 | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -955,7 +955,7 @@ | |||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||
| parsed = urlparse(url) | ||||||||||||||||||||||||||||||||||||||||
| hostname = parsed.hostname | ||||||||||||||||||||||||||||||||||||||||
| if not hostname: | ||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Prospector (reported by Codacy) Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
Use lazy % formatting in logging functions (logging-fstring-interpolation)
|
||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return validate_hostname(hostname) | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1089,7 +1089,7 @@ | |||||||||||||||||||||||||||||||||||||||
| log.error(f"Invalid data from {sanitize_for_log(url)}: Missing 'group' key.") | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
| if not isinstance(data["group"], dict): | ||||||||||||||||||||||||||||||||||||||||
| log.error( | ||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Pylintpython3 (reported by Codacy) Use lazy % formatting in logging functions Note
Use lazy % formatting in logging functions
|
||||||||||||||||||||||||||||||||||||||||
| f"Invalid data from {sanitize_for_log(url)}: 'group' must be an object." | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1112,6 +1112,30 @@ | |||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # Validate 'rules' if present (must be a list) | ||||||||||||||||||||||||||||||||||||||||
| if "rules" in data and not isinstance(data["rules"], list): | ||||||||||||||||||||||||||||||||||||||||
| log.error(f"Invalid data from {sanitize_for_log(url)}: 'rules' must be a list.") | ||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Pylintpython3 (reported by Codacy) Use lazy % formatting in logging functions Note
Use lazy % formatting in logging functions
|
||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1115
to
+1118
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # Validate 'rule_groups' if present (must be a list of dicts) | ||||||||||||||||||||||||||||||||||||||||
| if "rule_groups" in data: | ||||||||||||||||||||||||||||||||||||||||
| if not isinstance(data["rule_groups"], list): | ||||||||||||||||||||||||||||||||||||||||
| log.error( | ||||||||||||||||||||||||||||||||||||||||
| f"Invalid data from {sanitize_for_log(url)}: 'rule_groups' must be a list." | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
| for i, rg in enumerate(data["rule_groups"]): | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
| if not isinstance(rg, dict): | ||||||||||||||||||||||||||||||||||||||||
| log.error( | ||||||||||||||||||||||||||||||||||||||||
| f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}] must be an object." | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
| if "rules" in rg and not isinstance(rg["rules"], list): | ||||||||||||||||||||||||||||||||||||||||
| log.error( | ||||||||||||||||||||||||||||||||||||||||
| f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}].rules must be a list." | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1133
to
+1137
|
||||||||||||||||||||||||||||||||||||||||
| if "rules" in rg and not isinstance(rg["rules"], list): | |
| log.error( | |
| f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}].rules must be a list." | |
| ) | |
| return False | |
| if "rules" in rg: | |
| if not isinstance(rg["rules"], list): | |
| log.error( | |
| f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}].rules must be a list." | |
| ) | |
| return False | |
| # Ensure each rule within the group is an object (dict), | |
| # because later code treats each rule as a mapping (e.g., rule.get(...)). | |
| for j, rule in enumerate(rg["rules"]): | |
| if not isinstance(rule, dict): | |
| log.error( | |
| f"Invalid data from {sanitize_for_log(url)}: rule_groups[{i}].rules[{j}] must be an object." | |
| ) | |
| return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation for 'rule_groups' is thorough, checking both the top-level type (must be a list) and iterating through its elements to ensure each is a dictionary. Furthermore, it correctly validates the 'rules' key within each rule group. This nested validation is critical for preventing crashes when processing complex, malformed JSON structures.
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -229,6 +229,7 @@ | |
| mock_args.dry_run = False | ||
| mock_args.no_delete = False | ||
| mock_args.plan_json = None | ||
| mock_args.clear_cache = False | ||
|
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. Adding |
||
| monkeypatch.setattr(m, "parse_args", lambda: mock_args) | ||
|
|
||
| # Mock internal functions to abort execution safely after prompts | ||
|
|
@@ -434,6 +435,7 @@ | |
| mock_args.dry_run = False | ||
| mock_args.no_delete = False | ||
| mock_args.plan_json = None | ||
| mock_args.clear_cache = False | ||
|
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. |
||
| monkeypatch.setattr(m, "parse_args", lambda: mock_args) | ||
|
|
||
| # Mock sync_profile to catch the call | ||
|
|
@@ -495,7 +497,7 @@ | |
| assert "Error message" in captured.out | ||
|
|
||
|
|
||
| # Case 12: get_password works with getpass | ||
Check warningCode scanning / Pylintpython3 (reported by Codacy) Variable name "m" doesn't conform to snake_case naming style Warning test
Variable name "m" doesn't conform to snake_case naming style
|
||
| def test_get_password(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
|
|
||
|
|
@@ -661,7 +663,7 @@ | |
| assert "β" * 24 in combined or len([c for c in combined if c == "β"]) == 24 | ||
|
|
||
|
|
||
| # Case 18: check_env_permissions uses secure file operations | ||
Check warningCode scanning / Pylint (reported by Codacy) Variable name "m" doesn't conform to snake_case naming style Warning test
Variable name "m" doesn't conform to snake_case naming style
|
||
| def test_check_env_permissions_secure(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
|
|
||
|
|
@@ -723,3 +725,51 @@ | |
| assert mock_open.called | ||
| assert not mock_fchmod.called | ||
| mock_close.assert_called_with(123) | ||
|
|
||
|
|
||
| def test_validate_folder_data_structure(monkeypatch): | ||
| """Test validation of 'rules' and 'rule_groups' structures.""" | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_log = MagicMock() | ||
| monkeypatch.setattr(m, "log", mock_log) | ||
|
|
||
| valid_base = { | ||
| "group": {"group": "ValidFolder"} | ||
| } | ||
|
|
||
| # 1. Invalid 'rules' type (string instead of list) | ||
| invalid_rules = valid_base.copy() | ||
| invalid_rules["rules"] = "not_a_list" | ||
| assert m.validate_folder_data(invalid_rules, "url") is False | ||
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.
|
||
| assert "rules" in str(mock_log.error.call_args) | ||
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.
|
||
| mock_log.reset_mock() | ||
|
|
||
| # 2. Invalid 'rule_groups' type (string instead of list) | ||
| invalid_rg_type = valid_base.copy() | ||
| invalid_rg_type["rule_groups"] = "not_a_list" | ||
| assert m.validate_folder_data(invalid_rg_type, "url") is False | ||
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.
|
||
| assert "rule_groups" in str(mock_log.error.call_args) | ||
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.
|
||
| mock_log.reset_mock() | ||
|
|
||
| # 3. Invalid 'rule_groups' content (list of strings instead of dicts) | ||
| invalid_rg_content = valid_base.copy() | ||
| invalid_rg_content["rule_groups"] = ["not_a_dict"] | ||
| assert m.validate_folder_data(invalid_rg_content, "url") is False | ||
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.
|
||
| assert "must be an object" in str(mock_log.error.call_args) | ||
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.
|
||
| mock_log.reset_mock() | ||
|
|
||
| # 4. Invalid 'rules' inside 'rule_groups' | ||
| invalid_rg_rules = valid_base.copy() | ||
| invalid_rg_rules["rule_groups"] = [{"rules": "not_a_list"}] | ||
| assert m.validate_folder_data(invalid_rg_rules, "url") is False | ||
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.
|
||
| assert "must be a list" in str(mock_log.error.call_args) | ||
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.
|
||
| mock_log.reset_mock() | ||
|
|
||
| # 5. Valid cases | ||
| valid_rules = valid_base.copy() | ||
| valid_rules["rules"] = [{"PK": "rule1"}] | ||
| assert m.validate_folder_data(valid_rules, "url") is True | ||
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.
|
||
|
|
||
| valid_rg = valid_base.copy() | ||
| valid_rg["rule_groups"] = [{"rules": [{"PK": "rule1"}]}] | ||
| assert m.validate_folder_data(valid_rg, "url") is True | ||
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 warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning