Skip to content

Commit ad06144

Browse files
chore(ci): census the call sites that set the vouched-reference mark (#31086)
security#1 · os-tesla · session_01L9qibvynRbCtMndW8EHNGc Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:新增门禁 `check:vouch-for-reference-callers`:在 `packages/`、`examples/`、`apps/` 下,只有 file-attachments 写入方(`file-attachment-service.ts`,恰 2 处直接调用)可以设置 vouched-reference 标记;对 `vouchForReference`、`VOUCHED_REFERENCES` 或其 Symbol 描述的任何其它写法(改名、命名空间、别名、`.call`、方括号、`defineProperty`、看不懂的 `Symbol.for` 参数)都转红并点名文件与行。 - **为什么改**:该标记是一项受控豁免,每个调用点都须单独复核;门禁把这条规则变成机械检查,新调用点只能以显式的白名单编辑落地。 - **风险与回滚**:纯工具改动,不发布任何包、不改运行时;最坏情况是门禁误红,回滚即还原这 7 个文件。 - **先看**:① `scripts/check-vouch-for-reference-callers.mjs` 的 `censusOf` 与 `ALLOWED_CALLERS`;② `.github/workflows/lint.yml` 的 Single authz resolver guard 步骤;③ `scripts/cross-package-test-inputs.mjs` 的 core 条目注释(`packages/**` `.ts` 边界)。 - **两分钟验证**:`pnpm check:vouch-for-reference-callers`,预期两行 ✓:self-test 39 cases pass,以及 `file-attachment-service.ts (2)`。 - **需要你做的**:本 PR 触及 `.github/workflows/**`(Tier H),需要维护者合并或授权批准。 ## What changes The only production code that may set the vouched-reference mark (`vouchForReference`, `@objectstack/core`) is `packages/services/service-storage/src/file-attachment-service.ts`, at exactly two call sites (`attachFileAs` and `attachUploadAs`). This PR adds a call-site census gate that holds that rule. - `scripts/check-vouch-for-reference-callers.mjs`. Outside the defining module, every mention of `vouchForReference`, `VOUCHED_REFERENCES` or the key's `Symbol.for` description, in code or in a string literal, is exactly one of three things: 1. A direct call, which is counted. 2. An unrenamed `import`/`export … from` specifier, which sets nothing. 3. A finding, naming the file and line. This covers renames on import or export, namespace or member access, bracket or string-keyed access, value aliases, `.call`/`.apply`, `require` destructuring, the key as a computed property or as an argument to `Object.defineProperty`/`Reflect.set`, and the description in any literal. A `Symbol.for(…)` whose argument is not a plain literal, an unsubstituted template, or a same-file `const` bound to a plain literal is a finding too. Counted calls are held equal to `ALLOWED_CALLERS`. A new caller, a count that moves either way, or a stale row is red. The stale-row message names both remedies: remove the row, or restore the call. Code is told from comments by `maskComments` (`scripts/js-comment-mask.mjs`), and `Symbol.for` arguments are read off `maskCommentsAndLiterals`. Test files are outside the census, and an empty scan is refused. The `--self-test` carries a 39-battery roster with a floor and the verdict handshake. - **Surfaces.** The bare run reads `packages/`, `examples/` and `apps/` over every source extension. It runs in the lint step on every PR, uncached, and it is the authority. `--ts-only` reads `packages/**` `.ts` alone. That is the radius of the `@objectstack/core` pin (its declaration-table entry and `test:repo` turbo inputs, which also name the script), so what the pin judges equals what re-runs it. `ROOT_DIR_WATCH_HINTS` spells the three roots as subtrees for the dispatch derivation, as `check-org-identifier.mjs` does over the same roots. The self-test holds it equal to `SCAN_ROOTS`. - Root alias `check:vouch-for-reference-callers` (self-test, then scan), wired into the existing `Single authz resolver guard` step of `lint.yml`. No new check context and no new required-status entry. - `packages/core/src/security/vouched-reference-callers.pin.test.ts`, listed in `vitest.repo-tests.json`, runs `--self-test` and `--ts-only` where `Test Core` runs them. The allowlist was re-read against `main` (`37371559b`): the file holds exactly two `vouchForReference(` calls, and nothing else under the three roots mentions the mark outside a comment. The tree has 57 `Symbol.for` code sites, and all of them are readable. ## Tests - Core `repo` project: 7 files / 96 tests pass on `ef6658f2d1`. The pin is compiled by `tsconfig.test.json` with no new test-typecheck debt. - Tree ablations, each through `scripts/ablation-replace.mjs`, with the restore proven by blob equal to HEAD and an empty `git diff HEAD`. Each injection reds the bare run, naming the file: - a call in `packages/rest/src/query-allowlist.ts` (also red under `--ts-only`, and the pin goes red); - removing the `attachFileAs` call (row pins 2, file holds 1); - `import { VOUCHED_REFERENCES as MARK }` (line 2); - `Symbol.for(PREFIX + …)` with an imported constant (line 3); - a value alias of `vouchForReference` (line 3); - a call after `'https://…'` on the same line; - a call in `examples/app-crm/src/actions/convert-lead.action.ts`, which is green under `--ts-only` by design; - a call in a `.mjs` file under `packages/`, which is green under `--ts-only` by design. - Rule ablations in the script itself. Each one turns its batteries red: - rename finding off: 3 batteries; - mention finding off: 13; - `Symbol.for` argument rule off: 2; - `maskComments` swapped for a `//`-to-end-of-line strip: 2; - `examples`/`apps` dropped from `SCAN_ROOTS`: 1; - stale-row remedy text cut: 1; - a root dropped from, or added to, `ROOT_DIR_WATCH_HINTS`: 2 and 1. - Removing the alias line from `lint.yml` reds `check-self-test-wired` (`alias-self-test-not-run`). ## Acceptance notes - Tier H: the diff touches `.github/workflows/lint.yml`. `check-governed-merges --pr` names exactly that path, landing tier H, 588 changed lines, under the 3,000-line threshold. - No published package moves. The only file under a package is a `*.test.ts` in `@objectstack/core`, which ships `dist` only. Check Changeset route 2 applies, through the `skip-changeset` label. - The step runs its two commands under the default `bash -e`, so a red `check:authz-resolver` stops the census from running in that same step. The step is red either way. ## Verification <details><summary>Gate run</summary> ``` ✓ check-vouch-for-reference-callers self-test: 39 cases pass. ✓ check-vouch-for-reference-callers: every call site that sets the vouched-reference mark is allow-listed (packages/, examples/, apps/, every source extension) — packages/services/service-storage/src/file-attachment-service.ts (2). ✓ check-vouch-for-reference-callers: every call site that sets the vouched-reference mark is allow-listed (the .ts surface) — packages/services/service-storage/src/file-attachment-service.ts (2). ``` </details> <details><summary>Derived gate families</summary> `dispatch-gates --commands` derives 94 commands on the final head `ef6658f2d1`; all 94 exit 0 and `--ran` reconciles 94 run, 0 unrun. `dispatch-gates --self-test --fast` (1954 cases), `bare-root-worklist --self-test`, `check-watch-hint-literal --self-test`, `check-self-test-wired` and `check-comment-mask-adoption` are green, and every check-run on `ef6658f2d1` concluded success or skipped. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibvynRbCtMndW8EHNGc)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7faa237 commit ad06144

7 files changed

Lines changed: 585 additions & 3 deletions

File tree

‎.github/workflows/lint.yml‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2550,10 +2550,20 @@ jobs:
25502550
# @objectstack/core). Guards against a duplicate resolver copy drifting on a
25512551
# security path (the REST-vs-dispatcher sys_user_role drift) and against an
25522552
# entry point silently dropping the delegation.
2553+
#
2554+
# The second command is the same kind of guard on the write-time reference
2555+
# check: the code that may set the vouched-reference mark (a service verb
2556+
# vouching, on its own write, for the one record it proved) is pinned to an
2557+
# allowlist, so a new caller is a reviewed edit and never a quiet one. Its
2558+
# alias runs the census's `--self-test` first, which is what
2559+
# `check:self-test-wired` requires. Folded into this step rather than a step
2560+
# of its own: no new check context and no new required-status entry.
25532561
- name: Single authz resolver guard
25542562
id: single-authz-resolver-guard
25552563
continue-on-error: true
2556-
run: pnpm check:authz-resolver
2564+
run: |
2565+
pnpm check:authz-resolver
2566+
pnpm check:vouch-for-reference-callers
25572567
25582568
# #11352. Product code may not read a test-RUNNER variable (`TEST`,
25592569
# `VITEST*`, `JEST_WORKER_ID`). Runner variables are INHERITED by every

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
"check:stall-guard-headroom": "node scripts/measure-stall-guard-headroom.mjs --self-test",
207207
"check:swallow-census-controls": "node scripts/measure-durability-swallow-family.mjs --self-test=gated",
208208
"check:system-context-census": "node scripts/check-system-context-census.mjs --self-test && node scripts/check-system-context-census.mjs",
209+
"check:vouch-for-reference-callers": "node scripts/check-vouch-for-reference-callers.mjs --self-test && node scripts/check-vouch-for-reference-callers.mjs",
209210
"gen:platform-object-tenancy-census": "node scripts/platform-object-tenancy-census.mjs --write",
210211
"check:platform-object-tenancy-census": "node scripts/check-platform-object-tenancy-census.mjs --self-test && node scripts/check-platform-object-tenancy-census.mjs"
211212
},
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* The call-site census of the vouched-reference mark (`vouchForReference`,
5+
* `./vouched-reference.ts`), run where `Test Core` runs it: the gate's own
6+
* self-test (its red and green cases, with the battery floor and the verdict
7+
* handshake), then the scan of this tree, which must be green — every
8+
* production file that sets the mark is in the gate's `ALLOWED_CALLERS` with
9+
* its exact call-site count. The gate's header is the authority on the rule.
10+
*
11+
* The scan here is `--ts-only`: the files this pin judges are the files whose
12+
* edits re-run it, the `packages/` glob ending in `.ts` of this package's
13+
* entry in scripts/cross-package-test-inputs.mjs. `examples/`, `apps/` and the
14+
* other source extensions are judged by the bare run in the lint step, on
15+
* every PR; the gate's header, "Two surfaces", says why the pin does not
16+
* judge them.
17+
*/
18+
19+
import { describe, expect, it } from 'vitest';
20+
import { spawnSync } from 'node:child_process';
21+
import { dirname, resolve } from 'node:path';
22+
import { fileURLToPath } from 'node:url';
23+
24+
const HERE = dirname(fileURLToPath(import.meta.url));
25+
const REPO_ROOT = resolve(HERE, '../../../..');
26+
const GATE = resolve(HERE, '../../../../scripts/check-vouch-for-reference-callers.mjs');
27+
28+
const run = (...args: string[]) =>
29+
spawnSync(process.execPath, [GATE, ...args], { cwd: REPO_ROOT, encoding: 'utf8' });
30+
31+
describe('check:vouch-for-reference-callers', () => {
32+
it('its self-test reaches its verdict: every red and green case holds, and no battery fell below its floor', () => {
33+
const out = run('--self-test');
34+
expect(out.stderr).toBe('');
35+
expect(out.status).toBe(0);
36+
expect(out.stdout).toMatch(/self-test: \d+ cases pass/);
37+
});
38+
39+
it('this tree is green on the .ts surface: the only production caller is the file-attachments writer, at its pinned count', () => {
40+
const out = run('--ts-only');
41+
expect(out.stderr).toBe('');
42+
expect(out.status).toBe(0);
43+
expect(out.stdout).toContain('(the .ts surface)');
44+
expect(out.stdout).toContain('packages/services/service-storage/src/file-attachment-service.ts (2)');
45+
});
46+
});

‎packages/core/vitest.repo-tests.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"src/security/operation-private-keys.pin.test.ts",
55
"src/security/row-serving-door-exposure.pin.test.ts",
66
"src/security/second-object-read-exposure.pin.test.ts",
7+
"src/security/vouched-reference-callers.pin.test.ts",
78
"src/utils/import-runner-sandbox-fault-row.test.ts"
89
]

0 commit comments

Comments
 (0)