Refactor and translate comments and docstrings in utility modules to English for better clarity and maintainability. Update Gunicorn and application startup messages for consistency in language. Enhance documentation with English translations for better accessibility.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
工具模块
|
||||
tool module
|
||||
"""
|
||||
from app.utils.logger import get_logger
|
||||
from app.utils.cache import CacheManager
|
||||
|
||||
@@ -35,7 +35,7 @@ def generate_token(user_id: int, username: str, role: str = 'user', token_versio
|
||||
'sub': username,
|
||||
'user_id': user_id,
|
||||
'role': role,
|
||||
'token_version': token_version, # 用于单一客户端登录控制
|
||||
'token_version': token_version, # For single client login control
|
||||
}
|
||||
return jwt.encode(
|
||||
payload,
|
||||
@@ -60,12 +60,12 @@ def verify_token(token: str) -> dict:
|
||||
try:
|
||||
payload = jwt.decode(token, Config.SECRET_KEY, algorithms=['HS256'])
|
||||
|
||||
# 验证 token_version(单一客户端登录控制)
|
||||
# Verify token_version (single client login control)
|
||||
user_id = payload.get('user_id')
|
||||
token_version = payload.get('token_version')
|
||||
|
||||
if user_id and token_version is not None:
|
||||
# 检查数据库中的 token_version 是否匹配
|
||||
# Check if token_version in database matches
|
||||
if not _verify_token_version(user_id, token_version):
|
||||
logger.debug(f"Token version mismatch for user {user_id}: expected current, got {token_version}")
|
||||
return None
|
||||
@@ -81,12 +81,12 @@ def verify_token(token: str) -> dict:
|
||||
|
||||
def _verify_token_version(user_id: int, token_version: int) -> bool:
|
||||
"""
|
||||
验证 token 版本是否与数据库中存储的版本匹配。
|
||||
用于实现单一客户端登录(踢出重复登录)。
|
||||
Verify that the token version matches the version stored in the database.
|
||||
Used to implement single client login (kick out duplicate logins).
|
||||
|
||||
Args:
|
||||
user_id: 用户ID
|
||||
token_version: Token中的版本号
|
||||
user_id: user ID
|
||||
token_version: version number in Token
|
||||
|
||||
Returns:
|
||||
True if version matches, False otherwise
|
||||
@@ -109,7 +109,7 @@ def _verify_token_version(user_id: int, token_version: int) -> bool:
|
||||
return int(token_version) == int(db_token_version)
|
||||
except Exception as e:
|
||||
logger.error(f"_verify_token_version failed: {e}")
|
||||
# 如果验证失败,为了安全起见,返回 False
|
||||
# If validation fails, for security reasons, return False
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MemoryCache:
|
||||
"""内存缓存(Redis 不可用时的备选方案)"""
|
||||
"""In-memory cache (an alternative if Redis is unavailable)"""
|
||||
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
@@ -47,7 +47,7 @@ class MemoryCache:
|
||||
|
||||
|
||||
class CacheManager:
|
||||
"""缓存管理器"""
|
||||
"""cache manager"""
|
||||
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
@@ -98,7 +98,7 @@ class CacheManager:
|
||||
self._use_redis = False
|
||||
|
||||
def get(self, key: str) -> Optional[Any]:
|
||||
"""获取缓存"""
|
||||
"""Get cache"""
|
||||
try:
|
||||
data = self._client.get(key)
|
||||
if data:
|
||||
@@ -109,14 +109,14 @@ class CacheManager:
|
||||
return None
|
||||
|
||||
def set(self, key: str, value: Any, ttl: int = 300):
|
||||
"""设置缓存"""
|
||||
"""Set up cache"""
|
||||
try:
|
||||
self._client.setex(key, ttl, json.dumps(value))
|
||||
except Exception as e:
|
||||
logger.error(f"Cache write failed: {e}")
|
||||
|
||||
def delete(self, key: str):
|
||||
"""删除缓存"""
|
||||
"""Delete cache"""
|
||||
try:
|
||||
self._client.delete(key)
|
||||
except Exception as e:
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# 配置缓存
|
||||
# Configure cache
|
||||
_config_cache: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ def load_addon_config() -> Dict[str, Any]:
|
||||
"""
|
||||
global _config_cache
|
||||
|
||||
# 如果缓存存在,直接返回
|
||||
# If the cache exists, return directly
|
||||
if _config_cache is not None:
|
||||
return _config_cache
|
||||
|
||||
@@ -147,16 +147,16 @@ def load_addon_config() -> Dict[str, Any]:
|
||||
|
||||
def _convert_config_value(value: str, value_type: str) -> Any:
|
||||
"""
|
||||
根据类型转换配置值(与PHP端convertConfigValue方法保持一致)
|
||||
Convert configuration values according to type (consistent with the convertConfigValue method on the PHP side)
|
||||
|
||||
Args:
|
||||
value: 配置值字符串(可能为None)
|
||||
value_type: 配置类型
|
||||
value: configuration value string (may be None)
|
||||
value_type: configuration type
|
||||
|
||||
Returns:
|
||||
转换后的配置值
|
||||
Converted configuration value
|
||||
"""
|
||||
# 处理 None 或空值
|
||||
# Handling None or null values
|
||||
if value is None or value == '':
|
||||
if value_type == 'int':
|
||||
return 0
|
||||
@@ -186,7 +186,7 @@ def _convert_config_value(value: str, value_type: str) -> Any:
|
||||
return str(value) if value is not None else ''
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.warning(f"Config value type conversion failed: value={value}, type={value_type}, error={str(e)}")
|
||||
# 转换失败时返回默认值
|
||||
# Returns default value if conversion fails
|
||||
if value_type == 'int':
|
||||
return 0
|
||||
elif value_type == 'float':
|
||||
@@ -201,10 +201,10 @@ def _convert_config_value(value: str, value_type: str) -> Any:
|
||||
|
||||
def get_internal_api_key() -> Optional[str]:
|
||||
"""
|
||||
获取内部API密钥(优先从环境变量读取)
|
||||
Get the internal API key (preferably read from environment variables)
|
||||
|
||||
Returns:
|
||||
内部API密钥,如果未配置则返回None
|
||||
Internal API key, returns None if not configured
|
||||
"""
|
||||
try:
|
||||
env_val = os.getenv('INTERNAL_API_KEY', '').strip()
|
||||
@@ -227,7 +227,7 @@ def get_internal_api_key() -> Optional[str]:
|
||||
|
||||
def clear_config_cache():
|
||||
"""
|
||||
清除配置缓存(配置更新后调用)
|
||||
Clear configuration cache (called after configuration update)
|
||||
"""
|
||||
global _config_cache
|
||||
_config_cache = None
|
||||
|
||||
@@ -223,7 +223,7 @@ class PostgresConnection:
|
||||
|
||||
|
||||
def _ensure_session_utc(conn) -> None:
|
||||
"""每条连接检出后固定为 UTC,与 API 序列化约定一致。"""
|
||||
"""Each connection is fixed to UTC after checkout, consistent with the API serialization convention."""
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SET TIME ZONE 'UTC'")
|
||||
@@ -250,7 +250,7 @@ def get_pg_connection():
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
# 记录更详细的错误信息
|
||||
# Log more detailed error information
|
||||
error_msg = str(e) if e else repr(e)
|
||||
error_type = type(e).__name__
|
||||
logger.error(f"PostgreSQL operation error ({error_type}): {error_msg}", exc_info=True)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
HTTP 工具模块
|
||||
HTTP tool module
|
||||
"""
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter
|
||||
@@ -12,15 +12,15 @@ def get_retry_session(
|
||||
status_forcelist: tuple = (500, 502, 503, 504)
|
||||
) -> requests.Session:
|
||||
"""
|
||||
获取带重试机制的 HTTP Session
|
||||
Get HTTP Session with retry mechanism
|
||||
|
||||
Args:
|
||||
retries: 重试次数
|
||||
backoff_factor: 重试间隔因子
|
||||
status_forcelist: 需要重试的 HTTP 状态码
|
||||
retries: number of retries
|
||||
backoff_factor: retry interval factor
|
||||
status_forcelist: HTTP status codes that need to be retried
|
||||
|
||||
Returns:
|
||||
配置好的 Session 实例
|
||||
Configured Session instance
|
||||
"""
|
||||
session = requests.Session()
|
||||
retry = Retry(
|
||||
@@ -36,6 +36,6 @@ def get_retry_session(
|
||||
return session
|
||||
|
||||
|
||||
# 全局共享 Session
|
||||
# Global shared session
|
||||
global_session = get_retry_session()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from logging.handlers import RotatingFileHandler
|
||||
|
||||
|
||||
def setup_logger():
|
||||
"""配置全局日志"""
|
||||
"""Configure global logs"""
|
||||
log_level = os.getenv('LOG_LEVEL', 'INFO')
|
||||
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
|
||||
@@ -16,22 +16,22 @@ def setup_logger():
|
||||
format=log_format
|
||||
)
|
||||
|
||||
# 过滤 werkzeug 的 INFO 级别日志(减少噪音)
|
||||
# 只保留 WARNING 及以上级别
|
||||
# Filter werkzeug's INFO level logs (reduce noise)
|
||||
# Only keep WARNING and above levels
|
||||
werkzeug_logger = logging.getLogger('werkzeug')
|
||||
werkzeug_logger.setLevel(logging.WARNING)
|
||||
|
||||
# 过滤 kline 路由的 INFO 级别日志(减少噪音)
|
||||
# 只保留 WARNING 及以上级别
|
||||
# Filter INFO level logs for kline routes (reduce noise)
|
||||
# Only keep WARNING and above levels
|
||||
kline_logger = logging.getLogger('app.routes.kline')
|
||||
kline_logger.setLevel(logging.WARNING)
|
||||
|
||||
# 创建日志目录
|
||||
# Create log directory
|
||||
log_dir = 'logs'
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
# 添加文件处理器
|
||||
# Add file handler
|
||||
file_handler = RotatingFileHandler(
|
||||
os.path.join(log_dir, 'app.log'),
|
||||
maxBytes=10*1024*1024, # 10MB
|
||||
@@ -44,13 +44,13 @@ def setup_logger():
|
||||
|
||||
def get_logger(name: str) -> logging.Logger:
|
||||
"""
|
||||
获取指定名称的日志记录器
|
||||
Get the logger with the specified name
|
||||
|
||||
Args:
|
||||
name: 日志记录器名称
|
||||
name: logger name
|
||||
|
||||
Returns:
|
||||
Logger 实例
|
||||
Logger instance
|
||||
"""
|
||||
return logging.getLogger(name)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
安全的代码执行工具
|
||||
提供超时、资源限制和沙箱环境
|
||||
Secure code execution tools
|
||||
Provides timeouts, resource limits and a sandbox environment
|
||||
"""
|
||||
import signal
|
||||
import sys
|
||||
@@ -16,36 +16,36 @@ logger = get_logger(__name__)
|
||||
|
||||
|
||||
class TimeoutError(Exception):
|
||||
"""代码执行超时异常"""
|
||||
"""Code execution timeout exception"""
|
||||
pass
|
||||
|
||||
|
||||
@contextmanager
|
||||
def timeout_context(seconds: int):
|
||||
"""
|
||||
代码执行超时上下文管理器
|
||||
Code execution timeout context manager
|
||||
|
||||
注意:
|
||||
- 仅在Unix/Linux系统上有效
|
||||
- 仅在主线程中有效,非主线程会降级为不限制超时
|
||||
- Windows上会降级为不限制超时
|
||||
Notice:
|
||||
- Only works on Unix/Linux systems
|
||||
- Only valid in the main thread, non-main threads will be downgraded to unlimited timeout
|
||||
- Will downgrade to unlimited timeout on Windows
|
||||
|
||||
Args:
|
||||
seconds: 超时时间(秒)
|
||||
seconds: timeout (seconds)
|
||||
"""
|
||||
# 检查是否在主线程中
|
||||
# Check if in main thread
|
||||
is_main_thread = threading.current_thread() is threading.main_thread()
|
||||
|
||||
if sys.platform == 'win32':
|
||||
# Windows不支持signal.alarm,只能记录警告
|
||||
# signal.alarm is not supported on Windows, only warnings can be logged
|
||||
logger.warning("Windows does not support signal-based timeouts; execution time limits may not work")
|
||||
yield
|
||||
return
|
||||
|
||||
if not is_main_thread:
|
||||
# 非主线程不能使用signal,记录警告但不限制超时
|
||||
# logger.warning(f"当前在非主线程中运行(线程: {threading.current_thread().name}),"
|
||||
# f"signal超时不可用,代码执行可能无法限制时间")
|
||||
# Non-main threads cannot use signal. Warnings are logged but timeouts are not limited.
|
||||
# logger.warning(f"Currently running in a non-main thread (thread: {threading.current_thread().name}),"
|
||||
# f"signal timeout is not available, code execution may not limit the time")
|
||||
yield
|
||||
return
|
||||
|
||||
@@ -53,18 +53,18 @@ def timeout_context(seconds: int):
|
||||
raise TimeoutError(f"代码执行超时(超过{seconds}秒)")
|
||||
|
||||
try:
|
||||
# 设置信号处理器
|
||||
# Set up signal handler
|
||||
old_handler = signal.signal(signal.SIGALRM, timeout_handler)
|
||||
signal.alarm(seconds)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
# 恢复原来的信号处理器
|
||||
# Restore original signal handler
|
||||
signal.alarm(0)
|
||||
signal.signal(signal.SIGALRM, old_handler)
|
||||
except ValueError as e:
|
||||
# 如果signal设置失败(比如在某些环境中),记录警告但不中断执行
|
||||
# If signal setup fails (such as in some environments), log a warning but do not interrupt execution
|
||||
logger.warning(f"Failed to set signal timeout: {str(e)}; execution will continue without timeout enforcement")
|
||||
yield
|
||||
|
||||
@@ -77,35 +77,35 @@ def safe_exec_code(
|
||||
max_memory_mb: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
安全执行Python代码
|
||||
Safe execution of Python code
|
||||
|
||||
Args:
|
||||
code: 要执行的Python代码
|
||||
exec_globals: 全局变量字典
|
||||
exec_locals: 局部变量字典(如果为None,则使用exec_globals)
|
||||
timeout: 超时时间(秒),默认30秒
|
||||
max_memory_mb: 最大内存限制(MB),默认500MB
|
||||
code: Python code to execute
|
||||
exec_globals: dictionary of global variables
|
||||
exec_locals: dictionary of local variables (if None, exec_globals is used)
|
||||
timeout: timeout (seconds), default 30 seconds
|
||||
max_memory_mb: Maximum memory limit (MB), default 500MB
|
||||
|
||||
Returns:
|
||||
执行结果字典,包含:
|
||||
- success: bool,是否执行成功
|
||||
- error: str,错误信息(如果失败)
|
||||
- result: Any,执行结果(如果有)
|
||||
Execution result dictionary, including:
|
||||
- success: bool, whether the execution was successful
|
||||
- error: str, error message (if failure)
|
||||
- result: Any, execution result (if any)
|
||||
|
||||
Raises:
|
||||
TimeoutError: 如果代码执行超时
|
||||
TimeoutError: If the code execution times out
|
||||
"""
|
||||
if exec_locals is None:
|
||||
exec_locals = exec_globals
|
||||
|
||||
# 设置内存限制(如果支持)
|
||||
# Set memory limit (if supported)
|
||||
if max_memory_mb is None:
|
||||
max_memory_mb = 500 # 默认500MB
|
||||
max_memory_mb = 500 # Default 500MB
|
||||
|
||||
try:
|
||||
# 注意:resource.setrlimit 是进程级别,会影响整个 API 进程。
|
||||
# 之前全局限制为 500MB 可能导致并行策略/线程无法分配内存。
|
||||
# 仅当显式开启 SAFE_EXEC_ENABLE_RLIMIT 时才设置。
|
||||
# Note: resource.setrlimit is process level and affects the entire API process.
|
||||
# The previous global limit of 500MB could prevent parallel strategies/threads from allocating memory.
|
||||
# Only set if SAFE_EXEC_ENABLE_RLIMIT is explicitly turned on.
|
||||
if sys.platform != 'win32' and os.getenv('SAFE_EXEC_ENABLE_RLIMIT', 'false').lower() == 'true':
|
||||
try:
|
||||
import resource
|
||||
@@ -117,8 +117,8 @@ def safe_exec_code(
|
||||
else:
|
||||
logger.debug("No resource memory limit (SAFE_EXEC_ENABLE_RLIMIT disabled or unsupported platform)")
|
||||
|
||||
# 在Windows上,timeout_context不会真正限制时间
|
||||
# 但会记录警告
|
||||
# On Windows, timeout_context doesn't really limit the time
|
||||
# But a warning will be logged
|
||||
with timeout_context(timeout):
|
||||
exec(code, exec_globals, exec_locals)
|
||||
|
||||
@@ -157,12 +157,12 @@ def safe_exec_code(
|
||||
|
||||
def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
"""
|
||||
验证代码安全性(基本检查)
|
||||
Verify code security (basic check)
|
||||
|
||||
检查代码中是否包含危险的函数调用或导入
|
||||
Check your code for dangerous function calls or imports
|
||||
|
||||
Args:
|
||||
code: 要检查的Python代码
|
||||
code: Python code to check
|
||||
|
||||
Returns:
|
||||
(is_safe: bool, error_message: Optional[str])
|
||||
@@ -170,9 +170,9 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
import ast
|
||||
import re
|
||||
|
||||
# 危险的关键字和函数名
|
||||
# Dangerous keywords and function names
|
||||
dangerous_patterns = [
|
||||
# 系统命令执行
|
||||
# System command execution
|
||||
r'\bos\.system\b',
|
||||
r'\bos\.popen\b',
|
||||
r'\bos\.spawn\b',
|
||||
@@ -180,16 +180,16 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
r'\bos\.fork\b',
|
||||
r'\bsubprocess\b',
|
||||
r'\bcommands\b',
|
||||
# 代码执行
|
||||
# code execution
|
||||
r'\b__import__\s*\(',
|
||||
r'\beval\s*\(',
|
||||
r'\bexec\s*\(',
|
||||
r'\bcompile\s*\(',
|
||||
# 文件操作
|
||||
# File operations
|
||||
r'\bopen\s*\(',
|
||||
r'\bfile\s*\(',
|
||||
r'\b__builtins__\b',
|
||||
# 模块导入
|
||||
# module import
|
||||
r'\bimport\s+os\b',
|
||||
r'\bimport\s+sys\b',
|
||||
r'\bimport\s+subprocess\b',
|
||||
@@ -208,7 +208,7 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
r'\bimport\s+multiprocessing\b',
|
||||
r'\bimport\s+threading\b',
|
||||
r'\bimport\s+concurrent\b',
|
||||
# 反射和元编程(可能用于绕过限制)
|
||||
# Reflection and metaprogramming (possibly used to bypass restrictions)
|
||||
r'\bgetattr\s*\(.*__import__',
|
||||
r'\bgetattr\s*\(.*eval',
|
||||
r'\bgetattr\s*\(.*exec',
|
||||
@@ -219,14 +219,14 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
r'\bglobals\s*\(',
|
||||
r'\blocals\s*\(',
|
||||
r'\bdir\s*\(',
|
||||
r'\btype\s*\(.*\)\s*\(', # type() 可能用于创建新类型
|
||||
r'\btype\s*\(.*\)\s*\(', # type() may be used to create new types
|
||||
r'\b__class__\b',
|
||||
r'\b__bases__\b',
|
||||
r'\b__subclasses__\b',
|
||||
r'\b__mro__\b',
|
||||
r'\b__init__\b.*__import__',
|
||||
r'\b__new__\b.*__import__',
|
||||
# 其他危险操作
|
||||
# Other dangerous operations
|
||||
r'\b__builtins__\s*\[',
|
||||
r'\b__builtins__\s*\.',
|
||||
r'\b__import__\s*\(',
|
||||
@@ -234,16 +234,16 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
r'\bimp\b',
|
||||
]
|
||||
|
||||
# 检查代码中是否包含危险模式
|
||||
# Check your code for dangerous patterns
|
||||
for pattern in dangerous_patterns:
|
||||
if re.search(pattern, code):
|
||||
return False, f"检测到危险代码模式: {pattern}"
|
||||
|
||||
# 尝试解析AST,检查是否有危险的节点
|
||||
# Try parsing the AST, checking for dangerous nodes
|
||||
try:
|
||||
tree = ast.parse(code)
|
||||
|
||||
# 危险模块列表(扩展)
|
||||
# List of dangerous modules (extended)
|
||||
dangerous_modules = [
|
||||
'os', 'sys', 'subprocess', 'pymysql', 'sqlite3',
|
||||
'requests', 'urllib', 'http', 'socket', 'ftplib', 'telnetlib',
|
||||
@@ -252,38 +252,38 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
'importlib', 'imp', 'builtins'
|
||||
]
|
||||
|
||||
# 危险函数列表(扩展)
|
||||
# 注意:hasattr 是安全的,只用于检查属性,不用于访问
|
||||
# List of dangerous functions (extended)
|
||||
# Note: hasattr is safe and is only used to check attributes, not to access
|
||||
dangerous_functions = [
|
||||
'eval', 'exec', 'compile', '__import__',
|
||||
'getattr', 'setattr', 'delattr', # hasattr 已移除,它是安全的
|
||||
'getattr', 'setattr', 'delattr', # hasattr has been removed, it is safe
|
||||
'globals', 'locals', 'vars', 'dir', 'type'
|
||||
]
|
||||
|
||||
# 检查是否有危险的函数调用
|
||||
# Check for dangerous function calls
|
||||
for node in ast.walk(tree):
|
||||
# 检查是否有对危险函数的调用
|
||||
# Check for calls to dangerous functions
|
||||
if isinstance(node, ast.Call):
|
||||
if isinstance(node.func, ast.Name):
|
||||
func_name = node.func.id
|
||||
if func_name in dangerous_functions:
|
||||
return False, f"检测到危险函数调用: {func_name}()"
|
||||
|
||||
# 检查是否有os.system等调用
|
||||
# Check if there are calls to os.system etc.
|
||||
if isinstance(node.func, ast.Attribute):
|
||||
if isinstance(node.func.value, ast.Name):
|
||||
if node.func.value.id in dangerous_modules:
|
||||
return False, f"检测到危险模块调用: {node.func.value.id}.{node.func.attr}"
|
||||
|
||||
# 检查是否有 getattr(builtins, '__import__') 等绕过方式
|
||||
# Check if there are bypass methods such as getattr(builtins, '__import__')
|
||||
if isinstance(node.func, ast.Name) and node.func.id == 'getattr':
|
||||
# 检查 getattr 的参数
|
||||
# Check the parameters of getattr
|
||||
if len(node.args) >= 2:
|
||||
if isinstance(node.args[0], ast.Name) and node.args[0].id in ['builtins', '__builtins__']:
|
||||
if isinstance(node.args[1], ast.Constant) and node.args[1].value in dangerous_functions:
|
||||
return False, f"检测到通过 getattr 绕过限制: getattr({node.args[0].id}, '{node.args[1].value}')"
|
||||
|
||||
# 检查导入语句:用户脚本中一律禁止使用 import(统一由平台注入安全依赖)
|
||||
# Check the import statement: the use of import is prohibited in user scripts (security dependencies are uniformly injected by the platform)
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
return False, "不允许在脚本中使用 import 语句,请直接使用平台提供的 pd/np 等对象"
|
||||
@@ -291,19 +291,19 @@ def validate_code_safety(code: str) -> Tuple[bool, Optional[str]]:
|
||||
if isinstance(node, ast.ImportFrom):
|
||||
return False, "不允许在脚本中使用 import 语句,请直接使用平台提供的 pd/np 等对象"
|
||||
|
||||
# 检查是否有访问 __builtins__ 的尝试
|
||||
# Check if there is an attempt to access __builtins__
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Attribute):
|
||||
if isinstance(node.attr, str) and node.attr.startswith('__') and node.attr.endswith('__'):
|
||||
if node.attr in ['__builtins__', '__import__', '__class__', '__bases__', '__subclasses__', '__mro__']:
|
||||
# 检查是否在危险上下文中使用
|
||||
# Check if used in dangerous context
|
||||
if isinstance(node.value, ast.Name) and node.value.id in ['builtins', '__builtins__']:
|
||||
return False, f"检测到访问危险属性: {node.value.id}.{node.attr}"
|
||||
|
||||
except SyntaxError as e:
|
||||
return False, f"代码语法错误: {str(e)}"
|
||||
except Exception as e:
|
||||
# 如果AST解析失败,记录警告但允许继续(可能是代码不完整)
|
||||
# If AST parsing fails, log a warning but allow to continue (possibly incomplete code)
|
||||
logger.warning(f"AST parse failed; skipping safety checks: {str(e)}")
|
||||
|
||||
return True, None
|
||||
|
||||
Reference in New Issue
Block a user