Files
mirror_server/core/utils.py
T
HYC Fixer ffae028664 P0-A: 修复鉴权体系
- check_endpoint_auth 路径归一化(无前导斜杠),v1/v2 端点级鉴权真正生效
- ADMIN_API_ENDPOINTS 重建:修复 files 单复数、补 mirrors/alerts/prewarm/api-docs/user-password
- handler check_auth 方法感知:公开端点仅 GET/HEAD,写操作默认要求认证(fail-closed)
- 补全受保护端点(monitor/activity/login-logs/mirrors/alerts/prewarm/file 元数据)
- _check_basic_auth/_check_token_auth 改用 hmac.compare_digest
- do_POST/do_HEAD 解析路径时剥离查询串;do_HEAD 增加 is_safe_path 检查
- is_safe_path 改用 realpath 防符号链接绕过
2026-08-30 12:16:14 +08:00

98 lines
2.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""工具函数模块"""
import os
import re
import hashlib
def format_file_size(size_bytes: int) -> str:
"""格式化文件大小为可读格式"""
if size_bytes == 0:
return "0 B"
size_names = ["B", "KB", "MB", "GB", "TB", "PB"]
i = 0
while size_bytes >= 1024 and i < len(size_names) - 1:
size_bytes /= 1024.0
i += 1
return f"{size_bytes:.2f} {size_names[i]}"
def get_file_hash(filepath: str, algorithm: str = 'sha256') -> str:
"""计算文件哈希值"""
if not filepath or not isinstance(filepath, str):
return "Error: Invalid filepath"
if not os.path.exists(filepath):
return "Error: File not found"
try:
hash_func = hashlib.new(algorithm)
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_func.update(chunk)
return hash_func.hexdigest()
except Exception as e:
return f"Error: {str(e)}"
def parse_size(size_str: str) -> int:
"""解析文件大小字符串为字节数"""
size_str = size_str.upper().strip()
match = re.match(r'^(\d+(?:\.\d+)?)\s*([BKMGT]?)B?$', size_str)
if not match:
raise ValueError(f"无效的文件大小格式: {size_str}")
number = float(match.group(1))
unit = match.group(2) or 'B'
units = {
'B': 1,
'K': 1024,
'M': 1024 ** 2,
'G': 1024 ** 3,
'T': 1024 ** 4
}
if unit not in units:
raise ValueError(f"无效的单位: {unit}")
return int(number * units[unit])
def sanitize_filename(filename: str) -> str:
"""清理文件名,防止路径遍历和安全问题"""
filename = os.path.basename(filename) # 移除路径分隔符
# 移除危险字符
filename = re.sub(r'[<>:"|?*\\\x00-\x1f]', '_', filename)
# 限制长度
if len(filename) > 255:
name, ext = os.path.splitext(filename)
filename = name[:255 - len(ext)] + ext
# 防止空文件名
if not filename or filename in ('.', '..'):
filename = 'unnamed_file'
return filename
def is_safe_path(base_dir: str, path: str) -> bool:
"""检查路径是否安全(防止目录遍历)
使用 realpath 解析符号链接,避免 base_dir 内 symlink 指向外部目录的绕过。
"""
try:
abs_path = os.path.realpath(path)
abs_base = os.path.realpath(base_dir)
common_path = os.path.commonpath([abs_path, abs_base])
return common_path == abs_base
except ValueError:
return False