fix: SSRF 内网判定改用 ipaddress 标准库,覆盖全部 IPv4/IPv6 私有段
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m20s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m30s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m48s
AI Code Review / AI Code Review (pull_request) Failing after 3m31s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m1s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 446h26m42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 446h28m10s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 446h29m5s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 446h29m7s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 446h31m45s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 446h31m46s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 446h31m48s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m20s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m30s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m48s
AI Code Review / AI Code Review (pull_request) Failing after 3m31s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m1s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 446h26m42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 446h28m10s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 446h29m5s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 446h29m7s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 446h31m45s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 446h31m46s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 446h31m48s
AI Code Review 第三轮指出:IPv6 链路本地地址使用字符串前缀匹配 (fe80/fe90/...)不严谨且易漏。 修复:新增 _is_private_or_reserved_host(),统一用标准库 ipaddress.ip_address().is_private/is_loopback/is_link_local/ is_reserved 判定,一次性准确覆盖: - IPv4: 10/8、172.16/12、192.168/16、127/8、169.254/16、0.0.0.0 - IPv6: ::1、fc00::/7(ULA)、fe80::/10(链路本地)、保留段 删除手工字符串切片逻辑,可读性和正确性都更好。 36 个测试全部通过。
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, List, Optional
|
||||
@@ -191,6 +192,23 @@ def _endpoint_host(value: str) -> str:
|
||||
return (urlparse("//" + v).hostname or "").lower()
|
||||
|
||||
|
||||
def _is_private_or_reserved_host(host: str) -> bool:
|
||||
"""判断主机名是否为内网/回环/链路本地/保留地址(IPv4 与 IPv6 统一处理)。
|
||||
|
||||
使用标准库 ipaddress 判定;非 IP 主机名(如 localhost)单独处理。
|
||||
"""
|
||||
h = host.strip().lower()
|
||||
if h in {"localhost", "0.0.0.0", "::", "::1"}:
|
||||
return True
|
||||
try:
|
||||
addr = ipaddress.ip_address(h)
|
||||
# is_private 覆盖 10/8、172.16/12、192.168/16、127/8、169.254/16、
|
||||
# ::1、fc00::/7、fe80::/10 等全部私有/保留段
|
||||
return bool(addr.is_private or addr.is_loopback or addr.is_link_local or addr.is_reserved)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def _is_trusted_media_url(url: str) -> bool:
|
||||
"""校验 URL 是否指向受信任的存储域名(OSS bucket / 本地存储),防止 SSRF。
|
||||
|
||||
@@ -206,25 +224,9 @@ def _is_trusted_media_url(url: str) -> bool:
|
||||
host = (parsed.hostname or "").lower()
|
||||
if not host:
|
||||
return False
|
||||
# 显式拒绝内网/保留地址(IPv4 + IPv6)
|
||||
if host in {"localhost", "0.0.0.0", "::", "::1"}:
|
||||
# 拒绝一切内网/回环/链路本地/保留地址(IPv4 + IPv6,标准库判定)
|
||||
if _is_private_or_reserved_host(host):
|
||||
return False
|
||||
if host.startswith(("127.", "10.", "192.168.", "169.254.")):
|
||||
return False
|
||||
# IPv6 本地/链路本地/唯一本地地址:[::1] / fe80:: / fc00::/7
|
||||
if ":" in host and (
|
||||
host == "::1"
|
||||
or host.startswith(("fe80", "fe90", "fea0", "feb0", "fec0", "fed0", "fee0", "fef0"))
|
||||
or host.startswith(("fc", "fd"))
|
||||
):
|
||||
return False
|
||||
# 172.16.0.0/12
|
||||
try:
|
||||
parts = [int(p) for p in host.split(".")]
|
||||
if len(parts) == 4 and parts[0] == 172 and 16 <= parts[1] <= 31:
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
# 允许:自家 OSS bucket 域名(<bucket>.<endpoint>)或 endpoint 自身及其子域
|
||||
try:
|
||||
storage_svc = get_shared_storage_service()
|
||||
|
||||
Reference in New Issue
Block a user