You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Measured while implementing #8819 (not fixed there — out of that card's declared region, which is rollbackMetaItem:15427–:15587).
What was measured
#8769 routed publishMetaItem through canonicalizeMetaRequestType (the seventh /meta entry point); #8819 routed rollbackMetaItem (the eighth). diffMetaItem is a ninth entry point on the same URL family and does not funnel through it either — after #8819 lands it is the last straggler I can find on this family.
Measured on the #8819 branch at merge with origin/main20067c56b:
packages/metadata-protocol/src/protocol.ts — async diffMetaItem(request: { type, name, fromVersion?, toVersion?, organizationId? }), no canonicalizeMetaRequestType call in its body.
The history read runs under that key: engineAny.find('sys_metadata_history', { where: { organization_id: orgId, type: singularType, name: request.name } }).
The response echoes the caller's spelling back: return { type: request.type, ... }.
Reachable from the wire: GET /api/v1/meta/:type/:name/diff is a registered route with a caller-supplied :type (packages/rest/src/rest-route-ledger.ts:184, handler at packages/rest/src/rest-server.ts:6587).
The consequence, stated at its real width
For the four manifest-absent types — field, seed, external_catalog, translation, legitimately absent from PLURAL_TO_SINGULAR because they are not stack collections — the plural spelling stays plural all the way into the history query, which then matches no row. The endpoint answers a well-formed empty diff (added: [], removed: [], changed: []) rather than the real diff or a refusal. That is a silent "nothing changed" for an item that does have history, on a routed live endpoint, and the echoed type names a spelling the read did not actually use.
For manifest-present types (view and friends) the manifest map folds and the diff resolves correctly, so this is narrower than #8819's window.
diffMetaItem is read-only: it takes no lock and performs no write, so there is no ADR-0010 gate to address around here and none of #8819's severity carries over. This is a wrong-answer defect, not a protection bypass. Filed separately rather than as a rider for that reason, and because the fix wants its own look at whether the echoed type should report the canonical spelling (which would change a response field consumers may read).
Backlink: #8819 · #8769 · the boundary fold's rationale is #7894.
Measured while implementing #8819 (not fixed there — out of that card's declared region, which is
rollbackMetaItem:15427–:15587).What was measured
#8769 routed
publishMetaItemthroughcanonicalizeMetaRequestType(the seventh/metaentry point); #8819 routedrollbackMetaItem(the eighth).diffMetaItemis a ninth entry point on the same URL family and does not funnel through it either — after #8819 lands it is the last straggler I can find on this family.Measured on the #8819 branch at merge with
origin/main20067c56b:packages/metadata-protocol/src/protocol.ts—async diffMetaItem(request: { type, name, fromVersion?, toVersion?, organizationId? }), nocanonicalizeMetaRequestTypecall in its body.const singularType = PLURAL_TO_SINGULAR[request.type] ?? request.type;— the MANIFEST-COLLECTION map, i.e. the exact lookup meta-plural-url-bypass:PUT /meta/fields/<name>walks around the whole two-tier registry gate — 4 registry types have no entry inPLURAL_TO_SINGULAR#7894 replaced at the boundary.engineAny.find('sys_metadata_history', { where: { organization_id: orgId, type: singularType, name: request.name } }).return { type: request.type, ... }.Reachable from the wire:
GET /api/v1/meta/:type/:name/diffis a registered route with a caller-supplied:type(packages/rest/src/rest-route-ledger.ts:184, handler atpackages/rest/src/rest-server.ts:6587).The consequence, stated at its real width
For the four manifest-absent types —
field,seed,external_catalog,translation, legitimately absent fromPLURAL_TO_SINGULARbecause they are not stack collections — the plural spelling stays plural all the way into the history query, which then matches no row. The endpoint answers a well-formed empty diff (added: [],removed: [],changed: []) rather than the real diff or a refusal. That is a silent "nothing changed" for an item that does have history, on a routed live endpoint, and the echoedtypenames a spelling the read did not actually use.For manifest-present types (
viewand friends) the manifest map folds and the diff resolves correctly, so this is narrower than #8819's window.Two smaller limbs, same cause:
PUT /meta/fields/<name>walks around the whole two-tier registry gate — 4 registry types have no entry inPLURAL_TO_SINGULAR#7894 boundary refusal never runs here, so an unrecognised spelling of a declared type is forwarded rather than refused400 INVALID_REQUEST;try { … } catch { /* history table unavailable */ }swallows a genuine outage into the same empty-diff answer, so "no rows because the type key is wrong" and "no rows because the store is down" are indistinguishable to a caller. (Thecatchis deliberate and documented by [finding] diffMetaItem awaits a full historyMetaItem read and discards it — a dead round trip on every live diff request #8798 — noted only because it is what makes the wrong key silent rather than loud.)Not the #8819 class
diffMetaItemis read-only: it takes no lock and performs no write, so there is no ADR-0010 gate to address around here and none of #8819's severity carries over. This is a wrong-answer defect, not a protection bypass. Filed separately rather than as a rider for that reason, and because the fix wants its own look at whether the echoedtypeshould report the canonical spelling (which would change a response field consumers may read).Backlink: #8819 · #8769 · the boundary fold's rationale is #7894.
Generated by Claude Code