Repository navigation
Commit 5870716
fix(service-analytics): declare the measured deny on a kernel with no security service, pin it on both in-repo kernels, give the reconcile runner an explicit security double (#22276)
Fixes #22235
Clause-②: no
Executes the maintainer's ruling B (`6056824332`): deny, as measured,
becomes the declaration. On a kernel that registers no `security`
service, the analytics read bridges deny, fail-closed, as they already
did. The comments and the test header now say so, a pin holds it on both
in-repo kernels, and the one in-repo producer registers an explicit
security double. No line that decides an answer moves, on any context.
## What changed
- `packages/services/service-analytics/src/plugin.ts`: **comments
only.** The resolution blocks of the row-scope bridge (near `:556`), the
object-level admission bridge (near `:700`) and the field-level and
query-side bridges now give the measured classification. ABSENT is
reached only by a context that answers a miss with nothing: the
package's test doubles do, and no in-repo kernel does. `ObjectKernel`
and `LiteKernel` throw on a never-registered name, so the bridges take
UNUSABLE and refuse with a located `error`. The same change is made in
the two `catch` comments, the two ABSENT-branch comments, the per-query
report comment (the block PR #22234 last changed) and the two init-time
comments. Proof that only comments moved: a comment-stripped transpile
(`ts.transpileModule`, `removeComments`) of the base file and of this
head is byte-identical (sha256 prefix `c1a4301c498f8087` both sides,
30086 bytes).
-
`packages/services/service-analytics/src/__tests__/admission-bridge-resolution.test.ts`:
- The header is rewritten to the ruling. The sentence that called a deny
on absence wrong is gone.
- The ABSENT case keeps its assertions. Its title and inline comment now
describe the context it actually models, a lookup that answers nothing.
- A new pin covers both kernels (`describe.each` over `ObjectKernel` and
`LiteKernel`), each booted with no `security` service ever registered.
It asserts that the kernel's own lookup throws, and that the dataset
query is refused with `code: PERMISSION_DENIED`, `status: 403` and
`object: employer_member`. It also asserts that nothing was read, and
that exactly one `error` line from the object-level bridge names the
object and the failed `security` lookup. Finally, it asserts that the
ABSENT branch's WARN did not fire.
- `scripts/analytics-reconcile/boot.ts` registers an explicit, OPEN
`security` test double as a named plugin. It answers `canReadObject`
true, `getReadFilter` undefined (no row restriction) and
`getReadableFields` undefined (no answer). The runner compares numbers,
not access: its legacy `engine.aggregate()` path runs with no caller
context on an engine with no security middleware, so the dataset path
gets the same posture, declared by name.
## Hypotheses, measured on `origin/main` `3513ac77`
- **H1, confirmed.** The ABSENT rows at `plugin.ts:556` and `:691`
stated "admits" and called a kernel shipping no plugin-security a
legitimate configuration. The same claim repeated at the two `catch`
comments, the two ABSENT-branch comments, the report block, the
field-level and query-side rows, and both init-time comments. All are
rewritten, comments only.
- **H2, done.** The header is rewritten and the pin is added. The pin's
ablation is below.
- **H3, confirmed, with a refinement.** Driven through `boot.ts`'s own
`reconcileApp()` with one dual-form widget (`todo_task` by `status`,
`task_metrics` / `task_count`), the composition is denied before this
change. The dataset query is refused with `Access denied` and 0 rows,
and the bridge logs the UNUSABLE `error`. After the change, the dataset
query answers 4 groups whose counts equal the legacy path's (2, 1, 4,
1). Refinement: on `3513ac77` none of the three shipping runners reaches
`queryDataset`. `app-todo.ts` boots and skips all 13 widgets and reports
as not dual-form, and it exits 0 before and after this change.
`app-crm.ts` and `app-showcase.ts` fail `MODULE_NOT_FOUND` on example
files that no longer exist (noted below).
- **H4, listed, not edited.** The release-owned sentences are listed
below for the docs card.
- **H5** is raised as an open question in the seat report, not built.
## Pin and ablation (head `183709e70c`)
The pin's subject is imported from source (`../plugin.js`), so no `dist`
leg applies.
- Green: `vitest run` over the two admission files gives 2 files and 13
tests passed. The new pin is 2 cases.
- Ablation A1 makes the object-level bridge's `catch` classify a throw
as ABSENT. It went through `scripts/ablation-replace.mjs`: anchor x1 to
x0, replacement x0 to x1, blob `ccecf120ec1d` to `1d21d50d6cea`, with an
outer `trap` restore.
- Result: 4 failed, 9 passed. Both new pins (`ObjectKernel`,
`LiteKernel`) are red, together with the double-based throw case here
and `admission-absence-report.test.ts`'s real-kernel case.
- Observed direction: red. The object gate admitted, and the row-scope
bridge then refused the same query with `READ_SCOPE_COMPILE_FAILED` /
500, so the declared `PERMISSION_DENIED` / 403 envelope naming the
object was lost.
- Restore: blob `ccecf120ec1d` equals `HEAD:plugin.ts`, and `git diff
HEAD` is empty.
## Local verification (head `183709e70c`)
- `@objectstack/service-analytics`:
- The build exits 0.
- `typecheck` (`tsc --noEmit`) exits 0. The program includes the test
file: `--listFiles` lists `admission-bridge-resolution.test.ts`.
- `vitest run --maxWorkers=2`: Test Files 180 passed (180). Tests 4443
passed, 262 skipped (4705).
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives 66 families for this change set. 65
exit 0. `pnpm check:dual-build-cjs-loads` exits 3, `PREREQUISITE NOT
MET`, because 37 workspace packages have no `dist/` in this worktree.
NOT MEASURED. A targeted substitute reading, a `require()` of this
package's `dist/index.cjs`, loads. `dispatch-gates --ran`: 66 derived,
65 run, 1 NOT-MEASURED, 0 UNRUN.
- `check:type-check-debt`: 26 raw tsc errors, none above the recorded
number. `boot.ts` carries the same 8 `TS2307` it carried before this
change, and no other diagnostic.
- Lint, narrowed: `eslint --no-inline-config --format json` over the 3
touched files reports 3 files, 0 errors and 0 warnings. The config lints
every TS/JS file outside its never-linted list, so all 3 are in scope.
It never enables type-aware linting (no `parserOptions.project`), so
this diff cannot move the verdict on a file it does not touch. The
repo-wide `pnpm lint` is left to CI.
## Changeset: none, `skip-changeset`
Nothing this package publishes changes behaviour. Its `files` are
`dist`, `README.md` and `CHANGELOG.md`. After a rebuild, three of the
new comment phrases occur in 0 files under `dist/`, while the positive
control (a runtime string from the same file) occurs in 2. The test file
and `scripts/` are not published.
## Released sentences that state "absent admits": for the docs card, not
edited here
`packages/services/service-analytics/CHANGELOG.md`:
- `:583-584`: "A deployment with no security service applies no
field-level check, as on the data API."
- `:645-647`: "A deployment with no security service applies no
object-level check, as on the data API."
- `:1211-1213`: "A deployment with no security service, and an object
the security service gives no field answer for, apply no field-level
check, as before."
- `:1926`: "a deployment with no `security` service registered keeps its
previous analytics behaviour by design, because on that deployment
`/data` carries no object-level gate either and the equivalence is what
is being defended."
- `:1930`: "an ABSENT `security` service admits (that deployment has no
object-level gate on `/data` either, so the two doors still agree, and
this is what keeps a deployment shipping no `plugin-security` working as
before)"
- `:1936`: "The object-level bridge tells three resolutions apart —
ABSENT admits, THROWING and METHOD-LESS deny at `error`."
- `:1952`: "**ABSENT** — no `security` service resolved: **unchanged**.
No row-scope provider on this deployment, which is a legitimate
configuration (a single-tenant kernel that ships no `plugin-security`
...) ... ⛔ Deliberately not tightened: refusing here would break every
such deployment."
- `:2773`: "a deployment with NO security service still runs unscoped
exactly as before."
The same lockstep entry repeats, with the `:1926` and `:1930` sentences,
in:
- `packages/plugins/plugin-security/CHANGELOG.md:1675` and `:1679`
- `packages/spec/CHANGELOG.md:13374` and `:13378`
- `packages/verify/CHANGELOG.md:571` and `:575`
`content/docs/releases/v17/17-5.mdx:482-484`: "A deployment with no
`security` service keeps its old behaviour and warns at init; a
registered one that cannot be used denies."
## Acceptance notes
- The same declaration repeats outside this card's file surface, in
`service-analytics`:
- the `read-admission.ts` module header (`:59-65`);
- the `admitObjectRead` and `getReadableFields` docs on
`AnalyticsServiceConfig` (`analytics-service.ts` near `:842` and
`:862`);
- the `assertReadAdmitted` doc (near `:1897`);
- `field-read-admission.ts:339-341`.
Each equates "no provider wired" with "a deployment with no security
service". Through the plugin a provider is always wired. Reported to the
seat with the docs card's list, not edited here.
- On a kernel with no security service, the UNUSABLE `error` line reads
"A security service is wired on this deployment". The
`assertObjectsReadable` denial line that follows reads "the same verdict
GET /data/OBJECT reaches". Neither holds on that deployment (measured
through `boot.ts` before this change). These are runtime strings,
outside this card's comments-only fence, and are reported to the seat.
- `scripts/analytics-reconcile/app-crm.ts` and `app-showcase.ts` import
example files that no longer exist (`MODULE_NOT_FOUND`), and
`app-todo.ts` has no dual-form widget. So no shipping runner exercises
the dataset path. Driving one synthetic dual-form widget showed that the
reconcile tool keys a select dimension by stored value on the legacy
path and by option label on the dataset path. Counts agree, keys differ.
Noted, not filed: no runner reaches it.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WkL6Eijt432S1Y7ekb6ovQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 81bd9fa commit 5870716
3 files changed
Lines changed: 240 additions & 97 deletions
File tree
- packages/services/service-analytics/src
- __tests__
- scripts/analytics-reconcile
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
21 | 22 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
28 | 27 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
34 | 46 | | |
35 | 47 | | |
36 | 48 | | |
| 49 | + | |
37 | 50 | | |
38 | 51 | | |
39 | 52 | | |
| |||
171 | 184 | | |
172 | 185 | | |
173 | 186 | | |
174 | | - | |
| 187 | + | |
175 | 188 | | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
181 | 194 | | |
182 | 195 | | |
183 | 196 | | |
| |||
224 | 237 | | |
225 | 238 | | |
226 | 239 | | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
0 commit comments