Skip to content

[security] GET /api/v1/meta/datasources still serves stored cleartext credentials — the metadata read path has no per-type redaction hook #8154

Description

@huangyiirene

Blocked-by: #8300

⚠️Scope grew on measurement — read the PM review (comment 5275779695) before implementing. Three rulings are settled there and are ⛔ not re-litigable: the fix is one PR carrying the hook, the datasource redactor, the read-exit application, the getMetaItemLayered exit and the saveMetaItem write-path carry-forward; the registry seam itself is split out to #8300 (packages/spec, another lane) and this card consumes it. Second, non-dependency gate: #8136 must land first (protocol.ts serialisation).

The read scrub may NOT ship alone. Measured, not argued: saveMetaItemaccepts a redacted datasource body and persists the credential away, so a read-path redaction without the write-path inverse converts today's loud 422 into silent credential deletion on an ordinary GET-edit-PUT round trip.


Split out of #8081 by the domain:services PM seat. The implementing dev hit this, stopped, and reported it rather than fixing it, per the dispatch order's STOP rule — the fix leaves the services lane. ⛔ Not graded by me; no domain:* label.

What is still open after PR #8126

PR #8126 stopped DatasourceAdminService.getDatasource() serving stored credentials. One door over, the platform still does.

decorateMetadataItem returns {...item, _diagnostics} — the whole stored body. So after #8126:

  • GET /api/v1/datasources/:name → clean
  • GET /api/v1/meta/datasourcesstill serves the stored cleartext

#8078's own refusal text asserts this door exists, in as many words: "the datasource is persisted whole into sys_metadata, which is served back by the ordinary data API."

Re-measured on origin/maina7e94e990 and CONFIRMED, on three exits, not one: a legacy datasource row is served with config.password: "hunter2"and the password embedded in config.url by getMetaItems (the door this card names), by getMetaItem, and by getMetaItemLayered in both its overlay and effective layers.

⚠️ This bounds what #8081 item 1 is allowed to claim

The honest statement after #8126 is: the datasource-admin read path no longer serves stored credentials. It is not "the surface is closed." The dev said so unprompted, which is the reason this card exists rather than a false sense of closure:

I would rather you hear that now than read "item 1 done" and believe the surface is closed.

Recording it prominently because this is the third time in this programme that a partially-closed credential surface read as fully closed — #7799#7986 (webhook headers), #7986 ①-a → #8118 (delivery rows), and now #8126 → this card. The pattern is the same each time: a fix lands on the door that was measured, and the reading generalises past what was measured.

Why it needs its own lane

  • The surface is packages/metadata-protocol / packages/rest, not service-datasource.
  • It needs a generic per-type redaction hook on the metadata read path — datasource is not the only type that could hold a secret, so a datasource-shaped patch here would be the narrow fix that leaves the next type exposed.
  • The blast radius is every metadata type, which is why it did not belong as a rider on a card scoped to one service.

Constraints for whoever takes it

Provenance: #8081 dev report (comment 5269923121, "A residual this PR does NOT close — STOP, leaves the lane") and my ACCEPT receipt (#8081 comment 5270102954). Related: #7990, #8078, #8081, #8118, #8300.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions