Skip to content

Releases

v0.7.1

Choose a tag to compare

@0QwQ0 0QwQ0 released this 16 Oct 14:33

0.7.1 — 修复 mux 跨用户泄漏(Issue #6,两个方向)

安全修复(使用 0.7.0 的部署请尽快升级)

  • 下行(宿主 → 客户端):宿主把 mux 通道当「单用户」用,会把全部工作区与会话推给每个连接。网关现在在本插件内透明代理 /api/remote.mux,并逐帧调用策略层 policy.frame() 按登录归属裁决:他人的 baseline.items(初始全量,最易漏)与 upsert.workspace 丢弃、order.workspaceIds/archivedSessionIds 裁剪、session/control 基线按属主投影、api-session/* 等事件按会话归属;未知流 fail-closed 丢弃;被隐藏的 waterfall 接收方由代理代其释放投递。
  • 上行(客户端 → 宿主):宿主对任何 sessionId 的 open 都照单服务,因此每个 open 先按归属授权,未授权不转发(只向客户端回 auth/forbidden)。
  • $events/result(审批/提问应答)删除「形状非空即放行」退档:必须能证明本登录被投递过该 eventId,否则 403,fail-closed。

实测 A/B(同一构建,仅切换 DSH_AUTH_MUX_PROXY)

普通用户可见工作区 对他人会话的 open
直通(排障退避) 20(泄漏) 到达宿主
代理(默认) 0 我方 auth/forbidden
管理员(阳性对照) 20 到达宿主

其他

  • 删除 v0.7.0 起已不可达的自研 mux 死代码(typertGateway.wireStream 自建流、MAX_STREAMS、StreamWork、sockets);升级入口收敛为 handleUpgrade,其他升级路径重新纳入 upgrade 策略(默认仅管理员,uiAuth.registerPolicy() 可放开)。
  • 修正安全套件分类汇总位置(此前 I18N/WS-ISO 与 v0.7.0 各面在表格里恒显示 0/0);现 164/164 通过、17 个分类。
  • 修正 fieldOf() 漏看 value 一层——宿主把 eventId 放在 value.eventId,此前 $events/result 的严格属主绑定从未匹配到过。

验证边界(如实说明,勿当作全覆盖)

  • 本环境宿主运行时对 remote 建出的会话回 session not found,因此「他人会话内容」这一层无法实证;被证实的是「未授权的 open 不再被转发」。
  • emit/api-session/* 帧在本环境从不投递(实测连管理员也为 0 帧)⇒ 事件面的过滤有单测与代理接线断言,但没有活体证据。
  • 排障退避 DSH_AUTH_MUX_PROXY=0 会重现上述问题,仅用于定位。
  • 帧级深层协议攻击仍未逐帧验证。

完整变更与回归清单见 CHANGELOG.md。

v0.7.0

Choose a tag to compare

@0QwQ0 0QwQ0 released this 30 Sep 13:37

目标宿主:DSH >=0.2.0-rc.2 <0.3.0(web profile)。v0.7.0 起只支持 0.2.0 线。
升级前请阅读下方的破坏性变更;安全模型见 docs/SECURITY.md。

0.7.0 — 只支持 DSH 0.2.0 线;模型与 API Key 按用户隔离

破坏性变更

  1. 完全移除 legacy 传输线(DSH 0.1.1-rc.2 的 dotted /api/<a>.<b> 与 apiProxy 事件流)。
    缺少 connection.authorizeIndex 时不降级:插件装上"明确拒绝"监听器对(HTTP 503 + 可操作文案、
    WS 直接销毁),面板不可访问并打印明确错误——宁可不可访问,也不无门暴露。
  2. 模型与 API Key 按用户隔离(详见 RBAC 设计):
    • 每个用户拥有只属于自己的 provider/模型/API Key;私有配置用 PBKDF2(登录口令) → KEK → AES-256-GCM
      信封加密(每配置随机 DEK,AAD 绑定 uid/profileId),会话只持有 32 字节 userKek、不持口令,
      密钥不写入宿主明文凭据存储;
    • 管理员在任何情况下都不能读取他人(含其他管理员)的私有配置(跨用户读取是代码级拒绝);
    • 管理员可把自己的模型分享给指定用户:被授权者可用、可看余额、看不到 Key,撤销立即 fail-closed;
    • 未配置即阻断(Q2):既无自有配置也无分享的用户,模型目录为空、切换模型被拒,不回退部署级配置;
    • 余额改由插件服务端代查(balanceQuery,只回数字;60s 缓存 + 限流);原生账户页对普通用户不可用(Q7)。
  3. 端点面按 DSH 0.2.0 重登:pluginManager 只读放行/写拒绝、permissionPresets/catalog 放行、
    userQuestions 按会话、officeToPdf 需 session+workspace+user 三重归属、account/* 全拒绝;
    自家 /auth/* 改为官方路由注册(webServer.register),最外层闸门保留(0.2.0 无全局前置钩子)。
  4. 兼容声明修正:旧的 >=0.1.1-rc.2 <0.2.0 在 npm 预发布匹配规则下只匹配 0.1.1-rc.2;
    现为 >=0.2.0-rc.2 <0.3.0 + 逐版本 dshReleases(见 0.2.0 兼容性)。

按用户模型的调用路径(R1-ii)

  • 每条自有配置与对外分享的配置注册一条宿主 provider 路由(ui-auth-<id>):认证头 x-api-key
    由我们的 resolveAuth 在调用瞬间解密(私有→会话 KEK;分享→服务端主密钥),
    因此既不必重写协议实现,也不把用户 Key 落到宿主明文存储。
  • 说明:llm/stream waterfall 对 agent-loop 请求深度冻结且只读,故不采用"在 waterfall 内改写凭据"的路线。

界面

  • 普通用户的【模型与密钥】页:解锁、列出自己的配置与收到的分享、设默认、选用分享、删除、查余额、新增配置;
  • 管理员新增【分享管理】页:创建分享、按用户名授予/撤销、查看每条分享的用量;
  • 原生账户页对普通用户替换为说明;移除了隐藏出厂导航行的 nth-child hack(0.2.0 由 settings-shell 掌管导航)。

在真实实例上验收时发现并修复

  • 凭据键语法(严重):宿主凭据记录要求恰好两段 <scope>/<id>,此前写成三段会让宿主的凭据服务在
    下次启动时拒绝解析整份文档,导致依赖凭据的插件全部等待、面板起不来。已改为两段并加写入前守卫与回归测试。
  • 按用户模型的调用路径(R1-ii):三处修复——宿主管包要从宿主进程入口解析(profile 里没有这些包)、
    ctx.llm 需要作用域注入、连接事实要由官方 schema 物化。修复后实测:普通用户的 llm/listProviders
    出现自己的 ui-auth-<id> 路由,模型目录被精确裁剪(R2)。
  • 查余额一直 unavailable:余额 fetcher 定义了却从未注入 ProfileService。修复后实测真 Key → CNY 5.78。
  • 设置页一直「重新连接中」:网关 gateUp 接管 WebSocket 升级并交给自研的 0.1.5 时代 mux,
    与 0.2.0 客户端协议不兼容 → 连接反复重建(管理员与普通用户都受影响)。现改为校验会话后把升级请求交给宿主。
    实测管理员与普通用户均不再重连,初始化正常继续。

验收阶段(真实 0.2.0 实例)追加修复

  • 跨实例互相踢下线:会话 Cookie 名按 DSH_HOME 唯一化(Cookie 不区分端口)。
  • 查余额:新增批量接口 balanceQueryAll(请求内顺序查询、整批节流一次);锁定时明确显示「需先解锁」。
  • 设置页一直「重新连接中」:升级请求不再交给自研 mux,改为会话校验后转交宿主。
  • 模型页排版:表格化 + 单组按钮 + 口令框独占一排(75% 高度)+「解锁」与其它按钮同排 + 「校验有效性」。
  • 修改密码 / 注册页:当前密码失焦校验(绿/红 + 2px 荧光)、新密码三档强度、确认框门控与实时比对。
  • 凭据键语法:两段 <scope>/<id>(三段会让宿主下次启动失败),加写入前守卫与回归测试。
  • R1-ii 接线:宿主管包从宿主进程入口解析、ctx.llm 作用域注入、连接事实由官方 schema 物化。

多语言(i18n,中/英)

  • 跟随 DSH 的 locale 服务自动切换:客户端 ctx.locale.register/bind + 订阅 locale/change;
    服务端渲染页按 Accept-Language(DSH 在无显式偏好时也是交给浏览器);错误消息走同一词典。
  • 词典两层:语义键(t())+ 约 320 条短语表(中文原文 → 英文,子串替换,支持拼装式文案);
    长键优先、未收录保持原文、只作用于本插件自己的面板且可双向还原。

验证

项目 结果
真实 DSH 0.2.0-rc.2 实例实测(npm run test:compat:0.2.0) 38/38
npm test(含 security-suite 158/159、host-smoke、login-page-check、客户端契约自检) 全绿
策略 / 加密 / 信封 / 服务 / 路由 单测 19/19 · 7/7 · 5/5 · 7/7 · 4/4
npm run store:check 20 passed / 0 failed

已知边界(矩阵已全测):0.2.0-rc.2 / 0.2.0-rc.1 / 0.1.7-rc.2 / 0.1.7-rc.1 均 38/38;
0.1.6-alpha.2 37/38、0.1.5-rc.3 / 0.1.5-rc.2 / 0.1.5-rc.1 均 34/38——失败项全部是这些宿主
不存在的端点(pluginManager/*、permissionPresets/catalog → 404),属能力收窄而非回归;
0.1.6-alpha.1 无法评估:该版本宿主自身启动失败(上游缺陷:@deepseek-ai/dsh-app-boot
未导出 bin.js 需要的 watchUserPatches),与本插件无关。
qrcode 依赖本次未改动(按所有者要求)。

v0.6.5

Choose a tag to compare

@0QwQ0 0QwQ0 released this 15 Sep 15:52

dsh-ui-auth v0.6.5

修复 modern 线(DSH 0.1.2+)上普通用户两个设置页加载失败的问题。

修复

1. 【Agent 预设】显示「无法加载 Agent 预设」

现象:普通用户打开【设置】→【Agent 预设】显示「无法加载 Agent 预设。」,
客户端日志为 client api: agentPresets/list failed: transport failure for /api/agentPresets/list: HTTP 403。

原因:modern 路径对普通用户是 deny-by-default,而 agentPresets/* 被整体拒绝;该页在加载时
先取预设清单(新建会话的预设选择器同样依赖它),一处被拒即整页失败。被拒的是只读元数据,
不是需要保护的写操作——属于白名单过窄的误伤。

2. 【插件】显示「暂时无法读取插件」

同一类问题的第二处:该页只调用 pluginInventory/list,也被整体拒绝。

修复后的权限边界

端点 普通用户
agentPresets/list(预设清单) ✅ 放行
agentPresets/read、agentPresets/select(本会话所用预设 / 为会话选择预设) ✅ 放行,按会话属主校验
pluginInventory/list(已安装插件清单) ✅ 放行(只读)
agentPresets/copy、agentPresets/deletePreset ❌ 仅管理员(预设可挂载插件与提示词,创建预设属提权面)
pluginInventory/install、uninstall、enable、disable、update ❌ 仅管理员(安装/卸载/启停插件属部署方能力)
插件设置与密钥(settings/*、credentials/*) ❌ 仅管理员

白名单是按端点逐条登记的,因此宿主后续新增的插件管理类写操作默认仍然被拒绝。
legacy 线(0.1.1-rc.2)不受影响(该线本就只拦管理员面)。

验证

验证项 结果
普通用户设置页验收(HTTP + 真实浏览器,新增 test/live-user-pages-check.mjs) 24/24
modern 策略单测 15/15
隔离 DSH 0.1.5-rc.1:HTTP/unary 与授权面 / remote.mux 逐帧隔离 29/29 / 12/12
legacy 线审计(真实 0.1.1-rc.2 部署,普通用户,浏览器 + HTTP) 16/16(两页均正常渲染,无同类问题)
npm test、typecheck、store:check、verify:clean 全绿(147/147 + 各冒烟)/ 通过 / 20/20 / 通过

文档

  • README:新增「普通用户能看到什么」权限表;顶部加入 legacy(DSH 0.1.1-rc.2)支持将于 v0.7.0 起结束
    的提示(如 DSH 架构有大变动会提前声明结束时间),并补上 npm 版本与下载量徽章。
  • 兼容性文档:补全普通用户可达面/拒绝面清单,并新增「已知边界」表——把仍有意收紧的设置项
    (模型与密钥、插件设置、预设创作、目录打开、设置写入、斜杠命令等)逐条列明。
  • 界面预览图在全新一次性实例上重拍。

升级提示:升级后重启面板即可,无需数据迁移;legacy 线行为不变。

v0.6.4

Choose a tag to compare

@0QwQ0 0QwQ0 released this 15 Sep 14:31

dsh-ui-auth v0.6.4 — 通行密钥(Passkey)登录

两步验证不再只等于 TOTP:这一版可以用指纹 / 面容 / 设备 PIN 代替密码登录,一个账号可绑定
多个通行密钥(本机密钥 + 手机密钥),并且不输入用户名就能登录。

新增:通行密钥(Passkey / WebAuthn)

  • 多设备、多密钥:每账号最多 20 个,可分别命名;两个添加入口——「本机通行密钥」用平台认证器
    (Windows Hello / Touch ID),「手机扫码添加」由浏览器自身显示跨设备二维码,手机无需与面板同网。
  • 免用户名登录:使用可发现凭据(residentKey: required),登录页一键「🔑 使用通行密钥登录」。
  • 协议交给成熟社区库:服务端 @simplewebauthn/server,浏览器端 @simplewebauthn/browser
    (登录页与设置面板共用同一份 bundle),不自行实现 base64url / CBOR / clientDataJSON 等细节。
  • 管理界面:设置面板新增「通行密钥(Passkey)」卡片(名称、类型、最近使用、备份状态、
    重命名、删除);管理员用户表格新增「通行密钥」列与「清除通行密钥」(设备丢失救援)。

变更:登录矩阵定稿(破坏性)

  • 移除「免密 + 动态码」登录:只提交动态码、不提交密码的登录请求一律返回 400,且不再区分
    账号是否启用 TOTP(避免账号状态枚举)。免密入口统一收敛到通行密钥。
账号状态 可用的登录方式
两步验证关闭 用户名 + 密码;通行密钥
两步验证开启,已绑定 TOTP 用户名 + 密码 + 动态码;通行密钥
两步验证开启,只绑定了通行密钥 用户名 + 密码 + 通行密钥;通行密钥(可免用户名)
  • 反锁死不变量:twoFactor 只在账号仍保有至少一种因子时为真;开启两步验证前必须先绑定因子;
    移除最后一个因子时自动关闭两步验证;两步验证开启且仅剩一个通行密钥时拒绝删除该密钥。
    该不变量在凭据库的读取、创建、修改三个边界统一规范化,手工改坏的记录也会被修正。

安全设计要点

  • 用户验证(userVerification: required)+ 证明书策略 attestation: none;
  • 挑战一次性、5 分钟过期、内存中限定条数,绑定用途与账号;严格校验来源(origin)与 RP ID;
  • 签名计数器回退检出并拒绝(认证器被复制的迹象);
  • 改动登录因子必须二次验证(密码 + 动态码,或密码 + 一次已有通行密钥断言),服务端签发
    5 分钟、绑定账号与来源 IP 的一次性票据——仅窃取会话不足以添加或删除登录手段;
  • 私钥永不离开用户设备,服务器只保存公钥与公开元数据;通行密钥相关操作全部写入审计日志。

已知限制:通行密钥对访问地址有硬性要求

浏览器不接受 IP 字面量作为通行密钥域(RP ID)——实测 Chrome 152 会抛
SecurityError: 127.0.0.1 is an invalid domain。因此请用 http://localhost:<端口> 或
域名 + HTTPS 访问面板;插件会在不可用地址上提前返回 409 并在界面给出应改用的地址,
不会出现"按钮点了没反应"。密码与 TOTP 登录不受影响。

验证(两条宿主线均以 0.6.4 构建实测)

验证项 结果
npm test(构建 + 全链) 安全套件 147/147、modern 策略 13/13、host-smoke(含通行密钥 16 项)、client-smoke(含 9 项)、登录页/端点联通 24/24
通行密钥端到端(隔离 DSH 0.1.5-rc.1 + 真实 Chrome + CDP 虚拟认证器) 27/27
隔离 DSH 0.1.5-rc.1:HTTP/unary 与授权面 / remote.mux 逐帧隔离 / 浏览器级 28/28 / 12/12 / 8/8
真实 DSH 0.1.1-rc.2:legacy 回归 / 浏览器级(127.0.0.1 源、localhost 源) 14/14 / 7/7 / 7/7
干净一次性环境下的安装 / 冷启动 / 卸载(含新增运行依赖) 通过(/auth/passkey/login/options → 409 + ip-literal,卸载后路由 404/405)
npm run store:check / npm run verify:clean 20/20 / 通过

其他

  • 新增登录页脚本端点 GET /auth/passkey/browser.js(IIFE bundle,带 ETag,匿名可取,无秘密)。
  • 新增可选环境变量 DSH_AUTH_RP_ID / DSH_AUTH_ORIGIN / DSH_AUTH_RP_NAME。
  • 注册成功引导页与登录提醒弹窗改为同时介绍两种第二因子(TOTP 或通行密钥)。
  • 文档:SECURITY.md 的测试矩阵改由测试自身生成;新增 npm run docs:matrix 与 npm run assets
    两个维护命令。

升级提示:从 0.6.2 及更早版本升级后,原先依赖「免密 + 动态码」登录的用户需要改用密码登录
(或用通行密钥);其余数据不受影响,通行密钥与 TOTP 均是新增/可选配置。

完整变更见 CHANGELOG.md。

dsh-ui-auth v0.6.2

Choose a tag to compare

@0QwQ0 0QwQ0 released this 14 Sep 16:04

dsh-ui-auth v0.6.2 — TypeScript 重写(行为不变)

本版本把插件改为 TypeScript 源码 + 构建产物:src/*.ts 是唯一源码,lib/*.js 由构建生成
(DSH 直接消费仓库,因此产物继续提交)。这是一次语言层面的重写——核心流程、UI 文案、CSS、
端点策略与错误文案全部保持不变。

目录与构建

源码 产物 构建
src/index.ts(宿主:网关/认证/用户管理/审计) lib/index.js(ESM) tsc -p tsconfig.json
src/modern-gateway.ts(0.1.2+ 传输适配) lib/modern-gateway.js 同上
src/modern-policy.ts(端点策略表) lib/modern-policy.js 同上
src/client.ts(设置面板客户端) lib/client.js node build/client.mjs(esbuild)

客户端为什么用 esbuild 包装:DSH 客户端模块契约是经典脚本 + 工厂形式 ——
window.__ModuleLoader__.load({ id, factory: (require) => … return module.exports }),
且 require('react') 取自宿主冻结模块表(React 不是全局变量)。build/client.mjs 复刻该包装并在
构建后自检产物形状;charset: 'utf8' 保证中文文案不被转义成 \uXXXX。

npm ci                 # 需要 devDependencies(typescript / esbuild / @types/*)
npm run typecheck      # 宿主 + 客户端两套 tsconfig 的严格类型检查
npm run build          # src/*.ts → lib/*.js
npm test               # 构建 + 全链测试
npm run verify:clean   # 校验 lib/ 与 src/ 同步(CI 使用)

安装期不需要构建:lib/*.js 已提交,dsh plugin add 直接消费产物;新增的
typescript/esbuild/@types/* 均为 devDependencies,不进运行产物。

保真证据

逐行 diff 无意义(tsc 会重排格式、esbuild 会重打印),因此用可判定的证据:

  • 新增 test/port-fidelity.mjs:对比重写前后的可观察要素多重集(字符串字面量、数字字面量、
    导出名)。lib/index.js、lib/modern-gateway.js、lib/modern-policy.js 完全一致;
    lib/client.js 仅剩注释被剥离与 undefined→void 0 的打印器差异。
  • 宿主主体:手写版与新构建的 lib/index.js 经 esbuild 压缩后 sha256 完全相同
    (token 级一致,仅格式与类型擦除差异)。
  • 客户端:两份 bundle 经同一 esbuild 规范化重打印后逐字节相同(等价于 AST 相同)。
  • 我在移植过程中引入的两处纯结构性改写(早退守卫、局部变量)已按"核心流程不动"改回原实现。

验证(2026-09-14)

验证项 结果
npm test(构建 + 全链) 安全套件 147/147、modern 策略 13/13、host-smoke 21 场景、client-smoke、crypto/TOTP 向量
隔离 DSH 0.1.5-rc.1 实例:HTTP/unary 28/28(登录门、carrier 桥接、跨用户隔离、deny-by-default、登出吊销)
隔离 DSH 0.1.5-rc.1 实例:/api/remote.mux 流 12/12(逐帧隔离、waterfall 不投递)
隔离 DSH 0.1.5-rc.1 实例:浏览器级 6/6(设置面板「用户管理」入口与页面)
真实 DSH 0.1.1-rc.2 部署:legacy 回归 14/14(dotted/apiProxy 路径不变)
真实 DSH 0.1.1-rc.2 部署:浏览器级 5/5
npm run store:check 20/20(权限信号集合不变:files/network/credentials)

升级说明

  • 无行为变更,直接升级即可;升级后重启面板(宿主半边为启动时加载)。
  • 仅审阅代码的人请注意:以 src/*.ts 为准,lib/*.js 是构建产物;CI 会校验两者同步。

工程变更

  • CI(.github/workflows/npm-publish.yml):typecheck → build + 全链测试 → 校验 lib/ 与 src/
    一致;PUPPETEER_SKIP_DOWNLOAD=true 避免在 CI 下载 Chromium;发布路径不变(Release 触发 + 每日同步)。
  • 新增 .gitattributes 统一 LF,保证上述同步校验在 Windows 与 Linux 上结果一致。

dsh-ui-auth v0.6.1

Choose a tag to compare

@0QwQ0 0QwQ0 released this 14 Sep 15:10

dsh-ui-auth v0.6.1 — 修复:DSH 0.1.2+ 上设置面板「用户管理」入口消失

这是 0.6.0 的回归修复,建议所有在 DSH 0.1.2 及以上版本使用本插件的部署升级。

现象

在 DSH 0.1.5-rc.1 实例中,0.6.0 的登录门、按用户隔离、原生 UI 都正常,但设置对话框的导航里没有「用户管理」——客户端插件什么都没注册,且没有任何报错(静默失效)。

根因

  1. DSH 0.1.5 把 slots 服务的提供方从 @deepseek-ai/dsh-client-runtime 改为 @deepseek-ai/dsh-client-ui-renderer;
  2. 同时把客户端模块的到达顺序变成显式依赖(dsh.client.inject 从信息性元数据变为工厂到达屏障);
  3. 本插件的客户端行没有声明任何依赖(boot 图为 {"id":"dsh-ui-auth","url":...},无 inject 字段),因此可能在 slots 就绪前 apply;
  4. 旧实现在 ctx.get('slots') === undefined 时直接 return,设置项从未注册。

该故障无法被 HTTP/RPC 级验收发现——只有真实浏览器能暴露。

修复

  • 客户端插件显式声明 exports.inject = ['slots'],由宿主保证服务就绪后再 apply;
  • 保留有界重试(40 × 250ms)作为兜底,并在最终仍取不到服务时
    console.error('[dsh-ui-auth] slots 服务不可用:设置面板「用户管理」未能注册')——不再静默;
  • 新增浏览器级验收脚本 test/live-ui-check.mjs(npm run test:ui):真实 Chromium 登录 →
    打开设置 → 断言导航含「用户管理」、页面渲染「我的账号 / 修改密码 / 两步验证」、
    管理员额外看到「创建用户 / 邀请码管理」、且无插件级错误日志;触发器兼容两种标注
    (0.1.5 为 aria-label="设置",0.1.1-rc.2 为按钮文本「设置」)。

验证(2026-09-14)

环境 结果
隔离 DSH 0.1.5-rc.1 实例(admin) 设置导航 [通用设置 | 模型 | 插件 | Agent 预设 | 用户管理],页面渲染正常,管理员额外项正常,无插件级错误 —— 6/6
真实 DSH 0.1.1-rc.2 面板(test1) 「用户管理」导航与页面同样正常 —— 5/5(确认未影响旧宿主)
npm test security-suite 147/147 + modern 策略 13/13

升级方式

dsh plugin --profile web add dsh-ui-auth          # 或按你现有的 link 安装方式
# 重启面板生效(Host 半边为启动时加载;客户端模块刷新页面即可)

纪律变更

客户端注册类改动必须用真实浏览器验收(npm run test:ui),不能只用 HTTP/RPC 断言——
这条已写入 docs/DSH-0.1.5-COMPATIBILITY.md 第 5 节「已知问题与修复记录」。

dsh-ui-auth v0.6.0

Choose a tag to compare

@0QwQ0 0QwQ0 released this 14 Sep 15:00

dsh-ui-auth v0.6.0 — DSH 0.1.5 适配(双线支持)

同一份代码按能力探测自动选择传输适配:ctx.get('connection')?.authorizeIndex 存在走 modern,否则走 legacy。无需任何配置。

Host 版本 路径 状态
0.1.1-rc.2(开发基线) legacy:dotted /api/<a>.<b> + apiProxy 事件流 行为完全不变
0.1.2-rc.1 … 0.1.5-rc.1(当前 npm latest) modern:slash Remote + /api/remote.mux + 原生浏览器 cookie 门 新增

1. 为什么需要适配(0.1.5 相对基线的真实变化)

  • apiProxy 服务整体移除,/api/events.mux、/api/events.host 不再存在;
  • RPC 改为 slash 命名(session.list → session/list)并新增大量端点——旧的 dotted 方法表会命中"未知端点默认放行"分支,造成登录门仍在但按用户隔离静默失效;
  • 流式传输合并为一条 mux(open/cancel/item/end/error),转发事件改为 $events 逻辑流 + POST /api/$events/result 回执;
  • 新增原生浏览器 cookie 门:浏览器必须持有 dsh-auth-<hash> 签名 cookie(由启动令牌换取),插件登录本身不足以让 UI 可用;
  • 存在绕过 Remote 的 /api 精确路由(session.export、session/uploadFileBinary、file、present.*)与 /open-in-app/*。

2. modern 路径做了什么

  • carrier 桥接:对每个已登录请求,进程内调用 connection.authenticatedUrl() + authorizeIndex() 换取原生 cookie 并注入转发请求,剥离客户端自带 dsh-auth-*,随后 connection.requestRejection() 复核。浏览器既不持有原生 cookie,也不持有进程启动令牌。
  • deny-by-default 策略:按 0.1.5 实际端点表(63 端点 / 15 命名空间)逐条授权;普通用户仅可访问自有 session / workspace / 文件范围 / agent 范围与共享只读元数据;settings/* 写、credentials/*、workspace/create、directoryPicker/*、commands/execute、subagents/*、agentPresets/*、llm/discoverModels、pluginInventory/list、dynamicCordisRunner/*、session/openWorkspacePath 与未知端点一律 403。
  • 归属与过滤:session/create|fork 先落盘归属再返回成功(失败回滚);session/list|search 过滤 items;session/control 的 queues/jobs/projections 三张 baseline 表与 workspace/follow 的 baseline/增量帧按键裁剪。
  • 流式隔离:mux 每次投递前复核登录态与属主;登出/改权 1 秒内关闭长连接;被过滤的 waterfall 由插件代答 $events/result {kind:'next'},避免 owner 的交互永久悬置;$events/result 只接受与本登录 + 本连接 + 已投递事件三者匹配的回执。
  • 绕过 Remote 的路由:/api/session.export、/api/session/uploadFileBinary 按 sessionId 属主放行;/api/file、/api/present.*、/open-in-app/* 仅管理员。
  • 投影修复:需要改写响应体的请求强制上游 accept-encoding: identity,写回时清除继承的 content-encoding/transfer-encoding/content-length(否则浏览器解压失败,实测中真实踩到)。

3. 其他修复与变更

  • 凭据记录键合规:不合 ^[a-z][a-z0-9-]*$ 的新用户名改用 user-<sha256> 记录键,并持久化退役用户名防止复用;小写用户名保持原键。该语法约束在基线即存在——旧写路径会让含大写/点用户名的注册在重启后拖垮整个凭据库(fail-closed 503),本版修复。
  • 去 timer 依赖:改用 setInterval + ctx.effect 清理,inject 为 ['webServer','connection'](两版 Host 都提供 connection 服务)。注:timer 在 0.1.2/0.1.5 的 base bundle 中仍存在,去掉它是可移植性改进而非激活阻断。
  • 新增运行时依赖 ws@^8.21.0(/api/remote.mux 的 WebSocket 实现,与 DSH 自身同线)。
  • engines 对齐 DSH:^22.19.0 || >=24.0.0。
  • 兼容声明:dsh.compatibility.dshReleases = { 0.1.1-rc.2: compatible, 0.1.5-rc.1: compatible }。

4. 升级说明

  • 0.1.1-rc.2 用户:行为不变;仅新增 ws 依赖(dsh plugin add 会自动安装)。
  • 0.1.2+ 用户:普通用户默认不可创建 workspace(路径属宿主能力,由部署方分配),且不可写任何设置命名空间;需要放开的部署可用 ctx.get('uiAuth').registerPolicy() 逐条登记。

5. 测试与验证

  • npm test:security-suite 147/147 + modern 策略 13/13
  • 隔离 DSH 0.1.5-rc.1 实例(官方 npm CLI + 一次性 DSH_HOME + profile link 安装):
    • HTTP/unary 28/28 — 登录门 302、未认证 API 401、登录后原生 UI 200(carrier 桥接成功)、slash Remote 可用、归属先落盘、跨用户隔离、cwd 覆盖 403、9 项管理面 403、未知端点 403、登出 401
    • mux 流 12/12 — 未认证升级被拒、$events ready、session/control baseline、普通用户 baseline 裁剪为空、api-session/added 逐帧隔离、普通用户不收到 waterfall
  • DSH 0.1.1-rc.2 真实部署回归:14/14 — dotted Remote 仍可用、管理面 403、会话导出属主检查 403、登出吊销生效
  • npm run store:check:20/20 门禁(运行时权限信号仍仅 files/network/credentials)

复现脚本:test/live-015-check.mjs、test/live-015-mux.mjs、test/live-legacy-check.mjs。

6. 文档

7. 致谢

现代适配层复用 PR #1(StormSeven1)的设计骨架:carrier 桥接、mux 处理、correlation 绑定、waterfall 释放语义、uiAuth seam 与策略回归用例;本版按 0.1.5 的真实端点/事件表重建,并补上其未覆盖的压缩响应投影与绕过 Remote 的 /api 路由。

dsh-ui-auth v0.5.1

Choose a tag to compare

@0QwQ0 0QwQ0 released this 30 Aug 13:19

dsh-ui-auth v0.5.1 — 安全加固

依据外部安全工程师审计报告核验后实施的 5 项加固:

1. Secure Cookie 动态启用(中危 → 已修)

TLS 直连或(仅信任反代时)X-Forwarded-Proto: https 的安全通道下,dsh_auth Cookie 自动追加 Secure;HTTP 内网调试不受影响;未信任反代时 XFP 不生效(防伪造)。

2. trustProxy 解析加固(低/中危 → 已修)

X-Forwarded-For 改取最右(最近受信反代追加、客户端不可伪造),修复开启 trustProxy 时伪造 XFF 可绕过登录锁定的问题;默认仍不信任 XFF。

3. bootstrap 自毁(低危 → 已修)

任意用户改密成功后自动删除 dsh-ui-auth-bootstrap.txt(明文初始密码不再长期残留)。

4. WS 未知帧 fail-closed 纪律固化

无归属的未知帧类型一律丢弃(实现本就默认拒绝),以测试断言固化,防未来新增帧类型时漏桶。

5. 会话 Token 哈希落盘(中危 → 已修)

dsh-ui-auth-sessions.json 只存 Token 的 SHA-256 哈希(64 位 hex),磁盘无明文 Token。
⚠️ 升级说明:升级后旧版明文会话记录不再恢复,所有用户需重新登录一次。

测试

  • security-suite 147/147(新增 Secure Cookie 3 项、clientIp 取最右/伪造 XFF 3 项、WS 未知帧 1 项)
  • host-smoke 场景 21 bootstrap 自毁、场景 16 会话哈希断言
  • 真实部署:会话哈希 save/verify 跨重启恢复 ✓、注册面 10/10、sessions 文件全部 64-hex 哈希校验

dsh-ui-auth v0.5.0

Choose a tag to compare

@0QwQ0 0QwQ0 released this 30 Aug 12:34

dsh-ui-auth v0.5.0 — 用户注册(邀请码)+ TOTP 两步验证 + 2FA

新增功能

用户注册(邀请码)

  • /auth/register 注册页:邮箱 + 用户名 + 密码(确认密码 + 眼睛图标)+ 有效邀请码
  • 密码策略:至少 8 位且包含两种及以上字符类型(大小写字母/数字/符号)
  • 管理员在【用户管理】→【邀请码管理】生成/撤销邀请码,可见每个码的使用数/剩余数
  • 注册成功自动登录并进入 TOTP 引导页(推荐立即添加两步验证令牌)

TOTP 两步验证(RFC 6238)

  • 绑定二维码(qrcode 库 SVG)+ 密钥/otpauth 手动输入兜底
  • 2FA 开关:绑定 TOTP 后默认不强制;开启后登录需「密码 + 动态码」,关闭时密码或动态码任选其一
  • 免密 TOTP 登录(密码留空 + 动态码);动态码错误计入登录失败锁定(防爆破)
  • 登录页动态码输入 + 眼睛按钮;未绑定用户登录后弹窗提醒(会话内一次,可永久忽略)
  • 用户管理页 TOTP 卡片(生成/启用/移除/忽略/2FA 开关);管理员可移除任意用户令牌

UI 修复

  • 输入框宽度越界(box-sizing)、角色徽章换行(nowrap)、TOTP 提醒频率(会话一次)

测试

  • security-suite 141/141、host-smoke 20 场景、TOTP RFC 6238 官方向量、client 冒烟全绿
  • 真实部署逐项验证:注册端点 10/10、TOTP 全链路 6/6、2FA 登录 4/4
  • 0.5.0 新增面安全矩阵补测(越权/注入/状态机/部署 9 项)

dsh-ui-auth v0.4.0

Choose a tag to compare

@0QwQ0 0QwQ0 released this 24 Aug 15:40

dsh-ui-auth v0.4.0 - 四项改进:1) WebSocket 事件流按用户隔离(网关代理逐帧过滤,网络层零泄漏,无需反代);2) 限流配置化(DSH_AUTH_MAX_FAILS/LOCK_MS/TRUST_PROXY);3) 会话持久化(重启不掉线)+ 启动竞态修复;4) 管理员操作审计 JSONL。测试:security-suite 98/98、host-smoke 17 场景、真实部署逐项验证通过。