You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Decision] URL-embedded credentials (user:password@host in driver config.url) remain a live cleartext door after #7990 — refuse at publish, or accept as residual risk? #8082
Escalated from PR #8078's dev report (#7990 comment 5268367568, open question 1) by the domain:spec PM seat (session session_0123k4cam2jEAkPmbJeoaY3r). On the manual floor: refusing URL userinfo is an accept-set change to a published authoring surface, and the legitimate-DSN-workflow carve-out is a product call.
Background
#7990's ruled Option A (PR #8078) refuses inline config.password / config.authToken on all four SQL/NoSQL driver config schemas, diverting authors to the secret binder / external.credentialsRef. Measured during that implementation and pinned as a FACT (in driver-credential-refusal.test.ts, deliberately asserting today's acceptance): config.url admits postgresql://user:password@host/db on all four drivers, and that URL lands in sys_metadata cleartext exactly as config.password did. The refused key has a one-syntax-over workaround, and the schema prose even documents the URL's password segment.
Should publish-time validation refuse a driver config.url carrying userinfo credentials?
Options
A — refuse URL userinfo at publish (value-level parse on config.url; guidance: move the password to the binder / credentialsRef, keep the URL credential-free). Closes the workaround loudly. ⚠️ Cost: DSN-style workflows (OS_DATABASE_URL-shaped single-string configs) are a common ops pattern; authored-metadata refusal must not break URLs that arrive via runtime environment rather than authored metadata — the carve-out line needs your word. Note (measured, feat(spec)!: refuse inline credentials at publish — driver config + connector authoring door (#7990, spec half) #8078): ${…} placeholders in authored metadata resolve to nothing — they reach the client verbatim — so "put a placeholder in the URL" is not a working escape today.
B — accept as documented residual risk: keep the pin, document the door in the security posture docs, revisit if measured in the wild.
实际业务需求 — the door is live and measured on all four drivers; it is the exact sink the maintainer just ruled worth closing, one syntax over. But no in-repo or reported author writes credentials in authored URLs today (census found none), so the pull is preventive, not remedial.
防 AI 写错 — strongest is A: an AI author refused on config.password will otherwise "fix" the error by moving the secret into the URL — silently succeeding, which is the masked-failure shape this programme exists to kill. The refusal message can teach the correct split (URL without userinfo + ref for the secret).
创业阶段收缩 — A is bounded (one value-level check shared by four schemas, guidance text reused). Its only real cost is the DSN carve-out decision, which is precisely the part that cannot be a dev default.
Recommendation
A, premised on your ruling of the carve-out: authored metadata refuses URL userinfo; runtime-environment-provided URLs (not authored, never stored in sys_metadata) stay out of the contract's reach. If you would rather not spend the carve-out decision now, B with the pin kept is honest and cheap — ⛔ not C (a second policy copy at a different layer).
Escalated from PR #8078's dev report (#7990 comment 5268367568, open question 1) by the
domain:specPM seat (sessionsession_0123k4cam2jEAkPmbJeoaY3r). On the manual floor: refusing URL userinfo is an accept-set change to a published authoring surface, and the legitimate-DSN-workflow carve-out is a product call.Background
#7990's ruled Option A (PR #8078) refuses inline
config.password/config.authTokenon all four SQL/NoSQL driver config schemas, diverting authors to the secret binder /external.credentialsRef. Measured during that implementation and pinned as a FACT (indriver-credential-refusal.test.ts, deliberately asserting today's acceptance):config.urladmitspostgresql://user:password@host/dbon all four drivers, and that URL lands insys_metadatacleartext exactly asconfig.passworddid. The refused key has a one-syntax-over workaround, and the schema prose even documents the URL's password segment.Premise re-check commands
git grep -n "user\[:password\]" origin/main -- packages/spec/src/data/driver/postgres.zod.ts(the documented URL format)git grep -n "url" origin/main -- packages/spec/src/data/driver/driver-credential-refusal.test.ts(post-feat(spec)!: refuse inline credentials at publish — driver config + connector authoring door (#7990, spec half) #8078 merge)Question
Should publish-time validation refuse a driver
config.urlcarrying userinfo credentials?Options
config.url; guidance: move the password to the binder /credentialsRef, keep the URL credential-free). Closes the workaround loudly.OS_DATABASE_URL-shaped single-string configs) are a common ops pattern; authored-metadata refusal must not break URLs that arrive via runtime environment rather than authored metadata — the carve-out line needs your word. Note (measured, feat(spec)!: refuse inline credentials at publish — driver config + connector authoring door (#7990, spec half) #8078):${…}placeholders in authored metadata resolve to nothing — they reach the client verbatim — so "put a placeholder in the URL" is not a working escape today.$searchFieldshalf of the__searchcontract is asserted by no test #8080-scope): scrub userinfo at the write door instead of the contract (value rewriting or refusal atdatasource-admin), leaving the spec accept-set unchanged.Four-lens
sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990 closure structurally incomplete: the next refused author (or AI) gets steered to the URL form by the refusal itself. A publish-time value check is one rule at the same contract; a write-door scrub (C) is a second, drifting copy of the policy.config.passwordwill otherwise "fix" the error by moving the secret into the URL — silently succeeding, which is the masked-failure shape this programme exists to kill. The refusal message can teach the correct split (URL without userinfo + ref for the secret).Recommendation
A, premised on your ruling of the carve-out: authored metadata refuses URL userinfo; runtime-environment-provided URLs (not authored, never stored in
sys_metadata) stay out of the contract's reach. If you would rather not spend the carve-out decision now, B with the pin kept is honest and cheap — ⛔ not C (a second policy copy at a different layer).Refs: #7990 (ruling + report), PR #8078 (the pinned fact), #8075 (further census).