security.py•4.4 kB
"""
安全验证模块
处理文件路径验证、权限检查等安全相关功能
"""
import os
import logging
from pathlib import Path
from typing import List, Optional
from .config import ConfigManager
class SecurityValidator:
"""安全验证器"""
def __init__(self, config_manager: ConfigManager):
self.config_manager = config_manager
self.allowed_paths = config_manager.get_allowed_paths()
self.forbidden_extensions = config_manager.get('security.forbidden_extensions', [])
def validate_file_path(self, file_path: str) -> bool:
"""验证文件路径是否安全"""
try:
# 解析路径
path = Path(file_path).expanduser().resolve()
# 检查路径是否存在
if not path.exists() and not self._is_creation_allowed(path):
logging.warning(f"文件路径不存在且不允许创建: {path}")
return False
# 检查是否在允许的路径内
if not self._is_path_allowed(path):
logging.warning(f"文件路径不在允许范围内: {path}")
return False
# 检查文件扩展名
if not self._is_extension_allowed(path):
logging.warning(f"文件扩展名不被允许: {path.suffix}")
return False
return True
except Exception as e:
logging.error(f"路径验证错误: {e}")
return False
def _is_creation_allowed(self, path: Path) -> bool:
"""检查是否允许创建文件"""
# 检查父目录是否在允许的路径内
parent = path.parent
return self._is_path_allowed(parent)
def _is_path_allowed(self, path: Path) -> bool:
"""检查路径是否在允许的范围内"""
path_str = str(path)
for allowed_path in self.allowed_paths:
try:
# 检查是否为允许路径的子路径
if path_str.startswith(allowed_path):
return True
# 检查是否为完全匹配
if str(Path(allowed_path).resolve()) == path_str:
return True
except Exception:
continue
return False
def _is_extension_allowed(self, path: Path) -> bool:
"""检查文件扩展名是否允许"""
extension = path.suffix.lower()
# 检查是否为禁止的扩展名
if extension in self.forbidden_extensions:
return False
# 检查是否为支持的Excel格式
supported_formats = self.config_manager.get_supported_formats()
if extension in supported_formats:
return True
# 对于其他扩展名,默认不允许
return False
def sanitize_path(self, file_path: str) -> Optional[str]:
"""清理和标准化文件路径"""
try:
path = Path(file_path).expanduser().resolve()
return str(path)
except Exception as e:
logging.error(f"路径清理错误: {e}")
return None
def check_file_size(self, file_path: str) -> bool:
"""检查文件大小是否在限制范围内"""
try:
path = Path(file_path)
if not path.exists():
return True # 新文件,允许创建
file_size = path.stat().st_size
max_size_str = self.config_manager.get('excel.max_file_size', '100MB')
max_size = self._parse_size(max_size_str)
return file_size <= max_size
except Exception as e:
logging.error(f"文件大小检查错误: {e}")
return False
def _parse_size(self, size_str: str) -> int:
"""解析大小字符串(如 '100MB')为字节数"""
size_str = size_str.strip().upper()
if size_str.endswith('KB'):
return int(size_str[:-2]) * 1024
elif size_str.endswith('MB'):
return int(size_str[:-2]) * 1024 * 1024
elif size_str.endswith('GB'):
return int(size_str[:-2]) * 1024 * 1024 * 1024
else:
return int(size_str) # 假设为字节数