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
feat(spec): treat nested datasource-config credential positions identically to the top-level keys they mirror (#13604)
A credential under the very spelling the top level refuses and redacts -
one object level down (options.auth.token, options.pool.password,
tunnel.password on a contract-less driver) - was accepted at publish and
served by every datasource read door in cleartext with
redactedConfigKeys: []. The nested judgment was a hand-enumerated
per-driver path table on the read side and absent on the write side,
while the top level was derived from the driver contract.
Both sides now consume one derivation: the canonical spellings and
former aliases move to driver/common.zod.ts (CREDENTIAL_KEY_SPELLINGS,
the bottom of the import graph); the read scrub applies the name
judgment and the URL composite at every object depth for every driver;
the write door's passthrough walk refuses the same spellings at any
depth; refusedCredentialPaths walks nested object shapes for z.never
leaves; arrays stay off the walk on both doors (row-shaped data is not
config). passthroughSecretPaths remains only as the client-measured
residue. restoreRedactedConfig is now derived from the redactor's own
redactedPaths, so every current and future redaction source is mirrored
on the untouched-Save round trip by construction.
Semantic migration entry
datasource-config-options-nested-credential-spelling-refused (major 18)
carries the authored-artifact upgrade.
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
Co-authored-by: Claude <noreply@anthropic.com>
feat(spec): treat a nested datasource-config credential position identically to the top-level key it mirrors — derived at every depth, on both doors (#13405)
7
+
8
+
**BREAKING** accept-set narrowing, the nested closure of the #9040 family. A
9
+
credential under the very spelling the top level refuses and redacts — one
10
+
object level down (`options.auth.token`, `options.pool.password`, a
11
+
`tunnel.password` on a contract-less driver) — was accepted at publish and
12
+
served back by every datasource read door in cleartext with
13
+
`redactedConfigKeys: []`, because the read side's nested judgment was only the
14
+
hand-enumerated `passthroughSecretPaths` table and the write side had no nested
15
+
name judgment at all.
16
+
17
+
Both sides are now derived from ONE source instead of hand-maintained:
18
+
19
+
- The canonical credential spellings and former aliases moved to
20
+
`driver/common.zod.ts` (`CREDENTIAL_KEY_SPELLINGS`) — the bottom of the
21
+
driver-schema import graph — so the write door's passthrough walk and the
22
+
read redactor consume the same list (#8300's no-second-copy posture applied
23
+
to the list itself).
24
+
-**Read door** (`redactDatasourceConfig`, behind both consumers — the
25
+
datasource-admin routes and the kernel per-type redaction hook): the
26
+
credential-name judgment and the URL composite (userinfo + query params) now
27
+
run at EVERY object depth, for every driver, contract-less included. Nested
28
+
removals are reported as dotted paths in `redactedKeys`, plus a new
29
+
`redactedPaths` field carrying exact segments. `passthroughSecretPaths`
30
+
remains only as the residue it always should have been: CLIENT-MEASURED
31
+
secret spellings (`proxyPassword`, `key`, `passphrase`, …) that mirror no
32
+
top-level key.
33
+
-**Write door** (`credentialFreeMongoOptions`): a non-empty string under a
34
+
credential-spelled key is refused at any object depth of the mongodb
35
+
`options` passthrough, with a prescription that does not inherit the
36
+
`auth.password`-only "wins over" reassurance. The measured `auth.password`
37
+
refusal keeps its own message; nothing is double-reported.
38
+
-**Schema derivation walked at depth**: `refusedCredentialPaths` /
39
+
`refusedCredentialPathsOfSchema` extend the `z.never()` derivation below the
40
+
top level, so a driver contract that refuses a key inside a nested object
41
+
shape is covered the day it lands (none exists today — pinned per driver).
42
+
-**Arrays are off the walk** on both doors — the same structural line
43
+
`valueAtPath`/`withoutPath` already drew — so row-shaped data (memory's
44
+
`initialData` seeds) keeps its own fields without a per-driver exclusion
45
+
list.
46
+
-`restoreRedactedConfig` (service-datasource) is now DERIVED from the
47
+
redactor instead of mirroring it rule by rule: it grafts stored material
48
+
back wherever the patch is indistinguishable from what the read path served,
49
+
so an untouched "Save" on an affected legacy row keeps its stored material
50
+
for every current and future redaction source, and an author's edit always
51
+
wins. The metadata write door's generic `carryForwardRedactedValues` already
52
+
walks the dotted paths and needs no change.
53
+
54
+
Semantic migration entry
55
+
`datasource-config-options-nested-credential-spelling-refused` (protocol major
56
+
18) carries the authored-artifact upgrade: remove the nested key, or bind the
57
+
real secret through `external.credentialsRef` / the connection form.
|**username**|`string`| optional | Authentication user |
49
49
|**password**|`never`| optional | Set through the connection form's secret field or `external.credentialsRef` — encrypted into `sys_secret`, never stored in `config`|
|**options**|`Record<string, any>`| optional | Extra MongoClient options (replicaSet, tls, timeouts, …). Only `auth.password` is refused inline — bind it via the connection form / external.credentialsRef. `proxyPassword`, `tlsCertificateKeyFilePassword`, `key`, and `passphrase` are accepted and stored at rest in cleartext; they're redacted only when the datasource is read back, not refused at write. |
51
+
|**options**|`Record<string, any>`| optional | Extra MongoClient options (replicaSet, tls, timeouts, …). Credential-spelled keys (`password`, `authToken`, and their former aliases) are refused inline at any depth — bind the secret via the connection form / external.credentialsRef. `proxyPassword`, `tlsCertificateKeyFilePassword`, `key`, and `passphrase` are accepted and stored at rest in cleartext; they're redacted only when the datasource is read back, not refused at write. |
0 commit comments