Skip to content

[security] Two more spec schemas permit inline credentials: ExternalDataSourceSchema.authentication (clientSecret/apiKey) and MessageQueueConfigSchema.sasl.password — census toward #7990's parked boundary-guard reopen trigger #8075

Description

@huangyiirene

Found during the #7990 spec-half census (dev session session_0123k4cam2jEAkPmbJeoaY3r, 2026-08-12). Filed unassigned for triage to grade and route; not fixed in the #7990 PR — the maintainer's Option-A ruling (#7990 comment 5266068845) scoped that fix to the two measured surfaces (driver config, connector authentication) and parked the class-level sys_metadata write-boundary guard with reopen trigger = "a third measured artefact-type surface". This card is the census input for that trigger, not a re-litigation of the ruling.

What was measured (file:line on origin/main, 2026-08-12)

  1. packages/spec/src/data/external-lookup.zod.ts — ExternalDataSourceSchema.authentication is { type: 'oauth2' | 'api-key' | 'basic' | 'none', config: … } whose module docblock's own example writes "clientSecret": "..." inline (~L23). The schema accepts inline secrets in authentication.config.
  2. packages/spec/src/system/message-queue.zod.ts:60-65 — MessageQueueConfigSchema.sasl declares password: z.string().describe('SASL password') — a required inline broker credential when sasl is present.

What was NOT measured (the grading question)

Whether either schema is reachable from a sys_metadata publish door (metadata-type binding, stack collection, or /meta route) — i.e. whether these are cleartext-at-rest sinks like #7990's ①-c/①-d, or dormant declared shapes with no persisting consumer. ExternalDataSourceSchema sits in data/ next to field-level external-lookup config (plausibly authored inside object metadata); MessageQueueConfigSchema's consumers were not traced. If either is (a) reachable and persisted, it is a third measured surface for the parked Option-B guard; if (b) unreachable, ADR-0049 enforce-or-remove applies instead.

Prior art


Generated by Claude Code

Activity

  1. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Triage: domain:spec (the two schemas are packages/spec acceptance surface; per the one-package-three-seats split any card that may change accept/reject behaviour is the semantic lane's), pm:queue. Scope as dispatched: measurement first, no fix without the ruling. The deliverable is the reachability trace the card itself names as the grading question — whether ExternalDataSourceSchema.authentication and MessageQueueConfigSchema.sasl.password are reachable from a sys_metadata publish door (metadata-type binding, stack collection, or /meta route) and persisted.

    Outcome fork, pre-agreed at triage so the dev does not have to guess:

    premise_still_valid: false with evidence is a fully successful run here. Mandatory model note for the claiming seat: this is domain:spec semantic lane ⇒ dispatch at claude-fable-5 (measurement may collapse into an acceptance-surface change; do not tier down).

    本评论来自分诊座位 Routine。


    Generated by Claude Code

  2. self-assigned this
    on Aug 12, 2026
  3. huangyiirene commented on Aug 12, 2026

    @huangyiirene
    CollaboratorAuthor

    Claim: PM loop round 2 (spec seat, seat post #6017) — measurement only, per the triage scope
    Session: session_0123k4cam2jEAkPmbJeoaY3r
    Branch: none expected (pr: null is the anticipated deliverable; a branch only if the trace needs a pinned probe test, named in the report if so)
    Worktree: objectstack-issue-8075 (read-mostly)
    Domain: domain:spec
    File surface: read-only reachability trace of ExternalDataSourceSchema.authentication and MessageQueueConfigSchema.sasl.password against the sys_metadata publish doors. ⛔ No fix in this dispatch — the triage-agreed fork governs: (a) reachable+persisted ⇒ evidence + STOP (third-surface reopen trigger → needs-user-decision); (b) unreachable ⇒ evidence, card re-scopes to the ADR-0049 retirement route in a later dispatch.
    Container & model: S–M, mode:subagent, model: fable (triage's mandatory note: semantic lane, measurement may collapse into an acceptance-surface question — do not tier down)
    Serial constraints cleared: read-only; no file-face intersection with #7831 (the only other in-flight claim).


    Generated by Claude Code

  4. huangyiirene commented on Aug 12, 2026

    @huangyiirene
    CollaboratorAuthor
    {
      "issue": 8075,
      "status": "done",
      "branch": null,
      "pr": null,
      "premise_still_valid": true,
      "summary": "Both schemas land on fork (b): unreachable / no persisting consumer — this census does NOT produce the third measured artefact-type surface, so the #7990 Option-B reopen trigger is not met. (1) ExternalDataSourceSchema.authentication (packages/spec/src/data/external-lookup.zod.ts:31-67; config is a record of unknown at :65, docblock inline clientSecret at :23): no metadata-type binding — kernel/metadata-type-schemas.ts imports neither module (its import block :29-71), BUILTIN_METADATA_TYPE_SCHEMAS has no entry, UNREGISTERED_KIND_SCHEMAS (:215-243) is webhook/connector/sharing_rule only, and no code outside spec calls registerMetadataTypeSchema (only docblock mentions in metadata-protocol); no stack collection — stack.zod.ts imports nothing from external-lookup.zod; no object/field embedding — field.zod.ts:1011 externalId is a plain boolean and object.zod.ts:1648 external binds ObjectExternalBindingSchema (:1003 — remoteName/remoteSchema/writable/columnMap, no authentication; the ADR-0015/0062 federated path routes credentials through datasources, not this schema). Exported-but-unconsumed, the NormalizedFilterSchema shape; concurs with the prior in-repo verdict at conversions/registry.ts:4467-4471 (no external-lookup document exists for the walker to visit). (2) MessageQueueConfigSchema.sasl.password (packages/spec/src/system/message-queue.zod.ts:54-65, password :63): zero consumers outside packages/spec repo-wide (definition + own tests + generated artifacts + auto-generated docs only); DEFAULT_METADATA_TYPE_REGISTRY has no message_queue type; integration/connector.zod.ts:584 'message_queue' is only a ConnectorType enum value (connector authentication is #7990's already-measured surface and does not reference this schema); the consumed near-namesake EventMessageQueueConfigSchema (kernel/events/integrations.zod.ts:104-150, used by EventBusConfigSchema.messageQueue at bus.zod.ts:59) deliberately carries NO credential field — so the consumed MQ shape has no credential key and the credential-bearing MQ shape has no consumer, not even a runtime-received one. Door-side context, not a consumer of these schemas: saveMetaItem's documented 'unregistered type stores without validation' branch (metadata-protocol/src/protocol.ts:416 and :8642-8648 — no static registry entry synthesises allowRuntimeCreate true) persists arbitrary JSON under any unregistered type name, but no payload through that door is parsed by either schema; that door class is already tracked in-code under #6245 with the #2657 B/C decision open, so nothing new to file. ADR-0049 enforce-or-remove applies to both; note the auto-generated reference page content/docs/references/data/external-lookup.mdx teaches the inline-clientSecret example straight from the schema docblock and falls out with a retirement.",
      "tests": "No build/test run — read-only static trace on origin/main @ 3670cf9f2; no branch, no worktree edits, no executable pin needed (per dispatch, pr: null anticipated; the template's branch field does not apply and is null). Zero-hit controls per the method requirement: (a) grep 'external-lookup|ExternalLookup|ExternalDataSource' over packages/** excluding packages/spec = 0 files, and 'message-queue|MessageQueue' same scope = 0 files, while the SAME scope returns 34 occurrences across 21 files for 'DeclarativeConnectorEntrySchema|DatasourceSchema' and 15+ files for 'getMetadataTypeSchema' — the search demonstrably reaches the corpus. (b) Value-level control: case-insensitive 'sasl' outside packages/spec hits only pnpm-lock.yaml (@mongodb-js/saslprep, unrelated mongodb dep) and the auto-generated docs table — no example or runtime authors a sasl block. (c) Artifact-census control: authorable-surface.base.json lists data/ExternalDataSource:authentication (:3254) and system/MessageQueueConfig:sasl (:6058), proving the generated census sweeps ALL exported schemas — presence there is not reachability evidence. Repo-wide grep of the export names (whole checkout, no glob) hit only packages/spec, content/docs, and .changeset.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  5. huangyiirene commented on Aug 12, 2026

    @huangyiirene
    CollaboratorAuthor

    ACCEPT (measurement) — report verified per step 7: marker comment present; the PM independently re-ran the zero-consumer grep with a corpus-reach control (DatasourceSchema returns hits under the identical exclusions; the two subject schemas return none) — concurs. Verdict recorded: both schemas land on fork (b) — exported-but-unconsumed, no sys_metadata door reaches either, the consumed MQ near-namesake deliberately carries no credential field. The #7990 Option-B reopen trigger is not met by this census. The #6245-tracked unregistered-type door is context, not a consumer — nothing new filed, correctly.

    Re-scope per the triage-agreed fork (b): ADR-0049 retirement route, dispatching now under the same claim.

    Claim addendum (same session session_0123k4cam2jEAkPmbJeoaY3r, seat post #6017):
    Branch: claude/issue-8075-retire-dead-credential-schemas
    Worktree: objectstack-issue-8075
    File surface: packages/spec/src/data/external-lookup.zod.ts, packages/spec/src/system/message-queue.zod.ts, their tests, ADR-0087 registries/ledger entries, regenerated baselines + references/** (os-regen faces ride; relay line currently empty). (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: fable (mandatory clause — acceptance surface shrinks)
    Serial constraints cleared: #7831 (only other in-flight claim) holds contracts/security-service.test.ts + debt ledger — disjoint.

    Retirement target is the whole dead declarations, not just the credential fields — the measurement shows nothing reads any member of either schema, and a dead schema minus one field is still a dead schema. Route choice, ledger discipline and obligations follow the spec-property-retirement playbook; the reference page teaching the inline-clientSecret example falls out with the regeneration.


    Generated by Claude Code

  6. huangyiirene commented on Aug 12, 2026

    @huangyiirene
    CollaboratorAuthor
    {
      "issue": 8075,
      "status": "done",
      "branch": "claude/issue-8075-retire-dead-credential-schemas",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/8230",
      "premise_still_valid": true,
      "summary": "Phase-2 retirement executed per the ACCEPTed fork-(b) measurement: both dead credential-bearing families removed whole — data/external-lookup.zod.ts (ExternalDataSource + ExternalFieldMapping + ExternalLookup; the module's internal shape roots at ExternalLookupSchema, consumed by nothing, so all three go) and system/message-queue.zod.ts (MessageQueueConfig + its four siblings, embedded only by it). Spot-verification re-ran the zero-consumer greps on the merged tree with the DatasourceSchema corpus-reach control passing in the same run; ledger verdict confirmed ABSENT (no rows, nothing to orphan). Route 3 per the playbook (nothing parses it — no tombstone, no D2): declaration = 8 RETIRED_DEFS_BY_MAJOR[17] entry files + D3 SemanticMigration external-lookup-message-queue-families-retired; the 8 manifest lines and 39 authorable-surface lines deleted deliberately under the manifest-deletion gate; the #5552 data/ExternalFieldMapping:transform retired-keys entry SUBSUMED (same unreleased major, the WidgetManifest.performance precedent) — shared/FieldMapping tombstone, ConnectorFieldMapping spelling and the D2 conversion untouched. Changeset: @objectstack/spec major with FROM→TO table and adr-0087 'registered' marker (PM assumptions 3 and 4 confirmed against the playbook; assumption 2 confirmed with one nuance: gen:schema did not need to move the .base.json anchor — check (c) reads it at merge-base, and check:authorable-surface is green with it lagging, as the skill says is allowed). Docs: reference pages fell out via gen:docs; quick-reference rows/counts, build-docs page lists, PROTOCOL_MAP rows, strictness-ledger rows and the ADR-0122 pin count (826→824) updated as mechanical fallout; releases-owned implementation-status.mdx staleness filed as #8165, NOT touched. origin/main merged twice (last fa4897335, spec delta = 3 contracts/ files, disjoint); full §10 re-check ran on the merged tree. Dispatch-template deviation stated plainly: route 3 has no tombstone/guidance message to pin — the refusal is TS2305 + zero holders on every public entry (pinned), and 'retained exports keep parsing byte-identically' is vacuous for whole-file deletions; it is carried by near-namesake/neighbour survivor pins instead, plus two security-face pins (object.external strict-rejects inline authentication; EventMessageQueueConfig strips a smuggled sasl block). The #7990 Option-B reopen trigger stays NOT met; the parked guard was not resurrected. PR #8230 stays draft; os-regen faces ride it — landing lap needed if main moves a spec face before flip.",
      "tests": "All on the merged tree (fa4897335 merged) unless noted. pnpm --filter @objectstack/spec test: 385/385 files, 10150/10150 tests PASS (433s pre-merge run caught 2 reds — the ADR-0122 pin count and protocol-map.test.ts's PROTOCOL_MAP link check, both fixed as in-scope fallout; final merged-tree run fully green, 343s). check:generated 13/13 up to date. The 7 not-run source audits as one group (empty-state, skill-examples, template-manifests, variant-docs, exported-any, dual-source-exports, scripts-typecheck): all PASS. check:adr-0087-registration PASS; check:i18n PASS (9 packages, after building the CLI); check:nul-bytes PASS; check:merge-driver PASS. pnpm --filter @objectstack/spec typecheck PASS (pre-merge; merge delta is contracts/-only and landed green on main's own required typecheck — scoped per AGENTS.md §10). Consumer smoke: turbo run build 72/72 packages PASS; examples validate app-crm/app-showcase/app-todo all exit 0 (pre-existing warnings only); dogfood expression-conformance 1 file / 3 tests PASS, and its covers ledger has zero entries naming either family (verified by grep -c per file, not a pipe-masked exit code). Ratchet direction matches the whole-def-deletion expectation: api-surface −22 (pure removals, zero additions, verified by diff), authorable-surface −39, manifest −8, export-origins −22.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #8165: releases-owned implementation-status.mdx still lists external-lookup as pending (docs-only follow-up, Blocked-by #8075)",
        "filed as #8166: build-docs.ts integration category lists 9 page names with no emitting module (the #4480 connector-template ghosts; observation, finding label)"
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  7. huangyiirene commented on Aug 12, 2026

    @huangyiirene
    CollaboratorAuthor

    ACCEPT (phase 2 — retirement) — PR #8230 (draft), reviewed per step 7 against GitHub.

    Landing: os-regen faces ride ⇒ flip only after re-verifying main hasn't moved a spec face past this branch's merge point (a5dcb74 at PR time); if it has, the regen-merge lap runs first (delegated back to this dev). #7176's dispatch is re-serialized to AFTER this PR MERGES (both write migrations/registry.ts — two open PRs on one hand-written file is the conflict the serialization exists to prevent; "PR up" was too early a trigger, corrected here).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions