Skip to content

A postgres config.url that pg itself cannot parse passes the authoring door — the shared credential-free URL check deliberately never parses #9091

Description

@os-project-manager

Found while implementing #8873 (the postgres DSN credential-injection arm) and filed rather than fixed there: it lands in packages/spec, and it is a different mechanism one door up.

The gap

PostgresConfigSchema.url documents a grammar in its own describe text —

Format: postgresql://[user@][host][:port][/dbname][?params]

— and enforces none of it. The value goes through placeholderFree(credentialFreeUrl(z.string(), …)), and both of those are string-boundary scans: they find userinfo, credential query parameters and ${…} spans. Neither asks whether the string is a DSN the client can open.

That is deliberate at the helper, and correctly so. common.zod.ts says it twice, in both halves:

Deliberately NOT new URL(): real DSNs take forms WHATWG parsing rejects or mangles

new URL() rejects the multi-host DSN form outright (#8696)

The multi-host form is the motivating case, and for mongo it is a real, working, documented shape (mongodb://app@h1:27017,h2:27017/app), so refusing it at the door would be wrong. The helper is shared across drivers, so its correct leniency for mongo becomes no check at all for postgres — where the same form is not a working shape but an unopenable one.

Measured

pg 8.22.0 does not implement libpq's multi-host DSN. Both its parser and its client constructor reject it:

parse('postgresql://app@h1:5432,h2:5433/app')                        -> TypeError [ERR_INVALID_URL]
new ConnectionParameters({connectionString: <the same string>})       -> TypeError [ERR_INVALID_URL]

PostgresConfigSchema accepts that exact value today. So an operator can publish a postgres datasource, see it saved, and discover only at connect time that the url can never open a connection — with a bare Invalid URL whose own input field pg redacts, i.e. an error that does not name the value that caused it.

Why it is worth a card rather than a shrug

This is the declared-≠-enforced shape of Prime Directive #10 at the authoring door: the schema publishes a format and validates a different, narrower property of the string. The connection form is the surface an operator (or an AI author) reaches for, and the one thing it could cheaply tell them — this is not a url this driver can open — is the thing it does not.

Note the fix is per-driver, not in the shared helper: credentialFreeUrl must keep refusing to parse, because mongo's multi-host and +srv forms depend on that. The candidate shape is a postgres-specific superRefine on url that asks the postgres client's own grammar, the same way #8873 made the runtime arm ask it rather than model it.

Scope note

Not a duplicate of #9041, which is about a different contradiction (credentialsRef bound while the url names no user — a semantic pair, both halves individually valid). This one is a url that is not a url for this driver at all.

#8873 is not addressed here and is unrelated in mechanism; it only made the runtime failure earlier and named (see #9090). Filed unassigned for triage.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions