Merge pull request #3885 from InfinityPacer/feature/security

This commit is contained in:
jxxghp
2025-02-11 17:21:04 +08:00
committed by GitHub
5 changed files with 15 additions and 4 deletions
+7 -2
View File
@@ -8,6 +8,7 @@ from pathlib import Path
from typing import Optional, Union from typing import Optional, Union
import aiofiles import aiofiles
import pillow_avif # noqa 用于自动注册AVIF支持
from PIL import Image from PIL import Image
from fastapi import APIRouter, Depends, HTTPException, Header, Request, Response from fastapi import APIRouter, Depends, HTTPException, Header, Request, Response
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
@@ -50,7 +51,6 @@ def fetch_image(
""" """
处理图片缓存逻辑,支持HTTP缓存和磁盘缓存 处理图片缓存逻辑,支持HTTP缓存和磁盘缓存
""" """
if not url: if not url:
raise HTTPException(status_code=404, detail="URL not provided") raise HTTPException(status_code=404, detail="URL not provided")
@@ -68,6 +68,10 @@ def fetch_image(
sanitized_path = SecurityUtils.sanitize_url_path(url) sanitized_path = SecurityUtils.sanitize_url_path(url)
cache_path = settings.CACHE_PATH / "images" / sanitized_path cache_path = settings.CACHE_PATH / "images" / sanitized_path
# 没有文件类型,则添加后缀,在恶意文件类型和实际需求下的折衷选择
if not cache_path.suffix:
cache_path = cache_path.with_suffix(".jpg")
# 确保缓存路径和文件类型合法 # 确保缓存路径和文件类型合法
if not SecurityUtils.is_safe_path(settings.CACHE_PATH, cache_path, settings.SECURITY_IMAGE_SUFFIXES): if not SecurityUtils.is_safe_path(settings.CACHE_PATH, cache_path, settings.SECURITY_IMAGE_SUFFIXES):
raise HTTPException(status_code=400, detail="Invalid cache path or file type") raise HTTPException(status_code=400, detail="Invalid cache path or file type")
@@ -88,7 +92,8 @@ def fetch_image(
# 请求远程图片 # 请求远程图片
referer = "https://movie.douban.com/" if "doubanio.com" in url else None referer = "https://movie.douban.com/" if "doubanio.com" in url else None
proxies = settings.PROXY if proxy else None proxies = settings.PROXY if proxy else None
response = RequestUtils(ua=settings.USER_AGENT, proxies=proxies, referer=referer).get_res(url=url) response = RequestUtils(ua=settings.USER_AGENT, proxies=proxies, referer=referer,
accept_type="image/avif,image/webp,image/apng,*/*").get_res(url=url)
if not response: if not response:
raise HTTPException(status_code=502, detail="Failed to fetch the image from the remote server") raise HTTPException(status_code=502, detail="Failed to fetch the image from the remote server")
+5
View File
@@ -3,6 +3,7 @@ import tempfile
from pathlib import Path from pathlib import Path
from typing import List from typing import List
import pillow_avif # noqa 用于自动注册AVIF支持
from PIL import Image from PIL import Image
from app.chain import ChainBase from app.chain import ChainBase
@@ -116,6 +117,10 @@ class RecommendChain(ChainBase, metaclass=Singleton):
sanitized_path = SecurityUtils.sanitize_url_path(url) sanitized_path = SecurityUtils.sanitize_url_path(url)
cache_path = settings.CACHE_PATH / "images" / sanitized_path cache_path = settings.CACHE_PATH / "images" / sanitized_path
# 没有文件类型,则添加后缀,在恶意文件类型和实际需求下的折衷选择
if not cache_path.suffix:
cache_path = cache_path.with_suffix(".jpg")
# 确保缓存路径和文件类型合法 # 确保缓存路径和文件类型合法
if not SecurityUtils.is_safe_path(settings.CACHE_PATH, cache_path, settings.SECURITY_IMAGE_SUFFIXES): if not SecurityUtils.is_safe_path(settings.CACHE_PATH, cache_path, settings.SECURITY_IMAGE_SUFFIXES):
logger.debug(f"Invalid cache path or file type for URL: {url}, sanitized path: {sanitized_path}") logger.debug(f"Invalid cache path or file type for URL: {url}, sanitized path: {sanitized_path}")
+1 -1
View File
@@ -247,7 +247,7 @@ class ConfigModel(BaseModel):
) )
# 允许的图片文件后缀格式 # 允许的图片文件后缀格式
SECURITY_IMAGE_SUFFIXES: List[str] = Field( SECURITY_IMAGE_SUFFIXES: List[str] = Field(
default_factory=lambda: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg"] default_factory=lambda: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg", ".avif"]
) )
# 重命名时支持的S0别名 # 重命名时支持的S0别名
RENAME_FORMAT_S0_NAMES: List[str] = Field( RENAME_FORMAT_S0_NAMES: List[str] = Field(
+1 -1
View File
@@ -42,7 +42,7 @@ class SecurityUtils:
@staticmethod @staticmethod
def is_safe_url(url: str, allowed_domains: Union[Set[str], List[str]], strict: bool = False) -> bool: def is_safe_url(url: str, allowed_domains: Union[Set[str], List[str]], strict: bool = False) -> bool:
""" """
验证URL是否在允许的域名列表中,包括带有端口的域名 验证URL是否在允许的域名列表中,包括带有端口的域名
:param url: 需要验证的 URL :param url: 需要验证的 URL
:param allowed_domains: 允许的域名集合,域名可以包含端口 :param allowed_domains: 允许的域名集合,域名可以包含端口
+1
View File
@@ -32,6 +32,7 @@ func_timeout==4.3.5
bs4~=0.0.1 bs4~=0.0.1
beautifulsoup4~=4.12.2 beautifulsoup4~=4.12.2
pillow~=10.4.0 pillow~=10.4.0
pillow-avif-plugin~=1.4.6
pyTelegramBotAPI~=4.12.0 pyTelegramBotAPI~=4.12.0
playwright~=1.37.0 playwright~=1.37.0
cf-clearance~=0.31.0 cf-clearance~=0.31.0