Skip to content

feat(spec): refuse a credential in the mongo options passthrough (config.options.auth.password) at publish (#9040) - #9123

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-9040-mongo-options-auth-credential
Aug 16, 2026
Merged

feat(spec): refuse a credential in the mongo options passthrough (config.options.auth.password) at publish (#9040)#9123
os-zhuang merged 2 commits into
mainfrom
claude/issue-9040-mongo-options-auth-credential

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#9040

Route ① — refuse at publish (the inherited #8082/#8337 direction)

config.options.auth.password was the FOURTH spelling of the inline credential (#7990 top-level key, #8082 URL userinfo, #8337 URL query params). Measured on origin/main @ 479ec31c9 before editing (premise re-probed, still valid): the config parsed green, the password persisted cleartext, redactDatasourceConfig('mongodb', …) served it back with redactedKeys: [] — and mongodb@7.5.0 transforms the block into MongoCredentials, so it genuinely authenticated.

The refusal set is a CLOSED measured list, not a heuristic scan (MONGO_OPTIONS_CREDENTIAL_PATHS in driver/common.zod.ts): a non-empty string options.auth.password. Why each candidate is in or out — every claim measured against mongodb@7.5.0, the client @objectstack/driver-mongodb spreads config.options into:

False-positive analysis for a generic deep key-name walk (the alternative the card allowed if strictly safer): it is not safer here — the client's OPTIONS table is a closed set, only the paths above resolve into secrets, and a name-walk would judge author-typed junk the client never reads while adding nothing over the measured list. Closed list chosen.

Composition with placeholderFreeDeep (PM-mechanism assumption, verified): both checks are independent superRefines on the same slot; a ${…} password reports BOTH issues at options.auth.password (pinned: exactly 2 issues, one naming #9040, one #8336). The wrapper's own semantics are unchanged.

Route ② — redaction, additive (measured cheap because the carry-forward machinery already generalizes)

Scope measurement (the card's question): is the door mongo-only?

Yes — measured, not assumed. postgres/mysql/turso/sqlite/sqlite-wasm/memory ship closed strictObject config contracts with no client-bound record slot; memory's initialData is seed DATA (deliberately unjudged — redacting a seeded row's own password field would corrupt served data). Pinned in the redaction suite (passthroughSecretPaths(driver) empty for every non-mongo builtin). No equally-open sibling door exists, so no shared rule is warranted; the helper still lives in driver/common.zod.ts beside credentialFreeUrl so a future passthrough joins the same mechanism.

ADR-0087 / changeset

Semantic entry 18.datasource-config-mongo-options-credential-refused (registry regenerated via gen:migration-registry); changeset minor for spec with BREAKING annotation + patch for service-datasource, following #8337's convention. No automatic rewrite, same reason as the three siblings (a source transform cannot encrypt into sys_secret; auto-dropping the leaf alone would brick the auth block).

Verification (union run at final commit e3b754e23)

  • pnpm --filter @objectstack/spec test — 406 files / 10777 passed. pnpm --filter @objectstack/service-datasource test — 453 passed (includes A bound external.credentialsRef is silently dropped on the DSN branches of the mysql and mongodb driver arms #8696's bound-secret-dsn-branches precedence pin, untouched). pnpm --filter @objectstack/metadata-protocol test — 112 files / 1565 passed. pnpm --filter @objectstack/driver-mongodb test — 385 passed / 143 skipped (mongod-gated). Typecheck green on spec + service-datasource; examples validate green.
  • Reverse verification (direction decided beforehand — write door only, red): mongo.zod.ts restored to origin/main ⇒ exactly the 3 write-door pins red (refusal, placeholder-composition, artefact re-path), 143 others green ⇒ restored from the committed branch, suite green again.
  • Forward probe on rebuilt dist/: the issue's own snippet now answers success: false at options.auth.password, and redaction serves auth: { username } with redactedKeys: ['options.auth.password'].
  • Gates: derived via scripts/pm/dispatch-gates.mjs against the actual diff — beyond the dispatch-named set it added docs-audit-scope, docs-redirects, objectui-changeset, quick-reference-counts, role-word, test-source-alias, adr-0087-registration, changeset-no-major, empty-changeset; ALL green, plus cross-package-test-inputs, doc-formula-expressions, merge-driver, spec-parsed-alias, type-source-resolution, dev-prereqs (green after full closure build — first red was stale-worktree, 45 unbuilt packages), nul-bytes, query-options-erasure, engine-double-contract, where-matcher, type-check-coverage, type-check-debt --re-measure (33 entries, none above measurement), and check:generated — all 13 artifacts up to date (api-surface, export-origins, references regenerated).

Generated by Claude Code

…fig.options.auth.password) at publish (#9040)
Write door: closed measured list (MONGO_OPTIONS_CREDENTIAL_PATHS) behind
credentialFreeMongoOptions, composed with placeholderFreeDeep on
MongoConfigSchema.options; non-empty string auth.password refused with the
binder prescription (bound secret measured outranking the passthrough at
connect, #8696). Read door: passthrough secret paths (auth.password,
proxyPassword, TLS key material, AWS_SESSION_TOKEN) redacted with dotted
redactedKeys; restoreRedactedConfig mirrors per leaf; the credential-migration
planner refuses stored passthrough-credential rows with the per-row remedy.
ADR-0087 semantic entry (registry regen to follow).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01225pUjnCKWqxcc1PeqKFUq
…ns, references; add changeset (#9040)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01225pUjnCKWqxcc1PeqKFUq
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-datasource, @objectstack/spec.

106 hand-written doc(s) reference the affected code — list omitted above 15 rows. Re-derive: node scripts/docs-audit/affected-docs.mjs --json origin/main.

7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentationImprovements or additions to documentationprotocol:datasize/lteststooling

Projects

None yet

2 participants

@os-zhuang@claude