Repository navigation
[Decision] 装包时包里声明的「默认权限集」怎么生效:管理员整部署接受一次、装包即自动生效,还是取消这条路(安全边界) #22801
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guards
on Oct 11, 2026 objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsRuling: batch #317 item 1 · letter A · maintainer 「同意」 2026-10-11T11:20Z
Director seat, summon #36,
session_019fWAt2renophxLVg5aJXMH(GitHubhotlong; written asobjectstack-fleet[bot]via the relay). Presented in batch #317 as item 1, from the epic seat's decision card (epic:#15194,session_01Rerax7QTjKMPCUZxQUtPFR). The epic seat recommended A, falling back to C; the U2 dev (report 6106844492) recommended C. This seat recommended A, falling back to B, and the maintainer answered 「同意」 to the batch.Thread-read: none (0 comments on this card at both reads). Freshness: body unchanged since filing; labels
priority:p1,security,needs-user-decision,domain:services,target:v18,area:access; no assignee.The card's premise commands, re-run on
origin/main(b7cd1af9df):sys_position_permission_setappears inresolve-authz-context.tsonly in the header comment (:16). The resolver does not read it.positionis registered withallowOrgOverride: false(metadata-plugin.zod.ts:1118).- The only
isDefault: truepermission set inexamples/*/src/securityis the showcase's own (permission-sets.ts:302;:288is a comment).
Added measurement, closing part of the card's confidence gap:
objectstack-ai/hotcrmmain1d7148bf, the one marketplace package this seat can read, declares noisDefault: truepermission set. ItsisDefault: truehits are pages only. It binds sets to positions by name (src/sales/profiles/service-agent.profile.ts, header). So the installed-package track has 0 producers, published package included. cloud is still unmeasured.The ruling
A: a package's
isDefault: truepermission set reaches every signed-in user only through ONE explicit, deployment-level acceptance by an admin holdingmanage_metadata, and that acceptance takes effect.- The security plugin re-derives
everyoneas the baseline plus the accepted package defaults, and recomputes it on accept, uninstall and publish. - The definition side carries the high-risk gate (ADR-0090 D7).
- Reconciliation reads the definition.
- The acceptance record belongs to the default organization.
- 2c's "sealed" wording gets a stated reading: an acceptance changes the derivation's input. No caller writes the anchor's definition.
- The spec texts (
confirmAudienceBindingSuggestion"writes the binding", and the package-levelisDefaultnote "an admin confirms in Setup") are kept and made true. They are rewritten only where the mechanism's wording changes. - ADR-0090 D5 stands unchanged: "Packages suggest, admins confirm … never auto-bound."
Why A, on ① alone:
- Mainstream enterprise platforms all require an explicit admin act before a package widens user access, and none grants every user at install.
- Salesforce asks the installer "admins only / all users / specific profiles".
- ServiceNow and Dynamics leave role assignment to the admin.
- C would reverse D5, so C is out.
- Between A and B, B leaves no way at all to give every member a package's set. The two audience anchors are sealed (2c), so an admin could only assign user by user. "Install an app and let the members use it" is a marketplace's first flow.
- The protocol already promises A's behaviour. The implementation is what drifted. Aligning the consumer with the protocol is the rule, not deleting the protocol.
②④ lean B (0 producers measured; A adds a deployment-level record and a recompute path), and do not flip it. This is a security boundary on the human floor, and B removes the only "all members" route.
- ⛔ Not taken:
- B (remove the path and refuse
isDefaulton installed packages); - C (declared means granted to everyone, high-risk filtered, which reverses D5).
- B (remove the path and refuse
Workload (#22597's rule):
- A: refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage U2's build, about 1.2–1.8k lines plus tests,Clause-②: yes, under one contract review. Its landing meets release precondition (d) and unblocks 6b-2. - B would have been about 2.5k deletions plus a refusal and ADR edits.
- C would have been about 2.5k deletions plus 0.2–0.4k lines and a D5 reversal.
Prior rulings read: ADR-0090 D5/D7/D9 · ADR-0131 D3 (and §5's rejection of per-organization binding rows) · #22621 → A (definitions by
manage_metadata, organization admins assign only) · #22601 → B (no transitional piece) · 2c (#22736) and its Landed record 6105781131 · #15204 stage-1 ACCEPT 6102862135 · U2 measurement 6106844492. 自检: 只看①选 A;②③④ 是否翻转:否(② ④ 偏 B,但安全边界在人工地板,且 B 拿掉市场包「给全员」的唯一路径;③ 偏 A,人工闸)。置信缺口: cloud and the published marketplace catalogue beyond HotCRM are unmeasured. A realisDefaultproducer there changes nothing in A.State
needs-user-decision→pm:queuein this act, keepingpriority:p1,security,domain:services,target:v18andarea:access. TheRuled:line is added to the body; the body carries no filing-time ⛔ line to strike.- The
epic:#15194seat dispatches refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204's U2 build stage on this ruling, then closes this card as that stage's decision record. ⛔ No seat claims this card on its own.
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClosed as the decision record of #15204 stage U2 · epic PM
session_01Rerax7QTjKMPCUZxQUtPFR(epic:#15194) · 2026-10-11T11:40Z- Ruling: A, record 6108478638.
- Executed by: refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage U2's build.- Claim amendment: 6108623383.
- Cloud dev:
session_01D4f5gJKWdzmeZP57AVkRJd. - Branch:
claude/issue-15204-su2-suggestion-confirm. - Form: one PR,
Part of #15204, under a same-head contract-tier review.
- When it lands: it meets release-cut condition (d) of 6102862135, and 6b-2 follows.
- Correction to this card's filing text: it named C as the fallback. On ① alone the fallback is B, as the ruling seat also gave it. This card also omitted that B or C would have needed an ADR-0138 §5 note. Both points are moot under A. The U2 brief records the ADR notes A needs (ADR-0090 D9, ADR-0131 D3, ADR-0138 §5) as findings for docs(adr-0131): §8/D14 execution-plan note — C2's remainder and C3 land as one cutover, C7 splits into C7a/C7b, and C12 moves to the 18.x line (rulings 6094045326, 6094175435, 6094183024) #22619.
Generated by Claude Code
Ruled: 6108478638 · letter A · 2026-10-11T11:20Z
Filing gate ②:只有维护者能做的决定。这是一条权限安全边界(ADR-0090 D5「装包不得静默扩大所有用户的权限」),改与不改都要维护者裁。立卡:epic PM
session_01Rerax7QTjKMPCUZxQUtPFR(marchtian),席位epic:#15194。动手的读者:裁后由本席派 #15204 的 U2 阶段落实,并解锁 6b-2。查重:本仓needs-user-decision标签全部卡(含已关)的标题里搜 suggest / isDefault / audience / everyone / anchor,命中 0;标题含 Decision 的卡按同一组词搜,命中 0。维护者速读
一个软件包可以把自己的某个权限集标成「默认」(
isDefault: true),意思是「建议所有登录用户都拥有它」。今天的做法是:装包后,后台出现一条建议,管理员点「接受」才生效。问题是:从 #15204 第 1 阶段起,这个「接受」按钮点了也不生效。 实测,点完返回成功,但用户的权限一点没变。原因是权限现在只看岗位定义;而
everyone(所有登录用户)这个岗位的定义被封死了,任何人都改不了(2c 的规则)。按「声明了就必须兑现」的原则,这个按钮要么让它真正生效,要么去掉。它也卡着发版前提 (d) 和 6b-2。三个方案:
everyone。装包仍然不会自动扩权。本席推荐 A(开发席推荐 C)。选 A、B 还是 C?
背景(实测:objectstack
origin/main9f5eca52;U2 测量报告 6106844492)接受不生效,实测: 启动 showcase,把示例包的默认权限集当作「装包建议」。管理员调
POST /security/suggested-bindings/:id/confirm,返回 200bindingCreated: true。之后普通成员读 4 个对象,接受前后都是 403;接受后新注册的成员也是 403;everyone定义不变。对照组:把同一个权限集写进everyone定义,同样 4 次读取都是 200。没有门能改
everyone: 平台管理员PUT /api/v1/meta/position/everyone得 403NOT_OVERRIDABLE。position类型不接受环境覆盖(allowOrgOverride: false)。everyone的定义只在启动时由安全插件写一次。「默认」今天分两条轨: 正在运行的应用自己的默认权限集,启动时自动进
everyone;其他已安装包的,只生成一条建议。「接受」只为第二条轨存在。谁在用:
SuggestedBindingsPanel,挂在应用市场页和 Studio 上;6b-2、阶段 8 需要什么:
readDeclared、upsertPackagePermissionSet;sys_position、sys_permission_set、sys_position_permission_set三张表。三个方案都满足这两点。
Governing text
docs/adr/0090-permission-model-v2-concept-convergence.md:236):「Packages suggest, admins confirm.isDefault: trueon a package permission set produces an install-time prompt … It is never auto-bound: installing a package must not silently widen every tenant user's access.」docs/adr/0131-…md:282-299,已裁):manage_metadata者管,组织管理员只分配、不定义。confirmAudienceBindingSuggestion写着「writes the binding」,isDefault的包级说明写着「an admin confirms in Setup」,两者今天都不兑现。A 改写这两段文字;B 和 C 删掉它们。前提(每条带复核命令)
git grep -n "sys_position_permission_set" origin/main -- packages/core/src/security/resolve-authz-context.ts,应只命中第 16 行的文件头注释(解析器不读这张表)。everyone封死:git grep -n "allowOrgOverride" origin/main -- packages/spec/src/kernel/metadata-plugin.zod.ts,看position那一行。git grep -n -E "isDefault:\s*true" origin/main -- 'examples/*/src/security/*.ts',应只命中 showcase 的permission-sets.ts:302(另一处是:288的注释)。选项 × 真实代价
manage_metadata的管理员对整个部署接受一次,记录归属默认组织;安全插件重新推导everyone= 基线 + 已接受的包默认集,接受、卸载、发布时都重算;定义侧加高危权限闸;对账改看定义Clause-②: yes;要说明 2c「封死」的措辞:接受改变的是推导结果,不是由调用方去写定义isDefault在安装/发布时响亮拒绝everyone=member_default+ 每个已启用包里通过高危过滤的默认集,装、卸、发布时重算;建议面像 B 一样删除,不加拒绝、不加存储(D,按组织各自绑定锚点岗位:已被 ADR-0131 D3 §5、#22601 → B、#22621 → A 排除,不再列为选项。)
业务含义直译
四轴(从业务看)
isDefault: true,结果就是全员获得,所见即所得;高危权限在编写时被 D7 lint 响亮拒绝。但写错一个默认集,会直接扩到全体成员,没有人工闸。isDefault会被响亮拒绝。os-decision-facets
Prior rulings read:
isDefault,everyone,suggest,confirm,auto-boundindocs/adr/→ ADR-0090 D5/D9, ADR-0131 D3 §5; thread: #22601 → B, #22621 → A, #15204 stage-1 ACCEPT 6102862135, the 2c Landed record 6105781131.推荐
推荐 A,回退 C。
裁后执行
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 U2 构建阶段(约 1.2–1.8k 行,同头合同复核),落地即满足发版前提 (d),6b-2 随后派出。