You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
changed the title [-]cloud 测试发现:注册接口可判断邮箱是否已注册、邮箱验证只能全开或全关、展示型字段原样存储 HTML[/-][+]cloud 测试发现(框架部分):注册接口的邮箱存在性、邮箱验证只能全开或全关、展示型字段原样存储 HTML(细节按披露裁定隐去)[/+]on Oct 21, 2026
业务含义直译: A = 前台直接说「这个人在我们这儿有账」;B = 前台一律说「好的,请查收邮件」,邮件里才告诉本人实情;C = 对外人这样,对同事照实说。
四轴: ① 主流身份服务(Auth0、Cognito、Supabase)默认防枚举:注册已存在地址返回成功形态并发邮件;B 与之一致,A 把「地址存在」当公开事实。② 拉动来自 cloud staging 黑盒测试(cloud#3125),无客户工单。③ B 的失败方向正是 #15587 记录的黑洞——用户以为注册成功却没收到邮件;所以 B 必须以「邮件通道可用」为前提,否则退回拒绝;A 的失败方向是响亮但泄露存在性。④ B 新增一个内置邮件模板与一条分流;A 零增长。
配置三态(如 requireEmailVerification: 'sensitive'):未验证可登录浏览,创建 API 密钥、邀请成员、改邮箱等敏感门要求已验证
部署方不接邮件也能让人先体验;敏感操作前被要求验证
spec 配置键三态(协议类)+ plugin-auth 敏感门清单与统一断言 M;无先例
C 只补文档
在 audience 文档写明「open 关验证 = 预生产专用」
无行为变化
S
业务含义直译: A = 大楼要么查证件要么不查;B = 不查证件可以进大堂,进机房要查;C = 门口贴一张说明。
四轴: ① B 多一个永久的「敏感操作」清单,每新增一道门都要记得分类;A 一条规则。② 拉动是 staging 的部署便利(cloud#2249),不是客户需求;生产 open 姿态本就强制开。③ B 的失败方向是某个敏感门漏判、未验证账号静默做了敏感操作;A 的失败方向是部署卡在注册步(响亮)。④ B 新增一组门与一个三态键;A 零增长。
凡把文本字段渲染进 HTML 的出口——邮件模板引擎默认转义、HTML 导出、集成 payload——按上下文转义,并对出口清单加钉子;同时实测 maxLength: 255 是否在写入面强制,未强制则按 #11875 补齐
用户名可以含任何字符;任何 HTML 出口都安全
plugin-email 模板引擎默认转义 + 出口清单审计 M;长度强制 S
业务含义直译: A = 原件随便写,复印时各部门自己打码;B = 收件时就把所有尖括号剪掉;C = 复印机出厂默认打码。
四轴: ① 主流做法是存原文、出口转义(Salesforce、Rails、React 的默认);剥离标签有损且可绕过;C 是结构性正确形态。② 拉动是 staging 黑盒(cloud#3125);邮件出口是否真未转义未测(置信缺口)。③ B 的失败方向是误拒合法输入(响亮但错);C 的失败方向是漏一个出口(静默),用「模板引擎默认转义」把它变成结构性而不靠逐处记得。④ B 新增一条全局写入规则;C 不加规则,改默认值。
Ruling: batch #381 item 3 · letters 1 B · 2 A · 3 C · maintainer 「其他同意」 2026-10-21T05:01Z
Director seat, summon #45 continuation 3, session_01Lg6GQ4xjqmvEzYv78d6H7i. Presented in this session's chat as batch #381 item 3 (three items, one letter each), from this card's body, the triage R553 comment 6307632431 and the director's delta 6307902116 (which supplied the options and the four facets per item; its premises were re-read on origin/maineddffc3390cf). The maintainer's 「其他同意」 takes the recommendation on all three. Thread read through 6307902116 at this write; nothing newer. ⛔ Disclosure (#9387, 5323310017): location, rule and abstract reach only.
Governing text:#15587 (the sign-up path's deliberate refusal of an existing address, packages/plugins/plugin-auth/src/auth-manager.ts:2733–2787, and the black hole it records); #20389 (resolveEmailVerificationRequirement, audience-posture.ts:188–195); #11875 (the write seam and maxLength, spec/src/data/field.zod.ts:131–144); ADR-0080 / ADR-0081 (tag whitelists govern UI pages, not text fields).
What the ruling binds
Item 1 — B. Under the open and email_domain postures, a sign-up for an address that already has an account answers the same shape as a successful sign-up, and that address receives a mail saying it already has an account (sign in or reset the password) — the vendor's onExistingUserSignUp hook or a built-in template. Precondition, part of the rule: when the mail transport is not live, the path keeps today's explicit refusal, so the plugin-auth: a sign-up for an address that already has a sys_user row answers 200 and persists nothing (posture email_domain) #15587 black hole (a fake success with no mail) cannot recur. invite_only is unchanged (a stranger still learns nothing). The REST door's answer changes: protocol class, contract review at tier.
Item 2 — A. No middle verification tier. The boolean stays with its posture rules (email_domain forced on, open on unless the deployment declares it off, invite_only default off) and its one-line boot warning. cloud staging's registration stop is a deployment configuration (connect a mail transport, or declare verification off), not a framework change.
The appendix items (HSTS default, password_reject_breached default) are cloud configuration and are not this card's.
Workload of the chosen letters (the delta's columns): item 1 plugin-auth 1 PR (S–M, protocol class); item 2 none; item 3 plugin-email default escaping + outlet audit M, length enforcement S.
State:needs-user-decision → pm:queue + pm:retriage (held out of dispatch): triage splits this card by its own R553 note — one execution card for item 1 (domain:services, plugin-auth, Clause-②: yes, the mail-transport precondition written into the card), one for item 3 (domain:services, plugin-email outlets + length seam), none for item 2 — each under the same disclosure discipline, then closes this card as the coordination node.
Activity
[-]cloud 测试发现:注册接口可判断邮箱是否已注册、邮箱验证只能全开或全关、展示型字段原样存储 HTML[/-][+]cloud 测试发现(框架部分):注册接口的邮箱存在性、邮箱验证只能全开或全关、展示型字段原样存储 HTML(细节按披露裁定隐去)[/+]objectstack-fleet commentedon Oct 21, 2026
Triage(R553):转入决策箱,并按披露裁定改写本卡。 ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-21T03:54Zneeds-user-decision·security·domain:services·area:identity· p2。卡里写明「做不做、怎么做由维护者决定」,而且三项都是新增或收紧行为,属于维护者的决定。5323310017)。原文只留在本地,不在 GitHub 上恢复。6303865025)直接在 18.x 以 BREAKING minor 发。附注里的两项默认值属于 cloud 的配置,不在本卡执行。Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
总监增量,批 #381 项 3 — 项目总监席,第 45 场续场 3,
session_01Lg6GQ4xjqmvEzYv78d6H7i,2026-10-21T04:16Z。卡面是 cloud#3125 拆出的三项测试发现,无选项表、无四棱块,本增量按项补齐;只写本席复核与新增的内容。⛔ 披露纪律(#93875323310017):只写位置、规则与抽象触达,不写请求形态、返回码与探测字串。线程读至 6307632431。前提复核(
origin/maineddffc3390cf)packages/plugins/plugin-auth/src/auth-manager.ts:2733–2787,plugin-auth: a sign-up for an address that already has asys_userrow answers 200 and persists nothing (postureemail_domain) #15587):vendor 的防枚举护盾(shouldReturnGenericDuplicateResponse,验证开启时对已存在地址返回一个从不写入的「合成成功」)被平台绕开,改为在一处明确拒绝;注释记录的理由是合成成功造成「假成功、无验证邮件、下次登录被拒且无处解释」的黑洞。vendor 的onExistingUserSignUp钩子(发「你已有账号」邮件)仓内未接。invite_only姿态下陌生人一律得到SELF_REGISTRATION_CLOSED,不泄露任何事。resolveEmailVerificationRequirement(audience-posture.ts:188–195,plugin-auth: under theopenaudience posture, honour an explicitrequireEmailVerification: false(email_domainstays forced) #20389):email_domain恒开;open默认开、仅部署方显式关;invite_only默认关。显式关时启动告警一次(OPEN_POSTURE_VERIFICATION_OFF_WARNING)。仓内按「已验证邮箱」设门的只有邀请列表一处(list-user-invitations-verification.ts:188);API 密钥创建、邀请成员、改邮箱不按验证状态设门。cloud#2249 卡在注册步属部署配置(邮件通道未接)。sys_user.name在 main 上已声明maxLength: 255(platform-objects/src/identity/sys-user.object.ts:776–781);text属BOUNDED_STRING_FIELD_TYPES(spec/src/data/field.zod.ts:140),该上限是否在写入面强制、还是只落到建表,待执行席实测(signature/qrcodehave nomaxLengthenforcement anywhere, so they cannot join the TEXT family — a data-URI signature is refused at 255 chars and the declared bound binds nothing #11875 写明写入面只对两种类型强制)。仓内 spec/core/objectql 无对文本字段剥离或拒绝 HTML 标签的规则(grep 0 命中);ADR-0080/0081 的标签白名单只管 UI 页面。前端转义已测;邮件、导出、集成等出口是否转义未测。第 1 项 · 注册接口暴露邮箱是否已注册
open/email_domain姿态);用户得到清楚的指引open与email_domain姿态下,对已存在地址返回与注册成功一致的响应,并给该邮箱发一封「你已有账号,请登录或重置密码」邮件(接 vendor 的onExistingUserSignUp或自建模板);邮件通道不可用时退回现状的拒绝,不留黑洞;invite_only不变open做 Bemail_domain保持明确拒绝(同事域内枚举价值低)业务含义直译: A = 前台直接说「这个人在我们这儿有账」;B = 前台一律说「好的,请查收邮件」,邮件里才告诉本人实情;C = 对外人这样,对同事照实说。
四轴: ① 主流身份服务(Auth0、Cognito、Supabase)默认防枚举:注册已存在地址返回成功形态并发邮件;B 与之一致,A 把「地址存在」当公开事实。② 拉动来自 cloud staging 黑盒测试(cloud#3125),无客户工单。③ B 的失败方向正是 #15587 记录的黑洞——用户以为注册成功却没收到邮件;所以 B 必须以「邮件通道可用」为前提,否则退回拒绝;A 的失败方向是响亮但泄露存在性。④ B 新增一个内置邮件模板与一条分流;A 零增长。
推荐:B(带「邮件不可用则退回拒绝」的前提)。 置信缺口:vendor 钩子
onExistingUserSignUp的行为未实测;email_domain下的枚举价值未评估(若您认为同事域可照实说,选 C)。自检:只看①选 B;②③④ 是否翻转:否(③ 以前提闭合)。第 2 项 · 邮箱验证只能全开或全关
requireEmailVerification: 'sensitive'):未验证可登录浏览,创建 API 密钥、邀请成员、改邮箱等敏感门要求已验证audience文档写明「open 关验证 = 预生产专用」业务含义直译: A = 大楼要么查证件要么不查;B = 不查证件可以进大堂,进机房要查;C = 门口贴一张说明。
四轴: ① B 多一个永久的「敏感操作」清单,每新增一道门都要记得分类;A 一条规则。② 拉动是 staging 的部署便利(cloud#2249),不是客户需求;生产
open姿态本就强制开。③ B 的失败方向是某个敏感门漏判、未验证账号静默做了敏感操作;A 的失败方向是部署卡在注册步(响亮)。④ B 新增一组门与一个三态键;A 零增长。推荐:A,不做。 置信缺口:无。自检:只看①选 A;②③④ 是否翻转:否。
第 3 项 · 展示型字段原样存储 HTML
text)在写入面剥离/拒绝 HTML 标签<、>的合法文本被改写或拒绝(如「A<B」「Smith & Sons」);需要先定义哪些字段算「展示型」maxLength: 255是否在写入面强制,未强制则按 #11875 补齐业务含义直译: A = 原件随便写,复印时各部门自己打码;B = 收件时就把所有尖括号剪掉;C = 复印机出厂默认打码。
四轴: ① 主流做法是存原文、出口转义(Salesforce、Rails、React 的默认);剥离标签有损且可绕过;C 是结构性正确形态。② 拉动是 staging 黑盒(cloud#3125);邮件出口是否真未转义未测(置信缺口)。③ B 的失败方向是误拒合法输入(响亮但错);C 的失败方向是漏一个出口(静默),用「模板引擎默认转义」把它变成结构性而不靠逐处记得。④ B 新增一条全局写入规则;C 不加规则,改默认值。
推荐:C,附长度强制实测。 置信缺口:邮件模板引擎当前是否转义变量未读;出口清单未穷举。自检:只看①选 C;②③④ 是否翻转:否。
裁后执行(三项皆按推荐时): 分诊按裁定拆成每项一张执行卡(本卡 R553 评论已写明):第 1 项 plugin-auth(
domain:services,REST 响应变化按协议类走达档复核,Clause-②: yes),第 2 项不立卡,第 3 项 plugin-email 出口转义 + 长度强制(domain:services)。附注两项(HSTS、HIBP 默认值)属 cloud 配置,不在本卡。os-decision-facets
Prior rulings read: sign-up duplicate 422 anti-enumeration(plugin-auth: a sign-up for an address that already has a
sys_userrow answers 200 and persists nothing (postureemail_domain) #15587,auth-manager.ts:2733–2787)· requireEmailVerification posture(plugin-auth: under theopenaudience posture, honour an explicitrequireEmailVerification: false(email_domainstays forced) #20389,audience-posture.ts:188)· maxLength write seam(signature/qrcodehave nomaxLengthenforcement anywhere, so they cannot join the TEXT family — a data-URI signature is refused at 255 chars and the declared bound binds nothing #11875,field.zod.ts:131–144)· HTML tag whitelist(ADR-0080、ADR-0081,页面而非字段)→ none decides the three items; thread: 6307632431; repo: objectstack-ai/objectstackobjectstack-fleet commentedon Oct 21, 2026
Ruling: batch #381 item 3 · letters 1 B · 2 A · 3 C · maintainer 「其他同意」 2026-10-21T05:01Z
Director seat, summon #45 continuation 3,
session_01Lg6GQ4xjqmvEzYv78d6H7i. Presented in this session's chat as batch #381 item 3 (three items, one letter each), from this card's body, the triage R553 comment 6307632431 and the director's delta 6307902116 (which supplied the options and the four facets per item; its premises were re-read onorigin/maineddffc3390cf). The maintainer's 「其他同意」 takes the recommendation on all three. Thread read through 6307902116 at this write; nothing newer. ⛔ Disclosure (#9387,5323310017): location, rule and abstract reach only.Governing text:#15587 (the sign-up path's deliberate refusal of an existing address,packages/plugins/plugin-auth/src/auth-manager.ts:2733–2787, and the black hole it records); #20389 (resolveEmailVerificationRequirement,audience-posture.ts:188–195); #11875 (the write seam andmaxLength,spec/src/data/field.zod.ts:131–144); ADR-0080 / ADR-0081 (tag whitelists govern UI pages, not text fields).What the ruling binds
openandemail_domainpostures, a sign-up for an address that already has an account answers the same shape as a successful sign-up, and that address receives a mail saying it already has an account (sign in or reset the password) — the vendor'sonExistingUserSignUphook or a built-in template. Precondition, part of the rule: when the mail transport is not live, the path keeps today's explicit refusal, so the plugin-auth: a sign-up for an address that already has asys_userrow answers 200 and persists nothing (postureemail_domain) #15587 black hole (a fake success with no mail) cannot recur.invite_onlyis unchanged (a stranger still learns nothing). The REST door's answer changes: protocol class, contract review at tier.email_domainforced on,openon unless the deployment declares it off,invite_onlydefault off) and its one-line boot warning. cloud staging's registration stop is a deployment configuration (connect a mail transport, or declare verification off), not a framework change.sys_user.name's declaredmaxLength: 255(platform-objects/src/identity/sys-user.object.ts:776–781) is enforced at the write seam and, if it is not, brings it undersignature/qrcodehave nomaxLengthenforcement anywhere, so they cannot join the TEXT family — a data-URI signature is refused at 255 chars and the declared bound binds nothing #11875's rule.password_reject_breacheddefault) are cloud configuration and are not this card's.Workload of the chosen letters (the delta's columns): item 1 plugin-auth 1 PR (S–M, protocol class); item 2 none; item 3 plugin-email default escaping + outlet audit M, length enforcement S.
State:
needs-user-decision→pm:queue+pm:retriage(held out of dispatch): triage splits this card by its own R553 note — one execution card for item 1 (domain:services, plugin-auth,Clause-②: yes, the mail-transport precondition written into the card), one for item 3 (domain:services, plugin-email outlets + length seam), none for item 2 — each under the same disclosure discipline, then closes this card as the coordination node.objectstack-fleet commentedon Oct 21, 2026
Split per the ruling, and closed as the coordination node. Triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-21T05:56ZThe ruling's state line (
6308462370) asked for this. Execution:openandemail_domainpostures, a sign-up for an address that already has an account answers the success shape and mails that address (ruling 1B on #25040); without a live mail transport the explicit refusal stays #25060.domain:services,plugin-auth,Clause-②: yes, with the mail-transport precondition in the card.sys_user.namemaxLength held at the write seam (ruling 3C, #25040) #25061.domain:services, outlet escaping by context, with an outlet-list pin and thesys_user.namelength seam.password_reject_breacheddefaults) are cloud configuration.Both cards carry the disclosure discipline (#9387).
Generated by Claude Code
fix(service-messaging): an html or mjml notification template escapes…
fix(rest): escape the API docs viewer's request-derived openapi URL f…
feat(plugin-auth): a sign-up for an existing address answers like a s…