Repository navigation
[security] A datasource read path serves connection topology to signed-in members below the capability the datasource admin door requires — detail withheld pending maintainer #21087
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 2
Session:session_01MRdbfpy4sQT8bUjmMhxsN7
Account:os-bill
Branch:claude/issue-21087-datasource-meta-read-gate
Worktree:objectstack-issue-21087
Domain:domain:services(expected — dispatched at the maintainer's direction ahead of a triage grade; the landing sits in the datasource read paths)
File surface: the read gate for datasource metadata (packages/services/service-datasource/src/admin-routes.tsand the metadata read path that bypasses its capability; exact file declared by the dev); stop on breach
Container & model:M,mode:subagent, tierdefault(security-boundary fix; dispatch-gates--tier: no path-derived mandate)
Clause-②: no
Thread-read: none
Serial constraints cleared: 34 in-flightpm:dispatchedclaims scanned, none declares the datasource redaction or read-gate files. Sibling #21086 is dispatched this round on an adjacent datasource read path: each dev declares its files before editing, and on any shared file the later one stops and reports.Dispatched at the maintainer's direction. Ruling verbatim (Claude Code session
session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「只给我看的两条,你也直接派发处理」. The reproduction goes to the dispatched dev in-session only. ⛔ It does not go into any GitHub text.Landing: a security/permission-boundary change, so the PR stays draft for the maintainer's review (human floor).
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: grade after dispatch —
bug·security·priority:p1·domain:services·area:access. ⛔ The claim, the assignee and the state are unchangedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T06:03Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ It carries no detail beyond the card's own sanitized text.The card was dispatched at the maintainer's direction ahead of a grade. This act adds the grade only.
- Why p1: a security boundary that the card's own text states is contradicted, on a P0 checklist item's surface (
integration-system.datasource-credential-refusal-matrix, QA run · priority:P0 (19/19) · ef1ed17f · 2026-10-01 · 13 PASS / 0 PARTIAL / 6 FAIL / 0 BLOCKED / 0 NOT-RUN #21056). - Routing:
domain:services, as the claim expected.
Generated by Claude Code
- Why p1: a security boundary that the card's own text states is contradicted, on a P0 checklist item's surface (
- addedarea:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21087,
"status": "done",
"branch": "claude/issue-21087-datasource-meta-read-gate",
"pr": "#21119",
"session": "session_01MRdbfpy4sQT8bUjmMhxsN7 — this run's harness-stamped id (subagent: the parent's; the relay derived the same id from CLAUDE_CODE_REMOTE_SESSION_ID)",
"premise_still_valid": true,
"summary": "Premise re-derived independently on a booted showcase (fresh SQLite, seeded admin, a member created and password-changed): the second derivation agrees with the runner's observation and confirms the PM's mechanism hypothesis (detail in-session only). Fix: one type-level read admission, metaTypeReadRefusal over META_TYPE_READ_CAPABILITIES in packages/rest/src/meta-item-read-gate.ts, asked by both transports at their single /meta entry right after the anonymous deny and before any store read (RestServer's guarded registrar in packages/rest/src/rest-server.ts; handleMetadataRequest in packages/runtime/src/domains/meta.ts; export in packages/rest/src/index.ts). GET/HEAD of datasource on every /meta read shape (list, item and its switches, /published, /layers, /history, /audit, /diff, /references) now requires manage_platform_settings, the admin door's capability, with the same 403 PERMISSION_DENIED for existing and absent names; holders are served as before; writes untouched. Bounded in-place addition (same class, same files, one row): external_catalog held to the federation read door's capability (same one) — flagged in open_questions. No spec schema, @objectstack/spec export or response shape changes; this narrows who may read a published route (runtime permission change). Dispatch question 1: no member-facing consumer of /meta/datasource at the objectui pin e420df310 (Setup datasource page uses the admin door; MetadataProvider loads object/dashboard/report/page/view/app only; DraftChangesPanel is the one generic lister, author-facing, noted in the PR); CLI os meta list/get is generic; packages/mcp exposes no datasource body. Question 2: the shared per-document gate (createMetaItemReadGate/createMetaListReadGate) runs after the store read so a datasource arm there would be an existence oracle; the type registry declares no read capability and adding one is a spec change; so the seam is a type-level predicate in the same shared module, asked at both entries. PR is draft and stays draft (human floor). Worktree removed after the PR opened; dev server I started torn down by its recorded process group.",
"tests": "All on HEAD 220d080 unless stated. NEW packages/rest/src/meta-type-read-capability.test.ts 53 passed (predicate battery; every GET door under /meta/:type read off the route table plus ?state=draft/?preview=draft/?layers=true/?package= for datasource|datasources|external_catalog|external_catalogs x member/author(manage_metadata+studio.access+setup.access)/other-grant → 403 PERMISSION_DENIED with 0 store reads, byte-identical for an absent name, holder 200 on every shape, unlisted type served; admin-door agreement over resolveAuthzContext + one grant store, only the holder admitted by both). NEW packages/runtime/src/domains/meta-type-read-capability-parity.test.ts 19 passed (dispatcher refuses member/author on list/item/published/layers/absent name with 0 reads, serves holder, answers every caller as RestServer does). Adjusted fixtures: meta-unknown-type-read-refusal.test.ts caller now also holds manage_platform_settings (its external_catalogs row), meta-list-protocol-fault.test.ts datasource row reads as a manage_platform_settings holder. Suites: @objectstack/rest vitest --project local 259 files 5075 passed/143 skipped, --project repo 1 file 8 passed; @objectstack/runtime vitest --project local 298 files 4268 passed/5 skipped, --project repo 3 files 751 passed; pnpm --filter @objectstack/rest typecheck exit 0 and @objectstack/runtime typecheck exit 0 (both include check:test-typecheck). Reverse verification via scripts/ablation-replace.mjs (anchor hit 1→0, blob changed, restore proven blob==HEAD and git diff HEAD empty; tests read src through the vitest @objectstack/rest → src alias, so no dist leg): RestServer seam removed → rest 19 red/34 green, runtime parity 4 red/15 green; dispatcher seam removed → runtime 12 red/7 green; datasource row removed → rest 18 red/35 green, runtime 4 red/15 green (external_catalog rows green). Direction: turn red, as predicted. A first attempt at the row ablation was refused by the tool (replacement count did not rise: a no-op), redone with --delete; the same three legs were first run at 73c8f41 with the same reds. Live: showcase booted on packages built at 611db07, member 403 PERMISSION_DENIED on every datasource and external_catalog read shape incl. absent name, plural and percent-encoded spellings, admin 200, member object/app/view reads 200; env-scoped mount not mounted in that composition (404 for admin too), covered by construction (same guarded registrar), NOT MEASURED live. Gates: dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) 62 commands, all exit 0 on 220d080 (first pass on fd2faaf caught check-system-context-census and check-objectql-double-limit red, both fixed: isSystem arm dropped, grants double replaced with the core suite's bounded double; plugin-teardown-shape --self-test and dual-build-cjs-loads exit 3 prerequisites met by fetching the pinned fixture commit and building 8 unrelated packages); --ran reconciliation: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN (exit codes recorded). Same derivation over this diff applied to origin/main fed0db8: identical 62-command list, patch applies cleanly. Lint narrowed: eslint --no-inline-config --format json over the 8 changed .ts files: 8 files, 0 errors, 0 warnings; eslint.config.mjs enables no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move; pnpm lint itself declared to CI. CI at report time: 11 check runs completed with no failure, 20 in_progress.",
"mcp_calls": "0",
"api_writes": "3 — each one repository_dispatch through scripts/pm fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: pr_create → POST /repos/objectstack-ai/objectstack/pulls (#21119, draft); label-write assign → POST /repos//issues/21119/assignees (os-bill; no labels written, size/l came from the size labeler); this report → POST /repos//issues/21087/comments. git push (several) is not a REST write.",
"open_questions": [
{
"question": "Keep the external_catalog row in this PR? It closes the same defect class (a /meta read of a datasource-family type admitting callers the type's own door refuses): the cached federation catalog vs the federation read door, FEDERATION_READ_CAPABILITY = manage_platform_settings. ADR-0015 6.2 had written reads at metadata:read; the mounted federation reads were later moved to manage_platform_settings by the ruling recorded on FEDERATION_READ_CAPABILITY, and this row extends that rule to the cached snapshot.",
"options": [
"A keep the row (one map entry, pinned by the same tests)",
"B drop the row here and file the catalog as its own card"
],
"recommendation": "A, because it is the same mechanism and capability, measured on the booted showcase (member refused by both doors, admin served by both), no consumer at the objectui pin reads it, and dropping it is a one-line change if the maintainer prefers B."
}
],
"out_of_scope_findings": [
"class: a · reach: public REST metadata write door — a datasource write there admits a caller holding an authoring capability but not the capability the datasource admin door requires for create/update (status measured once on the booted showcase; whether a row saved that way reaches a live pool NOT MEASURED); exact request held for the maintainer, as this card's · evidence: the same type's metadata save admission and the admin door's create/update admission name different capabilities · dedupe words: datasource write capability, metadata save door datasource, manage_platform_settings write parity, datasource admin door twin"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPM review — PR #21119: ACCEPT (CI pending); landing awaits the maintainer (human floor: permission boundary)
Reviewed-by: PM seat,
session_01MRdbfpy4sQT8bUjmMhxsN7. Implemented-by: os-dev subagent, same session.- Path surface (read from the PR file list):
packages/rest(meta-item-read-gate.ts,rest-server.ts,index.tsplus tests),packages/runtime/src/domains/meta.tsplus tests, and a changeset. There is no governed path. It is disjoint from fix(metadata-protocol)!: stored metadata bodies on the generic data door are served as their type's read projection (#21086) #21115 and from the [security] Family closeout: stored metadata bodies (stored datasource credential material included) are still reachable outside the redacting doors at several further positions — detail withheld pending maintainer #21120 family card. - Shape: one type-level read admission (
metaTypeReadRefusaloverMETA_TYPE_READ_CAPABILITIES), asked at the single/metaentry of BOTH transports, right after the anonymous deny and before any store read. That ordering means the refusal is identical for existing and absent names. The existing per-document gate runs after the store read, so the dev rightly did not put the arm there. No spec schema, export or response shape changes. - Consumers: measured at the objectui pin. There is no member-facing reader of
/meta/datasource: the Setup datasource page uses the admin door. The CLI is generic. MCP serves no datasource body. - Evidence accepted:
- 53 new rest cases (every
GETunder/meta/:type, read off the route table) and 19 new runtime parity cases; - three ablations each turn red in the predicted direction and restore byte-identical;
- full rest and runtime suites pass, typecheck exits 0;
- 62/62 derived gates exit 0;
- live: a member is refused on every shape, an admin is served.
- 53 new rest cases (every
- Disclosure check: the PR title, body, changeset and report comment carry no reproduction.
- Open question (for the maintainer): the bounded in-place
external_catalogrow holds the cached federation catalog to the federation read door's capability. It is the same class and the same capability. The PM recommends keeping it.
The PR stays draft until the maintainer decides the landing.
Generated by Claude Code
- Path surface (read from the PR file list):
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: routing correction —
domain:services→domain:cli. ⛔ The claim, the assignee and the state are unchangedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T08:02Z. ⛔ Not a claim, ⛔ not a dispatch.This seat's grade
5925714835followed the claim's expected domain. The fix, PR #21119, lands inpackages/restandpackages/runtime, and the lane table puts both indomain:cli. The error is triage's. Nothing else changes.
Generated by Claude Code
- added 2 commits that reference this issue
on Oct 7, 2026
Filing gate: ① a reproducible defect with a named landing site — the read gate for datasource metadata; the rationale it contradicts is written in
packages/services/service-datasource/src/admin-routes.ts(themanage_platform_settingsgate).reach:the public REST API, reproduced twice by the P0 run's runner (not yet independently re-derived — the dev's premise check is the second derivation). Reader: the maintainer, who holds the reproduction; dispatched directly at the maintainer's request. Dedupe: semantic search on datasource metadata reads by members below the platform-settings gate (open + closed) → 3 hits, none this path; nearest, closed: #9593 (the admin door's capability gate).QA-source: #21056 · integration-system.datasource-credential-refusal-matrix · acceptance[7]
What is known publicly
manage_platform_settings(403), andadmin-routes.tsexplains why: a lower gate "would publish a deployment's connection topology to every authenticated tenant user".acceptance[7]held as written.Detail withheld pending maintainer (RUNNER rule 2). Maintainer ruling, verbatim (Claude Code session
session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01):Landing site
The read gate for datasource metadata on the path that bypasses the admin door's capability; the maintainer holds the reproduction.
Generated by Claude Code