Repository navigation
Commit 3ac22c9
fix(spec): the connector read withholds root keys the entry shape does not declare (#28764)
Branched from the head of #28688, which has since landed; `main` is
merged in, so the diff against `main` is this change alone.
Clause-②: no
Metadata-protocol: none
Skills-docs: none
## 维护者速读
- 改了什么:connector 元数据的读取脱敏(`redactConnectorItem`)现在整键扣下条目 schema
未声明的顶层键;`providerConfig` 下装有带凭据 URL 的数组也整体扣下。
- 为什么改:读取侧加固,与 #28688 的写入侧收紧配套,让所有读出口只返回条目 schema 声明过的顶层键。
- 风险与回滚:读出口少返回未声明的顶层键;被扣的键记入 `redactedKeys`,保存时由 carry-forward
原样保留在库里,运行时执行读不受影响。回滚即 revert 本 PR。
-
先看:`packages/spec/src/integration/connector-credential-redaction.ts`(`credentialPathsOf`
顶层键循环与 `credentialUrlRewritesOf` 数组分支)→
`packages/qa/dogfood/test/connector-meta-door-root-key-read.dogfood.test.ts`
→ `packages/metadata-protocol/src/protocol.metadata-redaction.test.ts`
末尾新块。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/integration/connector-credential-redaction.test.ts`,预期 41 个用例全绿。
- 需要你做的:无,席位落地(本 PR 待席位做 contract review)。
## What changes
Read-side hardening of the built-in `connector` redactor in
`@objectstack/spec`:
- **Undeclared top-level keys are withheld whole.** The declared set is
read from the closed entry schema itself
(`DeclarativeConnectorEntrySchema`'s read-through `shape`, built from
`connectorBaseShape`), never a hand-written list, so a key the entry
declares later is served with nothing to edit. The read decorations
(`METADATA_READ_DECORATIONS`: `_diagnostics`, `_draft`) are the
response's and are left alone; the ADR-0010 protection envelope is
declared in the shape and is served as before. A cleared value (`null`,
`''`, an empty map) is served as written and not reported, the module's
existing convention.
- **Each withheld key is named in `redactedKeys`**, so the protocol's
carry-forward keeps the stored value at rest on a save of the served
form. The save door order is unchanged: strip read decorations, then the
schema gate judges the author's body, then the carry-forward restores
the stored key.
- **Under `providerConfig`, an array that holds (at any depth) a URL
whose credential parts the URL rewrite would remove is withheld whole**,
the posture a non-object `auth` already has. An array holding no such
URL is served as stored. The previous pin that served such arrays is
flipped deliberately.
- Module docblock items 6 and 7 record both positions.
No public export changes; nothing an author can write changes.
## Tests
-
`packages/spec/src/integration/connector-credential-redaction.test.ts`:
withholds undeclared root keys on read (credential-shaped scalar,
several keys, nested container, plain key); the declared set equals the
entry schema's shape; read decorations untouched; cleared forms served;
combined positions each named once; the array pin flipped (array
withheld whole at any depth, a URL-free array served).
- `packages/metadata-protocol/src/protocol.metadata-redaction.test.ts`:
the item read, the list read (both spellings) and the layered read
withhold the keys; a save of the served form keeps them at rest; a key
the author types is refused with `INVALID_METADATA` / 422 and the row is
unchanged; identity-redactor ablation arm.
-
`packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts`:
the history diff, all three buckets, reports the path and serves neither
value; identity-redactor ablation arm.
-
`packages/qa/dogfood/test/connector-meta-door-root-key-read.dogfood.test.ts`:
the showcase's HTTP read doors (`/meta/connector/:name`, `/layers`,
`/meta/connector`, `/meta/connectors`, `/diff`) for a member and an
administrator, with the row seeded directly in storage; a save of the
served form keeps the keys at rest.
## Acceptance notes
- A top-level key spelled with a dot is withheld on read, but the
carry-forward addresses positions by dotted path and cannot address it,
so a save of the served form does not keep such a key at rest. Such a
key is undeclared, read by nothing at runtime, and refused by the closed
entry at every authoring door.
- The history diff keeps a withheld key's path in its bucket with the
value absent, the same posture the diff already has for every redacted
position (diff raw, emit redacted values).
- The branch started from the head of #28688 (the closed entry shape
this redaction reads its declared set from). That PR landed before this
one opened, and `origin/main` was merged in (a merge, no rewrite), so
this PR carries no stacked dependency.
## Verification
Every reading below is on head `acbd2ecbf3` (after merging `origin/main`
at `7a48bf73f5`).
<details><summary>Package suites and typecheck</summary>
- `pnpm --filter @objectstack/spec test`: 783 files, 23158 passed, 1
todo (measured on the pre-merge head `7e0c1cb4b1`); after the merge
`src/integration/`: 14 files, 261 passed.
- `pnpm --filter @objectstack/metadata-protocol test`: 291 files passed,
4 skipped; 21952 tests passed, 26 skipped (pre-merge head); after the
merge the two touched suites: 65 passed.
- Dogfood, the new file plus both connector-door siblings: 3 files, 25
passed (after the merge).
- `typecheck` exit 0 for `@objectstack/spec`,
`@objectstack/metadata-protocol` (after the merge) and
`@objectstack/dogfood`.
- `pnpm --filter @objectstack/spec check:generated`: all 14 generated
artifacts up to date.
</details>
<details><summary>Ablation (each fix, rebuilt and proved on
disk)</summary>
Done with `scripts/ablation-replace.mjs` (anchor hit 1 to 0, restore
proved: blob equals HEAD and `git diff HEAD` empty) and
`scripts/ablation-dist-preflight.mjs`.
| Fix ablated | Suite | Result while ablated |
|:---|:---|:---|
| root-key withholding | spec redaction suite (source) | 5 failed / 41 |
| root-key withholding | metadata-protocol suites (rebuilt `dist/`,
marker absent from all 588 built files) | 5 failed / 62: item, list,
layered, history diff, save keeps at rest |
| root-key withholding | dogfood (same rebuilt `dist/`) | 3 failed / 5:
item + layered, list, save + diff |
| array withholding under `providerConfig` | spec redaction suite
(source) | 1 failed / 41: the array test |
Restore leg: spec rebuilt, preflight reads the marker present in 4 built
files, metadata-protocol suites 62/62 and dogfood 5/5 green again.
</details>
<details><summary>Derived gate families</summary>
`node scripts/pm/dispatch-gates.mjs --commands` on the merged head
derives 80 families from the 6 changed paths; all 80 run locally, each
exit 0, and the `--ran` reconciliation reads 80 derived, 80 run, 0
NOT-MEASURED. Repo-wide scans (`pnpm lint` and the rest of the lint
workflow) are left to CI.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01AavokzJ5DndAwitDXvKy4U)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9a9823d commit 3ac22c9
7 files changed
Lines changed: 789 additions & 27 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- qa/dogfood/test
- spec/src/integration
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
516 | 516 | | |
517 | 517 | | |
518 | 518 | | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
519 | 554 | | |
520 | 555 | | |
521 | 556 | | |
| |||
535 | 570 | | |
536 | 571 | | |
537 | 572 | | |
538 | | - | |
539 | | - | |
540 | | - | |
541 | | - | |
542 | | - | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
543 | 582 | | |
544 | 583 | | |
545 | 584 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
331 | 331 | | |
332 | 332 | | |
333 | 333 | | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
0 commit comments