ForcePilot/backend/package/yuxi/channels/adapters/imessage/media_security.py

85 lines
2.2 KiB
Python
Raw Normal View History

from __future__ import annotations
import os
from pathlib import Path
from yuxi.utils.logging_config import logger
DEFAULT_IMESSAGE_ATTACHMENT_ROOTS = [
"/Users/*/Library/Messages/Attachments",
]
def resolve_attachment_roots(
configured_roots: list[str] | None = None,
) -> list[str]:
if configured_roots:
return configured_roots
expanded: list[str] = []
for pattern in DEFAULT_IMESSAGE_ATTACHMENT_ROOTS:
import glob as _glob_module
matches = _glob_module.glob(pattern)
if matches:
expanded.extend(matches)
return expanded or DEFAULT_IMESSAGE_ATTACHMENT_ROOTS
def validate_attachment_path(
file_path: str,
allowed_roots: list[str] | None = None,
) -> bool:
"""校验附件路径是否在允许的根路径内。
阻止路径遍历攻击 ../../etc/passwd
"""
if not file_path:
return False
real_path = os.path.realpath(file_path) if os.path.exists(file_path) else os.path.abspath(file_path)
roots = allowed_roots or []
if not roots:
return True
for root in roots:
root_real = os.path.realpath(root) if os.path.exists(root) else os.path.abspath(root)
try:
Path(real_path).relative_to(root_real)
return True
except ValueError:
continue
logger.warning(f"[iMessage/Security] Attachment path rejected: {file_path} not in allowed roots")
return False
def validate_remote_attachment_url(
url: str,
allowed_roots: list[str] | None = None,
server_url: str = "",
) -> bool:
"""校验远程附件 URL 是否安全。"""
if not url:
return False
if allowed_roots:
for root in allowed_roots:
if url.startswith(root):
return True
return False
if server_url and url.startswith(server_url):
return True
return False
def sanitize_attachment_filename(filename: str) -> str:
"""净化附件文件名,移除路径分隔符。"""
if not filename:
return "attachment"
basename = os.path.basename(filename)
safe = "".join(c for c in basename if c.isalnum() or c in "._-() ")
return safe or "attachment"