feat(lint): navigation reachability vs. granted access (#3583) - #3698
Merged
Conversation
Assessment R5. Joins what an app's navigation exposes against
buildAccessMatrix — the first lint consumer of the ADR-0090 D6 matrix,
which until now only backed `os compile`'s snapshot gate.
Navigation and permissions are separate metadata, each valid alone, so an
app can ship a menu entry for an object no permission set grants read on.
The entry renders and opening it fails permission-denied. The failure
hides during development because the platform's built-in
`admin_full_access` carries a wildcard grant, so it works while you
browse as an administrator and breaks for exactly the users the app ships
permission sets for.
Advisory severity: a grant may come from a set another installed package
ships, which a static check cannot see. Quiet by construction in three
cases — platform-provided objects (granted by their own packages), a
stack that declares no permission sets at all (permissions are managed
elsewhere, so flagging every entry says nothing), and any stack whose set
carries a wildcard `objects: { '*': … }` grant. That last one is a bug
found while verifying against the real apps: the access matrix records the
wildcard under the literal key `*`, so without it the rule would have
fired hardest on the stacks that granted the most.
Verification also corrected the finding text. An earlier draft claimed the
entry fails "for every user, including admins" — untrue, since
admin_full_access is a wildcard set. The message now says what actually
happens, because a diagnostic that overstates its case teaches authors to
discount it.
Findings on the example apps: app-crm 0, app-todo 0 (declares no
permission sets), app-showcase 7 — verified TRUE positives, not false
ones: showcase binds `everyone` to `showcase_member_default`, which
grants 7 objects while the navigation exposes ~20, so a member genuinely
cannot open Teams / Categories / Field Zoo / Cascading Select / Expense
Reports / Business Units / Preferences. Left for the showcase maintainers
rather than silently rewriting an example app's permission model.
Suites: lint 447, cli 642 passing; eslint, nul-bytes and api-surface clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBks5G7AkwrvgF2kL5rfkT
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 27, 2026 15:09
This was referenced Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
方案里的 R5:把应用导航暴露的对象与
buildAccessMatrix做 join —— 这是 ADR-0090 D6 那张矩阵的第一个 lint 消费者(此前只有os compile的快照门禁在用)。问题
导航和权限是两份互相独立、各自合法的元数据,所以应用可以为一个「没有任何权限集授予 read」的对象挂菜单。入口正常渲染,点开就 permission-denied。
这个故障在开发期会被掩盖:平台内置的
admin_full_access带通配授权,所以你以管理员身份浏览时一切正常,而对应用真正发放权限集的那批用户是坏的。三个静默条件(误报地板)
objects: { '*': … }最后一条是在真实应用上验证时发现的规则 bug:访问矩阵把通配记在字面 key
*下,不处理的话,规则会对「授权最充分」的 stack 报得最凶。同时修正了 finding 文案。初版写的是「对所有用户失败,包括管理员」—— 这是错的,
admin_full_access是通配集,管理员进得去。诊断信息夸大其词会教会作者忽略它,所以改成如实描述。在真实应用上的结果
showcase 那 7 条经核实是真实发现,不是误报:它把
everyone绑定到showcase_member_default,该集授权 7 个对象,而导航暴露约 20 个 —— 普通成员确实打不开 Teams / Categories / Field Zoo / Cascading Select / Expense Reports / Business Units / Preferences。我没有顺手改 showcase 的权限模型:那是示例应用的产品决策(有些入口可能本就打算只给管理员),应由维护者决定是补授权还是给这些导航项加
requiredPermissions门控。验证
validate/lint/compile三个入口。关联:#3583 · #3640(方案)· #3657(Phase 1)· #3684(Phase 2)· ADR-0090 D6
Generated by Claude Code