Repository navigation
settings 写入路径只执行 required / options / pattern —— 声明的 min / max / minLength / maxLength 一条都不校验(auth.password_min_length 可被写成 1) #5932
Copy link
Copy link
Closed
Description
Activity
分诊:
pm:queue+domain:services。- 落点锚定:
packages/services/service-settings/src/settings-service.ts的SettingsService.validatePatch(约 :899-1025)—— 在required/options/pattern三个分支旁补min/max/minLength/maxLength。声明侧packages/spec/src/system/settings-manifest.zod.ts的SpecifierSchema已经声明了这五类约束,本单是执行侧补齐,⛔ 不需要改packages/spec⇒ 不转 spec 座位;若实现中发现必须动SpecifierSchema或errors.zod.ts的码表,STOP 上报走跨座位转移。 - 入队而非决策箱:修法形状由同一函数上的 service-settings: select 型 specifier 的 options 在保存期完全不校验 —— 声明的枚举不被强制 #5131(options 表的执行)钉死并已落地 —— 补分支 + 发
FieldError(码从packages/spec/src/api/errors.zod.ts现有集合里挑,ADR-0114 的constraint带{min,max})+ 沿用 service-settings: select 型 specifier 的 options 在保存期完全不校验 —— 声明的枚举不被强制 #5131 的 TOUCH 闸门语义(只校验本次 patch 触及的键,避免历史漂移锁死工作区)。无待答的产品问题。 - 一并处理的第二判定点:env 侧(env 来源的 settings 值绕过 manifest 的 options 表校验 —— #5094 在写入 API 上堵住的洞,在 OS_* 覆盖这一侧原样敞开 #5204)目前只对 options 表拒收;补 min/max 要走
effectiveEnvOverride那一个判定点,⛔ 不开第二份实现 —— env 来源的 settings 值绕过 manifest 的 options 表校验 —— #5094 在写入 API 上堵住的洞,在 OS_* 覆盖这一侧原样敞开 #5204 的成因就是同一比较有两份。 - 严重度(分诊定级,回应正文「建议 triage 时定」):按
pm:queue常规档,不升 p0 —— 写入需setup.write权限,不是未授权可达;但auth.manifest.ts的六项口令策略键(password_min_length声明min:6却接受1)使其带安全外溢,车道排序时优于纯配置面缺陷。正文「未核实各消费者对越界值的容忍度」不是入队阻塞项,承接方在 PR 里如实记录即可。 - 查重:三仓搜
validatePatch/password_min_length/minLengthsettings 命中 service-settings: select 型 specifier 的 options 在保存期完全不校验 —— 声明的枚举不被强制 #5131(options 半边,已闭)、env 来源的 settings 值绕过 manifest 的 options 表校验 —— #5094 在写入 API 上堵住的洞,在 OS_* 覆盖这一侧原样敞开 #5204(env 对齐)、localization 的 timezone / currency 用「策展式 options 表」当穷尽执行边界:合法的 IANA 时区与 ISO 4217 币种被拒 #5712(发现上下文,services 队列)与本单;feat(sms): 短信全局/每租户日发送配额(成本总量闸) #2814(smsdaily_quota将声明min:0)是本单落地后的受益方,已在正文点名。无重复入口。 - 过时前提:
git log --oneline origin/main -- packages/services/service-settings/src/settings-service.ts无 service-settings: select 型 specifier 的 options 在保存期完全不校验 —— 声明的枚举不被强制 #5131 之后触及validatePatch的改动,四个键在写入路径上仍零读取点。前提成立。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- 落点锚定:
认领:PM 循环第 6 轮(rc.5 发布后恢复)
会话:session_015a5qkLzpGXhLL2F5gvJ7dD
分支:claude/issue-5932-settings-range-enforce
Worktree:objectstack-5932
域:domain:services
文件面:packages/services/service-settings/src/(settings-service.ts 的 validatePatch + env 侧 effectiveEnvOverride 判定点 + 测试)按分诊钉死的修法实施:照 #5131 同形补
min/max/minLength/maxLength四分支 + 既有码表 FieldError(ADR-0114constraint带{min,max})+ TOUCH 闸门语义;env 侧走单判定点不开第二份实现。⛔ 若必须动packages/spec(SpecifierSchema / errors.zod.ts 码表)即 STOP 上报走跨座位转移,不自行实施。
Generated by Claude Code
ACCEPT → PR #6201(draft 转 ready 入队中)。
- 修法照分诊钉死的形状落地:
validatePatch补取值窗口分支,发既有码表min_value/max_value/min_length/max_length(ADR-0114 D2,constraint带完整窗口,长度类附actual);TOUCH 闸门继承(收紧窗口不锁死老工作区);env 侧走effectiveEnvOverride单判定点,并把 reporter 泛化成共享句段——两族约束一份去重、一份脱敏,env 来源的 settings 值绕过 manifest 的 options 表校验 —— #5094 在写入 API 上堵住的洞,在 OS_* 覆盖这一侧原样敞开 #5204 的「两份实现各自漂移」病根被顺手拆掉。⛔packages/spec零改动(约束早已声明、码表现员够用,STOP 未触发)。 - 验收主证:issue 三形状全钉(含
auth.password_min_length: 1端到端拒绝 +OS_AUTH_PASSWORD_MIN_LENGTH=1同裁决);逆向验证方向预判命中(仅回退源码 ⇒ 恰好 16 条拒绝 pin 红、6 条宽容 pin 绿);42 个在库声明窗口扫描 0 默认值越窗,无 manifest 因此不可写。 - 一个判断,采信:窗口按声明而非按 specifier
type收口——SpecifierSchema的 superRefine 只把 options 绑到三型,窗口键不绑任何型;在服务侧自拟类型清单即「会漂移的第三份清单」,会把本 issue 复制到下一层。 service-sms越面说明(认领文件面外,正当):sms-daily-quota.ts注释曾以「settings 写入路径只执行 required / options / pattern —— 声明的 min / max / minLength / maxLength 一条都不校验(auth.password_min_length 可被写成 1) #5932 未执行 ⇒ min:0 惰性」论证消费侧 clamp,本 PR 证伪该句;dev 做纯注释更正(零断言),并写清 clamp 仍然成立的三个理由(非数值形状 / 小数 / TOUCH 前存量行)。- 衍生:
step(同族第五键)按观察类立 settings 的step是同族第五个声明但不执行的值约束键 —— 先要一个「它算不算值约束」的判断 #6199 交分诊;「消费者对越界值的容忍度」如实记为未验,不扩范围。 - 远端 CI 23 检全绿;消费半径(sms 70 / cli 902 / verify 17)全绿。
Generated by Claude Code
- 修法照分诊钉死的形状落地:
- added a commit that references this issue
on Oct 7, 2026
在 #5712(localization options 表)的核查中发现,记录下来交 triage。不在 #5712 的完成范围内 —— #5712 处理的是
select的 options 词表,这条是同一个validatePatch里另外四个已声明但完全未执行的约束。事实
packages/spec/src/system/settings-manifest.zod.ts的SpecifierSchema声明了五类值约束,pattern/min/max/minLength/maxLength。packages/services/service-settings/src/settings-service.ts的SettingsService.validatePatch(约 :899-1025)只有三个分支:required、options(#5131)、pattern。min/max/minLength/maxLength在整个写入路径上没有任何读取点 —— grep 全仓,这四个键在service-settings里只出现在 manifest 字面量中,没有消费者。实测(worktree 内探针,
SettingsService+ 内存后端):为什么不是休眠项:已上线的 manifest 真的在声明这些边界
不是「没人用的能力」,而是在库的声明与执行不一致,且落点包含安全项:
packages/services/service-settings/src/manifests/auth.manifest.ts:85-90password_min_lengthmin: 6, max: 641(甚至负数)auth.manifest.ts:94-100password_max_lengthmin: 16, max: 256auth.manifest.ts:128-131password_min_classesmin: 1, max: 4auth.manifest.ts:139-142password_history_countmin: 0, max: 24auth.manifest.ts:150-153password_expiry_daysmin: 0, max: 3650ai.manifest.ts:189/193/197/216/288/294min/max即
PUT /api/settings/auth写password_min_length: 1会被接受并生效,而 UI 的数字框声明的下限是 6。攻击面不是「绕过 UI」本身,而是 Prime Directive #10 的正面形状:声明了却没执行,下游(better-auth 口令策略)按写入值工作,没有任何一层把它拉回声明的区间。与 #5131 的关系(为什么算同一族)
#5131 关掉的正是同一个洞的
options那一半:在它之前 options 表「只是前端约定」,PUT /api/settings/:ns什么都收。min/max/minLength/maxLength今天仍停在 #5131 之前的状态。修法应当与 #5131 同形:在validatePatch里补分支,发out_of_range/too_short/too_long一类FieldError(packages/spec/src/api/errors.zod.ts已有的码里挑,ADR-0114 的constraint字段带上{ min, max }),并沿用 #5131 的 TOUCH 闸门语义 —— 只校验本次 patch 触及的键,避免历史漂移把工作区锁死。需要一并决定的一点:env 侧(#5204)目前只对 options 表做拒收。若补 min/max,应同样走
effectiveEnvOverride那一个判定点,而不是在 env 路径上开第二份实现(#5204 的成因就是同一比较有两份)。影响面(已核实)
setup.write的调用方(REST / 脚本 / AI 生成的引导代码)都能写越界值。OS_AUTH_PASSWORD_MIN_LENGTH=1同样不被拦。相关
daily_quota(0 = 不限),若按min: 0声明,今天同样不会被执行。