Skip to content

[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

Description

@objectstack-fleet

Filing gate: ① reproducible defects with named landing sites, folded into ONE family closeout card as the same-family rule requires (no single-point cards). reach: exception — could leak data. Measured in-session on fresh showcase boots by the #21086 dev, after that fix. Reader: the maintainer, who holds the positions and recipes; dispatched directly at the maintainer's request. Dedupe: this is the closeout of the #7990 / #20590 / #13405 / #21086 family (all closed except #21086, whose PR #21115 is in review); a family-level search (open + closed) finds no open card covering these positions.

QA-source: #21056 · integration-system.datasource-credential-refusal-matrix · negative[0]

What is known publicly

  • Stored metadata bodies, including stored datasource credential material, are declared write-only. They are redacted on the /meta and datasource doors and, once PR fix(metadata-protocol)!: stored metadata bodies on the generic data door are served as their type's read projection (#21086) #21115 lands, on the generic data door's reads of the stored metadata tables.
  • Further positions remain where a stored body (or a value derived from it) can still be served, kept or recovered outside those doors. All positions measured so far are admin-reachable only:
    • a read surface that serves the body directly;
    • a write path that keeps an at-rest cleartext copy of the body in a second admin-readable store, whose fix needs a writer change AND a migration of existing rows;
    • a query shape on the generic data door that still evaluates the stored body;
    • one position that is unmeasured and must be measured first.
  • The maintainer has ruled on the query-shape position: the generic data door refuses it on the stored metadata tables (a further accept-set narrowing, the same family as fix(metadata-protocol)!: stored metadata bodies on the generic data door are served as their type's read projection (#21086) #21115's grouping refusal). Ruling, verbatim (Claude Code session session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「A 拒绝(推荐)」. Decision to file and dispatch this card, verbatim from the same session: 「立一张脱敏收口卡并派发(推荐)」.

Detail withheld pending maintainer (RUNNER rule 2).

Acceptance (family level)

  • An enumeration pin over every surface that can serve, copy or evaluate a stored metadata body. Each surface is either covered by the shared redaction seam or refused there, so that a future surface fails the pin instead of joining the family silently.
  • Each measured position fixed at its own landing, through the one shared redaction seam. No second redaction dialect.
  • Existing at-rest copies migrated.
  • The unmeasured position measured, and then fixed or recorded with evidence.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_01MRdbfpy4sQT8bUjmMhxsN7
    Account: os-bill
    Branch: claude/issue-21120-stored-metadata-body-family
    Worktree: objectstack-issue-21120
    Domain: domain:services (expected; the positions span packages/services, packages/plugins and packages/metadata-protocol — dispatched at the maintainer's direction ahead of a triage grade)
    File surface: the landing files of the measured positions (named to the dev in-session; the dev declares the exact files in the report) + a new enumeration pin + a data migration for existing at-rest copies + a changeset; stop on breach
    Container & model: M, mode:subagent, tier default (security family closeout; dispatch-gates --tier: no path-derived mandate)
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: PR #21115 (#21086, in review) owns packages/metadata-protocol/src/metadata-redaction.ts and protocol.ts. The data-door position of this card touches the same files, so it is held until #21115 is on origin/main (no stacked branch). The other positions are disjoint and go first. #21087 (packages/rest, packages/runtime/src/domains/meta.ts) is disjoint. 34 earlier in-flight claims were scanned for these surfaces with none found.

    Dispatched at the maintainer's direction. Rulings, verbatim (Claude Code session session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「立一张脱敏收口卡并派发(推荐)」 and, on the data-door position, 「A 拒绝(推荐)」. The reproduction stays in-session. ⛔ It does not go into any GitHub text.

    Landing: security boundary, human floor. The PR stays draft for the maintainer's review.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: grade after dispatch — bug · security · priority:p1 · domain:services · area:access. ⛔ The claim, the assignee and the state are unchanged

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T07:53Z. ⛔ 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.


    Generated by Claude Code

  3. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p1High: required for production / M2
    on Oct 1, 2026
  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21120,
      "status": "done",
      "branch": "claude/issue-21120-stored-metadata-body-family",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21144",
      "session": "session_01MRdbfpy4sQT8bUjmMhxsN7",
      "premise_still_valid": true,
      "clause_2": "no (narrowing)",
      "summary": "Family closeout. One shared seam added to @objectstack/spec/kernel (object set, predicate, column names, body redactor) and every further surface that can serve, copy or evaluate a stored metadata body now routes through it or refuses: plugin-audit projects the write-time audit/activity copy and a new `os migrate audit-metadata-bodies` command rewrites the copies already at rest; service-analytics refuses the body column as a query member (INVALID_FIELD/400); objectql projects the data.record.* realtime event body; and, on the merged #21086 seam, the generic data door refuses a filter or sort on the body column (maintainer ruling A). An enumeration pin fails a future surface instead of letting it join the family silently. #21115 was merged mid-run; origin/main was merged in (no stacked branch) and F3 built on its seam. PR stays draft (security boundary, human floor).",
      "tests": "Per-package vitest (all pass): spec metadata-type-redaction.test.ts (seam, 19); metadata-protocol stored-metadata-body-family.pin.test.ts + merged protocol.data-door-stored-metadata-redaction.test.ts (24, green on the reconciled seam); plugin-audit audit-writers.test.ts (78, incl. create/update-rotation/clean writer redaction) + stored-metadata-body-migration.test.ts (12, planners + driven runner dry/apply/idempotent); service-analytics stored-metadata-body-refusal.test.ts (5); objectql engine-realtime-stored-metadata-body.test.ts (4) + engine-data-events.test.ts green. typecheck green for spec/metadata-protocol/objectql/service-analytics/plugin-audit/cli. Local gates run (exit 0): check:nul-bytes, check:adr-0087-registration --base origin/main, check:changeset-no-major, check:empty-changeset, check:undeclared-dep-imports, check:issue-citations, spec check:generated/api-surface/export-origins, check:doc-authoring, check:cross-package-test-inputs, check:test-source-alias, check:engine-double-contract, check:durability-log-level, check:merge-driver, check:published-files, check:tier-file-adoption. Full turbo build (72/72) green after the merge. Reconciliation: 16 gate families run locally, 89 derived families deferred to CI (the lint/i18n/temporal/dogfood farm is CI's). No ablation needed (this is additive redaction + refusal, not a new gate).",
      "mcp_calls": "0 — no MCP GitHub tools used; reads were gh CLI (gh api) reads of the card, its comments and the PR.",
      "api_writes": "3 REST writes via the fleet relay: pr_create (POST /repos/objectstack-ai/objectstack/pulls -> #21144, draft forced), assign (POST /repos/.../issues/21144/assignees os-bill), and this comment (POST /repos/.../issues/21120/comments). git push is separate (branch pushes, not REST).",
      "open_questions": [],
      "pm_questions_answered": "Answered in the session-only file (reachability by persona, the F1 refuse-vs-exclude choice justified from the analytics posture, and the migration-framework choice). Pointer below.",
      "session_only_pointer": "/tmp/claude-0/-home-user/4f67a396-f4bd-55f0-a625-6a7c371668ad/scratchpad/issue-21120/session-only-21120-dev.txt",
      "analytics_files_touched": "service-analytics/src/analytics-service.ts (callCtx + a new assertStoredMetadataBodyNotQueried method) and the new src/stored-metadata-body-refusal.ts + its test. NONE of #21117's files (read-scope-sql.ts, native-sql-strategy.ts, contains-membership-sql.ts) were edited.",
      "out_of_scope_findings": [
        "class: a (reproducible defect) · reach: public door POST /api/v1/analytics/dataset/query, measured non-admin reachable, serves a stored metadata body (credential material) to a persona who is refused it on every /data and /analytics/query door · a broader analytics evaluation surface (an inline-dataset expression dimension is compiled verbatim and bypasses object- and field-level admission) OUTSIDE the stored-metadata-body family · its fix lands in the analytics native-SQL files owned by in-flight PR #21117 (card #20987), so blocked on #21117 and needs its own card · exploit detail is session-only (pointer above) · carrier: #21117 · dedupe words: analytics dataset expression dimension raw-sql admission bypass"
      ]
    }
    
  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    PM review — PR #21144: ACCEPT; landing on the maintainer's pre-authorization

    Reviewed-by: PM seat, session_01MRdbfpy4sQT8bUjmMhxsN7. Implemented-by: os-dev subagent, same session.

    • Shape: one shared seam in @objectstack/spec/kernel. Every further exit routes through it or refuses:
      • the write-time audit/activity copy is projected, and os migrate audit-metadata-bodies rewrites the copies already at rest (the established operator-runnable os migrate shape, dry-run by default and idempotent);
      • analytics refuses the body column as a query member;
      • the realtime record-event body is projected;
      • the generic data door refuses a filter or sort on the body column, per the maintainer's ruling A, built on fix(metadata-protocol)!: stored metadata bodies on the generic data door are served as their type's read projection (#21086) #21115's merged seam with no stacked branch.
        An enumeration pin fails any future exit that does not route through the seam.
    • Path surface (read from the PR file list): spec/kernel, metadata-protocol, objectql, plugin-audit, service-analytics (none of fix(service-analytics)!: the analytics read scope and the native where answer $contains on a JSON-stored field by membership (#20987) #21117's files), cli, two changesets, the engine-double ledger row, and two gate-demanded census figure updates (content/docs/permissions/tenant-audit-census.mdx, docs/audits/…counts.md). There is no governed path, and the changed lines are under the 5,000 human-merge threshold.
    • CI: the first push was red on Lint & Repo Gates (ESLint: a service lookup erased to any). It was fixed at the root, and 127/127 derived gates were then run locally with exit codes recorded. CI on head 5397aa8ba8 is 33 success / 2 skipped / 0 failed; mergeable clean.
    • Clause-②: no (narrowing). The data-door and analytics refusals narrow the accept set; the changesets carry the disposition.
    • Disclosure check: the PR title, body, commits and changesets carry no reproduction.
    • Landing: maintainer pre-authorization, selected verbatim 「绿了就转 ready + 进队列(推荐)」 in Claude Code session session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01.
    • Out of scope: a broader analytics evaluation position, outside this family, goes to its own sanitized card per the maintainer's ruling in the same session.

    Generated by Claude Code

  6. added 4 commits that reference this issue on Oct 7, 2026
    336e191
    ce4e205
    5d0e4e2
    72b6333
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:servicespriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions