fix(scripts): check-single-authz-resolver 判据改判「同时查询两张表」+ 真实仓库阳性对照 (#6286) - #6353
Merged
Conversation
) ADR-0090 D3 把 sys_user_role 改名为 sys_user_position 之后,检查 (1) 的判据 `src.includes('sys_user_role') && src.includes('sys_user_permission_set')` 在全仓命中 0 个文件 —— 门禁结构上无法变红,两条 ALLOW 豁免的是一条永不触发 的启发式。规范解析器自己都不触发自己的启发式。 三处改动: 1. 词表收敛到 GRANT_TABLES 单一出处,判据从「同时提到两个字符串」改为 「同时查询两张表」(表名作为 find/query/select/count/aggregate 调用的引号 实参)。实测:一词替换命中 20 个文件、其中 18 个是噪声(生成的翻译包、 zod schema 注释、常量表、lint Set 字面量、页面元数据、testkit fixture); 查询形状把同一语料收到 2 个,且不靠豁免名单做收窄。 按落点收窄(限 security/)已量证不可取:原始 bug 就在 packages/rest/ src/rest-server.ts,不在任何 security/ 目录下。 2. ALLOW 按新判据重新策展,每条豁免带理由: - CANONICAL 保留(唯一合法解析器,同时是阳性对照的对象); - default-permission-sets.ts 移除(新判据不再触及它,实测 0 命中); - explain-engine.ts 新增(explain 诊断镜像,非 request-context 解析器)。 3. assertCanonicalStillMatches:真实仓库上的阳性对照,每次真实运行都跑 —— 规范解析器必须被检查 (1) 的启发式命中,否则按名变红。这是本次改名能 悄无声息废掉判据的直接原因:此前全部断言都跑在合成 fixture 上,而合成 fixture 与判据同步漂移,永远绿。匹配与上报在代码里分成两步 (queriesAllGrantTables 决定匹配,ALLOW 决定上报),阳性对照才可断言。 self-test 的 fixture body 改为从 GRANT_TABLES 生成,并补上改名的反向证明 (整体改名 / 部分改名 / 解析器移走,三种都必须红且点名)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
原先 default-permission-sets.ts 的「已移除」说明写在 explain-engine 条目的 数组内部,读起来像是在解释 explain-engine 的豁免。移到 ALLOW 的文档块里, 与两条现存豁免分开;explain-engine 的理由补上 #6352 单号,读者能直接找到 那条 parity 风险的观察单。 纯注释改动,判据与 ALLOW 成员未变。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
`[^)'"`]{0,80}` 排除了右括号却没排除左括号,于是 `find(wrap('sys_user_position'))`
会被判为「read call 读取该表」—— 表名其实是内层调用的实参,不是这次读取的。
与该正则自己注释里的声明(「是这次调用的靠前实参,而非嵌套在其中之物」)不符。
改为 `[^()'"`]{0,80}`。真实语料命中面不变(仍是规范解析器 + explain-engine
两个,门禁真实运行仍绿),这是精度修正而非收窄结果。
self-test 补上 11 条读取调用拼法断言,把召回侧钉死:
- 肯定式(会红):member 调用、helper 调用带前置实参(规范解析器用的正是
tryFind 这种拼法,丢了它阳性对照本身就会塌)、双引号、模板字面量、实参换行;
- 否定式(平凡成立,仅备案):注释、常量表、无引号对象键、Set 字面量、
页面元数据,以及本次修的嵌套调用。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
上一提交把 `[^)'"`]` 改成 `[^()'"`]`,注释仍写「不跨越另一个引号或右括号」。
补上左括号并给出具体例子(`find(wrap('sys_user_position'))` 不算读取该表),
让注释与正则一致。纯注释。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
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.
Fixes #6286
ADR-0090 D3 的表改名把检查 (1) 的判据词表废掉之后,这道门禁结构上无法变红。本 PR 修判据、重新策展 ALLOW,并补上这一族门禁普遍缺失的那一半:判据侧的阳性对照。
叠在 PR #6282(#6070,语料侧扩展名过滤)之后,两者正交 —— 那条改
walk()与排除列表,这条改:237一带的判据。1. 前提复现(自行在
origin/main@7e1b480复测)判据现行形状(
audit()内):sys_user_role的文件sys-user-position.object.ts:9的 "ADR-0090 D3; formerly sys_user_role" 注释)sys_user_permission_set的文件sys_user_position+sys_user_permission_set三个实测数字(0 / 32 / 20)与立单人一致。阳性对照(证明扫描器没坏):32 与 20 都非零。
规范解析器自己都不触发自己的启发式 ——
resolve-authz-context.ts读sys_user_position(:317)与sys_user_permission_set(:341),没有sys_user_role。2. 各候选形状的命中面实测
同一语料(1495 文件)上逐个跑出来的,不是估计:
sys_user_role&&sys_user_permission_setsys_user_position&&sys_user_permission_setsecurity/resolve-authz-context.ts、grant-validity.ts(纯注释)、explain.zod.ts(纯注释)、permission.zod.ts(纯注释)platform-object-names.ts、lint 的Set字面量、页面元数据new Set([...])、delegated-admin-gate.tsresolve-authz-context.ts(规范)+explain-engine.ts(explain 镜像)S1 的 18 个噪声具体是:4 个 generated translations、
object.zod.ts/permission.zod.ts/component.zod.ts/explain.zod.ts的注释散文、platform-object-names.ts常量表、validate-security-posture.ts的Set字面量、sys-user.page.ts页面元数据、exec-context-seam.testkit.tsfixture、grant-validity.ts/security-plugin.ts/auth-manager.ts/auth-plugin.ts/delegated-admin-gate.ts的注释或非查询用法。选择理由
(a)「查询」而非「提到」。 重复解析器不是一个说出这些表名的文件,而是一个从它们读行的文件。判据要求表名作为数据读取调用(
find/query/select/count/aggregate,含tryFind这类 helper 拼法)的引号实参,把 20 收到 2,且收窄完全由判据完成,没有一条靠豁免名单。(b)⛔ 不采用按落点收窄(S2),这是量证的否决而非偏好。 原始 bug 就在
packages/rest/src/rest-server.ts—— 不在任何security/目录下。S2 这个形状根本抓不到该门禁立案要防的那个缺陷,而且会让 self-test 自己的重复解析器 fixture(packages/rest/src/my-own-resolver.ts)一路放行。把守卫收窄到「正确代码所在之处」,就看不见种在别处的代码。(c)⛔ 刻意不做剥注释预处理。 实测过:剥与不剥都是同样的 2 个命中(S6 行),而一个词法剥离器只要错解一个正则字面量,就会悄悄缩小守卫所审视的内容 —— 正是这个文件整族失败模式(#4930 / #5916 / #6070)在裁决步骤上的翻版。少一个会撒谎的活动件。
3. ALLOW 重新策展(逐条理由)
旧的两条是对着死判据写的,按新判据逐条重判:
CANONICAL(resolve-authz-context.ts)default-permission-sets.tssys_user_permission_set: { allowRead: true, ... })提到两表,从不查询任何一张,实测 0 命中。豁免一个不再需要豁免的文件是死重量,而豁免名单里的死重量,在别人读到它的那天与一条活的压制无法区分。explain-engine.tsALLOW从Set改为Map(路径 → 理由),self-test 断言每条都带非空理由。旧名单烂掉正是因为没有任何东西要求一条豁免为自己辩护。explain-engine 为什么是豁免而不是「真发现」
buildContextForUser()为任意 userId 重建授权,唯一调用者是security-plugin.ts:2221explainAccessForCaller();调用者自身的授权(manage_users或 ADR-0090 D6/D12 的 delegated adminScope)走正常路径,与这份聚合无关。它不解析任何人的强制上下文。但它确实是一份手工镜像(其自身注释:"mirroring the runtime resolver's semantics"、"IDENTICAL rule"),与规范解析器之间没有任何 parity 断言。这是一条真实但不同的不变量,已按 Prime Directive #10 单独立观察单 #6352(
finding,未认领),并在 ALLOW 理由里写明「不要靠放宽本门禁 remit 来守它」。这次判据修复是它第一次被任何门禁看见 —— 旧判据一个文件都匹配不到。
4. 阳性对照的设计,以及它与 ALLOW 如何分层
assertCanonicalStillMatches()在每次真实运行(不只--self-test)跑:规范解析器必须被检查 (1) 的启发式命中,否则按名变红,点出哪几张表不再匹配。CI 的check:authz-resolver=--self-test && 真实运行,两侧都覆盖。分层是这条断言得以成立的前提。 「被启发式匹配」与「被上报为错误」是两步:
queriesAllGrantTables→ 决定匹配(与 ALLOW 完全无关)ALLOW→ 决定上报规范解析器必须通过第一步(这就是对照),并在第二步被豁免(它是合法的那一份)。两步在代码里是两个函数,所以对照可以只断言第一步,而不必断言「它是坏的」。
同样的分层在 self-test 里对 explain-engine 也断言了一遍:肯定式断言它被启发式命中,再断言它不被上报。
self-test 的 fixture body 全部改为从
GRANT_TABLES生成。这不是整洁癖:写死的 fixture 正是判据烂掉却无人察觉的原因 —— 写死的 fixture 只证明启发式对它自己包含的那些词有效,而这些词与判据同步漂移、与真实仓库一起走开,self-test 恰好在门禁失去意义的那段时间里保持全绿。5. 反向验证(先申报,后执行)
packages/rest/src/reverse-verify-duplicate-resolver.ts种一份重复解析器 ⇒ 新门禁按名抓出EXIT=1,输出Possible duplicate authorization resolver: packages/rest/src/reverse-verify-duplicate-resolver.tsorigin/main的门禁 ⇒ 预期绿,而这个绿就是 bug 本身EXIT=0,✓ check:authz-resolver: single shared authorization resolver intactGRANT_TABLES→sys_user_position_v2)⇒ 对照转红并点名EXIT=1,resolve-authz-context.ts does not query sys_user_position_v2origin/main的--self-test在其门禁已死的情况下 ⇒ 预期绿EXIT=0—— 合成 fixture 结构上看不见这个缺陷D1b 值得单独读一遍:
origin/main的门禁对着一份真实种下的、拼写正确的、会漂移的重复解析器打印绿灯。这是 phantom check 的现场演示,不是推论。断言极性(按要求逐条标明)
6. 门禁 EXIT 表(均在
git add之后跑)node scripts/check-single-authz-resolver.mjs --self-testnode scripts/check-single-authz-resolver.mjs(真实运行)pnpm exec eslint scripts/check-single-authz-resolver.mjs --no-inline-configpnpm check:nul-bytesgrep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'真实运行为绿,且不是靠放宽判据换来的:新判据在真实仓库上抓出的 2 个文件里,1 个是规范解析器本身,另 1 个经逐调用者核对确认不在强制路径上,豁免理由写进代码,风险另立 #6352。
7. 不在本 PR 里
packages/core/src/security/**(规范解析器只读)。packages/lint/**(defineStackparses at definition time, so the authoring registry'snormalized(pre-parse) tier may never actually be pre-parse for a TS config — the tier's whole reason for existing #6073 在飞)、未动packages/plugins/**。.changeset/:scripts/门禁,不发布任何包 ⇒skip-changeset。content/docs/releases/**、未改 required 集。buildContextForUser手工镜像resolveAuthzContext的授权聚合,两者无任何 parity 断言,只靠注释保持一致 #6352,不在本 PR 收敛 —— 那需要动packages/core或packages/plugins,越出本单文件面。check:engine-double-contract/check:error-code-casing)同样缺判据侧阳性对照:本 PR 只在此处做对一次,未推广。