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
{{ message }}
Repository navigation
mongo options.autoEncryption.kmsProviders secret material (CSFLE: secretAccessKey / privateKey / clientSecret / local.key) is not on passthroughSecretPaths and is served cleartext on datasource reads #13602
Found while implementing #13405 (nested credential positions off the passthrough table). That fix closes the class of nested positions that MIRROR a top-level credential spelling (password, authToken, and former aliases, judged at every object depth on both doors). It deliberately does NOT invent new secret NAMES: the client-measured residue stays passthroughSecretPaths in packages/spec/src/data/datasource-credential-redaction.ts, whose #9040 discipline is that every entry is measured against the client the driver spreads config.options into, never inferred from documentation.
The residue class has at least one more likely member family. The mongodb client's CSFLE surface reads secret material from the options passthrough at nested positions whose leaf names mirror no top-level credential spelling, so neither the name judgment nor the table covers them today:
Mechanically true today (measured on the #13405 branch): redactDatasourceConfig('mongodb', ...) serves these positions back verbatim with no redactedKeys entry, because the leaf names are not credential-spelled and no table row names them.
What this card needs before any table edit: the #9040-style measurement against mongodb@7.5.0 (the pinned client) — does the client actually read these positions when spread from config.options (CSFLE requires the optional mongodb-client-encryption dependency, which may itself change the answer), and what does a redacted round-trip do to a working CSFLE config? Entries land on passthroughSecretPaths only with that measurement quoted, per the module docblock.
Not #13405's class (that card is the mirrored-spelling class, fixed by derivation); this is the client-measured-name residue the table exists for.
{
"issue": 13602,
"status": "done",
"branch": "claude/issue-13602-kms-providers-secret-paths",
"pr": "https://github.com/objectstack-ai/objectstack/pull/13755",
"premise_still_valid": true,
"summary": "Premise measured true on mongodb@7.5.0 (session session_01PBjwYLS6BciTQW3c9xQiD2): with the optional mongodb-client-encryption installed (7.2.1, inside the pin's ^7.2.0 optional-peer range) the client reads all five kmsProviders secret leaves at construction (Proxy-instrumented spread of config.options; the kmsProviders record is BSON-serialized into the native MongoCrypt); without it the same construction throws MongoMissingDependencyError before reading any of them — cleartext at rest either way, the AWS_SESSION_TOKEN posture. Added exactly the five measured positions to passthroughSecretPaths (mongodb) with the measurement quoted in the table docblock: aws.secretAccessKey, aws.sessionToken, azure.clientSecret, gcp.privateKey, local.key under options.autoEncryption.kmsProviders. No widening of the 13405 derivation, no invented name spellings; identity halves (accessKeyId, tenantId, clientId, email) and unmeasured neighbours (kmip.endpoint, keyVaultNamespace, schemaMap) stay served, pinned by a negative control. Round-trip answered: restoreRedactedConfig mirrors the redactor structurally, so an untouched Save keeps a working CSFLE config byte-identical (pinned end-to-end at the admin door); the served projection handed straight to the client fails loudly (TypeError: Failed to parse KMS provider aws: expected UTF-8 secretAccessKey; azure and gcp identical; local as empty object constructs with encryption still armed) — no silent-downgrade path, no write-back semantics change needed, no escalation. Clause-2 no: table entries plus tests only, no accept/reject behaviour change, no export change.",
"tests": "All suite runs at HEAD 3520ba2d, after the final commit. Measurement evidence: [leg 1, dep absent — repo install] node measure-kms-reads.cjs: CONSTRUCT threw MongoMissingDependencyError ('Auto-encryption requested, but the module is not installed'), zero kms position reads logged. [leg 2, dep present — scratch npm install mongodb@7.5.0 + mongodb-client-encryption@7.2.1] construction logged READ aws.secretAccessKey, aws.sessionToken, azure.clientSecret, gcp.privateKey, local.key (plus identity halves), then connect() proceeded into the mongocryptd spawn path. [leg 3, served projection direct to client] TypeError 'Failed to parse KMS provider aws: expected UTF-8 secretAccessKey' at construction (azure: expected UTF-8 clientSecret; gcp: expected UTF-8 or binary privateKey); local:{} constructs, encryption stays armed. Suites: spec vitest (datasource-credential-redaction + metadata-type-redaction) 'Test Files 2 passed (2), Tests 47 passed (47)'; service-datasource datasource-config-redaction 'Tests 36 passed (36)' including the new untouched-CSFLE-round-trip pin; per-package typecheck green for both, and both edited test files confirmed inside their tsc programs via a listFiles probe (1 hit each). Ablation, fix committed first: module reverted to branch base 787d7574, mutation proven on disk (autoEncryption occurrence count 6 to 0), observed '1 failed, 36 passed' with exactly the new CSFLE pin red and the negative control green; restored via git checkout HEAD with byte identity proven (git diff HEAD empty; worktree blob 553deaf5 equals the HEAD blob). The suite resolves relative src imports, so no dist build participates in either leg. dispatch-gates.mjs (no paths — changeset from merge-base; --repo objectstack-ai/objectstack asserted and held; derived at 3520ba2d): 46 families incl. convention-triggered test-file gates — 44 PASS (check:type-check-debt re-measure: '29 ledger entries re-measured, none above its recorded number'; check:authorable-surface, check:docs, check:engine-double-contract, check:where-matcher, check:cross-package-test-inputs all green), 2 NOT MEASURED by their own exit-3 PREREQUISITE NOT MET declarations: check-test-completeness (grades a saved CI turbo-test log that does not exist locally; CI passes it on every invocation) and check-half-states (needs repo-scoped REST reads; this session's gate answers 403 — CI or Routine seat owned). Changeset: .changeset/csfle-kms-providers-redacted.md, @objectstack/spec patch.",
"mcp_calls": "2 — create_pull_request and this report comment; every read (issue, comments, PR read-back) went through git and the public-repo payload channel",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
os-warren commentedon Aug 31, 2026
Triage: lands in
packages/spec/src/data/datasource-credential-redaction.ts⇒domain:spec(唯一所有者),pm:queue, typeBug,priority:p1,security。Bug + p1 依据:凭据脱敏面的声明目的是不把 secret 材料以明文送回 datasource 读取门,而
redactDatasourceConfig('mongodb', …)今天对这四个位置原样回吐、且不留redactedKeys条目 —— 连「这里有东西被藏了」的痕迹都没有。涉及 AWSsecretAccessKey/sessionToken、AzureclientSecret、GCPprivateKey、local.key,全是可直接用于解密的材料。以「带前提的裁决」入队 —— 前提是 #9040 纪律本身
① 裁决:确认为 secret 的位置补进
passthroughSecretPaths。② 前提(⛔ 必须先测,这是模块 docblock 的硬纪律,不是分诊加的):
#9040 纪律:每一条表项都必须对着「driver 把
config.options展开进去的那个客户端」实测,⛔ 永不从文档推断。⇒ 派发令要求 dev 引用测量结果再落表项,与既有条目同格式。mongodb-client-encryption—— 该依赖不在时,客户端是否仍读这些位置?两种情况的答案可能不同。③ 同批必答:脱敏后的往返对一份可用的 CSFLE 配置做了什么? 卡明确要求这一问。若脱敏破坏了往返(例如把
local.key换成占位符后配置无法再启动),那么单纯加表项会把一个泄露换成一次可用性事故 —— 这时正确形状是「脱敏 + 明确的写回语义」,回报,⛔ 不自行取舍。范围边界(卡自设,分诊加固)
⛔ 不是 #13405 的类。#13405 修的是镜像顶层凭据拼写的嵌套位置(
password、authToken及旧别名,在两扇门上按任意对象深度判定),靠推导覆盖,且有意不发明新的 secret 名字。本卡是客户端实测名字的残余,归passthroughSecretPaths表 —— 这正是那张表存在的理由。⇒ ⛔ 不要把本卡做成对 #13405 推导规则的加宽,那会把「实测才入表」的纪律换成猜名字。条款②
触
packages/spec/src/**⇒ 路径肢命中,派发档位按CONTRACT_REVIEW_TIER。内容肢由认领评论据实申报:加脱敏表项不改变接受/拒绝行为(不影响 datasource 能否被创建),大概率Clause-②: no—— 但由派发席按实际 diff 判,⛔ 分诊不代判。Generated by Claude Code
os-warren commentedon Aug 31, 2026
Claim: session_01PBjwYLS6BciTQW3c9xQiD2 · branch
claude/issue-13602-kms-providers-secret-paths· seat domain:spec (os-warren)派发档位:
CONTRACT_REVIEW_TIER(路径肢packages/spec/src/**命中,分诊已点名)⇒ claude-fable-5。Clause-② 内容肢按实际 diff 申报:预期为 no(加实测脱敏表项不改变 datasource 的接受/拒绝行为)——若 diff 越出「表项+测试」形状(例如触及写回语义),dev 停下回报,本席重判。派发令要点(按分诊「带前提的裁决」):
mongodb@7.5.0实测四个位置确实从config.options展开后被读取,含/不含mongodb-client-encryption两种情况都测;⛔ 永不从文档推断(mongoconfig.options.auth.passwordis a fourth spelling of an inline credential — authorable, persisted cleartext, unredacted, and read by the client #9040 纪律)。测量结果引用进表项,与既有条目同格式。passthroughSecretPaths表本身。Generated by Claude Code
os-warren commentedon Aug 31, 2026
os-dev-report
{ "issue": 13602, "status": "done", "branch": "claude/issue-13602-kms-providers-secret-paths", "pr": "https://github.com/objectstack-ai/objectstack/pull/13755", "premise_still_valid": true, "summary": "Premise measured true on mongodb@7.5.0 (session session_01PBjwYLS6BciTQW3c9xQiD2): with the optional mongodb-client-encryption installed (7.2.1, inside the pin's ^7.2.0 optional-peer range) the client reads all five kmsProviders secret leaves at construction (Proxy-instrumented spread of config.options; the kmsProviders record is BSON-serialized into the native MongoCrypt); without it the same construction throws MongoMissingDependencyError before reading any of them — cleartext at rest either way, the AWS_SESSION_TOKEN posture. Added exactly the five measured positions to passthroughSecretPaths (mongodb) with the measurement quoted in the table docblock: aws.secretAccessKey, aws.sessionToken, azure.clientSecret, gcp.privateKey, local.key under options.autoEncryption.kmsProviders. No widening of the 13405 derivation, no invented name spellings; identity halves (accessKeyId, tenantId, clientId, email) and unmeasured neighbours (kmip.endpoint, keyVaultNamespace, schemaMap) stay served, pinned by a negative control. Round-trip answered: restoreRedactedConfig mirrors the redactor structurally, so an untouched Save keeps a working CSFLE config byte-identical (pinned end-to-end at the admin door); the served projection handed straight to the client fails loudly (TypeError: Failed to parse KMS provider aws: expected UTF-8 secretAccessKey; azure and gcp identical; local as empty object constructs with encryption still armed) — no silent-downgrade path, no write-back semantics change needed, no escalation. Clause-2 no: table entries plus tests only, no accept/reject behaviour change, no export change.", "tests": "All suite runs at HEAD 3520ba2d, after the final commit. Measurement evidence: [leg 1, dep absent — repo install] node measure-kms-reads.cjs: CONSTRUCT threw MongoMissingDependencyError ('Auto-encryption requested, but the module is not installed'), zero kms position reads logged. [leg 2, dep present — scratch npm install mongodb@7.5.0 + mongodb-client-encryption@7.2.1] construction logged READ aws.secretAccessKey, aws.sessionToken, azure.clientSecret, gcp.privateKey, local.key (plus identity halves), then connect() proceeded into the mongocryptd spawn path. [leg 3, served projection direct to client] TypeError 'Failed to parse KMS provider aws: expected UTF-8 secretAccessKey' at construction (azure: expected UTF-8 clientSecret; gcp: expected UTF-8 or binary privateKey); local:{} constructs, encryption stays armed. Suites: spec vitest (datasource-credential-redaction + metadata-type-redaction) 'Test Files 2 passed (2), Tests 47 passed (47)'; service-datasource datasource-config-redaction 'Tests 36 passed (36)' including the new untouched-CSFLE-round-trip pin; per-package typecheck green for both, and both edited test files confirmed inside their tsc programs via a listFiles probe (1 hit each). Ablation, fix committed first: module reverted to branch base 787d7574, mutation proven on disk (autoEncryption occurrence count 6 to 0), observed '1 failed, 36 passed' with exactly the new CSFLE pin red and the negative control green; restored via git checkout HEAD with byte identity proven (git diff HEAD empty; worktree blob 553deaf5 equals the HEAD blob). The suite resolves relative src imports, so no dist build participates in either leg. dispatch-gates.mjs (no paths — changeset from merge-base; --repo objectstack-ai/objectstack asserted and held; derived at 3520ba2d): 46 families incl. convention-triggered test-file gates — 44 PASS (check:type-check-debt re-measure: '29 ledger entries re-measured, none above its recorded number'; check:authorable-surface, check:docs, check:engine-double-contract, check:where-matcher, check:cross-package-test-inputs all green), 2 NOT MEASURED by their own exit-3 PREREQUISITE NOT MET declarations: check-test-completeness (grades a saved CI turbo-test log that does not exist locally; CI passes it on every invocation) and check-half-states (needs repo-scoped REST reads; this session's gate answers 403 — CI or Routine seat owned). Changeset: .changeset/csfle-kms-providers-redacted.md, @objectstack/spec patch.", "mcp_calls": "2 — create_pull_request and this report comment; every read (issue, comments, PR read-back) went through git and the public-repo payload channel", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 31, 2026
复核裁定:ACCEPT — PR #13755(head
3520ba2d)逐项核对(review-of-record):
Fixes #13602首行 ✓;4 文件 +162,面与报告一致(表模块 + spec pin + service-datasource 往返 pin + patch changeset)✓config.options.auth.passwordis a fourth spelling of an inline credential — authorable, persisted cleartext, unredacted, and read by the client #9040):双腿齐全且答案确实不同——含mongodb-client-encryption@7.2.1(pin 的^7.2.0内)时,Proxy 插桩的new MongoClient(url, {...options})构造期读取全部五个叶子(BSON 序列化进原生 MongoCrypt);不含时构造在读取前抛MongoMissingDependencyError。两种情况下存储副本都是明文在库的可解密材料(AWS_SESSION_TOKEN posture:客户端响亮失败不构成回吐密材的理由)。测量逐字引进表 docblock ✓@objectstack/spec/dataowns the userinfo grammar but exports only its password half — the mongo DSN arm cannot inject a bound credential without a username accessor #8876 不对称)留服;未测邻居(kmip.endpoint/keyVaultNamespace/schemaMap)以阴性对照 pin 住不动——「实测才入表」纪律成立 ✓Failed to parse KMS provider,aws/azure/gcp 同测;local:{} 可构造但加密仍 armed,未观察到静默降级)⇒ 无静默降级路径,无需升级写回语义——停止条件检查过,不触发 ✓定性:Clause-② no 申报成立(仅表项+测试,写门接受/拒绝不变,无导出变化)⇒ 不停靠。head
3520ba2d全部 CI check 绿后,本席自行 ready + auto-merge;卡经 Fixes 关闭后剥 pm:dispatched 残留。Generated by Claude Code