myagent-ai 1.31.3 → 1.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -99,9 +99,15 @@ class BrowserProfile:
99
99
  base_dir: Profile 根目录
100
100
  config: 预置配置(来自 PRESET_PROFILES)
101
101
  """
102
+ # [v1.32.2] 安全修复: 路径遍历防护
103
+ if ".." in name or "/" in name or "\\" in name or not name:
104
+ raise ValueError(f"无效的 Profile 名称: {name}")
102
105
  self.name = name
103
106
  self.base_dir = base_dir
104
- self.profile_dir = base_dir / name
107
+ # 二次校验: 确保解析后的路径仍在 base_dir
108
+ self.profile_dir = (base_dir / name).resolve()
109
+ if not str(self.profile_dir).startswith(str(base_dir.resolve())):
110
+ raise ValueError(f"Profile 名称 '{name}' 导致路径逃逸: {self.profile_dir}")
105
111
  self.user_data_dir = self.profile_dir / "user_data"
106
112
  self.cookies_file = self.profile_dir / "cookies.json"
107
113
  self.config_file = self.profile_dir / "config.json"
@@ -713,17 +713,41 @@ class WebControlManager:
713
713
  if parsed.scheme not in ('http', 'https'):
714
714
  raise ValueError(f"Unsupported URL scheme: {parsed.scheme}")
715
715
 
716
- # 禁止访问内网地址
716
+ # 禁止访问内网地址(防止 SSRF 攻击)
717
717
  hostname = parsed.hostname
718
- if hostname and (
719
- hostname in ('localhost', '127.0.0.1', '0.0.0.0', '::1') or
720
- hostname.startswith('10.') or
721
- hostname.startswith('192.168.') or
722
- hostname.startswith('172.') or
723
- hostname.endswith('.local')
724
- ):
725
- # 允许本地访问(开发环境需要)
726
- pass # 暂不禁用内网, 可通过配置控制
718
+ import ipaddress
719
+ if hostname:
720
+ # 解析 hostname 为 IP(处理 DNS Rebinding)
721
+ try:
722
+ import socket
723
+ resolved_ips = socket.getaddrinfo(hostname, parsed.port or 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
724
+ for family, _, _, _, sockaddr in resolved_ips:
725
+ ip = sockaddr[0]
726
+ try:
727
+ ip_obj = ipaddress.ip_address(ip)
728
+ # 阻止所有内网地址
729
+ if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_reserved or ip_obj.is_multicast:
730
+ raise ValueError(f"SSRF blocked: {hostname} resolves to private/reserved IP {ip}")
731
+ except ValueError:
732
+ raise
733
+ except socket.gaierror:
734
+ pass
735
+ # 字符串级别的快速检查(防止绕过)
736
+ blocked = (
737
+ hostname in ('localhost', '127.0.0.1', '0.0.0.0', '::1', '::') or
738
+ hostname.endswith('.local') or
739
+ hostname.endswith('.internal') or
740
+ hostname.startswith('10.') or
741
+ hostname.startswith('192.168.') or
742
+ # 172.16.0.0 - 172.31.255.255
743
+ (hostname.startswith('172.') and len(hostname.split('.')) >= 3 and 16 <= int(hostname.split('.')[1]) <= 31) or
744
+ # 169.254.0.0/16 云元数据
745
+ hostname.startswith('169.254.') or
746
+ # 100.64.0.0/10 CGN
747
+ hostname.startswith('100.') and len(hostname.split('.')) >= 2 and 64 <= int(hostname.split('.')[1]) <= 127
748
+ )
749
+ if blocked:
750
+ raise ValueError(f"SSRF blocked: cannot access internal address {hostname}")
727
751
 
728
752
  # 检查缓存(仅对非 session 请求)
729
753
  cache_key = hashlib.md5(url.encode()).hexdigest()