fix(cli): the startup banner reads a DSN-declared datasource, and stops printing credentials (#3793) - #3819
Merged
Conversation
…ps printing credentials (#3793) `OS_DATABASE_URL="postgres://u:p@127.0.0.1:59437/nope" os serve` bannered `Driver: SqlDriver(pg) → (unknown)` — the driver identified, the address gone, at exactly the moment (an unreachable database) when the address is the one thing worth reading. `describeRegisteredDriver` knew three of the four shapes a driver's `config.connection` arrives in — a DSN string, `{ filename }`, and discrete `{ host, port, database }` — but not `{ connectionString }`, which is what `defaultDatasourceDriverFactory` builds for a pg datasource declared with `config.url` / `config.connectionString`. `driver.config` keeps the shape its author passed (pinned by a driver-sql test), so #3791's knex-side rewrite neither caused nor worsened this. Two more `(unknown)`s had the same cause: the reader returned as soon as a driver had a `config` at all, so only a SqlDriver could ever succeed. MongoDBDriver keeps its DSN in a top-level `config.url` and *does* have a `config`, so the "mongo/turso expose the URL on the instance" arm below was unreachable; InMemoryDriver's `config` is `{}`, which is truthy, so the `(in-memory)` arm was unreachable too. Both are now read by shape. A latent label bug goes with them: SqliteWasmDriver passes knex a dialect *class* as `client`, and `SqlDriver(${cfg.client})` would have pasted the class source into the banner — only a string `client` is interpolated now. Both halves are one renderer, `utils/connection-display.ts`: - `describeDriverConnection(config)` knows every `connection` shape plus the `{ uri }` / `{ url }` spellings, applies the same precedence to a top-level address, and returns `undefined` — not a guess — for a function-valued `connection` the host builds per pool checkout. - `redactConnectionUrl(value)` drops credentials rather than masking them. The old `//user:****@` mask left two holes: a password with no username (`postgres://:secret@host/db`) did not match its regex and printed verbatim, and a Turso DSN carries its secret in the query string, which was never touched. Userinfo and query are both dropped; non-URL values (`:memory:`, a sqlite path) pass through; the function is idempotent. Three byte-identical copies of the old mask (serve / start / dev) and a fourth variant in `schema-migrate` collapse into it. The `schema-migrate` copy had the same DSN blindness with higher stakes — it feeds the `Apply N change(s) to …?` confirm, where a `{ connectionString }` config rendered as a bare `pg`, asking an operator to approve a destructive migration against an unnamed database. Verified end to end on `examples/app-todo` with a preset-wired `SqlDriver({ connection: { connectionString } })`: `→ (unknown)` before, `→ postgres://127.0.0.1:59437/nope` after, with `admin:hunter2` gone. Display only — no configuration or API surface moves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E1DiRxoPdcTSGkzywm5zN4
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 18 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 28, 2026 07:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3793.
现象
driver 认出来了,地址没了 —— 而且恰好在数据库连不上、最需要看地址的时候没了。
根因
describeRegisteredDriver认识config.connection四种形状里的三种(DSN 字符串、{ filename }、离散{ host, port, database }),唯独不认{ connectionString }—— 也就是defaultDatasourceDriverFactory为「用 DSN 声明的 pg datasource」产出的那一种。driver.config保持作者传入的原形状(driver-sql 有测试钉住),所以正如 issue 里说的,#3791 既不是起因也没让它变严重。顺着查出同一个根因的另外两处
(unknown):那个 reader 只要 driver 有config就直接 return,于是只有 SqlDriver 能成功。config.url,而MongoDBDriver恰好有config—— 下面那条「mongo/turso 把 URL 挂在实例上」的分支根本到不了,每次 mongo 启动横幅都是(unknown)。config在没传参时是{},truthy ——(in-memory)那条分支同样到不了,preset 装配的 memory driver 显示成com.objectstack.driver.memory → (unknown)。还有一个同处的潜伏 bug:
SqliteWasmDriver传给 knex 的client是类而不是字符串,SqlDriver(${cfg.client})会把整个类源码贴进横幅。处置
按 issue 的建议,两件事收进一个 renderer ——
packages/cli/src/utils/connection-display.ts:describeDriverConnection(config)认全部四种connection形状,外加 mongo 系的{ uri }/{ url }拼法;同一套字段优先级也应用到顶层地址上(覆盖 MongoDBDriver)。对函数形式的connection(host 每次取连接时现造)返回undefined而不是瞎猜。redactConnectionUrl(value)直接丢弃凭据而不是打码。原来的//user:****@掩码漏了两处:没有用户名的密码(postgres://:secret@host/db)不匹配那个正则,原样打印;Turso 的 DSN 把 secret 放在 query string 里(?authToken=…),从来没被碰过。现在 userinfo 和 query 一起丢掉;非 URL 的值(:memory:、sqlite 路径、(in-memory))原样透传;函数幂等。serve/start/dev里三份逐字节相同的旧掩码,加上schema-migrate里的第四个变体,一起收敛到这个 helper。schema-migrate那份有同样的 DSN 盲区,但后果更重:它喂的是Apply N change(s) to …?这个确认提示,{ connectionString }配置在那里渲染成一个光秃秃的pg—— 等于让操作者对着一个没名字的数据库批准破坏性迁移。变更清单
packages/cli/src/utils/connection-display.tsredactConnectionUrl+describeDriverConnectionpackages/cli/src/commands/serve.tsdescribeRegisteredDriver按形状读地址(不再「有 config 就 return」);label 只在client是字符串时插值;导出以便测试packages/cli/src/commands/start.ts/dev.tsredactDbUrl副本packages/cli/src/utils/schema-migrate.tsdescribeDb复用同一个 renderer;删掉redactUrl.changeset/banner-dsn-connection-display.md验证
新增单测 26 个(
connection-display.test.ts+serve-driver-banner.test.ts),CLI 全量vitest run72 files / 734 tests 全绿;tsc -p tsconfig.build.json+eslint src干净。端到端跑通 issue 的复现路径:在
examples/app-todo里用 preset 装配一个SqlDriver({ connection: { connectionString: 'postgres://admin:hunter2@127.0.0.1:59437/nope' } }),OS_ALLOW_DRIVER_CONNECT_FAILURE=1 os serve,同一份 app、同一条命令:SqlDriver(pg) → (unknown)SqlDriver(pg) → postgres://127.0.0.1:59437/nopeadmin:hunter2不见了。另外单独跑了OS_DATABASE_URL那条路径(走createStorageDriver,不走 probe),横幅同样输出脱敏后的地址。注意
纯展示层改动,不动任何配置或 API surface。横幅/提示的输出串本身有变化:
postgres://admin:hunter2@db:5432/app原来显示为postgres://admin:****@db:5432/app,现在是postgres://db:5432/app。schema-migrate的提示也随之统一:sqlite 从sqlite:./a.db变成./a.db,离散字段的 pg 从pg://host/db变成host:5432/db(DSN 情形保留 scheme)。Generated by Claude Code