Skip to content

fix(analytics): scope the dimension-label lookup to the referenced object's RLS (#3602 residual 1)#3639

Merged
os-zhuang merged 4 commits into
mainfrom
fix/analytics-label-scope-3602
Jul 27, 2026
Merged

fix(analytics): scope the dimension-label lookup to the referenced object's RLS (#3602 residual 1)#3639
os-zhuang merged 4 commits into
mainfrom
fix/analytics-label-scope-3602

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3602 残留 1。写 #3597 端到端门时定性的那个更窄的真实泄露,单独修。

泄露

数据集按 lookup/master_detail 维度分组时,analytics 要把分组的 FK id 解析成关联记录的显示名 —— 走的是一次"按 id 分组"的 per-record 读,套在 aggregate 桥上(plugin.tsfetchRecordLabels)。这次读不带任何 read scope

修法(自足,不碰 spec)

取名读现在应用被引用对象自己的 read scope —— 用 aggregate 路径同一个 getReadScope provider 绑定到本请求,$and 组合(非 key 合并,id 谓词顶不掉它)。fail-closed:该对象 scope 解析失败则跳过这一维的取名(渲染原始 id),绝不无 scope 取。无 provider 时行为不变。

改动全在 service-analytics 内:DimensionLabelDeps.fetchRecordLabels 加可选 scoperesolveDimensionLabels 加可选 resolveScope,都是包内部类型,不动 spec 契约

测试

  • dimension-labels 单测(4):证明被引用对象的 scope 被穿进 fetchRecordLabels;resolveScope 抛错时fail-closed 跳过取名(不发生无 scope 读);select 维度不受影响;无 resolver 时维持旧行为。
  • query-dataset 集成(1):getReadScope 按对象返回不同 scope,断言取名收到的是被引用对象 crm_account 的 scope 而非基对象的 —— 回退 analytics-service.ts 接线即变红(已验)。
  • service-analytics 184 passed(原 179 + 5);tsc 无新增错误(2 个存量在未触文件)。

一并做的诚实更正(见 #3602 评论)

引擎侧独立追查确认 engine.aggregate 根本没有 join/lookup 遍历能力。因此:

本 PR 只做残留 1。残留 2(generateSql 补 WHERE,纯展示)未做,优先级最低。

🤖 Generated with Claude Code

…'s RLS (#3602)

A dataset that groups by a lookup/master_detail dimension resolves the
grouped FK ids to the related record's display name via a per-record read
(`group by id`) dressed as an aggregate. That read carried no read scope,
so it revealed related-record display names whenever the referenced
object's RLS is stricter than the base object whose rows carry the id — a
user could read a name the referenced object's own RLS would hide.
(Same-object and looser-referenced cases were already safe: the ids come
from the post-#3597 scoped aggregate. This closes the stricter-referenced
case, the one residual flagged in #3602.)

The label lookup now applies the REFERENCED object's own read scope, bound
to the request via the same getReadScope provider the aggregate path uses,
composed with `$and` (never key-merge) so the id predicate can't displace
it. Fail-closed: if that object's scope can't be resolved, the dimension's
labels are skipped (raw id renders) instead of fetched unscoped. No change
when no read-scope provider is configured.

`DimensionLabelDeps.fetchRecordLabels` gains an optional `scope` arg and
`resolveDimensionLabels` an optional `resolveScope` resolver — both
service-analytics-internal, no spec/contract change.

Tests: dimension-labels unit cases prove the referenced-object scope is
threaded and that resolution failure fails closed; a query-dataset
integration case proves AnalyticsService.queryDataset binds the referenced
object's scope (not the base object's) — reverting the wiring turns it red.

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 27, 2026 12:58pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): packages/qa, packages/services.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/permissions/authorization.mdx (via packages/qa)
  • content/docs/permissions/delegated-administration.mdx (via packages/qa)
  • content/docs/plugins/packages.mdx (via packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Boots the real stack (HTTP → SecurityPlugin → AnalyticsServicePlugin
auto-bridges → the plugin's real fetchRecordLabels closure) with a
two-object owner-scoped fixture: a member owns a deal pointing at a
vendor OWNED BY THE ADMIN. Grouping deals by vendor, resolving the
grouped id to a NAME reads the vendor object — which the member cannot
read.

Asserts the member gets the vendor id RAW (the name does not leak) while
the admin, who owns the vendor, still gets the resolved name — proving
the fix scopes rather than blanks. Reverting the plugin's scope
application reproduces the leak (member receives "Admin-Owned Vendor").

This is the only gate that exercises the plugin's real label closure; the
service-analytics unit/integration tests fake fetchRecordLabels.

Co-Authored-By: Claude <noreply@anthropic.com>
@os-zhuang

Copy link
Copy Markdown
Contributor Author

补充:加了一个真栈 e2e 门 analytics-label-scope.dogfood.test.ts —— 之前的单测/集成测试都 fake 掉了 fetchRecordLabels,这个门专门覆盖插件里真实的取名闭包(把被引用对象 scope $and 进 id filter)。

两对象 owner-scoped fixture:member 拥有一个指向admin 名下 vendor 的 deal。按 vendor 分组、把 id 解析成名字时会读 vendor 对象,而 member 读不到它。断言 member 拿到 vendor 原始 id(名字不泄露),admin(拥有该 vendor)仍拿到解析后的名字 —— 证明是加 scope 而非一律清空。回退插件的 scope 应用后,member 直接拿到 Admin-Owned Vendor(复现泄露)。

完整 dogfood 门 62 files / 357 tests 全绿。

@os-zhuang
os-zhuang merged commit 415254c into main Jul 27, 2026
19 checks passed
@os-zhuang
os-zhuang deleted the fix/analytics-label-scope-3602 branch July 27, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant