Repository navigation
v0.1.7 — DSH 0.1.7-rc.2 适配、安全加固与全量缺陷修复
release: v0.1.7 — DSH 0.1.7-rc.2 适配、安全加固与全量缺陷修复
版本号与 DSH 对齐(0.1.7)。本次一次性包含自 0.1.5 以来的全部更新:
DSH 0.1.7 协议适配、控制台 UI 重构、token 消耗计费、安全修复与大批缺陷修复。
════════════════════════════════════════════════════════════════════
一、DSH 0.1.7 适配(本次的核心,三处接口断裂)
════════════════════════════════════════════════════════════════════
-
鉴权链路重建(最严重)
DSH 0.1.7 的进程启动 token 改为「每进程现生成的 32 字节随机数,只存在内存
WeakMap,不落盘、不可推导」,仅打印在dsh web的 URL 上。桥接原先靠读
~/.dsh/guard/logs/server-*.out.log里的?token=来发现它 —— 这条路彻底失效
(读到的是上一个进程的陈旧 token,换 Cookie 只会得到 401,桥接静默失联)。
新方案:用~/.dsh/.credentials.yaml里持久化的
client-connection/browser-session签名密钥离线铸造会话 Cookie
(Cookie 名 = dsh-auth-<base64url(sha256(authority))>,载荷含 authority/issuedAt/
expiresAt,HMAC-SHA256 签名)。不再依赖任何进程期状态,DSH 重启/换启动方式都不需要
重新适配。附带:到期前主动重铸、401 时在「铸造」与「token 交换」两种策略间自动回落。
新增scripts/test-dsh-auth.mjs(17 项断言)钉住该链路。 -
agent preset 机制迁移
0.1.7 的 preset 注册表既不扫描目录也不接受 preset 路径:preset 变成普通的
Cordis 行(@deepseek-ai/dsh-agent-preset,config.plugins必填),由 bundle patch
插入。旧的~/.dsh/.agent-presets/目录机制已死,qq-chat/qq-chat-v2会从
清单里消失,而桥接是 strict 解析、找不到就 fail-closed ⇒ QQ 会话完全起不来。
新增plugins/qq-agent-presets/bundle(含两个 preset patch + 权威守卫实现),
由scripts/build-agent-preset-patches.mjs从仓库权威源生成,注册进 profile 的
dependencies + bundles(重启后依然生效)。 -
工具改名与新危险工具
守卫名单里的cordis_define/cordis_undefine/cordis_run/cordis_stop在 0.1.7
已不存在(restrict遇未知名字会静默失效 ⇒ 安全边界被无声破坏);同时 0.1.7 新增
了load_workspace_dependencies(自带 Python/Node/pnpm 绝对路径,等于执行能力)与
list_subagent_models—— 旧守卫根本不认识它们。已全部按 0.1.7 实际注册的工具名对账。 -
settings写入契约(模式无法持久化)
0.1.7 的settings.write()要求:loader entry 的 id 必须等于命名空间名、插件模块
导出Config、且Config至少有一个meta.volatile字段。qq-mode-console三条全不
满足 ⇒ 控制台改模式永远抛No configurable plugin entry "qq-mode",模式只在本地生效、
下一次 DSH 轮询即被覆盖回滚。已修正(entry id →qq-mode,新增Config)。
注意.volatile()只是 DSH 自带 schemastery 的语法糖,本仓库钉的版本没有该方法,
故统一用两边都有的.extra('volatile', true)。
════════════════════════════════════════════════════════════════════
二、安全修复
════════════════════════════════════════════════════════════════════
- agent token 失效即提权(confused deputy):MCP 端在模型未传 token 时整个省略
x-agent-token头,而桥接把「无该头」等同于「管理端全权」⇒ 漏传令牌反而升级为控制台
权限。现 MCP 端一律声明x-agent-call: 1,桥接集中把关(不再依赖 40 余处端点各自判断),
并修掉/api/status会把 ownerQQ/白名单/其它群 activity 回给模型的分支。 - 明文凭据落盘:
state/与config.json对BUILTIN\Users可读(含控制台令牌、
SnowLuma OneBot 令牌 —— 后者等于 QQ 账号收发控制权)。现收紧为
属主/SYSTEM/Administrators。 - 控制台鉴权 fail-open 分支(
if (!consoleToken) return true)→ 改为启动即拒绝。 - 语音库绝对路径不再回给模型;提权命令改用环境变量传参(原为字符串拼接,路径含引号/
反引号会在提权后执行)。 - 新增
src/state-acl.mjs:ACL 逻辑收敛为唯一一份。原实现在 bridge.js 与
scripts/harden-state-acl.mjs 各写一份且都带同一个 bug ——
icacls DIR /inheritance:r /grant:r "u:(OI)(CI)F" /T会把子文件 DACL 清空
((OI)(CI) 是目录专用标志,套到文件上失败,而 /inheritance:r 已摘掉继承 ACE),
实测使 state/ 下 50 个文件变成空 DACL、连属主都读不了;叠加「回读自检被 Low 完整性
标签行的 (I) 误判」⇒ 每次启动都重跑这个破坏性命令。现为安全四步流程 + 收紧后自检。
════════════════════════════════════════════════════════════════════
三、控制台 UI 与 token 计费(0.1.5 以来)
════════════════════════════════════════════════════════════════════
- 控制台 UI 重构:设置页、逐轮花费视图、模式/角色面板;配套
scripts/console-ui-fixture.mjs、test-console-ui.mjs与 UI 契约清单。 - token 消耗计费与账本:
src/token-ledger.js+src/model-prices.js(含节假日分时价、
跨年份价目表)、state/token-usage.jsonl记账、控制台用量页与
docs/guides/TOKEN_USAGE_CONSOLE.md。 - 本次修掉的账本缺陷:
reset()未清evicted/evictedDirty⇒ 重置后仍显示旧花费,
且下一次 compact 会把「已删除」的额度写回磁盘;series()重复全量 buildIndex
(3 秒轮询接口成本翻倍);turns与steps恒等的死去重。
════════════════════════════════════════════════════════════════════
四、缺陷修复(本次审计发现)
════════════════════════════════════════════════════════════════════
- 模式规范名
simulation在所有输入口被静默拒绝(VALID_MODES及其重复字面量),
设定后currentMode会退回chat(一代仿真:不同 preset、无 v2 工具闸门、不同发送
路径),无日志无报错;同一处还修掉「在已处于 reserved2 时提交 simulation 会清空 v2
定时器并排空待发队列」。现为单一别名表,VALID_MODES由其派生。 - 分条发送失败未记账 ⇒ 群里什么都没收到却被记为「已发言」并抑制后续接话。
- 鉴权策略锁死:
invalidateAuth()被每次 WS 流结束调用,一次普通断线就永久改用过期
token,此后所有 RPC 401。 - 静默数据丢失:表情库与
sessions.json读取失败被吞成「空库/空映射」,随后回写
⇒ 永久清零。现按 loadSlang 既有约定:不存在→空,损坏→抛错并降级只读不写。 /api/socialV2/config在缺socialV2段时以{}为基线,导致工具开关归一化写成
current.tools?.[k] !== false→ true,一次控制台保存就静默打开默认关闭的
setStickerRemark/sendVoice。- MCP 注册期工具过滤漏 10 个开关(含默认关闭的
setStickerRemark),默认关的工具
仍随每次请求推送给模型并诱导其调用一个必然 403 的工具。 - UTF-16 截断切断代理对(emoji 变乱码)共 15 处面向模型的位置,含一处
slice(-200)
的镜像 bug;新增truncateText/truncateTextTail,262,734 例属性测试 0 违例。 - 长消息分条硬切代理对;黑话/研究结果提取的贪婪正则丢数据;config.json 的 BOM 被抹;
角色缓存仅按 mtime 取键;KNOWN_AGENT_TOKENS无界增长;浮动 Promise;sticker tmp
残留;session-router迁移分支静默无审计事件等。
════════════════════════════════════════════════════════════════════
五、隐私与发布卫生
════════════════════════════════════════════════════════════════════
- 修复
.gitignore的系统性失效:原排除规则按仓库根目录书写
(/PROJECT_GUIDE.md、/RESEARCH_*.md、/TOKEN_COST.md…),但这些文档后来整体
挪进docs/,旧规则再也匹配不上 ⇒「原始开发文档不入公开仓库」的约定静默失效,
6 份含本机真实路径的文件随时会被一并提交。已按新路径补回。 privacy-scan现为绿(扫描 135 个入库文件),所有剩余命中均已逐条人工核对并记录
判断依据(scripts/privacy-rules.json,本地文件、不入库)。- 修复自伤:
.gitignore注释里曾写入路径样式的示例字符串,本身即是一次命中。
════════════════════════════════════════════════════════════════════
六、测试
════════════════════════════════════════════════════════════════════
- 审计套件 28/28;
verify:adaptation57/0;test:dsh-auth17/0;
src/core/session-router33/33;privacy-scan 绿。 src/core/session-router.mjs由 13/33 修至 33/33(该模块尚未接入生产,本次仅保证其
自身与测试自洽)。- 说明:部分套件在受限沙箱内会因「无法 spawn 子进程 / 无法创建真实 symlink」而报假失败,
需在普通终端运行;这属于环境限制,不是产品行为。