fix(aibox): Batch N 波次4+5 LLM 健壮性与权限隔离,Batch N 收束

波次 4 LLM 健壮性(T10/T19/T20/T21/T23 + T14 复现证伪):
- T10 调度器 await 改 wait_for 硬超时兜底(task_timeout+30s 宽限),
  软取消够不到的卡死路径不再永久停摆调度循环
- T19 FailoverClient 异常收窄为瞬时白名单(Connection/Timeout/
  ChunkedEncoding),MissingSchema 等永久配置错误不再污染冷却表
- T20 流式未收到 [DONE] 抛 TransientLLMError,回答无声截断可感知
- T21 env 覆盖黑名单 filter_env_overrides(LD_*/DYLD_*/PATH 等),
  executors 两处 + process_manager 接入
- T23 压缩后 tool 配对双向修复(失配 tool_calls 剥离防 API 400)
- T14 FTS5 复现证伪:JOIN 别名场景表名 MATCH 正常,无需修复

波次 5 权限隔离(T22/T-L17/T-L18;T1/T2 产品决策:仅告警/维持现状):
- T22a TOOL_PROFILE_TASK 定时任务精简工具面(无 update_identity/
  update_soul/set_proxy)
- T22b ProcessManager owner 隔离:bg_write/bg_kill 属主校验,
  观察面共享;DeepSeekChat 新增 owner_id 传导
- T1 绑定非回环地址时启动显著告警
- T-L17 model_config env 名按域名推荐(防跨厂商凭据外发)
- T-L18 list_profiles 过滤控制字符(防 ANSI 注入)

测试:test_n_fixes.py 增至 62 项;全量 598 passed / 1 failed
(Windows 向 ping 用例,环境项),零回归。审计报告补记 §24.7~24.11,
README 同步 owner 隔离语义。八轮审计 T 系列 28 项全部处置完毕。
This commit is contained in:
qing committed 2026-08-21 14:53:42 +00:00
1 parent 7e44025671
commit 67acae708c
10 files changed
+824 -107

No files matched your search

+40 -4
View File
@@ -22,6 +22,36 @@ from typing import Dict, Optional, Tuple
from common import ConfigManager, constants, Logger
# T21(审计 2026-08-21):env 覆盖黑名单——工具 schema 中 env 是开放
# object,LLM(可被网页内容提示注入诱导)可传 LD_PRELOAD/PYTHONPATH/
# PATH 等在 shell=True 之上进一步劫持进程加载(POSIX LD_PRELOAD 可加载
# 任意共享库)。命中黑名单的覆盖键拒绝并告警
ENV_DENY_EXACT = frozenset({
"PATH", "PYTHONPATH", "PYTHONHOME", "SHELL", "IFS",
"LD_LIBRARY_PATH", "LD_PRELOAD", "LD_AUDIT",
"DYLD_LIBRARY_PATH", "DYLD_INSERT_LIBRARIES",
"PSMODULEPATH", "COMSPEC",
})
ENV_DENY_PREFIXES = ("LD_", "DYLD_")
def filter_env_overrides(env) -> Tuple[Dict, list]:
"""T21:过滤 env 覆盖项,返回 (安全覆盖, 被拒绝键列表)。
键大小写不敏感匹配(Windows 环境变量不区分大小写);
LD_*/DYLD_* 前缀整体拒绝(加载器劫持面)。
"""
if not env:
return {}, []
safe, rejected = {}, []
for key, value in dict(env).items():
ku = str(key).upper()
if ku in ENV_DENY_EXACT or ku.startswith(ENV_DENY_PREFIXES):
rejected.append(key)
else:
safe[key] = value
return safe, rejected
class CommandExecutor(ABC):
"""命令执行器抽象基类 - 负责执行系统命令"""
@@ -293,10 +323,13 @@ class SubprocessCommandExecutor(CommandExecutor):
self.logger.info(f"执行命令: {command} (work_dir={work_dir})")
# 准备环境变量
# 准备环境变量(T21:黑名单键拒绝,防加载器/解释器劫持)
exec_env = os.environ.copy()
if env:
exec_env.update(env)
safe_env, rejected_keys = filter_env_overrides(env)
if rejected_keys:
self.logger.warning(f"env 覆盖被黑名单拒绝(T21): {rejected_keys}")
exec_env.update(safe_env)
process = None
token = None
@@ -384,10 +417,13 @@ class SubprocessCommandExecutor(CommandExecutor):
output_queue.put(("line", f"📁 工作目录: {work_dir}\n"))
output_queue.put(("line", "-" * 50 + "\n"))
# 准备环境变量
# 准备环境变量(T21:黑名单键拒绝,防加载器/解释器劫持)
exec_env = os.environ.copy()
if env:
exec_env.update(env)
safe_env, rejected_keys = filter_env_overrides(env)
if rejected_keys:
self.logger.warning(f"env 覆盖被黑名单拒绝(T21): {rejected_keys}")
exec_env.update(safe_env)
process = None
token = None