Prevent arbitrary file write and repo traversal by strictly validating `github_repo` and `file_path` inputs in `update_github_cdn_urls`. Added `validate_github_repo` and `validate_file_path` functions to enforce strict whitelisting of characters and reject path traversal sequences. Added comprehensive unit tests in `tests/test_github_validation.py`.
100 lines
4.0 KiB
Python
100 lines
4.0 KiB
Python
|
|
import unittest
|
|
import sys
|
|
from unittest.mock import MagicMock
|
|
|
|
# Mock torch and other heavy dependencies
|
|
sys.modules["torch"] = MagicMock()
|
|
sys.modules["numpy"] = MagicMock()
|
|
sys.modules["PIL"] = MagicMock()
|
|
sys.modules["cv2"] = MagicMock()
|
|
sys.modules["folder_paths"] = MagicMock()
|
|
sys.modules["comfy"] = MagicMock()
|
|
sys.modules["comfy.cli_args"] = MagicMock()
|
|
|
|
# Now we can safely import
|
|
from discordsend_utils.github_integration import validate_github_repo, validate_file_path, update_github_cdn_urls
|
|
|
|
class TestGitHubValidation(unittest.TestCase):
|
|
|
|
def test_validate_github_repo_valid(self):
|
|
"""Test valid GitHub repository formats."""
|
|
valid_repos = [
|
|
"username/repo",
|
|
"user-name/repo-name",
|
|
"user-name/repo.name", # Dot in repo is valid
|
|
"user-name/repo_name", # Underscore in repo is valid
|
|
"0123/4567"
|
|
]
|
|
for repo in valid_repos:
|
|
with self.subTest(repo=repo):
|
|
self.assertTrue(validate_github_repo(repo), f"Failed for {repo}")
|
|
|
|
def test_validate_github_repo_invalid(self):
|
|
"""Test invalid GitHub repository formats (traversal, injection, invalid chars)."""
|
|
invalid_repos = [
|
|
"username/repo/../other", # Traversal
|
|
"username/repo?query=1", # Query injection
|
|
"username", # Missing slash
|
|
"/repo", # Missing username
|
|
"user/", # Missing repo
|
|
"user/repo/", # Trailing slash (strict check)
|
|
"../../user/repo", # Traversal at start
|
|
"user/repo#fragment", # Fragment
|
|
"user/repo;rm -rf", # Command injection style
|
|
"user/repo\nnewline", # Newline
|
|
"user.name/repo", # Dot in username (invalid)
|
|
"user_name/repo", # Underscore in username (invalid)
|
|
]
|
|
for repo in invalid_repos:
|
|
with self.subTest(repo=repo):
|
|
self.assertFalse(validate_github_repo(repo), f"Should have failed for {repo}")
|
|
|
|
def test_validate_file_path_valid(self):
|
|
"""Test valid file paths."""
|
|
valid_paths = [
|
|
"file.txt",
|
|
"path/to/file.txt",
|
|
"folder/subfolder/file.md",
|
|
"README.md",
|
|
"docs/image.png"
|
|
]
|
|
for path in valid_paths:
|
|
with self.subTest(path=path):
|
|
self.assertTrue(validate_file_path(path), f"Failed for {path}")
|
|
|
|
def test_validate_file_path_invalid(self):
|
|
"""Test invalid file paths (traversal, absolute)."""
|
|
invalid_paths = [
|
|
"../file.txt", # Traversal
|
|
"path/../file.txt", # Traversal inside
|
|
"/etc/passwd", # Absolute path
|
|
"/file.txt", # Absolute path
|
|
"../../secret", # Deep traversal
|
|
"", # Empty
|
|
None # None
|
|
]
|
|
for path in invalid_paths:
|
|
with self.subTest(path=path):
|
|
self.assertFalse(validate_file_path(path), f"Should have failed for {path}")
|
|
|
|
def test_update_github_cdn_urls_rejects_invalid_repo(self):
|
|
"""Test that update_github_cdn_urls rejects invalid repo before making requests."""
|
|
repo = "user/repo/../malicious"
|
|
success, message = update_github_cdn_urls(repo, "token", "file.md", [("f", "u")])
|
|
|
|
self.assertFalse(success)
|
|
self.assertIn("Invalid GitHub repository format", message)
|
|
|
|
def test_update_github_cdn_urls_rejects_invalid_path(self):
|
|
"""Test that update_github_cdn_urls rejects invalid path before making requests."""
|
|
path = "../../../secret.txt"
|
|
success, message = update_github_cdn_urls("user/repo", "token", path, [("f", "u")])
|
|
|
|
self.assertFalse(success)
|
|
self.assertIn("Invalid file path", message)
|
|
self.assertIn("Path traversal", message)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|