fix(aibox): round-5 audit batch K - max_turns clamp, task input validation, XSS hardening, atomic writes, broadcast timeout

第五轮审计(J1~J16)全量修复 + 子代理 max_turns 钳制:
- 子代理 max_turns 钳制(0/None 回退默认 10,负数钳制 1)+ 14 项回归测试
- J1 任务系统投毒:add_memo 入库校验(时间/间隔/类型)、调度器预检与触发事务
  防御、展示路径兜底、register 移入 try、add_user_task 补 repeat_end_time 透传
- J2/J13 前端 XSS:三页面 marked 输出经 DOMPurify 净化、entry1 系统消息/任务
  分隔条改 textContent、CDN 脚本补 SRI
- J3 FTS 删除换序(先 fts 后内容表,索引不再残留)
- J5 原子写推广:新增 common/atomic.py(mkstemp+fsync+os.replace),config/prompt/
  history 全部接入;损坏配置备份后回退
- J6 单例初始化加锁(PromptManager/ConfigManager 并发首例不双重加载)
- J7 broadcast 并发发送 + wait_for 超时回收僵死连接
- J4/J8/J9~J12/J14~J16:验证异常脱敏、交互输入容错、MemoDatabase WAL+busy_timeout、
  memo 批量删除、/new 竞态、skills 引号转义、voice_client 清理、死代码移除、
  skill_manager 同名键/help 截断
This commit is contained in:
qing committed 2026-08-19 07:01:13 +08:00
1 parent f4fcf175ec
commit 457bb900fb
22 files changed
+1147 -126

No files matched your search

+25 -2
View File
@@ -27,6 +27,9 @@ class SkillManager:
2. Markdown 文件 (.md):第一行为简介(# 标题或普通文本),其余为技能内容
"""
# J16(审计 2026-08-19):--help 输出上限——内容会进系统提示词
MAX_HELP_LENGTH = 2000
def __init__(self, logger: Optional[Logger] = None):
self.logger = logger or Logger("SkillManager")
# name -> {path, file, summary, content, is_markdown}
@@ -38,7 +41,11 @@ class SkillManager:
# ------------------------------------------------------------------
def _run_help(self, skill_path: str) -> str:
"""执行 skill_path --help 并返回完整输出(stdout 优先,否则 stderr)。"""
"""执行 skill_path --help 并返回完整输出(stdout 优先,否则 stderr)。
J16(审计 2026-08-19):输出截断到 MAX_HELP_LENGTH——技能 --help
输出会进系统提示词,异常/恶意脚本可撑爆上下文。
"""
try:
result = subprocess.run(
[skill_path, "--help"],
@@ -50,6 +57,8 @@ class SkillManager:
output = result.stdout.strip()
if not output:
output = result.stderr.strip()
if len(output) > self.MAX_HELP_LENGTH:
output = output[:self.MAX_HELP_LENGTH] + "\n…(已截断)"
return output
except Exception as e:
self.logger.warning(f"运行 {skill_path} --help 失败: {e}")
@@ -129,12 +138,26 @@ class SkillManager:
and not f.startswith('.')
]
for skill_file in skill_files:
# J16(审计 2026-08-19):同名键冲突(foo 可执行文件与 foo.md)此前
# 由 listdir 顺序决定谁覆盖谁,行为不确定。改为可执行优先 + 确定性:
# 先加载可执行技能,Markdown 遇到已占用的同名键时跳过并告警
executable_files = [
f for f in skill_files
if not self._is_markdown_file(f)
]
markdown_files = [f for f in skill_files if self._is_markdown_file(f)]
for skill_file in executable_files + markdown_files:
skill_path = os.path.join(skills_dir, skill_file)
skill_name = os.path.splitext(skill_file)[0]
is_markdown = self._is_markdown_file(skill_file)
if is_markdown:
if skill_name in self.skills:
self.logger.warning(
f"Markdown 技能 '{skill_file}' 与同名可执行技能冲突,已跳过"
f"(可执行技能优先)")
continue
# Markdown 格式技能:直接读取内容
summary, content = self._read_markdown_skill(skill_path)
if content: