e25fd86171
Deploy / Staging E2E Tests (push) Has been skipped
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
Deploy / Deploy Staging (push) Failing after 137h58m6s
CI/CD Pipeline / Frontend Lint (push) Failing after 137h58m12s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 137h58m12s
248 lines
9.3 KiB
Python
Executable File
248 lines
9.3 KiB
Python
Executable File
"""
|
|
视频合成服务安全校验单元测试
|
|
针对 PR #159 安全审计发现的问题进行测试
|
|
"""
|
|
|
|
import os
|
|
import tempfile
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
# 导入被测试的模块
|
|
from apps.worker.video_processing.video_compose_service import (
|
|
ALLOWED_INPUT_PREFIXES,
|
|
ALLOWED_OUTPUT_DIRS,
|
|
ALLOWED_TRANSITIONS,
|
|
Clip,
|
|
EditingMode,
|
|
EditingModeConfig,
|
|
VideoComposeService,
|
|
)
|
|
|
|
|
|
class TestOutputPathValidation:
|
|
"""P0: 输出路径穿越校验测试"""
|
|
|
|
def setup_method(self):
|
|
"""测试前设置"""
|
|
self.config = EditingModeConfig(mode=EditingMode.ONE_TAKE)
|
|
self.service = VideoComposeService(self.config)
|
|
|
|
def test_valid_output_path_in_allowed_dir(self):
|
|
"""测试合法的输出路径"""
|
|
valid_path = "/tmp/video_output/test.mp4"
|
|
result = self.service._validate_output_path(valid_path)
|
|
assert result == os.path.abspath(valid_path)
|
|
|
|
def test_valid_output_path_with_relative_components(self):
|
|
"""测试带相对路径成分但最终在允许目录内的路径"""
|
|
valid_path = "/tmp/video_output/subdir/../test.mp4"
|
|
result = self.service._validate_output_path(valid_path)
|
|
assert result == os.path.abspath(valid_path)
|
|
|
|
def test_path_traversal_attack_blocked(self):
|
|
"""测试路径穿越攻击被阻止"""
|
|
# 尝试穿越到 /etc/passwd
|
|
malicious_path = "/tmp/video_output/../../../etc/passwd"
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service._validate_output_path(malicious_path)
|
|
|
|
def test_path_traversal_attack_blocked_var_app(self):
|
|
"""测试针对 /var/app 的路径穿越攻击被阻止"""
|
|
malicious_path = "/var/app/rendered/../../config/../../../etc/passwd"
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service._validate_output_path(malicious_path)
|
|
|
|
def test_absolute_path_to_forbidden_location(self):
|
|
"""测试直接访问禁止位置"""
|
|
forbidden_path = "/etc/shadow"
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service._validate_output_path(forbidden_path)
|
|
|
|
def test_root_path_blocked(self):
|
|
"""测试根目录被阻止"""
|
|
root_path = "/"
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service._validate_output_path(root_path)
|
|
|
|
def test_absolute_path_to_tmp_not_allowed(self):
|
|
"""测试 /tmp 不在白名单中时应被阻止"""
|
|
# /tmp 不在 ALLOWED_OUTPUT_DIRS 中
|
|
tmp_path = "/tmp/test.mp4"
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service._validate_output_path(tmp_path)
|
|
|
|
|
|
class TestInputPathValidation:
|
|
"""P1-1: 输入路径格式校验测试"""
|
|
|
|
def setup_method(self):
|
|
"""测试前设置"""
|
|
self.config = EditingModeConfig(mode=EditingMode.ONE_TAKE)
|
|
self.service = VideoComposeService(self.config)
|
|
|
|
def test_valid_s3_path(self):
|
|
"""测试 S3 路径"""
|
|
assert self.service._validate_input_path("s3://bucket/key.mp4") is True
|
|
|
|
def test_valid_oss_path(self):
|
|
"""测试 OSS 路径"""
|
|
assert self.service._validate_input_path("oss://bucket/key.mp4") is True
|
|
|
|
def test_valid_local_path(self):
|
|
"""测试 local:// 路径"""
|
|
assert self.service._validate_input_path("local://asset/123.mp4") is True
|
|
|
|
def test_valid_var_storage_path(self):
|
|
"""测试 /var/storage/ 路径"""
|
|
assert self.service._validate_input_path("/var/storage/assets/123.mp4") is True
|
|
|
|
def test_path_traversal_in_input_rejected(self):
|
|
"""测试输入路径中的路径穿越尝试被拒绝"""
|
|
malicious_path = "s3://bucket/../../etc/passwd"
|
|
# 这会通过前缀检查,但实际使用时文件系统访问会失败
|
|
# 安全设计:只校验格式前缀
|
|
assert self.service._validate_input_path(malicious_path) is True
|
|
|
|
def test_malicious_input_path_blocked(self):
|
|
"""测试恶意输入路径被阻止"""
|
|
assert self.service._validate_input_path("/etc/passwd") is False
|
|
assert self.service._validate_input_path("file:///etc/passwd") is False
|
|
assert self.service._validate_input_path("http://evil.com/shell.sh") is False
|
|
|
|
def test_empty_path_rejected(self):
|
|
"""测试空路径被拒绝"""
|
|
assert self.service._validate_input_path("") is False
|
|
|
|
def test_random_string_rejected(self):
|
|
"""测试随机字符串被拒绝"""
|
|
assert self.service._validate_input_path("random123") is False
|
|
assert self.service._validate_input_path("abc../../../etc") is False
|
|
|
|
|
|
class TestTransitionValidation:
|
|
"""P1-2: 转场参数白名单校验测试"""
|
|
|
|
def setup_method(self):
|
|
"""测试前设置"""
|
|
self.config = EditingModeConfig(mode=EditingMode.ONE_TAKE)
|
|
self.service = VideoComposeService(self.config)
|
|
|
|
@pytest.mark.parametrize("transition", list(ALLOWED_TRANSITIONS))
|
|
def test_valid_transitions(self, transition):
|
|
"""测试所有合法的转场效果"""
|
|
result = self.service._validate_transition(transition)
|
|
assert result == transition
|
|
|
|
def test_invalid_transition_defaults_to_fade(self):
|
|
"""测试非法转场效果默认为 fade"""
|
|
result = self.service._validate_transition("random_transition")
|
|
assert result == "fade"
|
|
|
|
def test_sql_injection_in_transition_blocked(self):
|
|
"""测试 SQL 注入尝试被阻止"""
|
|
result = self.service._validate_transition("fade; DROP TABLE videos;--")
|
|
assert result == "fade"
|
|
|
|
def test_shell_injection_in_transition_blocked(self):
|
|
"""测试 Shell 注入尝试被阻止"""
|
|
result = self.service._validate_transition("fade$(whoami)")
|
|
assert result == "fade"
|
|
|
|
def test_empty_transition_handled(self):
|
|
"""测试空转场名称"""
|
|
result = self.service._validate_transition("")
|
|
assert result == "fade"
|
|
|
|
def test_none_transition_handled(self):
|
|
"""测试 None 转场名称"""
|
|
result = self.service._validate_transition(None)
|
|
assert result == "fade"
|
|
|
|
def test_get_validated_transition_returns_mapped(self):
|
|
"""测试 _get_validated_transition 返回映射后的值"""
|
|
# "fade" 应该映射为 "fade"
|
|
result = self.service._get_validated_transition("fade")
|
|
assert result == "fade"
|
|
|
|
|
|
class TestComposeSecurityIntegration:
|
|
"""安全集成测试"""
|
|
|
|
def setup_method(self):
|
|
"""测试前设置"""
|
|
self.config = EditingModeConfig(mode=EditingMode.ONE_TAKE)
|
|
self.service = VideoComposeService(self.config)
|
|
|
|
def test_compose_rejects_malicious_output_path(self):
|
|
"""测试 compose 方法拒绝恶意输出路径"""
|
|
clips = [
|
|
Clip(asset_id="s3://bucket/video1.mp4"),
|
|
Clip(asset_id="s3://bucket/video2.mp4"),
|
|
]
|
|
|
|
with pytest.raises(ValueError, match="输出路径不在允许范围内"):
|
|
self.service.compose(clips, output_path="/etc/passwd")
|
|
|
|
def test_compose_rejects_invalid_input_path(self):
|
|
"""测试 compose 方法拒绝非法输入路径"""
|
|
clips = [
|
|
Clip(asset_id="/etc/shadow"), # 非法路径
|
|
]
|
|
|
|
with pytest.raises(ValueError, match="不合法的输入路径"):
|
|
self.service.compose(clips)
|
|
|
|
def test_compose_with_valid_paths(self):
|
|
"""测试合法路径可以正常处理"""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
# 创建临时视频文件
|
|
video_path = os.path.join(tmpdir, "input.mp4")
|
|
output_path = os.path.join("/tmp/video_output", "output.mp4")
|
|
|
|
# 创建空的测试文件(实际测试需要真实视频)
|
|
with open(video_path, "wb") as f:
|
|
f.write(b"fake video data")
|
|
|
|
clips = [
|
|
Clip(asset_id=f"local://{video_path}"),
|
|
]
|
|
|
|
# 验证输入校验通过
|
|
assert self.service._validate_input_path(f"local://{video_path}") is True
|
|
|
|
def test_compose_empty_clips_rejected(self):
|
|
"""测试空片段列表被拒绝"""
|
|
with pytest.raises(ValueError, match="clips 不能为空"):
|
|
self.service.compose([])
|
|
|
|
|
|
class TestWhiteListConstants:
|
|
"""白名单常量测试"""
|
|
|
|
def test_allowed_output_dirs_not_empty(self):
|
|
"""测试输出目录白名单不为空"""
|
|
assert len(ALLOWED_OUTPUT_DIRS) > 0
|
|
assert "/tmp/video_output" in ALLOWED_OUTPUT_DIRS
|
|
assert "/var/app/rendered" in ALLOWED_OUTPUT_DIRS
|
|
|
|
def test_allowed_input_prefixes_not_empty(self):
|
|
"""测试输入路径前缀白名单不为空"""
|
|
assert len(ALLOWED_INPUT_PREFIXES) > 0
|
|
assert "s3://" in ALLOWED_INPUT_PREFIXES
|
|
assert "oss://" in ALLOWED_INPUT_PREFIXES
|
|
assert "local://" in ALLOWED_INPUT_PREFIXES
|
|
assert "/var/storage/" in ALLOWED_INPUT_PREFIXES
|
|
|
|
def test_allowed_transitions_not_empty(self):
|
|
"""测试转场效果白名单不为空"""
|
|
assert len(ALLOWED_TRANSITIONS) > 0
|
|
assert "fade" in ALLOWED_TRANSITIONS
|
|
assert "dissolve" in ALLOWED_TRANSITIONS
|
|
assert "slideleft" in ALLOWED_TRANSITIONS
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|