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

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

claude added 2 commits August 16, 2026 11:20
…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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants