Skip to content

fix: honor and validate source fields set alongside a DSN (#334)#337

Merged
tianzhou merged 5 commits into
mainfrom
fix/dsn-field-conflicts-334
Jun 23, 2026
Merged

fix: honor and validate source fields set alongside a DSN (#334)#337
tianzhou merged 5 commits into
mainfrom
fix/dsn-field-conflicts-334

Conversation

@tianzhou

Copy link
Copy Markdown
Member

Problem

Closes #334.

When a TOML source specified a dsn, buildDSNFromSource() returned the DSN verbatim — so any field also set on the source was silently ignored at connection time, while still appearing in API/metadata. The reporter set sslmode = "require" next to a dsn (the pattern the docs recommend) and it did nothing; their DB rejected the non-SSL connection.

Reviewing docs/config/toml.mdx, the same footgun applied to every field that can also be encoded in a DSN, not just sslmode:

Bucket Fields Behavior with dsn
Identity (DSN alternative) type, host, port, database, user, password Must not contradict the DSN
Dual-home (TOML field or DSN query param) sslmode, sslrootcert, instanceName, authentication, domain Merge if DSN lacks it; error if it contradicts
TOML-only (never in DSN) connection_timeout, query_timeout, lazy, search_path, timezone, ssh_*, … Always safe — untouched

Changes

  • mergeSourceFieldsIntoDSN() — when a dsn is present, merge the dual-home fields into the DSN query string when absent, mirroring exactly what the connection-params path of buildDSNFromSource() produces. So dsn + sslmode = "require" (or instanceName = "ENV1", etc.) now actually affects the connection.
  • validateDSNFieldConflicts() — reject any identity or dual-home field that contradicts the DSN. The DSN wins at connection time, so a differing field is now a hard load-time error instead of a silent drop. Matching/absent values are fine. Password is compared against the DSN directly and the error never echoes the values.

Behavior change

The only user-visible change: a config that set an identity/dual-home field to a value different from the DSN used to silently ignore the field — it now errors at load time (fail-fast). Matching or DSN-only configs are unaffected, and the docs already describe the now-correct behavior (no doc change needed).

Tests

  • Updated the one stale test that encoded the old silent-mismatch behavior; split into reject-conflict + accept-matching.
  • Added conflict tests for host, user, database, password, instanceName, sslmode, plus merge tests for sslmode/sslrootcert/instanceName.
  • 122 tests pass in toml-loader.test.ts; full non-integration suite green apart from a pre-existing json-rpc-integration server-spawn flake that fails identically on main.

🤖 Generated with Claude Code

When a TOML source provided a `dsn`, buildDSNFromSource returned it
verbatim, so any field also set on the source (sslmode, sslrootcert,
instanceName, authentication, domain, and the connection identity
fields) was silently ignored at connection time while still appearing
in API/metadata. This was the footgun reported in #334 for sslmode.

- mergeSourceFieldsIntoDSN: merge dual-home fields (fields that can also
  be expressed as DSN query params) into the DSN when it lacks them,
  mirroring the connection-params query builder.
- validateDSNFieldConflicts: reject any identity or dual-home field that
  contradicts the DSN (matching/absent is fine). The DSN wins at
  connection time, so a differing field is now a hard load-time error
  instead of being silently dropped. Password is compared without
  echoing values.

Fixes #334

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings June 23, 2026 12:52
Note that fields which can also live in the DSN query string are merged
when the DSN omits them, and that a field contradicting the DSN is
rejected at startup instead of being silently ignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a configuration “footgun” in the TOML loader where supplying a dsn caused other source fields (notably “dual-home” DSN query params like sslmode) to be silently ignored at connection time, despite still appearing in config metadata. It now merges supported standalone fields into the DSN when missing and fails fast on contradictions.

Changes:

  • Add DSN-vs-field conflict validation to reject contradictory identity and dual-home fields at load time.
  • Merge dual-home fields (sslmode, sslrootcert, and SQL Server params) into an existing DSN when the DSN omits them.
  • Update/add TOML loader tests to cover conflict rejection and merge behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/config/toml-loader.ts Adds DSN conflict validation and merges dual-home fields into DSNs to ensure standalone TOML fields affect actual connections.
src/config/tests/toml-loader.test.ts Updates tests for the new fail-fast conflict behavior and verifies DSN merging behavior.

Comment thread src/config/toml-loader.ts Outdated
Comment thread src/config/toml-loader.ts
- Detect SQLite from the DSN's parsed type instead of the source.type
  field, and check the type conflict before short-circuiting. A
  `type = "sqlite"` paired with a non-SQLite DSN (or the reverse) is now
  rejected rather than silently skipped.
- Distinguish "DSN has no password" from "DSN has a different password"
  in the password-conflict error, without echoing either value.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread src/config/toml-loader.ts Outdated
Comment thread src/config/toml-loader.ts Outdated
Comment thread src/config/toml-loader.ts Outdated
Comment thread src/config/__tests__/toml-loader.test.ts
- Compare hostnames case-insensitively (hostnames are case-insensitive,
  so DB.EXAMPLE.COM and db.example.com are the same host).
- Avoid a "?&" sequence when merging into a DSN that already ends with a
  bare "?" (empty query string).
- Reword validateDSNFieldConflicts docstring to describe the general
  "user set both DSN and field" invariant instead of listing only the
  subset of fields copied by processSourceConfigs.
- Add merge tests for sslrootcert (postgres + verify-ca, with encoding),
  SQL Server authentication/domain, the verify-mode guard, and the bare
  "?" separator case.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/config/toml-loader.ts Outdated
Comment thread src/config/toml-loader.ts Outdated
SafeURL drops query params with empty values (e.g. ?sslmode=), so an
empty-but-present param looked absent — a conflicting field went
undetected and the merge step could append a duplicate, yielding an
ambiguous ?sslmode=&sslmode=require.

Add getRawDSNQueryParam() to read raw key presence (distinguishing
absent vs present-but-empty) and use it for both the dual-home conflict
checks and the merge presence checks.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@tianzhou tianzhou merged commit 14d8368 into main Jun 23, 2026
4 checks passed
@tianzhou tianzhou deleted the fix/dsn-field-conflicts-334 branch June 23, 2026 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configuring with a dsn attribute silently ignores sslmode field

2 participants