Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/qa/platform-checklist/areas/access-security.json
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@
],
"source": [
"packages/rest/src/rest-route-ledger.ts#REST_ROUTE_LEDGER (record-shares family: GET/POST /data/:object/:id/shares, DELETE .../:shareId; sharing-rules family evaluate — re-pointed #18104: shareId is a route PATH PARAMETER, carried in this file only inside a route string)",
"packages/rest/src/rest-server.ts#registerSharingRuleEndpoints (the evaluate leg; the record-shares family GET/POST /data/:object/:id/shares + DELETE .../:shareId is registered inline, under no registrar of its own — #13788 measured that the registerRecordShareEndpoints this citation used to name is not in the file)",
"packages/rest/src/sharing-routes.ts#registerSharingRuleEndpoints (the evaluate leg; the record-shares family GET/POST /data/:object/:id/shares + DELETE .../:shareId is registered inline, under no registrar of its own — #13788 measured that the registerRecordShareEndpoints this citation used to name is not in the file)",
"packages/plugins/plugin-sharing/src/objects/sys-record-share.object.ts#recipient_type (recipient_type/recipient_id/access_level/source fields)",
"packages/plugins/plugin-sharing/src/sharing-rule-service.ts#evaluateRule (evaluateRule → SharingRuleEvaluationResult)",
"examples/app-showcase/src/security/sharing-rules.ts#share_key_account_qualified_contacts_with_managers (share_key_account_qualified_contacts_with_managers seeded criteria rule)",
Expand Down Expand Up @@ -2002,7 +2002,7 @@
"hydration-race"
],
"source": [
"packages/rest/src/rest-server.ts#registerSharingRuleEndpoints (list/create/get/delete)",
"packages/rest/src/sharing-routes.ts#registerSharingRuleEndpoints (list/create/get/delete)",
"packages/spec/src/security/sharing.zod.ts (criteria rule authoring + match-all refusal #3896)",
"packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.object.ts + sys-record-share.object.ts",
"objectui packages/plugins/plugin-sharing/src/sharing-plugin.ts#nav_sharing_rules (nav_sharing_rules Setup nav → sys_sharing_rule)",
Expand Down
3 changes: 2 additions & 1 deletion packages/qa/dogfood/test/authz-conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,8 @@ describe('#9084 — the rest-server.ts transport tripwire keys off mechanics', (
// 0 over the list, whose length is pinned so a module added to it makes this
// re-read. The control is that both probes really READ the list — their
// text is every file on it, in order — so the zero is not a list ignored.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(4);
// [#23050 slice 4] Length 4 → 5 (`sharing-routes.ts`), still 0 over it.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(5);
expect(REST_SERVER_REGISTRAR_PATHS[0]).toBe(REST_SERVER);
const listed = REST_SERVER_REGISTRAR_PATHS.map((file) => readFileSync(join(REPO_ROOT, file), 'utf8'));
for (const probe of [MECHANICS, ROUTES]) {
Expand Down
23 changes: 19 additions & 4 deletions packages/qa/dogfood/test/authz-probe-blind-spot.census.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,9 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
'indentation and the subtrahend is pinned at 1 by the `forwarder slice:` control, never inferred from a ' +
'terminator spelling), PLUS `registerPerItemRoute(` call sites; counted over this file AND every ' +
'module on the registrar list (`packages/rest/src/rest-server-registrar-files.ts`) — ' +
'`data-action-routes.ts`, `batch-routes.ts` and `approvals-routes.ts`, the modules the bodies of ' +
'`registerDataActionEndpoints`, `registerBatchEndpoints` and `registerApprovalsEndpoints` moved to; ' +
'`data-action-routes.ts`, `batch-routes.ts`, `approvals-routes.ts` and `sharing-routes.ts`, the modules the ' +
'bodies of `registerDataActionEndpoints`, `registerBatchEndpoints`, `registerApprovalsEndpoints`, ' +
'`registerSharingEndpoints` and `registerSharingRuleEndpoints` moved to; ' +
'reachable = those inside registerMetadataEndpoints',
// [#15542 / #15854] ⭐ THE POPULATION RULE LEARNED A SECOND SPELLING, and
// the numbers it produces did NOT move: 80 / 19 / 61, exactly as before.
Expand Down Expand Up @@ -677,21 +678,35 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
// readings; the four new occurrences are this slice's seam, the same two
// spellings (the host literal's forwarder, two; the host interface's
// member, two). No call site was added.
// [#23050 slice 4] 70 / 19 / 51 UNCHANGED, the same measurement again:
// `registerSharingEndpoints`' and `registerSharingRuleEndpoints`' bodies
// (eight registration sites, three and five, all outside
// `registerMetadataEndpoints`, so all in the blind spot) moved to ONE
// module, `sharing-routes.ts`, which joins the list (length 4 -> 5) with its
// own control. `this.routeManager.register(` stays 63 over the five files
// (34 here + 9 + 5 + 7 + 8), `private register*Endpoints(` stays 16 (both
// registrars are still declared here), and `enforceAuth` 72 -> 80 is
// RE-ANCHORED, not relaxed: the eight call sites moved with their routes
// and are counted in both readings; the eight new occurrences are this
// slice's seam, the same two spellings TWICE, because the module carries
// two registrars (two host literals' forwarders, four; two host
// interfaces' members, four). No call site was added.
controls: {
'private register*Endpoints(': 16,
'this.routeManager.register(': 63,
'registrar list length': 4,
'registrar list length': 5,
'data-action-routes.ts: this.routeManager.register(': 9,
'batch-routes.ts: this.routeManager.register(': 5,
'approvals-routes.ts: this.routeManager.register(': 7,
'sharing-routes.ts: this.routeManager.register(': 8,
// Both halves of the new rule carry their own control, so neither can go
// silently to zero: a helper deleted and its routes inlined back would
// still read population 80, and only these two controls would notice the
// shape moved and force this provenance to be re-read.
'registerPerItemRoute(': 8,
'const registerPerItemRoute =': 1,
'forwarder slice: this.routeManager.register(': 1,
enforceAuth: 72,
enforceAuth: 80,
},
note:
'The single non-tripwire probe names ONE registrar of 16. The other 15 can never mint a key: ' +
Expand Down
10 changes: 6 additions & 4 deletions packages/rest/src/error-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,9 @@ function resolveErrorResponse(error: any, object?: string): { status: number; bo
* ## Why this exists as an export rather than as a rule each route re-states
*
* Two route families build their error body by hand and share no branch with
* either door in this file: `/analytics/dataset/query` and the three
* record-share routes, both in `rest-server.ts`. Both re-derived
* either door in this file: `/analytics/dataset/query` (in `rest-server.ts`)
* and the three record-share routes (in `sharing-routes.ts` since #23050
* slice 4, `rest-server.ts` before). Both re-derived
* classification locally — analytics from an in-line `error.status` +
* `error.code` read, the share family from `message.startsWith(CODE)` over
* five literal prefixes — and both landed a *different* answer from `/data`
Expand Down Expand Up @@ -770,8 +771,9 @@ export function sandboxFaultAnswer(
*
* ## The derivation is the record-share family's
*
* `respondSharingError` in `rest-server.ts` re-dresses the same answer inline,
* and its comments carry the argument for each field; they are not restated:
* `respondSharingError` in `sharing-routes.ts` (moved out of `rest-server.ts`
* by #23050 slice 4) re-dresses the same answer inline, and its comments carry
* the argument for each field; they are not restated:
*
* - `code` is REQUIRED by the nested envelope and the flat classification
* legitimately carries none for an undeclared sandbox refusal, so the
Expand Down
41 changes: 33 additions & 8 deletions packages/rest/src/execctx-consumer-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
* census reads every file on THE list of the files `RestServer`'s registrars
* live in (`rest-server-registrar-files.ts`), so the slice that moves the next
* body widens it by adding its module there. §2 pins the list's length.
*
* [#23050 slice 4] …and `registerSharingEndpoints`' and
* `registerSharingRuleEndpoints`' bodies to `sharing-routes.ts`, with their eight
* (three and five) — the first module on the list to carry TWO registrars.
*/
const SITE_FILES = REST_SERVER_REGISTRAR_FILES;
const SOURCES = new Map<string, string>(SITE_FILES.map((file) => [
Expand All @@ -96,17 +100,27 @@ const siteKey = (file: string, line: number) => `${file}:${line}`;
/**
* The frames in `rest-server.ts` that are NOT consumers: the host literal each
* moved registrar (`registerDataActionEndpoints`, `registerBatchEndpoints`,
* `registerApprovalsEndpoints`) hands
* `registerApprovalsEndpoints`, `registerSharingEndpoints`,
* `registerSharingRuleEndpoints`) hands
* its moved body forwards each member to the server with
* `(...a) => this.<member>(...a)`, so a moved site's stack shows that forwarder
* above the site itself. The site is the first censused frame that is not one.
* Found by their spelling, and §2 pins ONE per moved module, so a respelt
* forwarder — or a module moved without joining `SITE_FILES` — is a control
* failure there rather than every moved site silently attributed to it.
* Found by their spelling, and §2 pins ONE per moved REGISTRAR — one per moved
* module, plus {@link EXTRA_MOVED_REGISTRARS} — so a respelt forwarder — or a
* module moved without joining `SITE_FILES` — is a control failure there rather
* than every moved site silently attributed to it.
*/
const FORWARDER_LINES = SOURCE.split('\n').flatMap((l, i) =>
l.includes('resolveExecCtx: (...a) => this.resolveExecCtx(...a),') ? [i + 1] : []);
const FORWARDERS = new Set(FORWARDER_LINES.map((line) => siteKey('rest-server.ts', line)));
/**
* [#23050 slice 4] The moved registrars beyond one per moved module:
* `sharing-routes.ts` carries two registrars' bodies, each handed over by its
* own host literal and so through its own forwarder.
*/
const EXTRA_MOVED_REGISTRARS: Readonly<Partial<Record<(typeof SITE_FILES)[number], number>>> = {
'sharing-routes.ts': 1,
};

/** A stack frame in any censused file, as `<file>:<line>:<column>`. */
const SITE_FRAME = new RegExp(`(${SITE_FILES.map((file) => file.replace(/\./g, '\\.')).join('|')}):(\\d+):\\d+`, 'g');
Expand Down Expand Up @@ -565,11 +579,22 @@ describe('[#13160] §2 the consumer surface, counted from the tree', () => {
// list, whose length is pinned here. The four new mentions are this
// slice's seam, the same two spellings; three forwarders now, one per
// moved module.
expect(SITE_FILES).toHaveLength(4);
//
// [#23050 slice 4] 62 sites UNCHANGED / 89 → 97 mentions — the same MOVE.
// `registerSharingEndpoints`' and `registerSharingRuleEndpoints`' bodies
// left for `sharing-routes.ts` with their eight BARE sites (three and
// five): 33 + 9 + 5 + 7 + 8 = 62 sites. The eight new mentions are this
// slice's seam, the same two spellings TWICE — two host literals'
// forwarders (four) and two host interfaces' members (four) — because
// the module carries two registrars. Five forwarders now: one per moved
// registrar, which is one per moved module plus `EXTRA_MOVED_REGISTRARS`.
expect(SITE_FILES).toHaveLength(5);
expect(SITES.length).toBe(62);
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(89);
expect(FORWARDER_LINES, 'one host-literal resolveExecCtx forwarder per moved module: one was respelt or removed, or a module moved without joining SITE_FILES')
.toHaveLength(SITE_FILES.length - 1);
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(97);
const extraMovedRegistrars = Object.values(EXTRA_MOVED_REGISTRARS).reduce((n, k) => n + (k ?? 0), 0);
expect(extraMovedRegistrars).toBe(1);
expect(FORWARDER_LINES, 'one host-literal resolveExecCtx forwarder per moved registrar: one was respelt or removed, or a module moved without joining SITE_FILES')
.toHaveLength(SITE_FILES.length - 1 + extraMovedRegistrars);
});

it('the split is 16 locally caught / 46 bare — NOT 12 / 46, which does not add to 62', () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/rest/src/meta-draft-read-door-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,9 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
// A module added to the list makes this re-read its number rather than
// widening unnoticed. 8 switches, all in `rest-server.ts` (the `/meta`
// and analytics doors); the data-action, batch and approval bodies read
// no draft.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(4);
// no draft. [#23050 slice 4] Nor do the two record-sharing bodies
// (`sharing-routes.ts`): 8 = 8 + 0 + 0 + 0 + 0, length 4 → 5.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(5);
expect(SOURCE_FILES.map((file) => file.fileName)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
expect(SITES).toHaveLength(8);
});
Expand Down
10 changes: 8 additions & 2 deletions packages/rest/src/rest-server-canonical-query-ast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,18 @@ interface CensusEntry {
* census reads it. The §1 controls pin the list's length and the census's sum
* over it (4 = 1 + 3 + 0 + 0; the batch and approval bodies build no `query:`
* literal).
*
* [#23050 slice 4] 4 = 1 + 3 + 0 + 0 + 0: the two record-sharing bodies
* (`sharing-routes.ts`) build no `query:` literal either — the rule list hands
* `listRules` an `object` / `activeOnly` pair read off `req.query`, never a
* `query:` slot.
*/
const REGISTRAR_QUERY_SLOT_FLOORS: Readonly<Record<RestServerRegistrarFile, number>> = {
'rest-server.ts': 1,
'data-action-routes.ts': 3,
'batch-routes.ts': 0,
'approvals-routes.ts': 0,
'sharing-routes.ts': 0,
};

const CENSUS: CensusEntry[] = [
Expand Down Expand Up @@ -319,8 +325,8 @@ describe('[#16638] §1 CONTROLS on the census instrument itself', () => {

it('[#23050 slice 3] censuses every file on the registrar list — its length and the slot sum are pinned', () => {
// A module added to the list makes this re-read its number rather than
// widening unnoticed.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(4);
// widening unnoticed. [#23050 slice 4] Length 4 → 5, the sum unchanged.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(5);
expect(Object.keys(REGISTRAR_QUERY_SLOT_FLOORS).sort()).toEqual([...REST_SERVER_REGISTRAR_FILES].sort());
expect(CENSUS.filter(({ file }) => REGISTRAR_SOURCES.has(file)).map(({ file }) => file))
.toEqual([...REST_SERVER_REGISTRAR_FILES]);
Expand Down
12 changes: 10 additions & 2 deletions packages/rest/src/rest-server-docblock-position.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,18 @@ describe('rest-server.ts docblock position', () => {
* carry three apiece (the module header, the host interface's block and the
* function's) — and a module added to the list needs one here before it reads.
* The case below the loop pins the list's length and the sum of the floors.
*
* [#23050 slice 4] `sharing-routes.ts` carries eight: the module header, two
* host interfaces' blocks and two functions' (it holds two registrars' bodies),
* plus the three blocks that moved with the record-share body (`respondError`'s,
* `sharingDeclaredExtra`'s and `sharingFaultMessage`'s), which `rest-server.ts`
* therefore no longer holds (three fewer there, still far above its floor).
*/
const MOVED_FILE_FLOORS: Readonly<Record<Exclude<RestServerRegistrarFile, typeof FILE>, number>> = {
'data-action-routes.ts': 4,
'batch-routes.ts': 3,
'approvals-routes.ts': 3,
'sharing-routes.ts': 8,
};
const MOVED_FILES: ReadonlyArray<{ file: string; minBlocks: number }> = REST_SERVER_REGISTRAR_FILES
.filter((file): file is Exclude<RestServerRegistrarFile, typeof FILE> => file !== FILE)
Expand Down Expand Up @@ -174,9 +181,10 @@ describe('[#23050 slice 3] the registrar list this pin reads', () => {
it('reads every module on it, each against its own floor — the length and the floors\' sum are pinned', () => {
// A module added to the list makes this re-read its numbers rather than
// widening unnoticed: 4 + 3 + 3 = 10 blocks over the three modules.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(4);
// [#23050 slice 4] 4 + 3 + 3 + 8 = 18 over four, length 4 → 5.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(5);
expect(Object.keys(MOVED_FILE_FLOORS).sort())
.toEqual(REST_SERVER_REGISTRAR_FILES.filter((file) => file !== FILE).sort());
expect(MOVED_FILES.reduce((n, { minBlocks }) => n + minBlocks, 0)).toBe(10);
expect(MOVED_FILES.reduce((n, { minBlocks }) => n + minBlocks, 0)).toBe(18);
});
});
6 changes: 4 additions & 2 deletions packages/rest/src/rest-server-query-number-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,10 @@ describe('#20139 census — every numeric coercion in rest-server.ts is classifi
// A module added to the list makes this re-read its number rather than
// widening unnoticed. 9 sites = 8 in `rest-server.ts` + 1 in
// `data-action-routes.ts` (the export door's `page`); the batch and
// approval bodies carry none.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(4);
// approval bodies carry none. [#23050 slice 4] Nor do the two
// record-sharing bodies (`sharing-routes.ts`): 9 = 8 + 1 + 0 + 0 + 0,
// length 4 → 5.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(5);
expect(SOURCES.map(([file]) => file)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
expect(sites).toHaveLength(9);
});
Expand Down
2 changes: 2 additions & 0 deletions packages/rest/src/rest-server-registrar-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export const REST_SERVER_REGISTRAR_FILES = [
'batch-routes.ts',
// [#23050 slice 3] `registerApprovalsEndpoints`' body.
'approvals-routes.ts',
// [#23050 slice 4] `registerSharingEndpoints`' and `registerSharingRuleEndpoints`' bodies.
'sharing-routes.ts',
] as const;

/** One file on {@link REST_SERVER_REGISTRAR_FILES}. */
Expand Down
Loading
Loading