Skip to content
Closed
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
25 changes: 25 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,21 @@

return True

def is_valid_folder_name(name: str) -> bool:
"""
Validates that a folder name is safe.
Rejects potentially dangerous characters (XSS/Injection).
"""
if not name or not name.isprintable():
return False

# Block XSS and injection chars
dangerous_chars = set("<>\"'`")
Comment on lines +284 to +285
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The dangerous character set for folder names is less restrictive than for rules. The is_valid_rule function blocks <>"'\();{}[]while this function only blocks<>"'``.

If the Control D dashboard renders folder names in JavaScript contexts (event handlers, script tags, or embedded in JSON), characters like ();{}[] could still enable injection attacks. For example, a folder name like alert(1) would pass this validation but could be dangerous if rendered as onclick="handleClick('alert(1)')".

Consider aligning the character validation with is_valid_rule or adding a comment explaining why folder names require less strict validation.

Suggested change
# Block XSS and injection chars
dangerous_chars = set("<>\"'`")
# Block XSS and injection chars (aligned with is_valid_rule)
dangerous_chars = set("<>\"'`();{}[]")

Copilot uses AI. Check for mistakes.
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.")
Expand All @@ -286,6 +301,16 @@
if "group" not in data["group"]:
log.error(f"Invalid data from {sanitize_for_log(url)}: Missing 'group.group' (folder name).")
return False

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.")

Check warning

Code scanning / Prospector (reported by Codacy)

Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning

Use lazy % formatting in logging functions (logging-fstring-interpolation)

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Use lazy % formatting in logging functions Note

Use lazy % formatting in logging functions
return False

if not is_valid_folder_name(folder_name):
log.error(f"Invalid data from {sanitize_for_log(url)}: Folder name contains unsafe characters.")

Check warning

Code scanning / Prospector (reported by Codacy)

Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning

Use lazy % formatting in logging functions (logging-fstring-interpolation)

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (104/100) Warning

Line too long (104/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (104/100) Warning

Line too long (104/100)

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Use lazy % formatting in logging functions Note

Use lazy % formatting in logging functions
return False

return True

def _api_get(client: httpx.Client, url: str) -> httpx.Response:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,33 @@
finally:
main._api_post_form = original_post_form
main.log = original_log

def test_validate_folder_data_sanitizes_names():
"""
Verify that validate_folder_data rejects unsafe folder names (XSS prevention).
"""
# Mock logger to check error messages
mock_log = MagicMock()
original_log = main.log
main.log = mock_log

try:
# 1. Valid Folder Name
valid_data = {"group": {"group": "My Safe Folder"}}
assert main.validate_folder_data(valid_data, "https://example.com/valid.json") is True

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.

# 2. XSS Payload in Folder Name
xss_data = {"group": {"group": "<script>alert(1)</script>"}}
assert main.validate_folder_data(xss_data, "https://example.com/xss.json") is False

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.
assert mock_log.error.called

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.
Comment on lines +105 to +106
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The assertion mock_log.error.called is too weak as it only checks if any error was logged at all, not specifically for this XSS validation case. Consider using a more specific assertion like checking if an error was called with a message containing "unsafe characters" or checking the call count increased by exactly 1.

Suggested change
assert main.validate_folder_data(xss_data, "https://example.com/xss.json") is False
assert mock_log.error.called
before_error_calls = mock_log.error.call_count
assert main.validate_folder_data(xss_data, "https://example.com/xss.json") is False
assert mock_log.error.call_count == before_error_calls + 1

Copilot uses AI. Check for mistakes.

# 3. Non-string Folder Name
bad_type_data = {"group": {"group": 12345}}
assert main.validate_folder_data(bad_type_data, "https://example.com/bad_type.json") is False

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 warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (101/100) Warning test

Line too long (101/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (101/100) Warning test

Line too long (101/100)

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.

# 4. Dangerous chars (quote)
quote_data = {"group": {"group": "Folder \"Bad\""}}
assert main.validate_folder_data(quote_data, "https://example.com/quote.json") is False

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.

finally:
main.log = original_log
Loading