304 lines
10 KiB
Python
304 lines
10 KiB
Python
"""Unit tests for path and URL validation functionality."""
|
|
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
import pytest
|
|
|
|
from src.security.secure_config import (
|
|
validate_path,
|
|
validate_youtube_url,
|
|
sanitize_filename,
|
|
)
|
|
|
|
|
|
class TestPathValidation:
|
|
"""Test cases for path validation functions."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.test_file = Path(self.temp_dir) / "test.txt"
|
|
self.test_file.write_text("test content")
|
|
|
|
def teardown_method(self):
|
|
"""Clean up test fixtures."""
|
|
import shutil
|
|
if self.temp_dir and os.path.exists(self.temp_dir):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def test_validate_path_allows_user_directories(self):
|
|
"""Test that validate_path allows paths in user directories."""
|
|
user_dirs = [
|
|
"~/Documents",
|
|
"~/Downloads",
|
|
"~/Desktop",
|
|
"~/Music",
|
|
"~/Videos",
|
|
"~/.trax",
|
|
]
|
|
|
|
for user_dir in user_dirs:
|
|
test_path = f"{user_dir}/test_file.txt"
|
|
assert validate_path(test_path) is True
|
|
|
|
def test_validate_path_blocks_directory_traversal(self):
|
|
"""Test that validate_path blocks directory traversal attempts."""
|
|
malicious_paths = [
|
|
"../../../etc/passwd",
|
|
"~/Documents/../../../etc/shadow",
|
|
"/tmp/../../../root/.ssh/id_rsa",
|
|
"~/Downloads/..//..//..//var/log/auth.log",
|
|
"~/Desktop/../../../../etc/hosts",
|
|
]
|
|
|
|
for path in malicious_paths:
|
|
assert validate_path(path) is False
|
|
|
|
def test_validate_path_blocks_system_directories(self):
|
|
"""Test that validate_path blocks access to system directories."""
|
|
system_paths = [
|
|
"/etc/passwd",
|
|
"/var/log/auth.log",
|
|
"/root/.ssh/id_rsa",
|
|
"/tmp/malicious_file",
|
|
"/usr/bin/evil",
|
|
"/bin/bash",
|
|
]
|
|
|
|
for path in system_paths:
|
|
assert validate_path(path) is False
|
|
|
|
def test_validate_path_allows_current_directory(self):
|
|
"""Test that validate_path allows paths in current working directory."""
|
|
# Create a file in current directory
|
|
test_file = "test_current_dir.txt"
|
|
with open(test_file, "w") as f:
|
|
f.write("test")
|
|
|
|
try:
|
|
assert validate_path(test_file) is True
|
|
assert validate_path("./test_current_dir.txt") is True
|
|
assert validate_path("../test_current_dir.txt") is False
|
|
finally:
|
|
os.remove(test_file)
|
|
|
|
def test_validate_path_handles_edge_cases(self):
|
|
"""Test that validate_path handles edge cases properly."""
|
|
edge_cases = [
|
|
"", # Empty string
|
|
None, # None value
|
|
" ", # Whitespace only
|
|
".", # Current directory
|
|
"..", # Parent directory
|
|
]
|
|
|
|
for path in edge_cases:
|
|
assert validate_path(path) is False
|
|
|
|
def test_validate_path_handles_absolute_paths(self):
|
|
"""Test that validate_path handles absolute paths correctly."""
|
|
# Test with actual user home directory
|
|
home = os.path.expanduser("~")
|
|
safe_absolute_paths = [
|
|
f"{home}/Documents/test.txt",
|
|
f"{home}/Downloads/file.mp4",
|
|
f"{home}/.trax/config.json",
|
|
]
|
|
|
|
for path in safe_absolute_paths:
|
|
assert validate_path(path) is True
|
|
|
|
def test_validate_path_handles_special_characters(self):
|
|
"""Test that validate_path handles special characters in paths."""
|
|
special_paths = [
|
|
"~/Documents/file with spaces.txt",
|
|
"~/Downloads/file-with-dashes.mp4",
|
|
"~/Desktop/file_with_underscores.pdf",
|
|
"~/Music/song (remix).mp3",
|
|
]
|
|
|
|
for path in special_paths:
|
|
assert validate_path(path) is True
|
|
|
|
|
|
class TestURLValidation:
|
|
"""Test cases for URL validation functions."""
|
|
|
|
def test_validate_youtube_url_allows_valid_urls(self):
|
|
"""Test that validate_youtube_url allows valid YouTube URLs."""
|
|
valid_urls = [
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://youtu.be/dQw4w9WgXcQ",
|
|
"http://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://www.youtube.com/embed/dQw4w9WgXcQ",
|
|
"https://www.youtube.com/v/dQw4w9WgXcQ",
|
|
"https://www.youtube.com/playlist?list=PL123456",
|
|
]
|
|
|
|
for url in valid_urls:
|
|
assert validate_youtube_url(url) is True
|
|
|
|
def test_validate_youtube_url_blocks_invalid_urls(self):
|
|
"""Test that validate_youtube_url blocks invalid URLs."""
|
|
invalid_urls = [
|
|
"https://www.google.com",
|
|
"https://malicious-site.com/fake-youtube",
|
|
"ftp://youtube.com/video",
|
|
"javascript:alert('xss')",
|
|
"data:text/html,<script>alert('xss')</script>",
|
|
"file:///etc/passwd",
|
|
"https://www.youtube.com.evil.com/watch?v=123",
|
|
"https://youtube.com.evil.com/watch?v=123",
|
|
]
|
|
|
|
for url in invalid_urls:
|
|
assert validate_youtube_url(url) is False
|
|
|
|
def test_validate_youtube_url_handles_edge_cases(self):
|
|
"""Test that validate_youtube_url handles edge cases."""
|
|
edge_cases = [
|
|
"", # Empty string
|
|
None, # None value
|
|
"not_a_url", # Plain text
|
|
"youtube.com", # Missing protocol
|
|
"https://", # Incomplete URL
|
|
"https://youtube.com", # Missing path
|
|
]
|
|
|
|
for url in edge_cases:
|
|
assert validate_youtube_url(url) is False
|
|
|
|
def test_validate_youtube_url_handles_complex_urls(self):
|
|
"""Test that validate_youtube_url handles complex YouTube URLs."""
|
|
complex_urls = [
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=30s",
|
|
"https://youtu.be/dQw4w9WgXcQ?t=30",
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ&list=PL123456&index=1",
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=share",
|
|
"https://www.youtube.com/embed/dQw4w9WgXcQ?autoplay=1",
|
|
]
|
|
|
|
for url in complex_urls:
|
|
assert validate_youtube_url(url) is True
|
|
|
|
def test_validate_youtube_url_handles_different_protocols(self):
|
|
"""Test that validate_youtube_url handles different protocols."""
|
|
protocols = [
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"http://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://youtu.be/dQw4w9WgXcQ",
|
|
"http://youtu.be/dQw4w9WgXcQ",
|
|
]
|
|
|
|
for url in protocols:
|
|
assert validate_youtube_url(url) is True
|
|
|
|
def test_validate_youtube_url_handles_subdomains(self):
|
|
"""Test that validate_youtube_url handles YouTube subdomains."""
|
|
subdomain_urls = [
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://m.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://music.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
]
|
|
|
|
for url in subdomain_urls:
|
|
assert validate_youtube_url(url) is True
|
|
|
|
|
|
class TestFilenameSanitization:
|
|
"""Test cases for filename sanitization."""
|
|
|
|
def test_sanitize_filename_removes_dangerous_characters(self):
|
|
"""Test that sanitize_filename removes dangerous characters."""
|
|
dangerous_filenames = [
|
|
"file<>:\"/\\|?*.txt",
|
|
"file with spaces.txt",
|
|
"file.with.dots.txt",
|
|
"file with (parentheses).txt",
|
|
"file with [brackets].txt",
|
|
"file with {braces}.txt",
|
|
]
|
|
|
|
for filename in dangerous_filenames:
|
|
sanitized = sanitize_filename(filename)
|
|
assert "<" not in sanitized
|
|
assert ">" not in sanitized
|
|
assert ":" not in sanitized
|
|
assert '"' not in sanitized
|
|
assert "/" not in sanitized
|
|
assert "\\" not in sanitized
|
|
assert "|" not in sanitized
|
|
assert "?" not in sanitized
|
|
assert "*" not in sanitized
|
|
|
|
def test_sanitize_filename_handles_edge_cases(self):
|
|
"""Test that sanitize_filename handles edge cases."""
|
|
edge_cases = [
|
|
"", # Empty string
|
|
None, # None value
|
|
" ", # Whitespace only
|
|
".", # Just a dot
|
|
"..", # Just dots
|
|
" . ", # Whitespace and dots
|
|
]
|
|
|
|
for filename in edge_cases:
|
|
sanitized = sanitize_filename(filename)
|
|
assert sanitized == "unnamed_file"
|
|
|
|
def test_sanitize_filename_preserves_safe_characters(self):
|
|
"""Test that sanitize_filename preserves safe characters."""
|
|
safe_filenames = [
|
|
"normal_file.txt",
|
|
"file_with_underscores.txt",
|
|
"file-with-dashes.txt",
|
|
"file123.txt",
|
|
"FILE.TXT",
|
|
"file.txt",
|
|
]
|
|
|
|
for filename in safe_filenames:
|
|
sanitized = sanitize_filename(filename)
|
|
assert sanitized == filename
|
|
|
|
def test_sanitize_filename_limits_length(self):
|
|
"""Test that sanitize_filename limits filename length."""
|
|
long_filename = "a" * 300 + ".txt"
|
|
sanitized = sanitize_filename(long_filename)
|
|
assert len(sanitized) <= 255
|
|
assert sanitized.endswith(".txt")
|
|
|
|
def test_sanitize_filename_removes_leading_trailing_dots(self):
|
|
"""Test that sanitize_filename removes leading and trailing dots."""
|
|
dot_filenames = [
|
|
".hidden_file.txt",
|
|
"file.txt.",
|
|
".file.txt.",
|
|
"...file.txt...",
|
|
]
|
|
|
|
expected_results = [
|
|
"hidden_file.txt",
|
|
"file.txt",
|
|
"file.txt",
|
|
"file.txt",
|
|
]
|
|
|
|
for filename, expected in zip(dot_filenames, expected_results):
|
|
sanitized = sanitize_filename(filename)
|
|
assert sanitized == expected
|
|
|
|
def test_sanitize_filename_handles_multiple_extensions(self):
|
|
"""Test that sanitize_filename handles multiple extensions."""
|
|
multi_ext_filenames = [
|
|
"file.txt.bak",
|
|
"file.tar.gz",
|
|
"file.backup.old",
|
|
]
|
|
|
|
for filename in multi_ext_filenames:
|
|
sanitized = sanitize_filename(filename)
|
|
assert sanitized == filename
|